Example #1
0
/**
 * egg_desktop_file_launch:
 * @desktop_file: an #EggDesktopFile
 * @documents: a list of URIs or paths to documents to open
 * @error: error pointer
 * @...: additional options
 *
 * Launches @desktop_file with the given arguments. Additional options
 * can be specified as follows:
 *
 *   %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments)
 *       clears the environment in the child process
 *   %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables)
 *       adds the NAME=VALUE strings in the given %NULL-terminated
 *       array to the child process's environment
 *   %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen)
 *       causes the application to be launched on the given screen
 *   %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace)
 *       causes the application to be launched on the given workspace
 *   %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir)
 *       causes the application to be launched in the given directory
 *   %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time)
 *       sets the "launch time" for the application. If the user
 *       interacts with another window after @launch_time but before
 *       the launched application creates its first window, the window
 *       manager may choose to not give focus to the new application.
 *       Passing 0 for @launch_time will explicitly request that the
 *       application not receive focus.
 *   %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags)
 *       Sets additional #GSpawnFlags to use. See g_spawn_async() for
 *       more details.
 *   %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer)
 *       Sets the child setup callback and the data to pass to it.
 *       (See g_spawn_async() for more details.)
 *
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid)
 *       On a successful launch, sets *@pid to the PID of the launched
 *       application.
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id)
 *       On a successful launch, sets *@startup_id to the Startup
 *       Notification "startup id" of the launched application.
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd)
 *       On a successful launch, sets *@fd to the file descriptor of
 *       a pipe connected to the application's stdin.
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd)
 *       On a successful launch, sets *@fd to the file descriptor of
 *       a pipe connected to the application's stdout.
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd)
 *       On a successful launch, sets *@fd to the file descriptor of
 *       a pipe connected to the application's stderr.
 *
 * The options should be terminated with a single %NULL.
 *
 * If @documents contains multiple documents, but
 * egg_desktop_file_accepts_multiple() returns %FALSE for
 * @desktop_file, then egg_desktop_file_launch() will actually launch
 * multiple instances of the application. In that case, the return
 * value (as well as any values passed via
 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the
 * first instance of the application that was launched (but the
 * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each
 * instance).
 *
 * Return value: %TRUE if the application was successfully launched.
 **/
gboolean
egg_desktop_file_launch (EggDesktopFile *desktop_file,
			 GSList *documents, GError **error,
			 ...)
{
  va_list args;
  gboolean success;
  EggDesktopFile *app_desktop_file;

  switch (desktop_file->type)
    {
    case EGG_DESKTOP_FILE_TYPE_APPLICATION:
      va_start (args, error);
      success = egg_desktop_file_launchv (desktop_file, documents,
					  args, error);
      va_end (args);
      break;

    case EGG_DESKTOP_FILE_TYPE_LINK:
      if (documents)
	{
	  g_set_error (error, EGG_DESKTOP_FILE_ERROR,
		       EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
		       /* translators: The 'Type=Link' string is found in a
			* desktop file, and should not be translated. */
		       _("Can't pass document URIs to a 'Type=Link' desktop entry"));
	  return FALSE;
	}	  

      if (!parse_link (desktop_file, &app_desktop_file, &documents, error))
	return FALSE;

      va_start (args, error);
      success = egg_desktop_file_launchv (app_desktop_file, documents,
					  args, error);
      va_end (args);

      egg_desktop_file_free (app_desktop_file);
      free_document_list (documents);
      break;

    case EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED:
    case EGG_DESKTOP_FILE_TYPE_DIRECTORY:
    default:
      g_set_error (error, EGG_DESKTOP_FILE_ERROR,
		   EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
		   _("Not a launchable item"));
      success = FALSE;
      break;
    }

  return success;
}
Example #2
0
/**
 * egg_desktop_file_parse_exec:
 * @desktop_file: a #EggDesktopFile
 * @documents: a list of document paths or URIs
 * @error: error pointer
 *
 * Parses @desktop_file's Exec key, inserting @documents into it, and
 * returns the result.
 *
 * If @documents contains non-file: URIs and @desktop_file does not
 * accept URIs, those URIs will be ignored. Likewise, if @documents
 * contains more elements than @desktop_file accepts, the extra
 * documents will be ignored.
 *
 * Return value: the parsed Exec string
 **/
