Commit 8990efaf authored by Nick Schermer's avatar Nick Schermer

Use the spawn code from libxfce4ui.

parent 90b3d9fe
......@@ -24,42 +24,12 @@
#include <config.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_MEMORY_H
#include <memory.h>
#endif
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <glib.h>
#include <gio/gio.h>
#include <gdk/gdk.h>
#include <gtk/gtk.h>
#include <libxfce4util/libxfce4util.h>
#include <thunar/thunar-exec.h>
#include <thunar/thunar-private.h>
#ifdef GDK_WINDOWING_X11
#include <X11/Xatom.h>
#include <gdk/gdkx.h>
#endif
static void te_string_append_quoted (GString *string,
......@@ -240,342 +210,3 @@ done:
g_string_free (command_line, TRUE);
return result;
}
#ifdef HAVE_LIBSTARTUP_NOTIFICATION
#include <libsn/sn.h>
/* the max. timeout for an application to startup */
#define TSN_STARTUP_TIMEOUT (30 * 1000)
typedef struct
{
SnLauncherContext *sn_launcher;
guint timeout_id;
guint watch_id;
GPid pid;
} TsnStartupData;
static gboolean
tvsn_startup_timeout (gpointer data)
{
TsnStartupData *startup_data = data;
GTimeVal now;
gdouble elapsed;
glong tv_sec;
glong tv_usec;
GDK_THREADS_ENTER ();
/* determine the amount of elapsed time */
g_get_current_time (&now);
sn_launcher_context_get_last_active_time (startup_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;
/* check if the timeout was reached */
if (elapsed >= TSN_STARTUP_TIMEOUT)
{
/* abort the startup notification */
sn_launcher_context_complete (startup_data->sn_launcher);
sn_launcher_context_unref (startup_data->sn_launcher);
startup_data->sn_launcher = NULL;
}
GDK_THREADS_LEAVE ();
/* keep the startup timeout if not elapsed */
return (elapsed < TSN_STARTUP_TIMEOUT);
}
static void
tvsn_startup_timeout_destroy (gpointer data)
{
TsnStartupData *startup_data = data;
_thunar_return_if_fail (startup_data->sn_launcher == NULL);
/* cancel the watch (if any) */
if (startup_data->watch_id != 0)
g_source_remove (startup_data->watch_id);
/* make sure we don't leave zombies (see bug #2983 for details) */
g_child_watch_add_full (G_PRIORITY_LOW, startup_data->pid,
(GChildWatchFunc) g_spawn_close_pid,
NULL, NULL);
/* release the startup data */
g_slice_free (TsnStartupData, startup_data);
}
static void
tvsn_startup_watch (GPid pid,
gint status,
gpointer data)
{
TsnStartupData *startup_data = data;
_thunar_return_if_fail (startup_data->sn_launcher != NULL);
_thunar_return_if_fail (startup_data->watch_id != 0);
_thunar_return_if_fail (startup_data->pid == pid);
/* abort the startup notification (application exited) */
sn_launcher_context_complete (startup_data->sn_launcher);
sn_launcher_context_unref (startup_data->sn_launcher);
startup_data->sn_launcher = NULL;
/* cancel the startup notification timeout */
g_source_remove (startup_data->timeout_id);
}
static gint
tvsn_get_active_workspace_number (GdkScreen *screen)
{
GdkWindow *root;
gulong bytes_after_ret = 0;
gulong nitems_ret = 0;
guint *prop_ret = NULL;
Atom _NET_CURRENT_DESKTOP;
Atom _WIN_WORKSPACE;
Atom type_ret = None;
gint format_ret;
gint ws_num = 0;
gdk_error_trap_push ();
root = gdk_screen_get_root_window (screen);
/* determine the X atom values */
_NET_CURRENT_DESKTOP = XInternAtom (GDK_WINDOW_XDISPLAY (root), "_NET_CURRENT_DESKTOP", False);
_WIN_WORKSPACE = XInternAtom (GDK_WINDOW_XDISPLAY (root), "_WIN_WORKSPACE", False);
if (XGetWindowProperty (GDK_WINDOW_XDISPLAY (root), GDK_WINDOW_XWINDOW (root),
_NET_CURRENT_DESKTOP, 0, 32, False, XA_CARDINAL,
&type_ret, &format_ret, &nitems_ret, &bytes_after_ret,
(gpointer) &prop_ret) != Success)
{
if (XGetWindowProperty (GDK_WINDOW_XDISPLAY (root), GDK_WINDOW_XWINDOW (root),
_WIN_WORKSPACE, 0, 32, False, XA_CARDINAL,
&type_ret, &format_ret, &nitems_ret, &bytes_after_ret,
(gpointer) &prop_ret) != Success)
{
if (G_UNLIKELY (prop_ret != NULL))
{
XFree (prop_ret);
prop_ret = NULL;
}
}
}
if (G_LIKELY (prop_ret != NULL))
{
if (G_LIKELY (type_ret != None && format_ret != 0))
ws_num = *prop_ret;
XFree (prop_ret);
}
gdk_error_trap_pop ();
return ws_num;
}
#endif
/**
* thunar_exec_on_screen:
* @screen : a #GdkScreen.
* @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.
* @icon_name : application icon or %NULL.
* @error : return location for errors or %NULL.
*
* Like gdk_spawn_on_screen(), but also supports startup notification
* (if Thunar was built with startup notification support).
*
* Return value: %TRUE on success, %FALSE if @error is set.
**/
gboolean
thunar_exec_on_screen (GdkScreen *screen,
const gchar *working_directory,
gchar **argv,
gchar **envp,
GSpawnFlags flags,
gboolean startup_notify,
const gchar *icon_name,
GError **error)
{
#ifdef HAVE_LIBSTARTUP_NOTIFICATION
SnLauncherContext *sn_launcher = NULL;
TsnStartupData *startup_data;
SnDisplay *sn_display = NULL;
gint sn_workspace;
#endif
#ifndef HAVE_UNISTD_H
extern gchar **environ;
#endif
gboolean succeed;
gchar *display_name;
gchar **cenvp = envp;
gint n_cenvp, n;
GPid pid;
/* setup the child environment (stripping $DESKTOP_STARTUP_ID and $DISPLAY) */
if (G_LIKELY (envp == NULL))
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);
#ifdef HAVE_LIBSTARTUP_NOTIFICATION
/* initialize the sn launcher context */
if (G_LIKELY (startup_notify))
{
sn_display = sn_display_new (GDK_SCREEN_XDISPLAY (screen),
(SnDisplayErrorTrapPush) gdk_error_trap_push,
(SnDisplayErrorTrapPop) gdk_error_trap_pop);
if (G_LIKELY (sn_display != NULL))
{
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)))
{
/* initiate the sn launcher context */
sn_workspace = tvsn_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], gtk_get_current_event_time ());
/* 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);
/* we want to watch the child process */
flags |= G_SPAWN_DO_NOT_REAP_CHILD;
}
}
}
#endif
/* try to spawn the new process */
succeed = g_spawn_async (working_directory, argv, cenvp, flags, NULL, NULL, &pid, error);
#ifdef HAVE_LIBSTARTUP_NOTIFICATION
/* handle the sn launcher context */
if (G_LIKELY (sn_launcher != NULL))
{
if (G_UNLIKELY (!succeed))
{
/* abort the sn sequence */
sn_launcher_context_complete (sn_launcher);
sn_launcher_context_unref (sn_launcher);
}
else
{
/* schedule a startup notification timeout */
startup_data = g_slice_new (TsnStartupData);
startup_data->sn_launcher = sn_launcher;
startup_data->timeout_id = g_timeout_add_full (G_PRIORITY_LOW, TSN_STARTUP_TIMEOUT, tvsn_startup_timeout,
startup_data, tvsn_startup_timeout_destroy);
startup_data->watch_id = g_child_watch_add_full (G_PRIORITY_LOW, pid, tvsn_startup_watch, startup_data, NULL);
startup_data->pid = pid;
}
}
else if (G_LIKELY (succeed))
{
/* make sure we don't leave zombies (see bug #2983 for details) */
g_child_watch_add_full (G_PRIORITY_LOW, pid, (GChildWatchFunc) g_spawn_close_pid, NULL, NULL);
}
/* release the sn display */
if (G_LIKELY (sn_display != NULL))
sn_display_unref (sn_display);
#endif
/* release the child environment */
g_strfreev (cenvp);
return succeed;
}
/**
* thunar_exec_sync:
* @command_fmt : the command to execute (can be a printf
* format string).
* @error : return location for errors or %NULL.
* @... : additional parameters to fill into
* @command_fmt.
*
* Executes the given @command_fmt and returns %TRUE if the
* command terminated successfully. Else, the @error is set
* to the standard error output.
*
* Return value: %TRUE if the @command_line was executed
* successfully, %FALSE if @error is set.
**/
gboolean
thunar_exec_sync (const gchar *command_fmt,
GError **error,
...)
{
gboolean result;
va_list args;
gchar *standard_error;
gchar *command_line;
gint exit_status;
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
g_return_val_if_fail (command_fmt != NULL, FALSE);
/* determine the command line */
va_start (args, error);
command_line = g_strdup_vprintf (command_fmt, args);
va_end (args);
/* try to execute the command line */
result = g_spawn_command_line_sync (command_line, NULL, &standard_error, &exit_status, error);
if (G_UNLIKELY (result))
{
/* check if the command failed */
if (G_UNLIKELY (exit_status != 0))
{
/* drop additional whitespace from the stderr output */
g_strstrip (standard_error);
/* strip all trailing dots from the stderr output */
while (*standard_error != '\0' && standard_error[strlen (standard_error) - 1] == '.')
standard_error[strlen (standard_error) - 1] = '\0';
/* generate an error from the stderr output */
if (G_LIKELY (*standard_error != '\0'))
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, "%s", standard_error);
else
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED, _("Unknown error"));
/* and yes, we failed */
result = FALSE;
}
/* release the stderr output */
g_free (standard_error);
}
/* cleanup */
g_free (command_line);
return result;
}
......@@ -36,19 +36,6 @@ gboolean thunar_exec_parse (const gchar *exec,
gchar ***argv,
GError **error);
gboolean thunar_exec_on_screen (GdkScreen *screen,
const gchar *working_directory,
gchar **argv,
gchar **envp,
GSpawnFlags flags,
gboolean startup_notify,
const gchar *icon_name,
GError **error);
gboolean thunar_exec_sync (const gchar *command_line,
GError **error,
...);
G_END_DECLS;
#endif /* !__THUNAR_EXEC_H__ */
......@@ -50,6 +50,7 @@
#endif
#include <gio/gio.h>
#include <libxfce4ui/libxfce4ui.h>
#include <thunarx/thunarx.h>
......@@ -1090,8 +1091,8 @@ thunar_file_execute (ThunarFile *file,
}
/* execute the command */
result = thunar_exec_on_screen (screen, directory, argv, NULL, G_SPAWN_SEARCH_PATH,
snotify, icon, error);
result = xfce_spawn_on_screen (screen, directory, argv, NULL, G_SPAWN_SEARCH_PATH,
snotify, gtk_get_current_event_time (), icon, error);
/* release the working directory */
g_free (directory);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment