[Xfce4-commits] <libxfce4ui:nick/spawn-child-watch> Add new API for watching the child process.

Nick Schermer noreply at xfce.org
Wed Nov 4 15:54:03 CET 2009


Updating branch refs/heads/nick/spawn-child-watch
         to 5eccdbe947a9f2398ac45d0551dfcb383632156e (commit)
       from c5360220766505587f39bf7a861b19e764d64635 (commit)

commit 5eccdbe947a9f2398ac45d0551dfcb383632156e
Author: Nick Schermer <nick at xfce.org>
Date:   Wed Nov 4 15:47:22 2009 +0100

    Add new API for watching the child process.
    
    xfce_spawn_on_screen_watch:
    Same function as xfce_spawn_on_screen, but with callback
    functions for watching the child, like you normally would
    do with g_child_watch_add(_full), but since we already
    need a child watch for startup notification and 2 watches
    on the same pid are not possible, this is a good alternative.
    
    xfce_spawn_(un)ref:
    Functions for handling the ref count of the structure returned
    from xfce_spawn_on_screen_watch().

 configure.in.in               |    6 +-
 libxfce4ui/libxfce4ui.symbols |    4 +
 libxfce4ui/xfce-spawn.c       |  560 ++++++++++++++++++++++++++++++-----------
 libxfce4ui/xfce-spawn.h       |   54 +++--
 4 files changed, 454 insertions(+), 170 deletions(-)

diff --git a/configure.in.in b/configure.in.in
index 7158fba..e82ba23 100644
--- a/configure.in.in
+++ b/configure.in.in
@@ -106,8 +106,10 @@ dnl ***************************************
 dnl *** Check for standard header files ***
 dnl ***************************************
 AC_HEADER_STDC()
-AC_CHECK_HEADERS([errno.h fcntl.h limits.h locale.h math.h memory.h \
-                  signal.h stdarg.h stdlib.h string.h unistd.h])
+AC_CHECK_HEADERS([crt_externs.h errno.h fcntl.h limits.h locale.h math.h \
+                  memory.h signal.h stdarg.h stdlib.h string.h unistd.h])
+AC_CHECK_DECLS([environ])
+AC_CHECK_FUNCS([_NSGetEnviron])
 
 dnl ******************************
 dnl *** Check for i18n support ***
diff --git a/libxfce4ui/libxfce4ui.symbols b/libxfce4ui/libxfce4ui.symbols
index 304b589..c67b7b0 100644
--- a/libxfce4ui/libxfce4ui.symbols
+++ b/libxfce4ui/libxfce4ui.symbols
@@ -87,6 +87,10 @@ xfce_gtk_window_center_on_active_screen
 /* xfce-spawn functions */
 #if IN_HEADER(__XFCE_SPAWN_H__)
 #if IN_SOURCE(__XFCE_SPAWN_C__)
+xfce_spawn_on_screen_watch G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC
+xfce_spawn_ref
+xfce_spawn_unref
+xfce_spawn_set_callback
 xfce_spawn_on_screen
 xfce_spawn_command_line_on_screen
 #endif
diff --git a/libxfce4ui/xfce-spawn.c b/libxfce4ui/xfce-spawn.c
index 2ba5fc5..03751e3 100644
--- a/libxfce4ui/xfce-spawn.c
+++ b/libxfce4ui/xfce-spawn.c
@@ -29,6 +29,9 @@
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#ifdef HAVE_CRT_EXTERNS_H
+#include <crt_externs.h> /* for _NSGetEnviron */
+#endif
 
 #include <gdk/gdk.h>
 
@@ -46,52 +49,61 @@
 #include <libxfce4ui/libxfce4ui-private.h>
 #include <libxfce4ui/libxfce4ui-alias.h>
 
-/* the maximum time for an application to startup */
-#define XFCE_SPAWN_STARTUP_TIMEOUT (30 * 1000)
+#ifdef HAVE__NSGETENVIRON
+/* for support under apple/darwin */
+#define environ (*_NSGetEnviron())
+#elif !HAVE_DECL_ENVIRON
+/* try extern if environ is not defined in unistd.h */
+extern gchar **environ;
+#endif
 