char *
egg_desktop_file_parse_exec (EggDesktopFile * desktop_file, GSList * documents,
                             GError ** error)
{
    GSList *translated, *docs;
    char *command;

    docs = translated = translate_document_list (desktop_file, documents);
    command = parse_exec (desktop_file, &docs, error);
    free_document_list (translated);

    return command;
}
Example #3
0
/**
 * egg_desktop_file_launch:
 * @desktop_file: an #EggDesktopFile
 * @documents: a list of URIs or paths to documents to open
 * @error: error pointer
 * @...: additional options
 *
 * Launches @desktop_file with the given arguments. Additional options
 * can be specified as follows:
 *
 *   %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments)
 *       clears the environment in the child process
 *   %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables)
 *       adds the NAME=VALUE strings in the given %NULL-terminated
 *       array to the child process's environment
 *   %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen)
 *       causes the application to be launched on the given screen
 *   %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace)
 *       causes the application to be launched on the given workspace
 *   %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir)
 *       causes the application to be launched in the given directory
 *   %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time)
 *       sets the "launch time" for the application. If the user
 *       interacts with another window after @launch_time but before
 *       the launched application creates its first window, the window
 *       manager may choose to not give focus to the new application.
 *       Passing 0 for @launch_time will explicitly request that the
 *       application not receive focus.
 *   %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags)
 *       Sets additional #GSpawnFlags to use. See g_spawn_async() for
 *       more details.
 *   %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer)
 *       Sets the child setup callback and the data to pass to it.
 *       (See g_spawn_async() for more details.)
 *
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid)
 *       On a successful launch, sets *@pid to the PID of the launched
 *       application.
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id)
 *       On a successful launch, sets *@startup_id to the Startup
 *       Notification "startup id" of the launched application.
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd)
 *       On a successful launch, sets *@fd to the file descriptor of
 *       a pipe connected to the application's stdin.
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd)
 *       On a successful launch, sets *@fd to the file descriptor of
 *       a pipe connected to the application's stdout.
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd)
 *       On a successful launch, sets *@fd to the file descriptor of
 *       a pipe connected to the application's stderr.
 *
 * The options should be terminated with a single %NULL.
 *
 * If @documents contains multiple documents, but
 * egg_desktop_file_accepts_multiple() returns %FALSE for
 * @desktop_file, then egg_desktop_file_launch() will actually launch
 * multiple instances of the application. In that case, the return
 * value (as well as any values passed via
 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the
 * first instance of the application that was launched (but the
 * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each
 * instance).
 *
 * Return value: %TRUE if the application was successfully launched.
 **/
