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