]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkapplaunchcontext-x11.c
Merge branch 'master' into broadway
[~andy/gtk] / gdk / x11 / gdkapplaunchcontext-x11.c
1 /* gdkapplaunchcontext-x11.c - Gtk+ implementation for GAppLaunchContext
2
3    Copyright (C) 2007 Red Hat, Inc.
4
5    The Gnome Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    The Gnome Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with the Gnome Library; see the file COPYING.LIB.  If not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.
19
20    Author: Alexander Larsson <alexl@redhat.com>
21 */
22
23 #include "config.h"
24
25 #include "gdkapplaunchcontext.h"
26
27 #include "gdkx.h"
28 #include "gdkscreen.h"
29 #include "gdkinternals.h"
30 #include "gdkintl.h"
31
32 #include <glib.h>
33 #include <gio/gdesktopappinfo.h>
34
35 #include <string.h>
36 #include <unistd.h>
37
38 static char *
39 get_display_name (GFile     *file,
40                   GFileInfo *info)
41 {
42   char *name, *tmp;
43
44   name = NULL;
45   if (info)
46     name = g_strdup (g_file_info_get_display_name (info));
47
48   if (name == NULL)
49     {
50       name = g_file_get_basename (file);
51       if (!g_utf8_validate (name, -1, NULL))
52         {
53           tmp = name;
54           name =
55             g_uri_escape_string (name, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
56           g_free (tmp);
57         }
58     }
59
60   return name;
61 }
62
63 static GIcon *
64 get_icon (GFile     *file,
65           GFileInfo *info)
66 {
67   GIcon *icon;
68
69   icon = NULL;
70
71   if (info)
72     {
73       icon = g_file_info_get_icon (info);
74       if (icon)
75         g_object_ref (icon);
76     }
77
78   return icon;
79 }
80
81 static char *
82 gicon_to_string (GIcon *icon)
83 {
84   GFile *file;
85   const char *const *names;
86
87   if (G_IS_FILE_ICON (icon))
88     {
89       file = g_file_icon_get_file (G_FILE_ICON (icon));
90       if (file)
91         return g_file_get_path (file);
92     }
93   else if (G_IS_THEMED_ICON (icon))
94     {
95       names = g_themed_icon_get_names (G_THEMED_ICON (icon));
96       if (names)
97         return g_strdup (names[0]);
98     }
99   else if (G_IS_EMBLEMED_ICON (icon))
100     {
101       GIcon *base;
102
103       base = g_emblemed_icon_get_icon (G_EMBLEMED_ICON (icon));
104
105       return gicon_to_string (base);
106     }
107
108   return NULL;
109 }
110
111 static void
112 end_startup_notification (GdkDisplay *display,
113                           const char *startup_id)
114 {
115   gdk_x11_display_broadcast_startup_message (display, "remove",
116                                              "ID", startup_id,
117                                              NULL);
118 }
119
120
121 /* This should be fairly long, as it's confusing to users if a startup
122  * ends when it shouldn't (it appears that the startup failed, and
123  * they have to relaunch the app). Also the timeout only matters when
124  * there are bugs and apps don't end their own startup sequence.
125  *
126  * This timeout is a "last resort" timeout that ignores whether the
127  * startup sequence has shown activity or not.  Metacity and the
128  * tasklist have smarter, and correspondingly able-to-be-shorter
129  * timeouts. The reason our timeout is dumb is that we don't monitor
130  * the sequence (don't use an SnMonitorContext)
131  */
132 #define STARTUP_TIMEOUT_LENGTH_SECONDS 30 
133 #define STARTUP_TIMEOUT_LENGTH (STARTUP_TIMEOUT_LENGTH_SECONDS * 1000)
134
135 typedef struct 
136 {
137   GdkDisplay *display;
138   char *startup_id;
139   GTimeVal time;
140 } StartupNotificationData;
141
142 static void
143 free_startup_notification_data (gpointer data)
144 {
145   StartupNotificationData *sn_data = data;
146
147   g_object_unref (sn_data->display);
148   g_free (sn_data->startup_id);
149   g_free (sn_data);
150 }
151
152 typedef struct 
153 {
154   GSList *contexts;
155   guint timeout_id;
156 } StartupTimeoutData;
157
158 static void
159 free_startup_timeout (void *data)
160 {
161   StartupTimeoutData *std;
162
163   std = data;
164
165   g_slist_foreach (std->contexts, (GFunc) free_startup_notification_data, NULL);
166   g_slist_free (std->contexts);
167
168   if (std->timeout_id != 0)
169     {
170       g_source_remove (std->timeout_id);
171       std->timeout_id = 0;
172     }
173
174   g_free (std);
175 }
176
177 static gboolean
178 startup_timeout (void *data)
179 {
180   StartupTimeoutData *std;
181   GSList *tmp;
182   GTimeVal now;
183   int min_timeout;
184
185   std = data;
186
187   min_timeout = STARTUP_TIMEOUT_LENGTH;
188
189   g_get_current_time (&now);
190
191   tmp = std->contexts;
192   while (tmp != NULL)
193     {
194       StartupNotificationData *sn_data;
195       GSList *next;
196       double elapsed;
197
198       sn_data = tmp->data;
199       next = tmp->next;
200
201       elapsed =
202         ((((double) now.tv_sec - sn_data->time.tv_sec) * G_USEC_PER_SEC +
203           (now.tv_usec - sn_data->time.tv_usec))) / 1000.0;
204
205       if (elapsed >= STARTUP_TIMEOUT_LENGTH)
206         {
207           std->contexts = g_slist_remove (std->contexts, sn_data);
208           end_startup_notification (sn_data->display, sn_data->startup_id);
209           free_startup_notification_data (sn_data);
210         }
211       else
212         {
213           min_timeout = MIN (min_timeout, (STARTUP_TIMEOUT_LENGTH - elapsed));
214         }
215
216       tmp = next;
217     }
218
219   if (std->contexts == NULL)
220     std->timeout_id = 0;
221   else
222     std->timeout_id = g_timeout_add_seconds ((min_timeout + 500)/1000, startup_timeout, std);
223
224   /* always remove this one, but we may have reinstalled another one. */
225   return FALSE;
226 }
227
228
229 static void
230 add_startup_timeout (GdkScreen  *screen,
231                      const char *startup_id)
232 {
233   StartupTimeoutData *data;
234   StartupNotificationData *sn_data;
235
236   data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data");
237
238   if (data == NULL)
239     {
240       data = g_new (StartupTimeoutData, 1);
241       data->contexts = NULL;
242       data->timeout_id = 0;
243
244       g_object_set_data_full (G_OBJECT (screen), "appinfo-startup-data",
245                               data, free_startup_timeout);
246     }
247
248   sn_data = g_new (StartupNotificationData, 1);
249   sn_data->display = g_object_ref (gdk_screen_get_display (screen));
250   sn_data->startup_id = g_strdup (startup_id);
251   g_get_current_time (&sn_data->time);
252
253   data->contexts = g_slist_prepend (data->contexts, sn_data);
254
255   if (data->timeout_id == 0)
256     data->timeout_id = g_timeout_add_seconds (STARTUP_TIMEOUT_LENGTH_SECONDS,
257                                               startup_timeout, data);
258 }
259
260
261 char *
262 _gdk_windowing_get_startup_notify_id (GAppLaunchContext *context,
263                                       GAppInfo          *info,
264                                       GList             *files)
265 {
266   static int sequence = 0;
267   GdkAppLaunchContextPrivate *priv;
268   GdkDisplay *display;
269   GdkScreen *screen;
270   int files_count;
271   char *description;
272   char *icon_name;
273   const char *binary_name;
274   const char *application_id;
275   char *screen_str;
276   char *workspace_str;
277   GIcon *icon;
278   guint32 timestamp;
279   char *startup_id;
280   GFileInfo *fileinfo;
281
282   priv = GDK_APP_LAUNCH_CONTEXT (context)->priv;
283
284   if (priv->screen)
285     {
286       screen = priv->screen;
287       display = gdk_screen_get_display (priv->screen);
288     }
289   else if (priv->display)
290     {
291       display = priv->display;
292       screen = gdk_display_get_default_screen (display);
293     }
294   else
295     {
296       display = gdk_display_get_default ();
297       screen = gdk_display_get_default_screen (display);
298     }
299
300   fileinfo = NULL;
301
302   files_count = g_list_length (files);
303   if (files_count == 0)
304     {
305       description = g_strdup_printf (_("Starting %s"), g_app_info_get_name (info));
306     }
307   else if (files_count == 1)
308     {
309       gchar *display_name;
310
311       if (g_file_is_native (files->data))
312         fileinfo = g_file_query_info (files->data,
313                                       G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME ","
314                                       G_FILE_ATTRIBUTE_STANDARD_ICON,
315                                       0, NULL, NULL);
316
317       display_name = get_display_name (files->data, fileinfo);
318       description = g_strdup_printf (_("Opening %s"), display_name);
319       g_free (display_name);
320     }
321   else
322     description = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
323                                                 "Opening %d Item",
324                                                 "Opening %d Items",
325                                                 files_count), files_count);
326
327   icon_name = NULL;
328   if (priv->icon_name)
329     icon_name = g_strdup (priv->icon_name);
330   else
331     {
332       icon = NULL;
333
334       if (priv->icon != NULL)
335         icon = g_object_ref (priv->icon);
336       else if (files_count == 1)
337         icon = get_icon (files->data, fileinfo);
338
339       if (icon == NULL)
340         {
341           icon = g_app_info_get_icon (info);
342           g_object_ref (icon);
343         }
344
345       if (icon)
346         icon_name = gicon_to_string (icon);
347
348       g_object_unref (icon);
349     }
350
351   binary_name = g_app_info_get_executable (info);
352
353   timestamp = priv->timestamp;
354   if (timestamp == GDK_CURRENT_TIME)
355     timestamp = gdk_x11_display_get_user_time (display);
356
357   screen_str = g_strdup_printf ("%d", gdk_screen_get_number (screen));
358   if (priv->workspace > -1)
359     workspace_str = g_strdup_printf ("%d", priv->workspace);
360   else
361     workspace_str = NULL;
362
363   if (G_IS_DESKTOP_APP_INFO (info))
364     application_id = g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (info));
365   else
366     application_id = NULL;
367
368   startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
369                                 g_get_prgname (),
370                                 (unsigned long)getpid (),
371                                 g_get_host_name (),
372                                 binary_name,
373                                 sequence++,
374                                 (unsigned long)timestamp);
375
376   gdk_x11_display_broadcast_startup_message (display, "new",
377                                              "ID", startup_id,
378                                              "NAME", g_app_info_get_name (info),
379                                              "SCREEN", screen_str,
380                                              "BIN", binary_name,
381                                              "ICON", icon_name,
382                                              "DESKTOP", workspace_str,
383                                              "DESCRIPTION", description,
384                                              "WMCLASS", NULL, /* FIXME */
385                                              "APPLICATION_ID", application_id,
386                                              NULL);
387
388   g_free (description);
389   g_free (screen_str);
390   g_free (workspace_str);
391   g_free (icon_name);
392   if (fileinfo)
393     g_object_unref (fileinfo);
394
395   add_startup_timeout (screen, startup_id);
396
397   return startup_id;
398 }
399
400
401 void
402 _gdk_windowing_launch_failed (GAppLaunchContext *context,
403                               const char        *startup_notify_id)
404 {
405   GdkAppLaunchContextPrivate *priv;
406   GdkScreen *screen;
407   StartupTimeoutData *data;
408   StartupNotificationData *sn_data;
409   GSList *l;
410
411   priv = GDK_APP_LAUNCH_CONTEXT (context)->priv;
412
413   if (priv->screen)
414     screen = priv->screen;
415   else if (priv->display)
416     screen = gdk_display_get_default_screen (priv->display);
417   else
418     screen = gdk_display_get_default_screen (gdk_display_get_default ());
419
420   data = g_object_get_data (G_OBJECT (screen), "appinfo-startup-data");
421
422   if (data)
423     {
424       for (l = data->contexts; l != NULL; l = l->next)
425         {
426           sn_data = l->data;
427           if (strcmp (startup_notify_id, sn_data->startup_id) == 0)
428             {
429               data->contexts = g_slist_remove (data->contexts, sn_data);
430               end_startup_notification (sn_data->display, sn_data->startup_id);
431               free_startup_notification_data (sn_data);
432
433               break;
434             }
435         }
436
437       if (data->contexts == NULL)
438         {
439           g_source_remove (data->timeout_id);
440           data->timeout_id = 0;
441         }
442     }
443 }