gboolean
egg_desktop_file_launch (EggDesktopFile *desktop_file,
			 GSList *documents, GError **error,
			 ...)
{
  va_list args;
  gboolean success;
  EggDesktopFile *app_desktop_file;

  switch (desktop_file->type)
    {
    case EGG_DESKTOP_FILE_TYPE_APPLICATION:
      va_start (args, error);
      success = egg_desktop_file_launchv (desktop_file, documents,
					  args, error);
      va_end (args);
      break;

    case EGG_DESKTOP_FILE_TYPE_LINK:
      if (documents)
	{
	  g_set_error (error, EGG_DESKTOP_FILE_ERROR,
		       EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
		       _("Can't pass documents to this desktop element"));
	  return FALSE;
	}	  

      if (!parse_link (desktop_file, &app_desktop_file, &documents, error))
	return FALSE;

      va_start (args, error);
      success = egg_desktop_file_launchv (app_desktop_file, documents,
					  args, error);
      va_end (args);

      egg_desktop_file_free (app_desktop_file);
      free_document_list (documents);
      break;

    default:
      g_set_error (error, EGG_DESKTOP_FILE_ERROR,
		   EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
		   _("Not a launchable item"));
      success = FALSE;
      break;
    }

  return success;
}
Example #4
0
static gboolean
egg_desktop_file_launchv (EggDesktopFile *desktop_file,
			  GSList *documents, va_list args,
			  GError **error)
{
  EggDesktopFileLaunchOption option;
  GSList *translated_documents = NULL, *docs = NULL;
  char *command, **argv;
  int argc, i, screen_num;
  gboolean success, current_success;
  GdkDisplay *display;
  char *startup_id;

  GPtrArray   *env = NULL;
  char       **variables = NULL;
  GdkScreen   *screen = NULL;
  int          workspace = -1;
  const char  *directory = NULL;
  guint32      launch_time = (guint32)-1;
  GSpawnFlags  flags = G_SPAWN_SEARCH_PATH;
  GSpawnChildSetupFunc setup_func = NULL;
  gpointer     setup_data = NULL;

  GPid        *ret_pid = NULL;
  int         *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL;
  char       **ret_startup_id = NULL;

  if (documents && desktop_file->document_code == 0)
    {
      g_set_error (error, EGG_DESKTOP_FILE_ERROR,
		   EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
		   _("Application does not accept documents on command line"));
      return FALSE;
    }

  /* Read the options: technically it's incorrect for the caller to
   * NULL-terminate the list of options (rather than 0-terminating
   * it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED,
   * it's more consistent with other glib/gtk methods, and it will
   * work as long as sizeof (int) <= sizeof (NULL), and NULL is
   * represented as 0. (Which is true everywhere we care about.)
   */
  while ((option = va_arg (args, EggDesktopFileLaunchOption)))
    {
      switch (option)
	{
	case EGG_DESKTOP_FILE_LAUNCH_CLEARENV:
	  if (env)
	    g_ptr_array_free (env, TRUE);
	  env = g_ptr_array_new ();
	  break;
	case EGG_DESKTOP_FILE_LAUNCH_PUTENV:
	  variables = va_arg (args, char **);
	  for (i = 0; variables[i]; i++)
	    env = array_putenv (env, variables[i]);
	  break;

	case EGG_DESKTOP_FILE_LAUNCH_SCREEN:
	  screen = va_arg (args, GdkScreen *);
	  break;
	case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE:
	  workspace = va_arg (args, int);
	  break;

	case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY:
	  directory = va_arg (args, const char *);
	  break;
	case EGG_DESKTOP_FILE_LAUNCH_TIME:
	  launch_time = va_arg (args, guint32);
	  break;
	case EGG_DESKTOP_FILE_LAUNCH_FLAGS:
	  flags |= va_arg (args, GSpawnFlags);
	  /* Make sure they didn't set any flags that don't make sense. */
	  flags &= ~G_SPAWN_FILE_AND_ARGV_ZERO;
	  break;
	case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC:
	  setup_func = va_arg (args, GSpawnChildSetupFunc);
	  setup_data = va_arg (args, gpointer);
	  break;

	case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID:
	  ret_pid = va_arg (args, GPid *);
	  break;
	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE:
	  ret_stdin = va_arg (args, int *);
	  break;
	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE:
	  ret_stdout = va_arg (args, int *);
	  break;
	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE:
	  ret_stderr = va_arg (args, int *);
	  break;
	case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID:
	  ret_startup_id = va_arg (args, char **);
	  break;

	default:
	  g_set_error (error, EGG_DESKTOP_FILE_ERROR,
		       EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION,
		       _("Unrecognized launch option: %d"),
		       GPOINTER_TO_INT (option));
	  success = FALSE;
	  goto out;
	}
    }

  if (screen)
    {
      char *display_name = gdk_screen_make_display_name (screen);
      char *display_env = g_strdup_printf ("DISPLAY=%s", display_name);
      env = array_putenv (env, display_env);
      g_free (display_name);
      g_free (display_env);

      display = gdk_screen_get_display (screen);
    }
  else
    {
      display = gdk_display_get_default ();
      screen = gdk_display_get_default_screen (display);
    }
  screen_num = gdk_screen_get_number (screen);

  translated_documents = translate_document_list (desktop_file, documents);
  docs = translated_documents;

  success = FALSE;

  do
    {
      command = parse_exec (desktop_file, &docs, error);
      if (!command)
	goto out;

      if (!g_shell_parse_argv (command, &argc, &argv, error))
	{
	  g_free (command);
	  goto out;
	}
      g_free (command);

#if GTK_CHECK_VERSION (2, 12, 0)
      startup_id = start_startup_notification (display, desktop_file,
					       argv[0], screen_num,
					       workspace, launch_time);
      if (startup_id)
	{
	  char *startup_id_env = g_strdup_printf ("DESKTOP_STARTUP_ID=%s",
						  startup_id);
	  env = array_putenv (env, startup_id_env);
	  g_free (startup_id_env);
	}
#else
      startup_id = NULL;
#endif /* GTK 2.12 */

      if (env != NULL)
	g_ptr_array_add (env, NULL);

      current_success =
	g_spawn_async_with_pipes (directory,
				  argv,
				  env ? (char **)(env->pdata) : NULL,
				  flags,
				  setup_func, setup_data,
				  ret_pid,
				  ret_stdin, ret_stdout, ret_stderr,
				  error);
      g_strfreev (argv);

      if (startup_id)
	{
#if GTK_CHECK_VERSION (2, 12, 0)
	  if (current_success)
	    {
	      set_startup_notification_timeout (display, startup_id);

	      if (ret_startup_id)
		*ret_startup_id = startup_id;
	      else
		g_free (startup_id);
	    }
	  else
#endif /* GTK 2.12 */
	    g_free (startup_id);
	}
      else if (ret_startup_id)
	*ret_startup_id = NULL;

      if (current_success)
	{
	  /* If we successfully launch any instances of the app, make
	   * sure we return TRUE and don't set @error.
	   */
	  success = TRUE;
	  error = NULL;

	  /* Also, only set the output params on the first one */
	  ret_pid = NULL;
	  ret_stdin = ret_stdout = ret_stderr = NULL;
	  ret_startup_id = NULL;
	}
    }
  while (docs && current_success);

 out:
  if (env)
    {
      g_ptr_array_foreach (env, (GFunc)g_free, NULL);
      g_ptr_array_free (env, TRUE);
    }
  free_document_list (translated_documents);

  return success;
}