+/* the maximum time (seconds) for an application to startup */
+#define XFCE_SPAWN_STARTUP_TIMEOUT (30)
 
 
-#ifdef HAVE_LIBSTARTUP_NOTIFICATION
-typedef struct
+
+struct _XfceSpawn
 {
+#ifdef HAVE_LIBSTARTUP_NOTIFICATION
+  /* startup notification data */
   SnLauncherContext *sn_launcher;
   guint              timeout_id;
+#endif
+
+  /* child watch information */
   guint              watch_id;
   GPid               pid;
-} XfceSpawnData;
 
+  /* child watch functions from the user */
+  GChildWatchFunc    cw_function;
+  gpointer           cw_data;
+  GDestroyNotify     cw_notify;
 
+  /* ref count of the structure, 0 means we
+   * can free directly, otherwise we wait for an unref */
+  gint               ref_count;
+};
 
 
+
+#ifdef HAVE_LIBSTARTUP_NOTIFICATION
 static gboolean
 xfce_spawn_startup_timeout (gpointer user_data)
 {
-  XfceSpawnData *spawn_data = user_data;
-  GTimeVal       now;
-  gdouble        elapsed;
-  glong          tv_sec;
-  glong          tv_usec;
+  XfceSpawn *spawn = user_data;
+  GTimeVal   now;
+  gdouble    elapsed;
+  glong      tv_sec;
+  glong      tv_usec;
 
-  GDK_THREADS_ENTER ();
+  _libxfce4ui_return_val_if_fail (spawn->sn_launcher != NULL, FALSE);
 
   /* determine the amount of elapsed time */
   g_get_current_time (&now);
-  sn_launcher_context_get_last_active_time (spawn_data->sn_launcher, &tv_sec, &tv_usec);
-  elapsed = (((gdouble) now.tv_sec - tv_sec) * G_USEC_PER_SEC + (now.tv_usec - tv_usec)) / 1000.0;
+  sn_launcher_context_get_last_active_time (spawn->sn_launcher, &tv_sec, &tv_usec);
+  elapsed = now.tv_sec - tv_sec + ((gdouble) (now.tv_usec - tv_usec) / G_USEC_PER_SEC);
 
-  /* check if the timeout was reached */
-  if (elapsed >= XFCE_SPAWN_STARTUP_TIMEOUT)
-    {
-      /* abort the startup notification */
-      sn_launcher_context_complete (spawn_data->sn_launcher);
-      sn_launcher_context_unref (spawn_data->sn_launcher);
-      spawn_data->sn_launcher = NULL;
-    }
-
-  GDK_THREADS_LEAVE ();
-
-  /* keep the startup timeout if not elapsed */
-  return (elapsed < XFCE_SPAWN_STARTUP_TIMEOUT);
+  return elapsed < XFCE_SPAWN_STARTUP_TIMEOUT;
 }
 
 
@@ -99,23 +111,28 @@ xfce_spawn_startup_timeout (gpointer user_data)
 static void
 xfce_spawn_startup_timeout_destroy (gpointer user_data)
 {
-  XfceSpawnData *spawn_data = user_data;
+  XfceSpawn *spawn = user_data;
+  GPid       pid;
 
-  g_return_if_fail (spawn_data->sn_launcher == NULL);
+  spawn->timeout_id = 0;
 
-  /* cancel the watch (if any) */
-  if (spawn_data->watch_id != 0)
-    g_source_remove (spawn_data->watch_id);
-
-  /* make sure we don't leave zombies */
-  g_child_watch_add_full (G_PRIORITY_LOW, spawn_data->pid,
-                          (GChildWatchFunc) g_spawn_close_pid,
-                          NULL, NULL);
+  if (G_LIKELY (spawn->sn_launcher != NULL))
+    {
+      /* abort the startup notification */
+      sn_launcher_context_complete (spawn->sn_launcher);
+      sn_launcher_context_unref (spawn->sn_launcher);
+      spawn->sn_launcher = NULL;
+    }
 
-  /* release the startup data */
-  g_slice_free (XfceSpawnData, spawn_data);
+  if (spawn->watch_id != 0 && spawn->ref_count == 0)
+    {
+      /* free spawn data and add watch to close pid so we don't leave zombies */
+      pid = spawn->pid;
+      g_source_remove (spawn->watch_id);
+      g_child_watch_add (pid, (GChildWatchFunc) g_spawn_close_pid, NULL);
+    }
 }
