]> Pileus Git - ~andy/gtk/blob - gtk/gtkmountoperation-x11.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkmountoperation-x11.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* GTK - The GIMP Toolkit
3  * Copyright (C) David Zeuthen <davidz@redhat.com>
4  * Copyright (C) 2001 Havoc Pennington
5  * Copyright (C) 2005-2007 Vincent Untz
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
26  */
27
28 #include "config.h"
29
30 #include <string.h>
31 #include <stdlib.h>
32 #include <gio/gio.h>
33 #include "x11/gdkx.h"
34 #include <X11/Xatom.h>
35 #include <gtk/gtkicontheme.h>
36 #include "gtkintl.h"
37
38 /* for the kill(2) system call and errno - POSIX.1-2001 and later */
39 #include <sys/types.h>
40 #include <signal.h>
41 #include <errno.h>
42
43 #if defined(__OpenBSD__)
44 #include <stdlib.h>
45 #include <sys/param.h>
46 #include <fcntl.h>
47 #include <sys/sysctl.h>
48 #endif
49
50 #include "gtkmountoperationprivate.h"
51
52 /* ---------------------------------------------------------------------------------------------------- */
53 /* these functions are based on code from libwnck (LGPLv2) */
54
55 static gboolean get_window_list   (Display   *xdisplay,
56                                    Window     xwindow,
57                                    Atom       atom,
58                                    Window   **windows,
59                                    int       *len);
60
61 static char*    get_utf8_property (Display   *xdisplay,
62                                    Window     xwindow,
63                                    Atom       atom);
64
65 static gboolean get_cardinal      (Display   *xdisplay,
66                                    Window     xwindow,
67                                    Atom       atom,
68                                    int       *val);
69
70 static gboolean read_rgb_icon     (Display   *xdisplay,
71                                    Window     xwindow,
72                                    int        ideal_width,
73                                    int        ideal_height,
74                                    int       *width,
75                                    int       *height,
76                                    guchar   **pixdata);
77
78
79 static gboolean
80 get_cardinal (Display *xdisplay,
81               Window   xwindow,
82               Atom     atom,
83               int     *val)
84 {
85   Atom type;
86   int format;
87   gulong nitems;
88   gulong bytes_after;
89   gulong *num;
90   int err, result;
91
92   *val = 0;
93
94   gdk_error_trap_push ();
95   type = None;
96   result = XGetWindowProperty (xdisplay,
97                                xwindow,
98                                atom,
99                                0, G_MAXLONG,
100                                False, XA_CARDINAL, &type, &format, &nitems,
101                                &bytes_after, (void*)&num);
102   XSync (xdisplay, False);
103   err = gdk_error_trap_pop ();
104
105   if (err != Success ||
106       result != Success)
107     return FALSE;
108
109   if (type != XA_CARDINAL)
110     {
111       XFree (num);
112       return FALSE;
113     }
114
115   *val = *num;
116
117   XFree (num);
118
119   return TRUE;
120 }
121
122 static char*
123 get_utf8_property (Display *xdisplay,
124                    Window   xwindow,
125                    Atom     atom)
126 {
127   Atom type;
128   int format;
129   gulong nitems;
130   gulong bytes_after;
131   gchar *val;
132   int err, result;
133   char *retval;
134   Atom utf8_string;
135
136   utf8_string = gdk_x11_get_xatom_by_name ("UTF8_STRING");
137
138   gdk_error_trap_push ();
139   type = None;
140   val = NULL;
141   result = XGetWindowProperty (xdisplay,
142                                xwindow,
143                                atom,
144                                0, G_MAXLONG,
145                                False, utf8_string,
146                                &type, &format, &nitems,
147                                &bytes_after, (guchar **)&val);
148   XSync (xdisplay, False);
149   err = gdk_error_trap_pop ();
150
151   if (err != Success ||
152       result != Success)
153     return NULL;
154
155   if (type != utf8_string ||
156       format != 8 ||
157       nitems == 0)
158     {
159       if (val)
160         XFree (val);
161       return NULL;
162     }
163
164   if (!g_utf8_validate (val, nitems, NULL))
165     {
166       g_warning ("Property %s contained invalid UTF-8\n",
167                  gdk_x11_get_xatom_name (atom));
168       XFree (val);
169       return NULL;
170     }
171
172   retval = g_strndup (val, nitems);
173
174   XFree (val);
175
176   return retval;
177 }
178
179 static gboolean
180 find_largest_sizes (gulong *data,
181                     gulong  nitems,
182                     int    *width,
183                     int    *height)
184 {
185   *width = 0;
186   *height = 0;
187
188   while (nitems > 0)
189     {
190       int w, h;
191
192       if (nitems < 3)
193         return FALSE; /* no space for w, h */
194
195       w = data[0];
196       h = data[1];
197
198       if (nitems < ((w * h) + 2))
199         return FALSE; /* not enough data */
200
201       *width = MAX (w, *width);
202       *height = MAX (h, *height);
203
204       data += (w * h) + 2;
205       nitems -= (w * h) + 2;
206     }
207
208   return TRUE;
209 }
210
211 static gboolean
212 find_best_size (gulong  *data,
213                 gulong   nitems,
214                 int      ideal_width,
215                 int      ideal_height,
216                 int     *width,
217                 int     *height,
218                 gulong **start)
219 {
220   int best_w;
221   int best_h;
222   gulong *best_start;
223   int max_width, max_height;
224
225   *width = 0;
226   *height = 0;
227   *start = NULL;
228
229   if (!find_largest_sizes (data, nitems, &max_width, &max_height))
230     return FALSE;
231
232   if (ideal_width < 0)
233     ideal_width = max_width;
234   if (ideal_height < 0)
235     ideal_height = max_height;
236
237   best_w = 0;
238   best_h = 0;
239   best_start = NULL;
240
241   while (nitems > 0)
242     {
243       int w, h;
244       gboolean replace;
245
246       replace = FALSE;
247
248       if (nitems < 3)
249         return FALSE; /* no space for w, h */
250
251       w = data[0];
252       h = data[1];
253
254       if (nitems < ((w * h) + 2))
255         break; /* not enough data */
256
257       if (best_start == NULL)
258         {
259           replace = TRUE;
260         }
261       else
262         {
263           /* work with averages */
264           const int ideal_size = (ideal_width + ideal_height) / 2;
265           int best_size = (best_w + best_h) / 2;
266           int this_size = (w + h) / 2;
267
268           /* larger than desired is always better than smaller */
269           if (best_size < ideal_size &&
270               this_size >= ideal_size)
271             replace = TRUE;
272           /* if we have too small, pick anything bigger */
273           else if (best_size < ideal_size &&
274                    this_size > best_size)
275             replace = TRUE;
276           /* if we have too large, pick anything smaller
277            * but still >= the ideal
278            */
279           else if (best_size > ideal_size &&
280                    this_size >= ideal_size &&
281                    this_size < best_size)
282             replace = TRUE;
283         }
284
285       if (replace)
286         {
287           best_start = data + 2;
288           best_w = w;
289           best_h = h;
290         }
291
292       data += (w * h) + 2;
293       nitems -= (w * h) + 2;
294     }
295
296   if (best_start)
297     {
298       *start = best_start;
299       *width = best_w;
300       *height = best_h;
301       return TRUE;
302     }
303   else
304     return FALSE;
305 }
306
307 static void
308 argbdata_to_pixdata (gulong  *argb_data,
309                      int      len,
310                      guchar **pixdata)
311 {
312   guchar *p;
313   int i;
314
315   *pixdata = g_new (guchar, len * 4);
316   p = *pixdata;
317
318   /* One could speed this up a lot. */
319   i = 0;
320   while (i < len)
321     {
322       guint argb;
323       guint rgba;
324
325       argb = argb_data[i];
326       rgba = (argb << 8) | (argb >> 24);
327
328       *p = rgba >> 24;
329       ++p;
330       *p = (rgba >> 16) & 0xff;
331       ++p;
332       *p = (rgba >> 8) & 0xff;
333       ++p;
334       *p = rgba & 0xff;
335       ++p;
336
337       ++i;
338     }
339 }
340
341 static gboolean
342 read_rgb_icon (Display   *xdisplay,
343                Window     xwindow,
344                int        ideal_width,
345                int        ideal_height,
346                int       *width,
347                int       *height,
348                guchar   **pixdata)
349 {
350   Atom type;
351   int format;
352   gulong nitems;
353   gulong bytes_after;
354   int result, err;
355   gulong *data;
356   gulong *best;
357   int w, h;
358
359   gdk_error_trap_push ();
360   type = None;
361   data = NULL;
362   result = XGetWindowProperty (xdisplay,
363                                xwindow,
364                                gdk_x11_get_xatom_by_name ("_NET_WM_ICON"),
365                                0, G_MAXLONG,
366                                False, XA_CARDINAL, &type, &format, &nitems,
367                                &bytes_after, (void*)&data);
368
369   XSync (xdisplay, False);
370   err = gdk_error_trap_pop ();
371
372   if (err != Success ||
373       result != Success)
374     return FALSE;
375
376   if (type != XA_CARDINAL)
377     {
378       XFree (data);
379       return FALSE;
380     }
381
382   if (!find_best_size (data, nitems,
383                        ideal_width, ideal_height,
384                        &w, &h, &best))
385     {
386       XFree (data);
387       return FALSE;
388     }
389
390   *width = w;
391   *height = h;
392
393   argbdata_to_pixdata (best, w * h, pixdata);
394
395   XFree (data);
396
397   return TRUE;
398 }
399
400 static void
401 free_pixels (guchar *pixels, gpointer data)
402 {
403   g_free (pixels);
404 }
405
406 static GdkPixbuf*
407 scaled_from_pixdata (guchar *pixdata,
408                      int     w,
409                      int     h,
410                      int     new_w,
411                      int     new_h)
412 {
413   GdkPixbuf *src;
414   GdkPixbuf *dest;
415
416   src = gdk_pixbuf_new_from_data (pixdata,
417                                   GDK_COLORSPACE_RGB,
418                                   TRUE,
419                                   8,
420                                   w, h, w * 4,
421                                   free_pixels,
422                                   NULL);
423
424   if (src == NULL)
425     return NULL;
426
427   if (w != h)
428     {
429       GdkPixbuf *tmp;
430       int size;
431
432       size = MAX (w, h);
433
434       tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, size, size);
435
436       if (tmp != NULL)
437         {
438           gdk_pixbuf_fill (tmp, 0);
439           gdk_pixbuf_copy_area (src, 0, 0, w, h,
440                                 tmp,
441                                 (size - w) / 2, (size - h) / 2);
442
443           g_object_unref (src);
444           src = tmp;
445         }
446     }
447
448   if (w != new_w || h != new_h)
449     {
450       dest = gdk_pixbuf_scale_simple (src, new_w, new_h, GDK_INTERP_BILINEAR);
451
452       g_object_unref (G_OBJECT (src));
453     }
454   else
455     {
456       dest = src;
457     }
458
459   return dest;
460 }
461
462 static gboolean
463 get_window_list (Display  *xdisplay,
464                  Window    xwindow,
465                  Atom      atom,
466                  Window  **windows,
467                  int      *len)
468 {
469   Atom type;
470   int format;
471   gulong nitems;
472   gulong bytes_after;
473   Window *data;
474   int err, result;
475
476   *windows = NULL;
477   *len = 0;
478
479   gdk_error_trap_push ();
480   type = None;
481   result = XGetWindowProperty (xdisplay,
482                                xwindow,
483                                atom,
484                                0, G_MAXLONG,
485                                False, XA_WINDOW, &type, &format, &nitems,
486                                &bytes_after, (void*)&data);
487   XSync (xdisplay, False);
488   err = gdk_error_trap_pop ();
489
490   if (err != Success ||
491       result != Success)
492     return FALSE;
493
494   if (type != XA_WINDOW)
495     {
496       XFree (data);
497       return FALSE;
498     }
499
500   *windows = g_new (Window, nitems);
501   memcpy (*windows, data, sizeof (Window) * nitems);
502   *len = nitems;
503
504   XFree (data);
505
506   return TRUE;
507 }
508
509
510 /* ---------------------------------------------------------------------------------------------------- */
511
512 struct _GtkMountOperationLookupContext
513 {
514   /* Hash from pid (gint) -> XID (gint)
515    *
516    * Note that XIDs are at most 27 bits - however, also note that sizeof(XID) == 8 on
517    * x86_64 - that's just xlib brokenness. So it's safe to stuff the XID into a pointer.
518    */
519   GHashTable *pid_to_window;
520   GdkDisplay *display;
521 };
522
523 GtkMountOperationLookupContext *
524 _gtk_mount_operation_lookup_context_get (GdkDisplay *display)
525 {
526   GtkMountOperationLookupContext *context;
527   Window *mapping;
528   gint mapping_length;
529   gint n;
530
531   context = g_new0 (GtkMountOperationLookupContext, 1);
532
533   context->pid_to_window = g_hash_table_new (g_direct_hash, g_direct_equal);
534   context->display = display;
535
536   mapping = NULL;
537   mapping_length = 0;
538   get_window_list (GDK_DISPLAY_XDISPLAY (context->display),
539                    GDK_ROOT_WINDOW(),
540                    gdk_x11_get_xatom_by_name_for_display (context->display,
541                                                           "_NET_CLIENT_LIST"),
542                    &mapping,
543                    &mapping_length);
544   for (n = 0; n < mapping_length; n++)
545     {
546       gint pid;
547
548       if (!get_cardinal (GDK_DISPLAY_XDISPLAY (context->display),
549                          mapping[n],
550                          gdk_x11_get_xatom_by_name_for_display (context->display,
551                                                                 "_NET_WM_PID"),
552                          &pid))
553         continue;
554
555       g_hash_table_insert (context->pid_to_window,
556                            GINT_TO_POINTER (pid),
557                            GINT_TO_POINTER ((gint) mapping[n]));
558     }
559   g_free (mapping);
560
561   return context;
562 }
563
564 void
565 _gtk_mount_operation_lookup_context_free (GtkMountOperationLookupContext *context)
566 {
567   g_hash_table_unref (context->pid_to_window);
568   g_free (context);
569 }
570
571 /* ---------------------------------------------------------------------------------------------------- */
572
573 #ifdef __linux__
574
575 static GPid
576 pid_get_parent (GPid pid)
577 {
578   GPid ppid;
579   gchar **tokens;
580   gchar *stat_filename;
581   gchar *stat_contents;
582   gsize stat_len;
583
584   ppid = 0;
585   tokens = NULL;
586   stat_contents = NULL;
587   stat_filename = NULL;
588
589   /* fail if trying to get the parent of the init process (no such thing) */
590   if (pid == 1)
591       goto out;
592
593   stat_filename = g_strdup_printf ("/proc/%d/status", pid);
594   if (g_file_get_contents (stat_filename,
595                            &stat_contents,
596                            &stat_len,
597                            NULL))
598     {
599       guint n;
600
601       tokens = g_strsplit (stat_contents, "\n", 0);
602
603       for (n = 0; tokens[n] != NULL; n++)
604         {
605           if (g_str_has_prefix (tokens[n], "PPid:"))
606             {
607               gchar *endp;
608
609               endp = NULL;
610               ppid = strtoll (tokens[n] + sizeof "PPid:" - 1, &endp, 10);
611               if (endp == NULL || *endp != '\0')
612                 {
613                   g_warning ("Error parsing contents of `%s'. Parent pid is malformed.",
614                              stat_filename);
615                   ppid = 0;
616                   goto out;
617                 }
618
619               break;
620             }
621         }
622     }
623
624  out:
625   g_strfreev (tokens);
626   g_free (stat_contents);
627   g_free (stat_filename);
628
629   return ppid;
630 }
631
632 static gchar *
633 pid_get_env (GPid         pid,
634              const gchar *key)
635 {
636   gchar *ret;
637   gchar *env_filename;
638   gchar *env;
639   gsize env_len;
640   gsize key_len;
641   gchar *end;
642
643   ret = NULL;
644
645   key_len = strlen (key);
646
647   env_filename = g_strdup_printf ("/proc/%d/environ", pid);
648   if (g_file_get_contents (env_filename,
649                            &env,
650                            &env_len,
651                            NULL))
652     {
653       guint n;
654
655       /* /proc/<pid>/environ in Linux is split at '\0' points, g_strsplit() can't handle that... */
656       n = 0;
657       while (TRUE)
658         {
659           if (env[n] == '\0' || n >= env_len)
660             break;
661
662           if (g_str_has_prefix (env + n, key) && (*(env + n + key_len) == '='))
663             {
664               ret = g_strdup (env + n + key_len + 1);
665
666               /* skip invalid UTF-8 */
667               if (!g_utf8_validate (ret, -1, (const gchar **) &end))
668                 *end = '\0';
669               break;
670             }
671
672           for (; env[n] != '\0' && n < env_len; n++)
673             ;
674           n++;
675         }
676       g_free (env);
677     }
678   g_free (env_filename);
679
680   return ret;
681 }
682
683 static gchar *
684 pid_get_command_line (GPid pid)
685 {
686   gchar *cmdline_filename;
687   gchar *cmdline_contents;
688   gsize cmdline_len;
689   guint n;
690   gchar *end;
691
692   cmdline_contents = NULL;
693
694   cmdline_filename = g_strdup_printf ("/proc/%d/cmdline", pid);
695   if (!g_file_get_contents (cmdline_filename,
696                             &cmdline_contents,
697                             &cmdline_len,
698                             NULL))
699     goto out;
700
701   /* /proc/<pid>/cmdline separates args by NUL-bytes - replace with spaces */
702   for (n = 0; n < cmdline_len - 1; n++)
703     {
704       if (cmdline_contents[n] == '\0')
705         cmdline_contents[n] = ' ';
706     }
707
708   /* skip invalid UTF-8 */
709   if (!g_utf8_validate (cmdline_contents, -1, (const gchar **) &end))
710       *end = '\0';
711
712  out:
713   g_free (cmdline_filename);
714
715   return cmdline_contents;
716 }
717
718 /* ---------------------------------------------------------------------------------------------------- */
719
720 #elif defined(__OpenBSD__)
721
722 /* ---------------------------------------------------------------------------------------------------- */
723
724 static GPid
725 pid_get_parent (GPid pid)
726 {
727   struct kinfo_proc kp;
728   size_t len;
729   GPid ppid;
730
731   int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, pid,
732                 sizeof(struct kinfo_proc), 0 };
733
734   if (sysctl(mib, G_N_ELEMENTS (mib), NULL, &len, NULL, 0) == -1)
735       return (-1);
736   mib[5] = (len / sizeof(struct kinfo_proc));
737
738   if (sysctl(mib, G_N_ELEMENTS (mib), &kp, &len, NULL, 0) < 0)
739       return -1;
740
741   ppid = kp.p_ppid;
742
743   return ppid;
744 }
745
746 static gchar *
747 pid_get_env (GPid pid, const gchar *key)
748 {
749   size_t len = PATH_MAX;
750   char **strs = NULL;
751   char *ret;
752   char *end;
753   int key_len;
754   int i;
755
756   int mib[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ENV };
757
758   strs = (char **)realloc(strs, len);
759
760   key_len = strlen (key);
761
762   ret = NULL;
763   if (sysctl(mib, G_N_ELEMENTS (mib), strs, &len, NULL, 0) != -1)
764     {
765       for (i = 0; strs[i] != NULL; i++)
766         {
767           if (g_str_has_prefix (strs[i], key) && (*(strs[i] + key_len) == '='))
768             {
769               ret = g_strdup (strs[i] + key_len + 1);
770
771               /* skip invalid UTF-8 */
772               if (!g_utf8_validate (ret, -1, (const gchar **) &end))
773                 *end = '\0';
774               break;
775             }
776         }
777     }
778
779   g_free (strs);
780   return ret;
781 }
782
783 static gchar *
784 pid_get_command_line (GPid pid)
785 {
786   size_t len = PATH_MAX;
787   char **strs = NULL;
788   char *ret = NULL;
789   char *end;
790
791   int mib[] = { CTL_KERN, KERN_PROC_ARGS, pid, KERN_PROC_ARGV };
792
793   strs = (char **)realloc(strs, len);
794
795   if (sysctl(mib, G_N_ELEMENTS (mib), strs, &len, NULL, 0) == -1) {
796     g_free (strs);
797     return ret;
798   }
799
800   ret = g_strjoinv (" ", strs);
801   /* skip invalid UTF-8 */
802   if (!g_utf8_validate (ret, -1, (const gchar **) &end))
803     *end = '\0';
804
805   g_free (strs);
806   return ret;
807 }
808
809 #else
810
811 /* TODO: please implement for your OS - must return valid UTF-8 */
812
813 static GPid
814 pid_get_parent (GPid pid)
815 {
816   return 0;
817 }
818
819 static gchar *
820 pid_get_env (GPid         pid,
821              const gchar *key)
822 {
823   return NULL;
824 }
825
826 static gchar *
827 pid_get_command_line (GPid pid)
828 {
829   return NULL;
830 }
831
832 #endif
833
834 /* ---------------------------------------------------------------------------------------------------- */
835
836 static gchar *
837 get_name_for_window_with_pid (GtkMountOperationLookupContext *context,
838                               GPid                            pid)
839 {
840   Window window;
841   Window windowid_window;
842   gchar *ret;
843
844   ret = NULL;
845
846   window = GPOINTER_TO_INT (g_hash_table_lookup (context->pid_to_window, GINT_TO_POINTER (pid)));
847   if (window == None)
848     {
849       gchar *windowid_value;
850
851       /* check for $WINDOWID (set by terminals) and see if we can get the title that way */
852       windowid_value = pid_get_env (pid, "WINDOWID");
853       if (windowid_value != NULL)
854         {
855           gchar *endp;
856
857           endp = NULL;
858           windowid_window = (Window) g_ascii_strtoll (windowid_value, &endp, 10);
859           if (endp != NULL || *endp == '\0')
860             {
861               window = windowid_window;
862             }
863           g_free (windowid_value);
864         }
865
866       /* otherwise, check for parents */
867       if (window == None)
868         {
869           do
870             {
871               pid = pid_get_parent (pid);
872               if (pid == 0)
873                 break;
874
875               window = GPOINTER_TO_INT (g_hash_table_lookup (context->pid_to_window, GINT_TO_POINTER (pid)));
876               if (window != None)
877                 break;
878             }
879           while (TRUE);
880         }
881     }
882
883   if (window != None)
884     {
885       ret = get_utf8_property (GDK_DISPLAY_XDISPLAY (context->display),
886                                window,
887                                gdk_x11_get_xatom_by_name_for_display (context->display,
888                                                                       "_NET_WM_NAME"));
889       if (ret == NULL)
890         ret = get_utf8_property (GDK_DISPLAY_XDISPLAY (context->display),
891                                  window, gdk_x11_get_xatom_by_name_for_display (context->display,
892                                                                                 "_NET_WM_ICON_NAME"));
893     }
894
895   return ret;
896 }
897
898 /* ---------------------------------------------------------------------------------------------------- */
899
900 static GdkPixbuf *
901 get_pixbuf_for_window_with_pid (GtkMountOperationLookupContext *context,
902                                 GPid                            pid,
903                                 gint                            size_pixels)
904 {
905   Window window;
906   GdkPixbuf *ret;
907
908   ret = NULL;
909
910   window = GPOINTER_TO_INT (g_hash_table_lookup (context->pid_to_window, GINT_TO_POINTER (pid)));
911   if (window == None)
912     {
913       /* otherwise, check for parents */
914       do
915         {
916           pid = pid_get_parent (pid);
917           if (pid == 0)
918             break;
919
920           window = GPOINTER_TO_INT (g_hash_table_lookup (context->pid_to_window, GINT_TO_POINTER (pid)));
921           if (window != None)
922             break;
923         }
924       while (TRUE);
925     }
926
927   if (window != None)
928     {
929       gint    width;
930       gint    height;
931       guchar *pixdata;
932
933       if (read_rgb_icon (GDK_DISPLAY_XDISPLAY (context->display),
934                          window,
935                          size_pixels, size_pixels,
936                          &width, &height,
937                          &pixdata))
938         {
939           /* steals pixdata */
940           ret = scaled_from_pixdata (pixdata,
941                                      width, height,
942                                      size_pixels, size_pixels);
943         }
944     }
945
946   return ret;
947 }
948
949 /* ---------------------------------------------------------------------------------------------------- */
950
951 static const gchar *well_known_commands[] =
952 {
953   /* translators: this string is a name for the 'less' command */
954   "less", N_("Terminal Pager"),
955   "top", N_("Top Command"),
956   "bash", N_("Bourne Again Shell"),
957   "sh", N_("Bourne Shell"),
958   "zsh", N_("Z Shell"),
959   NULL,
960 };
961
962 gboolean
963 _gtk_mount_operation_lookup_info (GtkMountOperationLookupContext *context,
964                                   GPid                            pid,
965                                   gint                            size_pixels,
966                                   gchar                         **out_name,
967                                   gchar                         **out_command_line,
968                                   GdkPixbuf                     **out_pixbuf)
969 {
970   g_return_val_if_fail (out_name != NULL && *out_name == NULL, FALSE);
971   g_return_val_if_fail (out_command_line != NULL && *out_command_line == NULL, FALSE);
972   g_return_val_if_fail (out_pixbuf != NULL && *out_pixbuf == NULL, FALSE);
973
974   /* We perform two different lookups for name and icon size.. this is
975    * because we want the name from the window with WINDOWID and this
976    * normally does not give you an icon
977    *
978    * (the canonical example is a tab in gnome-terminal - the shell/command running
979    *  in the shell will have WINDOWID set - but this window won't have an icon - so
980    *  we want to continue up until the gnome-terminal window so we can get that icon)
981    */
982
983   *out_command_line = pid_get_command_line (pid);
984
985   *out_name = get_name_for_window_with_pid (context, pid);
986
987   *out_pixbuf = get_pixbuf_for_window_with_pid (context, pid, size_pixels);
988
989   /* if we didn't manage to find the name via X, fall back to the basename
990    * of the first element of the command line and, for maximum geek-comfort,
991    * map a few well-known commands to proper translated names
992    */
993   if (*out_name == NULL && *out_command_line != NULL &&
994       strlen (*out_command_line) > 0 && (*out_command_line)[0] != ' ')
995     {
996       guint n;
997       gchar *s;
998       gchar *p;
999
1000       /* find the first character after the first argument */
1001       s = strchr (*out_command_line, ' ');
1002       if (s == NULL)
1003         s = *out_command_line + strlen (*out_command_line);
1004
1005       for (p = s; p > *out_command_line; p--)
1006         {
1007           if (*p == '/')
1008             {
1009               p++;
1010               break;
1011             }
1012         }
1013
1014       *out_name = g_strndup (p, s - p);
1015
1016       for (n = 0; well_known_commands[n] != NULL; n += 2)
1017         {
1018           /* sometimes the command is prefixed with a -, e.g. '-bash' instead
1019            * of 'bash' - handle that as well
1020            */
1021           if ((strcmp (well_known_commands[n], *out_name) == 0) ||
1022               ((*out_name)[0] == '-' && (strcmp (well_known_commands[n], (*out_name) + 1) == 0)))
1023             {
1024               g_free (*out_name);
1025               *out_name = g_strdup (_(well_known_commands[n+1]));
1026               break;
1027             }
1028         }
1029     }
1030
1031   return TRUE;
1032 }
1033
1034 gboolean
1035 _gtk_mount_operation_kill_process (GPid      pid,
1036                                    GError  **error)
1037 {
1038   gboolean ret;
1039
1040   ret = TRUE;
1041
1042   if (kill ((pid_t) pid, SIGTERM) != 0)
1043     {
1044       int errsv = errno;
1045
1046       /* TODO: On EPERM, we could use a setuid helper using polkit (very easy to implement
1047        *       via pkexec(1)) to allow the user to e.g. authenticate to gain the authorization
1048        *       to kill the process. But that's not how things currently work.
1049        */
1050
1051       ret = FALSE;
1052       g_set_error (error,
1053                    G_IO_ERROR,
1054                    g_io_error_from_errno (errsv),
1055                    _("Cannot end process with PID %d: %s"),
1056                    pid,
1057                    g_strerror (errsv));
1058     }
1059
1060   return ret;
1061 }