Blame gdk/x11/gdkapplaunchcontext-x11.c

Packit 98cdb6
/* gdkapplaunchcontext-x11.c - Gtk+ implementation for GAppLaunchContext
Packit 98cdb6
Packit 98cdb6
   Copyright (C) 2007 Red Hat, Inc.
Packit 98cdb6
Packit 98cdb6
   The Gnome Library is free software; you can redistribute it and/or
Packit 98cdb6
   modify it under the terms of the GNU Library General Public License as
Packit 98cdb6
   published by the Free Software Foundation; either version 2 of the
Packit 98cdb6
   License, or (at your option) any later version.
Packit 98cdb6
Packit 98cdb6
   The Gnome Library is distributed in the hope that it will be useful,
Packit 98cdb6
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 98cdb6
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 98cdb6
   Library General Public License for more details.
Packit 98cdb6
Packit 98cdb6
   You should have received a copy of the GNU Library General Public
Packit 98cdb6
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
Packit 98cdb6
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Packit 98cdb6
   Boston, MA 02111-1307, USA.
Packit 98cdb6
Packit 98cdb6
   Author: Alexander Larsson <alexl@redhat.com>
Packit 98cdb6
*/
Packit 98cdb6
Packit 98cdb6
#include "config.h"
Packit 98cdb6
Packit 98cdb6
#include <string.h>
Packit 98cdb6
#include <unistd.h>
Packit 98cdb6
Packit 98cdb6
#include <glib.h>
Packit 98cdb6
#include <gio/gdesktopappinfo.h>
Packit 98cdb6
Packit 98cdb6
#include "gdkx.h"
Packit 98cdb6
#include "gdkapplaunchcontext.h"
Packit 98cdb6
#include "gdkscreen.h"
Packit 98cdb6
#include "gdkinternals.h"
Packit 98cdb6
#include "gdkintl.h"
Packit 98cdb6
#include "gdkalias.h"
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
static char *
Packit 98cdb6
get_display_name (GFile     *file,
Packit 98cdb6
                  GFileInfo *info)