-
+#endif
 
 
 static void
@@ -123,26 +140,43 @@ xfce_spawn_startup_watch (GPid     pid,
                           gint     status,
                           gpointer user_data)
 {
-  XfceSpawnData *spawn_data = user_data;
+  XfceSpawn *spawn = user_data;
 
-  g_return_if_fail (spawn_data->sn_launcher != NULL);
-  g_return_if_fail (spawn_data->watch_id != 0);
-  g_return_if_fail (spawn_data->pid == pid);
+  _libxfce4ui_return_if_fail (spawn->pid == pid);
 
-  /* abort the startup notification (application exited) */
-  sn_launcher_context_complete (spawn_data->sn_launcher);
-  sn_launcher_context_unref (spawn_data->sn_launcher);
-  spawn_data->sn_launcher = NULL;
+  if (spawn->cw_function != NULL)
+    spawn->cw_function (pid, status, spawn->cw_data);
 
-  /* cancel the startup notification timeout */
-  g_source_remove (spawn_data->timeout_id);
+  g_spawn_close_pid (pid);
 }
 
 
 
+static void
+xfce_spawn_startup_watch_destroy (gpointer user_data)
+{
+  XfceSpawn *spawn = user_data;
+
+  spawn->watch_id = 0;
+
+#ifdef HAVE_LIBSTARTUP_NOTIFICATION
+  if (spawn->timeout_id != 0)
+    g_source_remove (spawn->timeout_id);
+#endif
+
+  if (spawn->cw_notify != NULL)
+    spawn->cw_notify (spawn->cw_data);
+  else if (spawn->ref_count == 0)
+    g_slice_free (XfceSpawn, spawn);
+}
+
+
+
+#ifdef HAVE_LIBSTARTUP_NOTIFICATION
 static gint
 xfce_spawn_get_active_workspace_number (GdkScreen *screen)
 {
+#ifdef GDK_WINDOWING_X11
   GdkWindow *root;
   gulong     bytes_after_ret = 0;
   gulong     nitems_ret = 0;
@@ -189,93 +223,62 @@ xfce_spawn_get_active_workspace_number (GdkScreen *screen)
   gdk_error_trap_pop ();
 
   return ws_num;
+#else
+  /* dunno what to do on non-X11 window systems */
+  return 0;
+#endif
 }
 #endif
 
 
 
-/**
- * xfce_spawn_on_screen:
- * @screen            : a #GdkScreen or %NULL to use the active screen,
- *                      see xfce_gdk_screen_get_active().
- * @working_directory : child's current working directory or %NULL to
- *                      inherit parent's.
- * @argv              : child's argument vector.
- * @envp              : child's environment vector or %NULL to inherit
- *                      parent's.
- * @flags             : flags from #GSpawnFlags.
- * @startup_notify    : whether to use startup notification.
- * @startup_timestamp : the timestamp to pass to startup notification, use
- *                      the event time here if possible to make focus
- *                      stealing prevention work property. If you don't
- *                      have direct access to the event time you could use
- *                      gtk_get_current_event_time() or if nothing is
- *                      available 0 is valid too.
- * @icon_name         : application icon or %NULL.
- * @error             : return location for errors or %NULL.
- *
- * Like gdk_spawn_on_screen(), but also supports startup notification
- * (if Libxfce4ui was built with startup notification support).
- *
- * Return value: %TRUE on success, %FALSE if @error is set.
- **/
-gboolean
-xfce_spawn_on_screen (GdkScreen    *screen,
-                      const gchar  *working_directory,
-                      gchar       **argv,
-                      gchar       **envp,
-                      GSpawnFlags   flags,
-                      gboolean      startup_notify,
-                      guint32       startup_timestamp,
-                      const gchar  *icon_name,
-                      GError      **error)
+static gboolean
+xfce_spawn_internal (GdkScreen        *screen,
+                     const gchar      *working_directory,
+                     gchar           **argv,
+                     gchar           **envp,
+                     GSpawnFlags       flags,
+                     gboolean          startup_notify,
+                     guint32           startup_timestamp,
+                     const gchar      *startup_icon_name,
+                     GChildWatchFunc   watch_function,
+                     gpointer          watch_data,
+                     GDestroyNotify    watch_notify,
+                     XfceSpawn       **spawn_return,
+                     GError          **error)
 {
-  gboolean           succeed;
-  gchar            **cenvp;
-  gint               n;
-  gint               n_cenvp;
-  gchar             *display_name;
-  GPid               pid;
+  guint               n;
+  guint               n_cenvp;
+  gchar             **cenvp;
+  gchar              *display_name;
+  GPid                pid;
+  gboolean            succeed;
+  XfceSpawn          *spawn;
 #ifdef HAVE_LIBSTARTUP_NOTIFICATION
-  SnLauncherContext *sn_launcher = NULL;
-  XfceSpawnData     *spawn_data;
-  SnDisplay         *sn_display = NULL;
-  gint               sn_workspace;
+  SnLauncherContext  *sn_launcher = NULL;
+  SnDisplay          *sn_display = NULL;
+  gint                sn_workspace;
+  const gchar        *prgname;
 #endif
 
   g_return_val_if_fail (screen == NULL || GDK_IS_SCREEN (screen), FALSE);
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
 
-  /* lookup the screen with the pointer */
-  if (screen == NULL)
+  /* use the screen that currently contains the pointer */
+  if (G_UNLIKELY (screen == NULL))
     screen = xfce_gdk_screen_get_active (NULL);
 
-  /* setup the child environment, without startup id and display */
+  /* setup the child environment (stripping $DESKTOP_STARTUP_ID and $DISPLAY) */
   if (G_LIKELY (envp == NULL))
-    {
-      /* use the portable g_listenv, but note that this function only returns the
-       * variable names, not with values, therefore the call to g_getenv */
-      envp = g_listenv ();
-      cenvp = g_new0 (gchar *, g_strv_length (envp) + 3);
-      for (n = n_cenvp = 0; envp[n] != NULL; n++)
-        if (strcmp (envp[n], "DESKTOP_STARTUP_ID") != 0
-            && strcmp (envp[n], "DISPLAY") != 0)
-          cenvp[n_cenvp++] = g_strconcat (envp[n], "=", g_getenv (envp[n]), NULL);
-
-      /* cleanup */
-      g_strfreev (envp);
-      envp = NULL;
-    }
-  else
-    {
-      cenvp = g_new0 (gchar *, g_strv_length (envp) + 3);
-      for (n = n_cenvp = 0; envp[n] != NULL; n++)
-        if (strncmp (envp[n], "DESKTOP_STARTUP_ID", 18) != 0
-            && strncmp (envp[n], "DISPLAY", 7) != 0)
-          cenvp[n_cenvp++] = g_strdup (envp[n]);
-    }
-
-  /* add the real display name */
+    envp = (gchar **) environ;
+  for (n = 0; envp[n] != NULL; ++n);
+  cenvp = g_new0 (gchar *, n + 3);
+  for (n_cenvp = n = 0; envp[n] != NULL; ++n)
+    if (strncmp (envp[n], "DESKTOP_STARTUP_ID", 18) != 0
+        && strncmp (envp[n], "DISPLAY", 7) != 0)
+      cenvp[n_cenvp++] = g_strdup (envp[n]);
+
+  /* add the real display name for the screen */
   display_name = gdk_screen_make_display_name (screen);
   cenvp[n_cenvp++] = g_strconcat ("DISPLAY=", display_name, NULL);
   g_free (display_name);
@@ -290,16 +293,28 @@ xfce_spawn_on_screen (GdkScreen    *screen,
 
       if (G_LIKELY (sn_display != NULL))
         {
+          /* create a new sn context */
           sn_launcher = sn_launcher_context_new (sn_display, GDK_SCREEN_XNUMBER (screen));
 
-          if (G_LIKELY (sn_launcher != NULL && !sn_launcher_context_get_initiated (sn_launcher)))
+          if (G_LIKELY (sn_launcher != NULL))
             {
               /* initiate the sn launcher context */
               sn_workspace = xfce_spawn_get_active_workspace_number (screen);
-              sn_launcher_context_set_binary_name (sn_launcher, argv[0]);
               sn_launcher_context_set_workspace (sn_launcher, sn_workspace);
-              sn_launcher_context_set_icon_name (sn_launcher, (icon_name != NULL) ? icon_name : "applications-other");
-              sn_launcher_context_initiate (sn_launcher, g_get_prgname (), argv[0], startup_timestamp);
+
+              sn_launcher_context_set_binary_name (sn_launcher, argv[0]);
+
+              if (startup_icon_name == NULL)
+                startup_icon_name = "applications-other";
+              sn_launcher_context_set_icon_name (sn_launcher, startup_icon_name);
+
+              if (G_LIKELY (!sn_launcher_context_get_initiated (sn_launcher)))
+                {
+                  prgname = g_get_prgname ();
+                  if (prgname == NULL)
+                    prgname = "unknown";
+                  sn_launcher_context_initiate (sn_launcher, prgname, argv[0], startup_timestamp);
+                }
 
               /* add the real startup id to the child environment */
               cenvp[n_cenvp++] = g_strconcat ("DESKTOP_STARTUP_ID=", sn_launcher_context_get_startup_id (sn_launcher), NULL);
@@ -314,46 +329,278 @@ xfce_spawn_on_screen (GdkScreen    *screen,
   /* try to spawn the new process */
   succeed = g_spawn_async (working_directory, argv, cenvp, flags, NULL, NULL, &pid, error);
 
+  /* release the child environment */
+  g_strfreev (cenvp);
+
+  if (G_LIKELY (succeed))
+    {
+      if ((flags & G_SPAWN_DO_NOT_REAP_CHILD) != 0
+          || spawn_return != NULL)
+        {
+          spawn = g_slice_new0 (XfceSpawn);
+          spawn->pid = pid;
+          spawn->cw_function = watch_function;
+          spawn->cw_data = watch_data;
+          spawn->cw_notify = watch_notify;
+          spawn->ref_count = (spawn_return == NULL) ? 0 : 1;
+
+          spawn->watch_id = g_child_watch_add_full (G_PRIORITY_LOW, pid,
+                                                    xfce_spawn_startup_watch,
+                                                    spawn,
+                                                    xfce_spawn_startup_watch_destroy);
+
 #ifdef HAVE_LIBSTARTUP_NOTIFICATION
-  if (G_LIKELY (sn_launcher != NULL))
+          if (G_LIKELY (sn_launcher != NULL))
+            {
+              spawn->sn_launcher = sn_launcher;
+              spawn->timeout_id = g_timeout_add_seconds_full (G_PRIORITY_LOW,
+                                                              XFCE_SPAWN_STARTUP_TIMEOUT,
+                                                              xfce_spawn_startup_timeout,
+                                                              spawn,
+                                                              xfce_spawn_startup_timeout_destroy);
+            }
+#endif
+
+          if (G_UNLIKELY (spawn_return != NULL))
+            *spawn_return = spawn;
+        }
+    }
+  else
     {
-      if (G_UNLIKELY (!succeed))
+#ifdef HAVE_LIBSTARTUP_NOTIFICATION
+      if (G_LIKELY (sn_launcher != NULL))
         {
           /* abort the startup notification sequence */
           sn_launcher_context_complete (sn_launcher);
           sn_launcher_context_unref (sn_launcher);
         }
-      else
-        {
-          /* schedule a startup notification timeout */
-          spawn_data = g_slice_new0 (XfceSpawnData);
-          spawn_data->sn_launcher = sn_launcher;
-          spawn_data->timeout_id = g_timeout_add_full (G_PRIORITY_LOW, XFCE_SPAWN_STARTUP_TIMEOUT, xfce_spawn_startup_timeout,
-                                                         spawn_data, xfce_spawn_startup_timeout_destroy);
-          spawn_data->watch_id = g_child_watch_add_full (G_PRIORITY_LOW, pid, xfce_spawn_startup_watch, spawn_data, NULL);
-          spawn_data->pid = pid;
-        }
-    }
-  else if (G_LIKELY (succeed))
-    {
-      /* make sure we don't leave zombies */
-      g_child_watch_add_full (G_PRIORITY_LOW, pid, (GChildWatchFunc) g_spawn_close_pid, NULL, NULL);
+#endif
     }
 
-  /* release the sn display */
+#ifdef HAVE_LIBSTARTUP_NOTIFICATION
+  /* release the startup notification display */
   if (G_LIKELY (sn_display != NULL))
     sn_display_unref (sn_display);
 #endif
 
-  /* cleanup */
-  g_strfreev (cenvp);
-
   return succeed;
 }
 
 
 
 /**
+ * xfce_spawn_on_screen_watch:
+ * @screen               : a #GdkScreen or %NULL to use the active screen,
+ *                         see xfce_gdk_screen_get_active().
+ * @working_directory    : child's current working directory or %NULL to
+ *                         inherit parent's.
+ * @argv                 : child's argument vector.
+ * @envp                 : child's environment vector or %NULL to inherit
+ *                         parent's.
+ * @flags                : flags from #GSpawnFlags.
+ * @startup_notify       : whether to use startup notification.
+ * @startup_timestamp    : the timestamp to pass to startup notification, use
+ *                         the event time here if possible to make focus
+ *                         stealing prevention work property. If you don't
+ *                         have direct access to the event time you could use
+ *                         gtk_get_current_event_time() or if nothing is
+ *                         available 0 is valid too.
+ * @startup_icon_name    : application icon or %NULL. This icon is used in
+ *                         for example the tasklist when the application is
+ *                         starting.
+ * @watch_function       : function that is called when the child exists, like
+ *                         you would use with g_child_watch_add(), but glib
+ *                         only allows 1 callback per process id.
+ * @watch_data           : data to pass to @watch_function.
+ * @watch_notify         : a function to call when @watch_data is no longer
+ *                         in use, or %NULL.
+ * @error                : return location for errors or %NULL.
+ *
+ * Same as xfce_spawn_on_screen() but with support for watching the child
+ * and allowing to set a custom @envp.
+ *
+ * Returns: A new #XfceSpawn data structure with a ref count of 1
+ *          or %NULL on when @error is set.
+ **/
+XfceSpawn *
+xfce_spawn_on_screen_watch (GdkScreen        *screen,
+                            const gchar      *working_directory,
+                            gchar           **argv,
+                            gchar           **envp,
+                            GSpawnFlags       flags,
+                            gboolean          startup_notify,
+                            guint32           startup_timestamp,
+                            const gchar      *startup_icon_name,
+                            GChildWatchFunc   watch_function,
+                            gpointer          watch_data,
+                            GDestroyNotify    watch_notify,
+                            GError          **error)
+{
+  XfceSpawn *spawn = NULL;
+
+  g_return_val_if_fail (screen == NULL || GDK_IS_SCREEN (screen), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+  g_return_val_if_fail ((flags & G_SPAWN_DO_NOT_REAP_CHILD) != 0, NULL);
+
+  xfce_spawn_internal (screen,
+                       working_directory,
+                       argv,
+                       envp,
+                       flags,
+                       startup_notify,
+                       startup_timestamp,
+                       startup_icon_name,
+                       watch_function,
+                       watch_data,
+                       watch_notify,
+                       &spawn,
+                       error);
+
+  return spawn;
+}
+
+
+
+/**
+ * xfce_spawn_ref:
+ * @spawn: the #XfceSpawn data from xfce_spawn_on_screen_watch().
+ *
+ * Increase the reference count on the @spawn data.
+ **/
+void
+xfce_spawn_ref (XfceSpawn *spawn)
+{
+  g_return_if_fail (spawn->ref_count > 0);
+  spawn->ref_count++;
+}
+
+
+
+/**
+ * xfce_spawn_unref:
+ * @spawn: the #XfceSpawn data from xfce_spawn_on_screen_watch().
+ *
+ * Decrease the reference count. If the reference count is 0, the
+ * watch functions will be set to %NULL and the spawn function will
+ * take care of freeing the data.
+ **/
+void
+xfce_spawn_unref (XfceSpawn *spawn)
+{
+  g_return_if_fail (spawn->ref_count > 0);
+
+  if (spawn->ref_count > 0
+      && --spawn->ref_count == 0)
+    {
+      /* unset the user watch data and functions */
+      spawn->cw_function = NULL;
+      spawn->cw_data = NULL;
+      spawn->cw_notify = NULL;
+
+      if (spawn->watch_id == 0)
+        {
+          /* free the data if the child already exited */
+          g_slice_free (XfceSpawn, spawn);
+        }
+#ifdef HAVE_LIBSTARTUP_NOTIFICATION
+      else if (spawn->timeout_id == 0)
+        {
+          /* timeout already exited, remove our watch and
+           * add a child watch for closing the pid */
+          xfce_spawn_startup_timeout_destroy (spawn);
+        }
+#endif
+    }
+}
+
+
+
+/**
+ * xfce_spawn_set_callback:
+ * @spawn          : the #XfceSpawn data from xfce_spawn_on_screen_watch().
+ * @watch_function : the @watch_function that should be called when the
+ *                   startup is finished or %NULL. Note that you should not
+ *                   handle g_spawn_close_pid() .
+ * @watch_data     : the data to pass to callback function
+ * @watch_notify   : a function to call when @watch_data is no longer in use,
+ *                   or %NULL.
+ *
+ * Use this function to set a new callback function for @spawn. You do
+ * not have to call this function before calling xfce_spawn_unref()
+ * to unset your functions, xfce_spawn_unref() will take care of all
+ * that.
+ **/
+void
+xfce_spawn_set_callback (XfceSpawn       *spawn,
+                         GChildWatchFunc  watch_function,
+                         gpointer         watch_data,
+                         GDestroyNotify   watch_notify)
+{
+  g_return_if_fail (spawn->ref_count > 0);
+
+  /* set the new functions */
+  spawn->cw_function = watch_function;
+  spawn->cw_data = watch_data;
+  spawn->cw_notify = watch_notify;
+}
+
+
+
+/**
+ * xfce_spawn_on_screen:
+ * @screen               : a #GdkScreen or %NULL to use the active screen,
+ *                         see xfce_gdk_screen_get_active().
+ * @working_directory    : child's current working directory or %NULL to
+ *                         inherit parent's.
+ * @argv                 : child's argument vector.
+ * @envp                 : child's environment vector or %NULL to inherit
+ *                         parent's.
+ * @flags                : flags from #GSpawnFlags.
+ * @startup_notify       : whether to use startup notification.
+ * @startup_timestamp    : the timestamp to pass to startup notification, use
+ *                         the event time here if possible to make focus
+ *                         stealing prevention work property. If you don't
+ *                         have direct access to the event time you could use
+ *                         gtk_get_current_event_time() or if nothing is
+ *                         available 0 is valid too.
+ * @startup_icon_name    : application icon name or %NULL.
+ * @error                : return location for errors or %NULL.
+ *
+ * Like gdk_spawn_on_screen(), but also supports startup notification
+ * (if Libxfce4ui is built with startup notification support).
+ *
+ * Return value: %TRUE on success, %FALSE if @error is set.
+ **/
+gboolean
+xfce_spawn_on_screen (GdkScreen        *screen,
+                      const gchar      *working_directory,
+                      gchar           **argv,
+                      gchar           **envp,
+                      GSpawnFlags       flags,
+                      gboolean          startup_notify,
+                      guint32           startup_timestamp,
+                      const gchar      *startup_icon_name,
+                      GError          **error)
+{
+  g_return_val_if_fail (screen == NULL || GDK_IS_SCREEN (screen), FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  return xfce_spawn_internal (screen,
+                              working_directory,
+                              argv,
+                              envp,
+                              flags,
+                              startup_notify,
+                              startup_timestamp,
+                              startup_icon_name,
+                              NULL, NULL, NULL, /* watch functions */
+                              NULL, /* spawn data return */
+                              error);
+}
+
+
+
+/**
  * xfce_spawn_command_line_on_screen:
  * @screen            : a #GdkScreen or %NULL to use the active screen, see xfce_gdk_screen_get_active().
  * @command_line      : command line to run.
@@ -376,7 +623,7 @@ xfce_spawn_command_line_on_screen (GdkScreen    *screen,
                                    GError      **error)
 {
   gchar    **argv;
-  gboolean   succeed;
+  gboolean   result;
 
   g_return_val_if_fail (screen == NULL || GDK_IS_SCREEN (screen), FALSE);
   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
@@ -400,15 +647,22 @@ xfce_spawn_command_line_on_screen (GdkScreen    *screen,
       argv[4] = NULL;
     }
 
-  /* execute the function */
-  succeed = xfce_spawn_on_screen (screen, NULL, argv, NULL,
-                                  G_SPAWN_SEARCH_PATH, startup_notify,
-                                  gtk_get_current_event_time (), NULL, error);
+  /* try to spawn the command line */
+  result = xfce_spawn_internal (screen,
+                                NULL,
+                                argv,
+                                NULL,
+                                G_SPAWN_SEARCH_PATH,
+                                startup_notify,
+                                gtk_get_current_event_time (), /* best guess for timestamp */
+                                NULL,
+                                NULL, NULL, NULL, /* watch functions */
+                                NULL, /* spawn data return */
+                                error);
 
-  /* cleanup */
   g_strfreev (argv);
 
-  return succeed;
+  return result;
 }
 
 
diff --git a/libxfce4ui/xfce-spawn.h b/libxfce4ui/xfce-spawn.h
index e262206..f7e7b3d 100644
--- a/libxfce4ui/xfce-spawn.h
+++ b/libxfce4ui/xfce-spawn.h
@@ -29,21 +29,45 @@
 
 G_BEGIN_DECLS
 
-gboolean xfce_spawn_on_screen              (GdkScreen    *screen,
-                                            const gchar  *working_directory,
-                                            gchar       **argv,
-                                            gchar       **envp,
-                                            GSpawnFlags   flags,
-                                            gboolean      startup_notify,
-                                            guint32       startup_timestamp,
-                                            const gchar  *icon_name,
-                                            GError      **error);
-
-gboolean xfce_spawn_command_line_on_screen (GdkScreen    *screen,
-                                            const gchar  *command_line,
-                                            gboolean      in_terminal,
-                                            gboolean      startup_notify,
-                                            GError      **error);
+typedef struct _XfceSpawn XfceSpawn;
+
+XfceSpawn *xfce_spawn_on_screen_watch        (GdkScreen        *screen,
+                                              const gchar      *working_directory,
+                                              gchar           **argv,
+                                              gchar           **envp,
+                                              GSpawnFlags       flags,
+                                              gboolean          startup_notify,
+                                              guint32           startup_timestamp,
+                                              const gchar      *startup_icon_name,
+                                              GChildWatchFunc   watch_function,
+                                              gpointer          watch_data,
+                                              GDestroyNotify    watch_notify,
+                                              GError          **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+
+void       xfce_spawn_ref                    (XfceSpawn        *spawn);
+
+void       xfce_spawn_unref                  (XfceSpawn        *spawn);
+
+void       xfce_spawn_set_callback           (XfceSpawn        *spawn,
+                                              GChildWatchFunc   watch_function,
+                                              gpointer          watch_data,
+                                              GDestroyNotify    watch_notify);
+
+gboolean   xfce_spawn_on_screen              (GdkScreen        *screen,
+                                              const gchar      *working_directory,
+                                              gchar           **argv,
+                                              gchar           **envp,
+                                              GSpawnFlags       flags,
+                                              gboolean          startup_notify,
+                                              guint32           startup_timestamp,
+                                              const gchar      *startup_icon_name,
+                                              GError          **error);
+
+gboolean   xfce_spawn_command_line_on_screen (GdkScreen        *screen,
+                                              const gchar      *command_line,
+                                              gboolean          in_terminal,
+                                              gboolean          startup_notify,
+                                              GError          **error);
 
 G_END_DECLS
 



More information about the Xfce4-commits mailing list