Packit 98cdb6
{
Packit 98cdb6
  char *name, *tmp;
Packit 98cdb6
Packit 98cdb6
  name = NULL;
Packit 98cdb6
  if (info)
Packit 98cdb6
    name = g_strdup (g_file_info_get_display_name (info));
Packit 98cdb6
Packit 98cdb6
  if (name == NULL)
Packit 98cdb6
    {
Packit 98cdb6
      name = g_file_get_basename (file);
Packit 98cdb6
      if (!g_utf8_validate (name, -1, NULL))
Packit 98cdb6
        {
Packit 98cdb6
          tmp = name;
Packit 98cdb6
          name =
Packit 98cdb6
            g_uri_escape_string (name, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
Packit 98cdb6
          g_free (tmp);
Packit 98cdb6
        }
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  return name;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
static GIcon *
Packit 98cdb6
get_icon (GFile     *file,
Packit 98cdb6
          GFileInfo *info)
Packit 98cdb6
{
Packit 98cdb6
  GIcon *icon;
Packit 98cdb6
Packit 98cdb6
  icon = NULL;
Packit 98cdb6
Packit 98cdb6
  if (info)
Packit 98cdb6
    {
Packit 98cdb6
      icon = g_file_info_get_icon (info);
Packit 98cdb6
      if (icon)
Packit 98cdb6
        g_object_ref (icon);
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  return icon;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
static char *
Packit 98cdb6
gicon_to_string (GIcon *icon)
Packit 98cdb6
{
Packit 98cdb6
  GFile *file;
Packit 98cdb6
  const char *const *names;
Packit 98cdb6
Packit 98cdb6
  if (G_IS_FILE_ICON (icon))
Packit 98cdb6
    {
Packit 98cdb6
      file = g_file_icon_get_file (G_FILE_ICON (icon));
Packit 98cdb6
      if (file)
Packit 98cdb6
        return g_file_get_path (file);
Packit 98cdb6
    }
Packit 98cdb6
  else if (G_IS_THEMED_ICON (icon))
Packit 98cdb6
    {
Packit 98cdb6
      names = g_themed_icon_get_names (G_THEMED_ICON (icon));
Packit 98cdb6
      if (names)
Packit 98cdb6
        return g_strdup (names[0]);
Packit 98cdb6
    }
Packit 98cdb6
  else if (G_IS_EMBLEMED_ICON (icon))
Packit 98cdb6
    {
Packit 98cdb6
      GIcon *base;
Packit 98cdb6
Packit 98cdb6
      base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
Packit 98cdb6
Packit 98cdb6
      return gicon_to_string (base);
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  return NULL;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
static void
Packit 98cdb6
end_startup_notification (GdkDisplay *display,
Packit 98cdb6
                          const char *startup_id)
Packit 98cdb6
{
Packit 98cdb6
  gdk_x11_display_broadcast_startup_message (display, "remove",
Packit 98cdb6
                                             "ID", startup_id,
Packit 98cdb6
                                             NULL);
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
/* This should be fairly long, as it's confusing to users if a startup
Packit 98cdb6
 * ends when it shouldn't (it appears that the startup failed, and
Packit 98cdb6
 * they have to relaunch the app). Also the timeout only matters when
Packit 98cdb6
 * there are bugs and apps don't end their own startup sequence.
Packit 98cdb6
 *
Packit 98cdb6
 * This timeout is a "last resort" timeout that ignores whether the
Packit 98cdb6
 * startup sequence has shown activity or not.  Metacity and the
Packit 98cdb6
 * tasklist have smarter, and correspondingly able-to-be-shorter
Packit 98cdb6
 * timeouts. The reason our timeout is dumb is that we don't monitor
Packit 98cdb6
 * the sequence (don't use an SnMonitorContext)
Packit 98cdb6
 */
Packit 98cdb6
#define STARTUP_TIMEOUT_LENGTH_SECONDS 30 
Packit 98cdb6
#define STARTUP_TIMEOUT_LENGTH (STARTUP_TIMEOUT_LENGTH_SECONDS * 1000)
Packit 98cdb6
Packit 98cdb6
typedef struct 
Packit 98cdb6
{
Packit 98cdb6
  GdkDisplay *display;
Packit 98cdb6
  char *startup_id;
Packit 98cdb6
  GTimeVal time;
Packit 98cdb6
} StartupNotificationData;
Packit 98cdb6
Packit 98cdb6
static void
Packit 98cdb6
free_startup_notification_data (gpointer data)
Packit 98cdb6
{
Packit 98cdb6
  StartupNotificationData *sn_data = data;
Packit 98cdb6
Packit 98cdb6
  g_object_unref (sn_data->display);
Packit 98cdb6
  g_free (sn_data->startup_id);
Packit 98cdb6
  g_free (sn_data);
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
typedef struct 
Packit 98cdb6
{
Packit 98cdb6
  GSList *contexts;
Packit 98cdb6
  guint timeout_id;
Packit 98cdb6
} StartupTimeoutData;
Packit 98cdb6
Packit 98cdb6
static void
Packit 98cdb6
free_startup_timeout (void *data)
Packit 98cdb6
{
Packit 98cdb6
  StartupTimeoutData *std;
Packit 98cdb6
Packit 98cdb6
  std = data;
Packit 98cdb6
Packit 98cdb6
  g_slist_foreach (std->contexts, (GFunc) free_startup_notification_data, NULL);
Packit 98cdb6
  g_slist_free (std->contexts);
Packit 98cdb6
Packit 98cdb6
  if (std->timeout_id != 0)
Packit 98cdb6
    {
Packit 98cdb6
      g_source_remove (std->timeout_id);
Packit 98cdb6
      std->timeout_id = 0;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  g_free (std);
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
static gboolean
Packit 98cdb6
startup_timeout (void *data)
Packit 98cdb6
{
Packit 98cdb6
  StartupTimeoutData *std;
Packit 98cdb6
  GSList *tmp;
Packit 98cdb6
  GTimeVal now;
Packit 98cdb6
  int min_timeout;
Packit 98cdb6
Packit 98cdb6
  std = data;
Packit 98cdb6
Packit 98cdb6
  min_timeout = STARTUP_TIMEOUT_LENGTH;
Packit 98cdb6
Packit 98cdb6
  g_get_current_time (&now;;
Packit 98cdb6
Packit 98cdb6
  tmp = std->contexts;
Packit 98cdb6
  while (tmp != NULL)
Packit 98cdb6
    {
Packit 98cdb6
      StartupNotificationData *sn_data;
Packit 98cdb6
      GSList *next;
Packit 98cdb6
      double elapsed;
Packit 98cdb6
Packit 98cdb6
      sn_data = tmp->data;
Packit 98cdb6
      next = tmp->next;
Packit 98cdb6
Packit 98cdb6
      elapsed =
Packit 98cdb6
        ((((double) now.tv_sec - sn_data->time.tv_sec) * G_USEC_PER_SEC +
Packit 98cdb6
          (now.tv_usec - sn_data->time.tv_usec))) / 1000.0;
Packit 98cdb6
Packit 98cdb6
      if (elapsed >= STARTUP_TIMEOUT_LENGTH)
Packit 98cdb6
        {
Packit 98cdb6
          std->contexts = g_slist_remove (std->contexts, sn_data);
Packit 98cdb6
          end_startup_notification (sn_data->display, sn_data->startup_id);
Packit 98cdb6
          free_startup_notification_data (sn_data);
Packit 98cdb6
        }
Packit 98cdb6
      else
Packit 98cdb6
        {
Packit 98cdb6
          min_timeout = MIN (min_timeout, (STARTUP_TIMEOUT_LENGTH - elapsed));
Packit 98cdb6
        }
Packit 98cdb6
Packit 98cdb6
      tmp = next;
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  if (std->contexts == NULL)
Packit 98cdb6
    std->timeout_id = 0;
Packit 98cdb6
  else
Packit 98cdb6
    std->timeout_id = g_timeout_add_seconds ((min_timeout + 500)/1000, startup_timeout, std);
Packit 98cdb6
Packit 98cdb6
  /* always remove this one, but we may have reinstalled another one. */
Packit 98cdb6
  return FALSE;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
static void
Packit 98cdb6
add_startup_timeout (GdkScreen  *screen,
Packit 98cdb6
                     const char *startup_id)
Packit 98cdb6
{
Packit 98cdb6
  StartupTimeoutData *data;
Packit 98cdb6
  StartupNotificationData *sn_data;
Packit 98cdb6
Packit 98cdb6
  data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data");
Packit 98cdb6
Packit 98cdb6
  if (data == NULL)
Packit 98cdb6
    {
Packit 98cdb6
      data = g_new (StartupTimeoutData, 1);
Packit 98cdb6
      data->contexts = NULL;
Packit 98cdb6
      data->timeout_id = 0;
Packit 98cdb6
Packit 98cdb6
      g_object_set_data_full (G_OBJECT (screen), "appinfo-startup-data",
Packit 98cdb6
                              data, free_startup_timeout);
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  sn_data = g_new (StartupNotificationData, 1);
Packit 98cdb6
  sn_data->display = g_object_ref (gdk_screen_get_display (screen));
Packit 98cdb6
  sn_data->startup_id = g_strdup (startup_id);
Packit 98cdb6
  g_get_current_time (&sn_data->time);
Packit 98cdb6
Packit 98cdb6
  data->contexts = g_slist_prepend (data->contexts, sn_data);
Packit 98cdb6
Packit 98cdb6
  if (data->timeout_id == 0)
Packit 98cdb6
    data->timeout_id = g_timeout_add_seconds (STARTUP_TIMEOUT_LENGTH_SECONDS,
Packit 98cdb6
                                              startup_timeout, data);
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
char *
Packit 98cdb6
_gdk_windowing_get_startup_notify_id (GAppLaunchContext *context,
Packit 98cdb6
                                      GAppInfo          *info,
Packit 98cdb6
                                      GList             *files)
Packit 98cdb6
{
Packit 98cdb6
  static int sequence = 0;
Packit 98cdb6
  GdkAppLaunchContextPrivate *priv;
Packit 98cdb6
  GdkDisplay *display;
Packit 98cdb6
  GdkScreen *screen;
Packit 98cdb6
  int files_count;
Packit 98cdb6
  char *description;
Packit 98cdb6
  char *icon_name;
Packit 98cdb6
  const char *binary_name;
Packit 98cdb6
  const char *application_id;
Packit 98cdb6
  char *screen_str;
Packit 98cdb6
  char *workspace_str;
Packit 98cdb6
  GIcon *icon;
Packit 98cdb6
  guint32 timestamp;
Packit 98cdb6
  char *startup_id;
Packit 98cdb6
  GFileInfo *fileinfo;
Packit 98cdb6
Packit 98cdb6
  priv = GDK_APP_LAUNCH_CONTEXT (context)->priv;
Packit 98cdb6
Packit 98cdb6
  if (priv->screen)
Packit 98cdb6
    {
Packit 98cdb6
      screen = priv->screen;
Packit 98cdb6
      display = gdk_screen_get_display (priv->screen);
Packit 98cdb6
    }
Packit 98cdb6
  else if (priv->display)
Packit 98cdb6
    {
Packit 98cdb6
      display = priv->display;
Packit 98cdb6
      screen = gdk_display_get_default_screen (display);
Packit 98cdb6
    }
Packit 98cdb6
  else
Packit 98cdb6
    {
Packit 98cdb6
      display = gdk_display_get_default ();
Packit 98cdb6
      screen = gdk_display_get_default_screen (display);
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  fileinfo = NULL;
Packit 98cdb6
Packit 98cdb6
  files_count = g_list_length (files);
Packit 98cdb6
  if (files_count == 0)
Packit 98cdb6
    {
Packit 98cdb6
      description = g_strdup_printf (_("Starting %s"), g_app_info_get_name (info));
Packit 98cdb6
    }
Packit 98cdb6
  else if (files_count == 1)
Packit 98cdb6
    {
Packit 98cdb6
      gchar *display_name;
Packit 98cdb6
Packit 98cdb6
      if (g_file_is_native (files->data))
Packit 98cdb6
        fileinfo = g_file_query_info (files->data,
Packit 98cdb6
                                      G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
Packit 98cdb6
                                      G_FILE_ATTRIBUTE_STANDARD_ICON,
Packit 98cdb6
                                      0, NULL, NULL);
Packit 98cdb6
Packit 98cdb6
      display_name = get_display_name (files->data, fileinfo);
Packit 98cdb6
      description = g_strdup_printf (_("Opening %s"), display_name);
Packit 98cdb6
      g_free (display_name);
Packit 98cdb6
    }
Packit 98cdb6
  else
Packit 98cdb6
    description = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
Packit 98cdb6
                                                "Opening %d Item",
Packit 98cdb6
                                                "Opening %d Items",
Packit 98cdb6
                                                files_count), files_count);
Packit 98cdb6
Packit 98cdb6
  icon_name = NULL;
Packit 98cdb6
  if (priv->icon_name)
Packit 98cdb6
    icon_name = g_strdup (priv->icon_name);
Packit 98cdb6
  else
Packit 98cdb6
    {
Packit 98cdb6
      icon = NULL;
Packit 98cdb6
Packit 98cdb6
      if (priv->icon != NULL)
Packit 98cdb6
        icon = g_object_ref (priv->icon);
Packit 98cdb6
      else if (files_count == 1)
Packit 98cdb6
        icon = get_icon (files->data, fileinfo);
Packit 98cdb6
Packit 98cdb6
      if (icon == NULL)
Packit 98cdb6
        {
Packit 98cdb6
          icon = g_app_info_get_icon (info);
Packit 98cdb6
          if (icon != NULL)
Packit 98cdb6
            g_object_ref (icon);
Packit 98cdb6
        }
Packit 98cdb6
Packit 98cdb6
      if (icon != NULL)
Packit 98cdb6
        {
Packit 98cdb6
          icon_name = gicon_to_string (icon);
Packit 98cdb6
          g_object_unref (icon);
Packit 98cdb6
        }
Packit 98cdb6
    }
Packit 98cdb6
Packit 98cdb6
  binary_name = g_app_info_get_executable (info);
Packit 98cdb6
Packit 98cdb6
  timestamp = priv->timestamp;
Packit 98cdb6
  if (timestamp == GDK_CURRENT_TIME)
Packit 98cdb6
    timestamp = gdk_x11_display_get_user_time (display);
Packit 98cdb6
Packit 98cdb6
  screen_str = g_strdup_printf ("%d", gdk_screen_get_number (screen));
Packit 98cdb6
  if (priv->workspace > -1)
Packit 98cdb6
    workspace_str = g_strdup_printf ("%d", priv->workspace);
Packit 98cdb6
  else
Packit 98cdb6
    workspace_str = NULL;
Packit 98cdb6
Packit 98cdb6
  if (G_IS_DESKTOP_APP_INFO (info))
Packit 98cdb6
    application_id = g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (info));
Packit 98cdb6
  else
Packit 98cdb6
    application_id = NULL;
Packit 98cdb6
Packit 98cdb6
  startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
Packit 98cdb6
                                g_get_prgname (),
Packit 98cdb6
                                (unsigned long)getpid (),
Packit 98cdb6
                                g_get_host_name (),
Packit 98cdb6
                                binary_name,
Packit 98cdb6
                                sequence++,
Packit 98cdb6
                                (unsigned long)timestamp);
Packit 98cdb6
Packit 98cdb6
  gdk_x11_display_broadcast_startup_message (display, "new",
Packit 98cdb6
                                             "ID", startup_id,
Packit 98cdb6
                                             "NAME", g_app_info_get_name (info),
Packit 98cdb6
                                             "SCREEN", screen_str,
Packit 98cdb6
                                             "BIN", binary_name,
Packit 98cdb6
                                             "ICON", icon_name,
Packit 98cdb6
                                             "DESKTOP", workspace_str,
Packit 98cdb6
                                             "DESCRIPTION", description,
Packit 98cdb6
                                             "WMCLASS", NULL, /* FIXME */
Packit 98cdb6
                                             "APPLICATION_ID", application_id,
Packit 98cdb6
                                             NULL);
Packit 98cdb6
Packit 98cdb6
  g_free (description);
Packit 98cdb6
  g_free (screen_str);
Packit 98cdb6
  g_free (workspace_str);
Packit 98cdb6
  g_free (icon_name);
Packit 98cdb6
  if (fileinfo)
Packit 98cdb6
    g_object_unref (fileinfo);
Packit 98cdb6
Packit 98cdb6
  add_startup_timeout (screen, startup_id);
Packit 98cdb6
Packit 98cdb6
  return startup_id;
Packit 98cdb6
}
Packit 98cdb6
Packit 98cdb6
Packit 98cdb6
void
Packit 98cdb6
_gdk_windowing_launch_failed (GAppLaunchContext *context,
Packit 98cdb6
                              const char        *startup_notify_id)
Packit 98cdb6
{
Packit 98cdb6
  GdkAppLaunchContextPrivate *priv;
Packit 98cdb6
  GdkScreen *screen;
Packit 98cdb6
  StartupTimeoutData *data;
Packit 98cdb6
  StartupNotificationData *sn_data;
Packit 98cdb6
  GSList *l;
Packit 98cdb6
Packit 98cdb6
  priv = GDK_APP_LAUNCH_CONTEXT (context)->priv;
Packit 98cdb6
Packit 98cdb6
  if (priv->screen)
Packit 98cdb6
    screen = priv->screen;
Packit 98cdb6
  else if (priv->display)
Packit 98cdb6
    screen = gdk_display_get_default_screen (priv->display);
Packit 98cdb6
  else
Packit 98cdb6
    screen = gdk_display_get_default_screen (gdk_display_get_default ());
Packit 98cdb6
Packit 98cdb6
  data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data");
Packit 98cdb6
Packit 98cdb6
  if (data)
Packit 98cdb6
    {
Packit 98cdb6
      for (l = data->contexts; l != NULL; l = l->next)
Packit 98cdb6
        {
Packit 98cdb6
          sn_data = l->data;
Packit 98cdb6
          if (strcmp (startup_notify_id, sn_data->startup_id) == 0)
Packit 98cdb6
            {
Packit 98cdb6
              data->contexts = g_slist_remove (data->contexts, sn_data);
Packit 98cdb6
              end_startup_notification (sn_data->display, sn_data->startup_id);
Packit 98cdb6
              free_startup_notification_data (sn_data);
Packit 98cdb6
Packit 98cdb6
              break;
Packit 98cdb6
            }
Packit 98cdb6
        }
Packit 98cdb6
Packit 98cdb6
      if (data->contexts == NULL)
Packit 98cdb6
        {
Packit 98cdb6
          g_source_remove (data->timeout_id);
Packit 98cdb6
          data->timeout_id = 0;
Packit 98cdb6
        }
Packit 98cdb6
    }
Packit 98cdb6
}