1 /* Xlib utils */
2 /* vim: set sw=2 et: */
3 
4 /*
5  * Copyright (C) 2001 Havoc Pennington
6  * Copyright (C) 2005-2007 Vincent Untz
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23 
24 #include <config.h>
25 #include "xutils.h"
26 #include <string.h>
27 #include <stdio.h>
28 #include "screen.h"
29 #include "window.h"
30 #include "private.h"
31 #include "inlinepixbufs.h"
32 
33 gboolean
_wnck_get_cardinal(Window xwindow,Atom atom,int * val)34 _wnck_get_cardinal (Window  xwindow,
35                     Atom    atom,
36                     int    *val)
37 {
38   Atom type;
39   int format;
40   gulong nitems;
41   gulong bytes_after;
42   gulong *num;
43   int err, result;
44 
45   *val = 0;
46 
47   _wnck_error_trap_push ();
48   type = None;
49   result = XGetWindowProperty (_wnck_get_default_display(),
50 			       xwindow,
51 			       atom,
52 			       0, G_MAXLONG,
53 			       False, XA_CARDINAL, &type, &format, &nitems,
54 			       &bytes_after, (void*)&num);
55   err = _wnck_error_trap_pop ();
56   if (err != Success ||
57       result != Success)
58     return FALSE;
59 
60   if (type != XA_CARDINAL)
61     {
62       XFree (num);
63       return FALSE;
64     }
65 
66   *val = *num;
67 
68   XFree (num);
69 
70   return TRUE;
71 }
72 
73 int
_wnck_get_wm_state(Window xwindow)74 _wnck_get_wm_state (Window  xwindow)
75 {
76   Atom type;
77   int format;
78   gulong nitems;
79   gulong bytes_after;
80   gulong *num;
81   int err, result;
82   Atom wm_state;
83   int retval;
84 
85   wm_state = _wnck_atom_get ("WM_STATE");
86   retval = NormalState;
87 
88   _wnck_error_trap_push ();
89   type = None;
90   result = XGetWindowProperty (_wnck_get_default_display(),
91 			       xwindow,
92 			       wm_state,
93 			       0, G_MAXLONG,
94 			       False, wm_state, &type, &format, &nitems,
95 			       &bytes_after, (void*)&num);
96   err = _wnck_error_trap_pop ();
97   if (err != Success ||
98       result != Success)
99     return retval;
100 
101   if (type != wm_state)
102     {
103       XFree (num);
104       return retval;
105     }
106 
107   retval = *num;
108 
109   XFree (num);
110 
111   return retval;
112 }
113 
114 gboolean
_wnck_get_window(Window xwindow,Atom atom,Window * val)115 _wnck_get_window (Window  xwindow,
116                   Atom    atom,
117                   Window *val)
118 {
119   Atom type;
120   int format;
121   gulong nitems;
122   gulong bytes_after;
123   Window *w;
124   int err, result;
125 
126   *val = 0;
127 
128   _wnck_error_trap_push ();
129   type = None;
130   result = XGetWindowProperty (_wnck_get_default_display(),
131 			       xwindow,
132 			       atom,
133 			       0, G_MAXLONG,
134 			       False, XA_WINDOW, &type, &format, &nitems,
135 			       &bytes_after, (void*)&w);
136   err = _wnck_error_trap_pop ();
137   if (err != Success ||
138       result != Success)
139     return FALSE;
140 
141   if (type != XA_WINDOW)
142     {
143       XFree (w);
144       return FALSE;
145     }
146 
147   *val = *w;
148 
149   XFree (w);
150 
151   return TRUE;
152 }
153 
154 gboolean
_wnck_get_pixmap(Window xwindow,Atom atom,Pixmap * val)155 _wnck_get_pixmap (Window  xwindow,
156                   Atom    atom,
157                   Pixmap *val)
158 {
159   Atom type;
160   int format;
161   gulong nitems;
162   gulong bytes_after;
163   Window *w;
164   int err, result;
165 
166   *val = 0;
167 
168   _wnck_error_trap_push ();
169   type = None;
170   result = XGetWindowProperty (_wnck_get_default_display(),
171 			       xwindow,
172 			       atom,
173 			       0, G_MAXLONG,
174 			       False, XA_PIXMAP, &type, &format, &nitems,
175 			       &bytes_after, (void*)&w);
176   err = _wnck_error_trap_pop ();
177   if (err != Success ||
178       result != Success)
179     return FALSE;
180 
181   if (type != XA_PIXMAP)
182     {
183       XFree (w);
184       return FALSE;
185     }
186 
187   *val = *w;
188 
189   XFree (w);
190 
191   return TRUE;
192 }
193 
194 gboolean
_wnck_get_atom(Window xwindow,Atom atom,Atom * val)195 _wnck_get_atom (Window  xwindow,
196                 Atom    atom,
197                 Atom   *val)
198 {
199   Atom type;
200   int format;
201   gulong nitems;
202   gulong bytes_after;
203   Atom *a;
204   int err, result;
205 
206   *val = 0;
207 
208   _wnck_error_trap_push ();
209   type = None;
210   result = XGetWindowProperty (_wnck_get_default_display(),
211 			       xwindow,
212 			       atom,
213 			       0, G_MAXLONG,
214 			       False, XA_ATOM, &type, &format, &nitems,
215 			       &bytes_after, (void*)&a);
216   err = _wnck_error_trap_pop ();
217   if (err != Success ||
218       result != Success)
219     return FALSE;
220 
221   if (type != XA_ATOM)
222     {
223       XFree (a);
224       return FALSE;
225     }
226 
227   *val = *a;
228 
229   XFree (a);
230 
231   return TRUE;
232 }
233 
234 static char*
text_property_to_utf8(const XTextProperty * prop)235 text_property_to_utf8 (const XTextProperty *prop)
236 {
237   char **list;
238   int count;
239   char *retval;
240 
241   list = NULL;
242 
243   count = gdk_text_property_to_utf8_list (gdk_x11_xatom_to_atom (prop->encoding),
244                                           prop->format,
245                                           prop->value,
246                                           prop->nitems,
247                                           &list);
248 
249   if (count == 0)
250     retval = NULL;
251   else
252     {
253       retval = list[0];
254       list[0] = g_strdup (""); /* something to free */
255     }
256 
257   g_strfreev (list);
258 
259   return retval;
260 }
261 
262 char*
_wnck_get_text_property(Window xwindow,Atom atom)263 _wnck_get_text_property (Window  xwindow,
264                          Atom    atom)
265 {
266   XTextProperty text;
267   char *retval;
268 
269   _wnck_error_trap_push ();
270 
271   text.nitems = 0;
272   if (XGetTextProperty (_wnck_get_default_display(),
273                         xwindow,
274                         &text,
275                         atom))
276     {
277       retval = text_property_to_utf8 (&text);
278 
279       if (text.value)
280         XFree (text.value);
281     }
282   else
283     {
284       retval = NULL;
285     }
286 
287   _wnck_error_trap_pop ();
288 
289   return retval;
290 }
291 
292 static char*
_wnck_get_string_property_latin1(Window xwindow,Atom atom)293 _wnck_get_string_property_latin1 (Window  xwindow,
294                                   Atom    atom)
295 {
296   Atom type;
297   int format;
298   gulong nitems;
299   gulong bytes_after;
300   gchar *str;
301   int err, result;
302   char *retval;
303 
304   _wnck_error_trap_push ();
305   str = NULL;
306   result = XGetWindowProperty (_wnck_get_default_display(),
307 			       xwindow, atom,
308 			       0, G_MAXLONG,
309 			       False, XA_STRING, &type, &format, &nitems,
310 			       &bytes_after, (guchar **)&str);
311 
312   err = _wnck_error_trap_pop ();
313   if (err != Success ||
314       result != Success)
315     return NULL;
316 
317   if (type != XA_STRING)
318     {
319       XFree (str);
320       return NULL;
321     }
322 
323   retval = g_strdup (str);
324 
325   XFree (str);
326 
327   return retval;
328 }
329 
330 char*
_wnck_get_utf8_property(Window xwindow,Atom atom)331 _wnck_get_utf8_property (Window  xwindow,
332                          Atom    atom)
333 {
334   Atom type;
335   int format;
336   gulong nitems;
337   gulong bytes_after;
338   gchar *val;
339   int err, result;
340   char *retval;
341   Atom utf8_string;
342 
343   utf8_string = _wnck_atom_get ("UTF8_STRING");
344 
345   _wnck_error_trap_push ();
346   type = None;
347   val = NULL;
348   result = XGetWindowProperty (_wnck_get_default_display(),
349 			       xwindow,
350 			       atom,
351 			       0, G_MAXLONG,
352 			       False, utf8_string,
353 			       &type, &format, &nitems,
354 			       &bytes_after, (guchar **)&val);
355   err = _wnck_error_trap_pop ();
356 
357   if (err != Success ||
358       result != Success)
359     return NULL;
360 
361   if (type != utf8_string ||
362       format != 8 ||
363       nitems == 0)
364     {
365       if (val)
366         XFree (val);
367       return NULL;
368     }
369 
370   if (!g_utf8_validate (val, nitems, NULL))
371     {
372       g_warning ("Property %s contained invalid UTF-8\n",
373                  _wnck_atom_name (atom));
374       XFree (val);
375       return NULL;
376     }
377 
378   retval = g_strndup (val, nitems);
379 
380   XFree (val);
381 
382   return retval;
383 }
384 
385 gboolean
_wnck_get_window_list(Window xwindow,Atom atom,Window ** windows,int * len)386 _wnck_get_window_list (Window   xwindow,
387                        Atom     atom,
388                        Window **windows,
389                        int     *len)
390 {
391   Atom type;
392   int format;
393   gulong nitems;
394   gulong bytes_after;
395   Window *data;
396   int err, result;
397 
398   *windows = NULL;
399   *len = 0;
400 
401   _wnck_error_trap_push ();
402   type = None;
403   result = XGetWindowProperty (_wnck_get_default_display(),
404 			       xwindow,
405 			       atom,
406 			       0, G_MAXLONG,
407 			       False, XA_WINDOW, &type, &format, &nitems,
408 			       &bytes_after, (void*)&data);
409   err = _wnck_error_trap_pop ();
410   if (err != Success ||
411       result != Success)
412     return FALSE;
413 
414   if (type != XA_WINDOW)
415     {
416       XFree (data);
417       return FALSE;
418     }
419 
420   *windows = g_new (Window, nitems);
421   memcpy (*windows, data, sizeof (Window) * nitems);
422   *len = nitems;
423 
424   XFree (data);
425 
426   return TRUE;
427 }
428 
429 gboolean
_wnck_get_atom_list(Window xwindow,Atom atom,Atom ** atoms,int * len)430 _wnck_get_atom_list (Window   xwindow,
431                      Atom     atom,
432                      Atom   **atoms,
433                      int     *len)
434 {
435   Atom type;
436   int format;
437   gulong nitems;
438   gulong bytes_after;
439   Atom *data;
440   int err, result;
441 
442   *atoms = NULL;
443   *len = 0;
444 
445   _wnck_error_trap_push ();
446   type = None;
447   result = XGetWindowProperty (_wnck_get_default_display(),
448 			       xwindow,
449 			       atom,
450 			       0, G_MAXLONG,
451 			       False, XA_ATOM, &type, &format, &nitems,
452 			       &bytes_after, (void*)&data);
453   err = _wnck_error_trap_pop ();
454   if (err != Success ||
455       result != Success)
456     return FALSE;
457 
458   if (type != XA_ATOM)
459     {
460       XFree (data);
461       return FALSE;
462     }
463 
464   *atoms = g_new (Atom, nitems);
465   memcpy (*atoms, data, sizeof (Atom) * nitems);
466   *len = nitems;
467 
468   XFree (data);
469 
470   return TRUE;
471 }
472 
473 gboolean
_wnck_get_cardinal_list(Window xwindow,Atom atom,gulong ** cardinals,int * len)474 _wnck_get_cardinal_list (Window   xwindow,
475                          Atom     atom,
476                          gulong **cardinals,
477                          int     *len)
478 {
479   Atom type;
480   int format;
481   gulong nitems;
482   gulong bytes_after;
483   gulong *nums;
484   int err, result;
485 
486   *cardinals = NULL;
487   *len = 0;
488 
489   _wnck_error_trap_push ();
490   type = None;
491   result = XGetWindowProperty (_wnck_get_default_display(),
492 			       xwindow,
493 			       atom,
494 			       0, G_MAXLONG,
495 			       False, XA_CARDINAL, &type, &format, &nitems,
496 			       &bytes_after, (void*)&nums);
497   err = _wnck_error_trap_pop ();
498   if (err != Success ||
499       result != Success)
500     return FALSE;
501 
502   if (type != XA_CARDINAL)
503     {
504       XFree (nums);
505       return FALSE;
506     }
507 
508   *cardinals = g_new (gulong, nitems);
509   memcpy (*cardinals, nums, sizeof (gulong) * nitems);
510   *len = nitems;
511 
512   XFree (nums);
513 
514   return TRUE;
515 }
516 
517 char**
_wnck_get_utf8_list(Window xwindow,Atom atom)518 _wnck_get_utf8_list (Window   xwindow,
519                      Atom     atom)
520 {
521   Atom type;
522   int format;
523   gulong nitems;
524   gulong bytes_after;
525   char *val;
526   int err, result;
527   Atom utf8_string;
528   char **retval;
529   guint i;
530   guint n_strings;
531   char *p;
532 
533   utf8_string = _wnck_atom_get ("UTF8_STRING");
534 
535   _wnck_error_trap_push ();
536   type = None;
537   val = NULL;
538   result = XGetWindowProperty (_wnck_get_default_display(),
539 			       xwindow,
540 			       atom,
541 			       0, G_MAXLONG,
542 			       False, utf8_string,
543 			       &type, &format, &nitems,
544 			       &bytes_after, (void*)&val);
545   err = _wnck_error_trap_pop ();
546 
547   if (err != Success ||
548       result != Success)
549     return NULL;
550 
551   if (type != utf8_string ||
552       format != 8 ||
553       nitems == 0)
554     {
555       if (val)
556         XFree (val);
557       return NULL;
558     }
559 
560   /* I'm not sure this is right, but I'm guessing the
561    * property is nul-separated
562    */
563   i = 0;
564   n_strings = 0;
565   while (i < nitems)
566     {
567       if (val[i] == '\0')
568         ++n_strings;
569       ++i;
570     }
571 
572   if (val[nitems - 1] != '\0')
573     ++n_strings;
574 
575   /* we're guaranteed that val has a nul on the end
576    * by XGetWindowProperty
577    */
578 
579   retval = g_new0 (char*, n_strings + 1);
580 
581   p = val;
582   i = 0;
583   while (i < n_strings)
584     {
585       if (!g_utf8_validate (p, -1, NULL))
586         {
587           g_warning ("Property %s contained invalid UTF-8\n",
588                      _wnck_atom_name (atom));
589           XFree (val);
590           g_strfreev (retval);
591           return NULL;
592         }
593 
594       retval[i] = g_strdup (p);
595 
596       p = p + strlen (p) + 1;
597       ++i;
598     }
599 
600   XFree (val);
601 
602   return retval;
603 }
604 
605 void
_wnck_set_utf8_list(Window xwindow,Atom atom,char ** list)606 _wnck_set_utf8_list (Window   xwindow,
607                      Atom     atom,
608                      char   **list)
609 {
610   Atom utf8_string;
611   GString *flattened;
612   int i;
613 
614   utf8_string = _wnck_atom_get ("UTF8_STRING");
615 
616   /* flatten to nul-separated list */
617   flattened = g_string_new ("");
618   i = 0;
619   while (list[i] != NULL)
620     {
621       g_string_append_len (flattened, list[i],
622                            strlen (list[i]) + 1);
623       ++i;
624     }
625 
626   _wnck_error_trap_push ();
627 
628   XChangeProperty (_wnck_get_default_display(),
629 		   xwindow,
630                    atom,
631 		   utf8_string, 8, PropModeReplace,
632 		   (guchar *) flattened->str, flattened->len);
633 
634   _wnck_error_trap_pop ();
635 
636   g_string_free (flattened, TRUE);
637 }
638 
639 void
_wnck_error_trap_push(void)640 _wnck_error_trap_push (void)
641 {
642   gdk_error_trap_push ();
643 }
644 
645 int
_wnck_error_trap_pop(void)646 _wnck_error_trap_pop (void)
647 {
648   XSync (_wnck_get_default_display(), False);
649   return gdk_error_trap_pop ();
650 }
651 
652 static GdkFilterReturn
filter_func(GdkXEvent * gdkxevent,GdkEvent * event,gpointer data)653 filter_func (GdkXEvent  *gdkxevent,
654              GdkEvent   *event,
655              gpointer    data)
656 {
657   XEvent *xevent = gdkxevent;
658 #ifdef HAVE_STARTUP_NOTIFICATION
659   int i;
660   Display *display;
661 #endif /* HAVE_STARTUP_NOTIFICATION */
662 
663   switch (xevent->type)
664     {
665     case PropertyNotify:
666       {
667         WnckScreen *screen;
668 
669         screen = wnck_screen_get_for_root (xevent->xany.window);
670         if (screen != NULL)
671           _wnck_screen_process_property_notify (screen, xevent);
672         else
673           {
674             WnckWindow *window;
675             WnckApplication *app;
676 
677             window = wnck_window_get (xevent->xany.window);
678             app = wnck_application_get (xevent->xany.window);
679 
680             if (app)
681               _wnck_application_process_property_notify (app, xevent);
682 
683             if (window)
684               _wnck_window_process_property_notify (window, xevent);
685           }
686       }
687       break;
688 
689     case ConfigureNotify:
690       {
691         WnckWindow *window;
692 
693         window = wnck_window_get (xevent->xconfigure.window);
694 
695         if (window)
696           _wnck_window_process_configure_notify (window, xevent);
697       }
698       break;
699 
700     case SelectionClear:
701       {
702         _wnck_desktop_layout_manager_process_event (xevent);
703       }
704       break;
705 
706     case ClientMessage:
707 #ifdef HAVE_STARTUP_NOTIFICATION
708       /* We're cheating as officially libsn requires
709        * us to send all events through sn_display_process_event
710        */
711       i = 0;
712       display = _wnck_get_default_display ();
713 
714       while (i < ScreenCount (display))
715         {
716           WnckScreen *s;
717 
718           s = _wnck_screen_get_existing (i);
719           if (s != NULL)
720             sn_display_process_event (_wnck_screen_get_sn_display (s),
721                                       xevent);
722 
723           ++i;
724         }
725 #endif /* HAVE_STARTUP_NOTIFICATION */
726       break;
727     }
728 
729   return GDK_FILTER_CONTINUE;
730 }
731 
732 void
_wnck_event_filter_init(void)733 _wnck_event_filter_init (void)
734 {
735   static gboolean initialized = FALSE;
736 
737   if (!initialized)
738     {
739       gdk_window_add_filter (NULL, filter_func, NULL);
740       initialized = TRUE;
741     }
742 }
743 
744 int
_wnck_xid_equal(gconstpointer v1,gconstpointer v2)745 _wnck_xid_equal (gconstpointer v1,
746                  gconstpointer v2)
747 {
748   return *((const gulong*) v1) == *((const gulong*) v2);
749 }
750 
751 guint
_wnck_xid_hash(gconstpointer v)752 _wnck_xid_hash (gconstpointer v)
753 {
754   gulong val = * (const gulong *) v;
755 
756   /* I'm not sure this works so well. */
757 #if G_SIZEOF_LONG > 4
758   return (guint) (val ^ (val >> 32));
759 #else
760   return val;
761 #endif
762 }
763 
764 void
_wnck_iconify(Window xwindow)765 _wnck_iconify (Window xwindow)
766 {
767   Display *display;
768 
769   display = _wnck_get_default_display ();
770 
771   _wnck_error_trap_push ();
772   XIconifyWindow (display, xwindow, DefaultScreen (display));
773   _wnck_error_trap_pop ();
774 }
775 
776 void
_wnck_deiconify(Window xwindow)777 _wnck_deiconify (Window xwindow)
778 {
779   /* We need special precautions, because GDK doesn't like
780    * XMapWindow() called on its windows, need to use the
781    * GDK functions
782    */
783   GdkWindow *gdkwindow;
784 
785   gdkwindow = gdk_xid_table_lookup (xwindow);
786 
787   _wnck_error_trap_push ();
788   if (gdkwindow)
789     gdk_window_show (gdkwindow);
790   else
791     XMapRaised (_wnck_get_default_display (), xwindow);
792   _wnck_error_trap_pop ();
793 }
794 
795 void
_wnck_close(Screen * screen,Window xwindow,Time timestamp)796 _wnck_close (Screen *screen,
797 	     Window  xwindow,
798 	     Time    timestamp)
799 {
800   Display *display;
801   Window   root;
802   XEvent   xev;
803 
804   display = DisplayOfScreen (screen);
805   root = RootWindowOfScreen (screen);
806 
807   xev.xclient.type = ClientMessage;
808   xev.xclient.serial = 0;
809   xev.xclient.send_event = True;
810   xev.xclient.display = display;
811   xev.xclient.window = xwindow;
812   xev.xclient.message_type = _wnck_atom_get ("_NET_CLOSE_WINDOW");
813   xev.xclient.format = 32;
814   xev.xclient.data.l[0] = timestamp;
815   xev.xclient.data.l[1] = _wnck_get_client_type ();
816   xev.xclient.data.l[2] = 0;
817   xev.xclient.data.l[3] = 0;
818   xev.xclient.data.l[4] = 0;
819 
820   _wnck_error_trap_push ();
821   XSendEvent (display,
822               root,
823               False,
824 	      SubstructureRedirectMask | SubstructureNotifyMask,
825 	      &xev);
826   _wnck_error_trap_pop ();
827 }
828 
829 #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT      0
830 #define _NET_WM_MOVERESIZE_SIZE_TOP          1
831 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT     2
832 #define _NET_WM_MOVERESIZE_SIZE_RIGHT        3
833 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT  4
834 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM       5
835 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT   6
836 #define _NET_WM_MOVERESIZE_SIZE_LEFT         7
837 #define _NET_WM_MOVERESIZE_MOVE              8
838 #define _NET_WM_MOVERESIZE_SIZE_KEYBOARD     9
839 #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD    10
840 
841 void
_wnck_keyboard_move(Screen * screen,Window xwindow)842 _wnck_keyboard_move (Screen *screen,
843                      Window  xwindow)
844 {
845   Display *display;
846   Window   root;
847   XEvent   xev;
848 
849   display = DisplayOfScreen (screen);
850   root = RootWindowOfScreen (screen);
851 
852   xev.xclient.type = ClientMessage;
853   xev.xclient.serial = 0;
854   xev.xclient.send_event = True;
855   xev.xclient.display = display;
856   xev.xclient.window = xwindow;
857   xev.xclient.message_type = _wnck_atom_get ("_NET_WM_MOVERESIZE");
858   xev.xclient.format = 32;
859   xev.xclient.data.l[0] = 0; /* unused */
860   xev.xclient.data.l[1] = 0; /* unused */
861   xev.xclient.data.l[2] = _NET_WM_MOVERESIZE_MOVE_KEYBOARD;
862   xev.xclient.data.l[3] = 0; /* unused */
863   xev.xclient.data.l[4] = _wnck_get_client_type ();
864 
865   _wnck_error_trap_push ();
866   XSendEvent (display,
867               root,
868               False,
869               SubstructureRedirectMask | SubstructureNotifyMask,
870               &xev);
871   _wnck_error_trap_pop ();
872 }
873 
874 void
_wnck_keyboard_size(Screen * screen,Window xwindow)875 _wnck_keyboard_size (Screen *screen,
876                      Window  xwindow)
877 {
878   Display *display;
879   Window   root;
880   XEvent   xev;
881 
882   display = DisplayOfScreen (screen);
883   root = RootWindowOfScreen (screen);
884 
885   xev.xclient.type = ClientMessage;
886   xev.xclient.serial = 0;
887   xev.xclient.send_event = True;
888   xev.xclient.display = display;
889   xev.xclient.window = xwindow;
890   xev.xclient.message_type = _wnck_atom_get ("_NET_WM_MOVERESIZE");
891   xev.xclient.format = 32;
892   xev.xclient.data.l[0] = 0; /* unused */
893   xev.xclient.data.l[1] = 0; /* unused */
894   xev.xclient.data.l[2] = _NET_WM_MOVERESIZE_SIZE_KEYBOARD;
895   xev.xclient.data.l[3] = 0; /* unused */
896   xev.xclient.data.l[4] = _wnck_get_client_type ();
897 
898   _wnck_error_trap_push ();
899   XSendEvent (display,
900               root,
901               False,
902               SubstructureRedirectMask | SubstructureNotifyMask,
903               &xev);
904   _wnck_error_trap_pop ();
905 }
906 
907 void
_wnck_change_state(Screen * screen,Window xwindow,gboolean add,Atom state1,Atom state2)908 _wnck_change_state (Screen  *screen,
909 		    Window   xwindow,
910                     gboolean add,
911                     Atom     state1,
912                     Atom     state2)
913 {
914   Display *display;
915   Window   root;
916   XEvent   xev;
917 
918 #define _NET_WM_STATE_REMOVE        0    /* remove/unset property */
919 #define _NET_WM_STATE_ADD           1    /* add/set property */
920 #define _NET_WM_STATE_TOGGLE        2    /* toggle property  */
921 
922   display = DisplayOfScreen (screen);
923   root = RootWindowOfScreen (screen);
924 
925   xev.xclient.type = ClientMessage;
926   xev.xclient.serial = 0;
927   xev.xclient.send_event = True;
928   xev.xclient.display = display;
929   xev.xclient.window = xwindow;
930   xev.xclient.message_type = _wnck_atom_get ("_NET_WM_STATE");
931   xev.xclient.format = 32;
932   xev.xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
933   xev.xclient.data.l[1] = state1;
934   xev.xclient.data.l[2] = state2;
935   xev.xclient.data.l[3] = _wnck_get_client_type ();
936   xev.xclient.data.l[4] = 0;
937 
938   _wnck_error_trap_push ();
939   XSendEvent (display,
940 	      root,
941               False,
942 	      SubstructureRedirectMask | SubstructureNotifyMask,
943 	      &xev);
944   _wnck_error_trap_pop ();
945 }
946 
947 void
_wnck_change_workspace(Screen * screen,Window xwindow,int new_space)948 _wnck_change_workspace (Screen     *screen,
949 			Window      xwindow,
950                         int         new_space)
951 {
952   Display *display;
953   Window   root;
954   XEvent   xev;
955 
956   display = DisplayOfScreen (screen);
957   root = RootWindowOfScreen (screen);
958 
959   xev.xclient.type = ClientMessage;
960   xev.xclient.serial = 0;
961   xev.xclient.send_event = True;
962   xev.xclient.display = display;
963   xev.xclient.window = xwindow;
964   xev.xclient.message_type = _wnck_atom_get ("_NET_WM_DESKTOP");
965   xev.xclient.format = 32;
966   xev.xclient.data.l[0] = new_space;
967   xev.xclient.data.l[1] = _wnck_get_client_type ();
968   xev.xclient.data.l[2] = 0;
969   xev.xclient.data.l[3] = 0;
970   xev.xclient.data.l[4] = 0;
971 
972   _wnck_error_trap_push ();
973   XSendEvent (display,
974 	      root,
975               False,
976 	      SubstructureRedirectMask | SubstructureNotifyMask,
977 	      &xev);
978   _wnck_error_trap_pop ();
979 }
980 
981 void
_wnck_activate(Screen * screen,Window xwindow,Time timestamp)982 _wnck_activate (Screen *screen,
983                 Window  xwindow,
984                 Time    timestamp)
985 {
986   Display *display;
987   Window   root;
988   XEvent   xev;
989 
990   if (timestamp == 0)
991     g_warning ("Received a timestamp of 0; window activation may not "
992                "function properly.\n");
993 
994   display = DisplayOfScreen (screen);
995   root = RootWindowOfScreen (screen);
996 
997   xev.xclient.type = ClientMessage;
998   xev.xclient.serial = 0;
999   xev.xclient.send_event = True;
1000   xev.xclient.display = display;
1001   xev.xclient.window = xwindow;
1002   xev.xclient.message_type = _wnck_atom_get ("_NET_ACTIVE_WINDOW");
1003   xev.xclient.format = 32;
1004   xev.xclient.data.l[0] = _wnck_get_client_type ();
1005   xev.xclient.data.l[1] = timestamp;
1006   xev.xclient.data.l[2] = 0;
1007   xev.xclient.data.l[3] = 0;
1008   xev.xclient.data.l[4] = 0;
1009 
1010   _wnck_error_trap_push ();
1011   XSendEvent (display,
1012 	      root,
1013               False,
1014 	      SubstructureRedirectMask | SubstructureNotifyMask,
1015 	      &xev);
1016   _wnck_error_trap_pop ();
1017 }
1018 
1019 void
_wnck_activate_workspace(Screen * screen,int new_active_space,Time timestamp)1020 _wnck_activate_workspace (Screen *screen,
1021                           int     new_active_space,
1022                           Time    timestamp)
1023 {
1024   Display *display;
1025   Window   root;
1026   XEvent   xev;
1027 
1028   display = DisplayOfScreen (screen);
1029   root = RootWindowOfScreen (screen);
1030 
1031   xev.xclient.type = ClientMessage;
1032   xev.xclient.serial = 0;
1033   xev.xclient.send_event = True;
1034   xev.xclient.display = display;
1035   xev.xclient.window = root;
1036   xev.xclient.message_type = _wnck_atom_get ("_NET_CURRENT_DESKTOP");
1037   xev.xclient.format = 32;
1038   xev.xclient.data.l[0] = new_active_space;
1039   xev.xclient.data.l[1] = timestamp;
1040   xev.xclient.data.l[2] = 0;
1041   xev.xclient.data.l[3] = 0;
1042   xev.xclient.data.l[4] = 0;
1043 
1044   _wnck_error_trap_push ();
1045   XSendEvent (display,
1046 	      root,
1047               False,
1048 	      SubstructureRedirectMask | SubstructureNotifyMask,
1049 	      &xev);
1050   _wnck_error_trap_pop ();
1051 }
1052 
1053 void
_wnck_change_viewport(Screen * screen,int x,int y)1054 _wnck_change_viewport (Screen *screen,
1055 		       int     x,
1056 		       int     y)
1057 {
1058   Display *display;
1059   Window   root;
1060   XEvent   xev;
1061 
1062   display = DisplayOfScreen (screen);
1063   root = RootWindowOfScreen (screen);
1064 
1065   xev.xclient.type = ClientMessage;
1066   xev.xclient.serial = 0;
1067   xev.xclient.send_event = True;
1068   xev.xclient.display = display;
1069   xev.xclient.window = root;
1070   xev.xclient.message_type = _wnck_atom_get ("_NET_DESKTOP_VIEWPORT");
1071   xev.xclient.format = 32;
1072   xev.xclient.data.l[0] = x;
1073   xev.xclient.data.l[1] = y;
1074   xev.xclient.data.l[2] = 0;
1075   xev.xclient.data.l[3] = 0;
1076   xev.xclient.data.l[4] = 0;
1077 
1078   _wnck_error_trap_push ();
1079   XSendEvent (display,
1080 	      root,
1081               False,
1082 	      SubstructureRedirectMask | SubstructureNotifyMask,
1083 	      &xev);
1084   _wnck_error_trap_pop ();
1085 }
1086 
1087 void
_wnck_toggle_showing_desktop(Screen * screen,gboolean show)1088 _wnck_toggle_showing_desktop (Screen  *screen,
1089                               gboolean show)
1090 {
1091   Display *display;
1092   Window   root;
1093   XEvent   xev;
1094 
1095   display = DisplayOfScreen (screen);
1096   root = RootWindowOfScreen (screen);
1097 
1098   xev.xclient.type = ClientMessage;
1099   xev.xclient.serial = 0;
1100   xev.xclient.send_event = True;
1101   xev.xclient.display = display;
1102   xev.xclient.window = root;
1103   xev.xclient.message_type = _wnck_atom_get ("_NET_SHOWING_DESKTOP");
1104   xev.xclient.format = 32;
1105   xev.xclient.data.l[0] = show != FALSE;
1106   xev.xclient.data.l[1] = 0;
1107   xev.xclient.data.l[2] = 0;
1108   xev.xclient.data.l[3] = 0;
1109   xev.xclient.data.l[4] = 0;
1110 
1111   _wnck_error_trap_push ();
1112   XSendEvent (display,
1113 	      root,
1114               False,
1115 	      SubstructureRedirectMask | SubstructureNotifyMask,
1116 	      &xev);
1117   _wnck_error_trap_pop ();
1118 }
1119 
1120 char*
_wnck_get_session_id(Window xwindow)1121 _wnck_get_session_id (Window xwindow)
1122 {
1123   Window client_leader;
1124 
1125   client_leader = None;
1126   _wnck_get_window (xwindow,
1127                     _wnck_atom_get ("WM_CLIENT_LEADER"),
1128                     &client_leader);
1129 
1130   if (client_leader == None)
1131     return NULL;
1132 
1133   return _wnck_get_string_property_latin1 (client_leader,
1134                                            _wnck_atom_get ("SM_CLIENT_ID"));
1135 }
1136 
1137 int
_wnck_get_pid(Window xwindow)1138 _wnck_get_pid (Window xwindow)
1139 {
1140   int val;
1141 
1142   if (!_wnck_get_cardinal (xwindow,
1143                            _wnck_atom_get ("_NET_WM_PID"),
1144                            &val))
1145     return 0;
1146   else
1147     return val;
1148 }
1149 
1150 char*
_wnck_get_name(Window xwindow)1151 _wnck_get_name (Window xwindow)
1152 {
1153   char *name;
1154 
1155   name = _wnck_get_utf8_property (xwindow,
1156                                   _wnck_atom_get ("_NET_WM_VISIBLE_NAME"));
1157 
1158   if (name == NULL)
1159     name = _wnck_get_utf8_property (xwindow,
1160                                     _wnck_atom_get ("_NET_WM_NAME"));
1161 
1162   if (name == NULL)
1163     name = _wnck_get_text_property (xwindow,
1164                                     XA_WM_NAME);
1165 
1166   return name;
1167 }
1168 
1169 char*
_wnck_get_icon_name(Window xwindow)1170 _wnck_get_icon_name (Window xwindow)
1171 {
1172   char *name;
1173 
1174   name = _wnck_get_utf8_property (xwindow,
1175                                   _wnck_atom_get ("_NET_WM_VISIBLE_ICON_NAME"));
1176 
1177   if (name == NULL)
1178     name = _wnck_get_utf8_property (xwindow,
1179                                     _wnck_atom_get ("_NET_WM_ICON_NAME"));
1180 
1181   if (name == NULL)
1182     name = _wnck_get_text_property (xwindow,
1183                                     XA_WM_ICON_NAME);
1184 
1185   return name;
1186 }
1187 
1188 static char*
latin1_to_utf8(const char * latin1)1189 latin1_to_utf8 (const char *latin1)
1190 {
1191   GString *str;
1192   const char *p;
1193 
1194   str = g_string_new (NULL);
1195 
1196   p = latin1;
1197   while (*p)
1198     {
1199       g_string_append_unichar (str, (gunichar) *p);
1200       ++p;
1201     }
1202 
1203   return g_string_free (str, FALSE);
1204 }
1205 
1206 char*
_wnck_get_res_class_utf8(Window xwindow)1207 _wnck_get_res_class_utf8 (Window xwindow)
1208 {
1209   char *res_class;
1210 
1211   _wnck_get_wmclass (xwindow, &res_class, NULL);
1212 
1213   return res_class;
1214 }
1215 
1216 void
_wnck_get_wmclass(Window xwindow,char ** res_class,char ** res_name)1217 _wnck_get_wmclass (Window xwindow,
1218                    char **res_class,
1219                    char **res_name)
1220 {
1221   XClassHint ch;
1222   char *retval;
1223 
1224   _wnck_error_trap_push ();
1225 
1226   ch.res_name = NULL;
1227   ch.res_class = NULL;
1228 
1229   XGetClassHint (_wnck_get_default_display (), xwindow,
1230                  &ch);
1231 
1232   _wnck_error_trap_pop ();
1233 
1234   retval = NULL;
1235 
1236   if (res_class)
1237     *res_class = NULL;
1238 
1239   if (res_name)
1240     *res_name = NULL;
1241 
1242   if (ch.res_name)
1243     {
1244       if (res_name)
1245         *res_name = latin1_to_utf8 (ch.res_name);
1246 
1247       XFree (ch.res_name);
1248     }
1249 
1250   if (ch.res_class)
1251     {
1252       if (res_class)
1253         *res_class = latin1_to_utf8 (ch.res_class);
1254 
1255       XFree (ch.res_class);
1256     }
1257 }
1258 
1259 gboolean
_wnck_get_frame_extents(Window xwindow,int * left_frame,int * right_frame,int * top_frame,int * bottom_frame)1260 _wnck_get_frame_extents (Window  xwindow,
1261                          int    *left_frame,
1262                          int    *right_frame,
1263                          int    *top_frame,
1264                          int    *bottom_frame)
1265 {
1266   gulong   *p_size;
1267   int       n_size;
1268   gboolean  retval;
1269 
1270   retval = FALSE;
1271   p_size = NULL;
1272   n_size = 0;
1273 
1274   _wnck_get_cardinal_list (xwindow,
1275                            _wnck_atom_get ("_NET_FRAME_EXTENTS"),
1276                            &p_size, &n_size);
1277 
1278   if (p_size != NULL && n_size == 4)
1279     {
1280       *left_frame   = p_size[0];
1281       *right_frame  = p_size[1];
1282       *top_frame    = p_size[2];
1283       *bottom_frame = p_size[3];
1284 
1285       retval = TRUE;
1286     }
1287 
1288   if (p_size != NULL)
1289     g_free (p_size);
1290 
1291   return retval;
1292 }
1293 
1294 void
_wnck_select_input(Window xwindow,int mask)1295 _wnck_select_input (Window xwindow,
1296                     int    mask)
1297 {
1298   GdkWindow *gdkwindow;
1299 
1300   gdkwindow = gdk_xid_table_lookup (xwindow);
1301 
1302   _wnck_error_trap_push ();
1303   if (gdkwindow)
1304     {
1305       /* Avoid breaking GDK's setup,
1306        * this somewhat relies on people setting
1307        * event masks right after realization
1308        * and not changing them again
1309        */
1310       XWindowAttributes attrs;
1311       XGetWindowAttributes (_wnck_get_default_display (), xwindow, &attrs);
1312       mask |= attrs.your_event_mask;
1313     }
1314 
1315   XSelectInput (_wnck_get_default_display (), xwindow, mask);
1316   _wnck_error_trap_pop ();
1317 }
1318 
1319 /* The icon-reading code is copied
1320  * from metacity, please sync bugfixes
1321  */
1322 static gboolean
find_largest_sizes(gulong * data,gulong nitems,int * width,int * height)1323 find_largest_sizes (gulong *data,
1324                     gulong  nitems,
1325                     int    *width,
1326                     int    *height)
1327 {
1328   *width = 0;
1329   *height = 0;
1330 
1331   while (nitems > 0)
1332     {
1333       int w, h;
1334       gboolean replace;
1335 
1336       replace = FALSE;
1337 
1338       if (nitems < 3)
1339         return FALSE; /* no space for w, h */
1340 
1341       w = data[0];
1342       h = data[1];
1343 
1344       if (nitems < ((w * h) + 2))
1345         return FALSE; /* not enough data */
1346 
1347       *width = MAX (w, *width);
1348       *height = MAX (h, *height);
1349 
1350       data += (w * h) + 2;
1351       nitems -= (w * h) + 2;
1352     }
1353 
1354   return TRUE;
1355 }
1356 
1357 static gboolean
find_best_size(gulong * data,gulong nitems,int ideal_width,int ideal_height,int * width,int * height,gulong ** start)1358 find_best_size (gulong  *data,
1359                 gulong   nitems,
1360                 int      ideal_width,
1361                 int      ideal_height,
1362                 int     *width,
1363                 int     *height,
1364                 gulong **start)
1365 {
1366   int best_w;
1367   int best_h;
1368   gulong *best_start;
1369   int max_width, max_height;
1370 
1371   *width = 0;
1372   *height = 0;
1373   *start = NULL;
1374 
1375   if (!find_largest_sizes (data, nitems, &max_width, &max_height))
1376     return FALSE;
1377 
1378   if (ideal_width < 0)
1379     ideal_width = max_width;
1380   if (ideal_height < 0)
1381     ideal_height = max_height;
1382 
1383   best_w = 0;
1384   best_h = 0;
1385   best_start = NULL;
1386 
1387   while (nitems > 0)
1388     {
1389       int w, h;
1390       gboolean replace;
1391 
1392       replace = FALSE;
1393 
1394       if (nitems < 3)
1395         return FALSE; /* no space for w, h */
1396 
1397       w = data[0];
1398       h = data[1];
1399 
1400       if (nitems < ((w * h) + 2))
1401         break; /* not enough data */
1402 
1403       if (best_start == NULL)
1404         {
1405           replace = TRUE;
1406         }
1407       else
1408         {
1409           /* work with averages */
1410           const int ideal_size = (ideal_width + ideal_height) / 2;
1411           int best_size = (best_w + best_h) / 2;
1412           int this_size = (w + h) / 2;
1413 
1414           /* larger than desired is always better than smaller */
1415           if (best_size < ideal_size &&
1416               this_size >= ideal_size)
1417             replace = TRUE;
1418           /* if we have too small, pick anything bigger */
1419           else if (best_size < ideal_size &&
1420                    this_size > best_size)
1421             replace = TRUE;
1422           /* if we have too large, pick anything smaller
1423            * but still >= the ideal
1424            */
1425           else if (best_size > ideal_size &&
1426                    this_size >= ideal_size &&
1427                    this_size < best_size)
1428             replace = TRUE;
1429         }
1430 
1431       if (replace)
1432         {
1433           best_start = data + 2;
1434           best_w = w;
1435           best_h = h;
1436         }
1437 
1438       data += (w * h) + 2;
1439       nitems -= (w * h) + 2;
1440     }
1441 
1442   if (best_start)
1443     {
1444       *start = best_start;
1445       *width = best_w;
1446       *height = best_h;
1447       return TRUE;
1448     }
1449   else
1450     return FALSE;
1451 }
1452 
1453 static void
argbdata_to_pixdata(gulong * argb_data,int len,guchar ** pixdata)1454 argbdata_to_pixdata (gulong *argb_data, int len, guchar **pixdata)
1455 {
1456   guchar *p;
1457   int i;
1458 
1459   *pixdata = g_new (guchar, len * 4);
1460   p = *pixdata;
1461 
1462   /* One could speed this up a lot. */
1463   i = 0;
1464   while (i < len)
1465     {
1466       guint argb;
1467       guint rgba;
1468 
1469       argb = argb_data[i];
1470       rgba = (argb << 8) | (argb >> 24);
1471 
1472       *p = rgba >> 24;
1473       ++p;
1474       *p = (rgba >> 16) & 0xff;
1475       ++p;
1476       *p = (rgba >> 8) & 0xff;
1477       ++p;
1478       *p = rgba & 0xff;
1479       ++p;
1480 
1481       ++i;
1482     }
1483 }
1484 
1485 static gboolean
read_rgb_icon(Window xwindow,int ideal_width,int ideal_height,int ideal_mini_width,int ideal_mini_height,int * width,int * height,guchar ** pixdata,int * mini_width,int * mini_height,guchar ** mini_pixdata)1486 read_rgb_icon (Window         xwindow,
1487                int            ideal_width,
1488                int            ideal_height,
1489                int            ideal_mini_width,
1490                int            ideal_mini_height,
1491                int           *width,
1492                int           *height,
1493                guchar       **pixdata,
1494                int           *mini_width,
1495                int           *mini_height,
1496                guchar       **mini_pixdata)
1497 {
1498   Atom type;
1499   int format;
1500   gulong nitems;
1501   gulong bytes_after;
1502   int result, err;
1503   gulong *data;
1504   gulong *best;
1505   int w, h;
1506   gulong *best_mini;
1507   int mini_w, mini_h;
1508 
1509   _wnck_error_trap_push ();
1510   type = None;
1511   data = NULL;
1512   result = XGetWindowProperty (_wnck_get_default_display (),
1513 			       xwindow,
1514 			       _wnck_atom_get ("_NET_WM_ICON"),
1515 			       0, G_MAXLONG,
1516 			       False, XA_CARDINAL, &type, &format, &nitems,
1517 			       &bytes_after, (void*)&data);
1518 
1519   err = _wnck_error_trap_pop ();
1520 
1521   if (err != Success ||
1522       result != Success)
1523     return FALSE;
1524 
1525   if (type != XA_CARDINAL)
1526     {
1527       XFree (data);
1528       return FALSE;
1529     }
1530 
1531   if (!find_best_size (data, nitems,
1532                        ideal_width, ideal_height,
1533                        &w, &h, &best))
1534     {
1535       XFree (data);
1536       return FALSE;
1537     }
1538 
1539   if (!find_best_size (data, nitems,
1540                        ideal_mini_width, ideal_mini_height,
1541                        &mini_w, &mini_h, &best_mini))
1542     {
1543       XFree (data);
1544       return FALSE;
1545     }
1546 
1547   *width = w;
1548   *height = h;
1549 
1550   *mini_width = mini_w;
1551   *mini_height = mini_h;
1552 
1553   argbdata_to_pixdata (best, w * h, pixdata);
1554   argbdata_to_pixdata (best_mini, mini_w * mini_h, mini_pixdata);
1555 
1556   XFree (data);
1557 
1558   return TRUE;
1559 }
1560 
1561 static void
free_pixels(guchar * pixels,gpointer data)1562 free_pixels (guchar *pixels, gpointer data)
1563 {
1564   g_free (pixels);
1565 }
1566 
1567 static void
get_pixmap_geometry(Pixmap pixmap,int * w,int * h,int * d)1568 get_pixmap_geometry (Pixmap       pixmap,
1569                      int         *w,
1570                      int         *h,
1571                      int         *d)
1572 {
1573   Window root_ignored;
1574   int x_ignored, y_ignored;
1575   guint width, height;
1576   guint border_width_ignored;
1577   guint depth;
1578 
1579   if (w)
1580     *w = 1;
1581   if (h)
1582     *h = 1;
1583   if (d)
1584     *d = 1;
1585 
1586   XGetGeometry (_wnck_get_default_display (),
1587                 pixmap, &root_ignored, &x_ignored, &y_ignored,
1588                 &width, &height, &border_width_ignored, &depth);
1589 
1590   if (w)
1591     *w = width;
1592   if (h)
1593     *h = height;
1594   if (d)
1595     *d = depth;
1596 }
1597 
1598 static GdkPixbuf*
apply_mask(GdkPixbuf * pixbuf,GdkPixbuf * mask)1599 apply_mask (GdkPixbuf *pixbuf,
1600             GdkPixbuf *mask)
1601 {
1602   int w, h;
1603   int i, j;
1604   GdkPixbuf *with_alpha;
1605   guchar *src;
1606   guchar *dest;
1607   int src_stride;
1608   int dest_stride;
1609 
1610   w = MIN (gdk_pixbuf_get_width (mask), gdk_pixbuf_get_width (pixbuf));
1611   h = MIN (gdk_pixbuf_get_height (mask), gdk_pixbuf_get_height (pixbuf));
1612 
1613   with_alpha = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
1614 
1615   dest = gdk_pixbuf_get_pixels (with_alpha);
1616   src = gdk_pixbuf_get_pixels (mask);
1617 
1618   dest_stride = gdk_pixbuf_get_rowstride (with_alpha);
1619   src_stride = gdk_pixbuf_get_rowstride (mask);
1620 
1621   i = 0;
1622   while (i < h)
1623     {
1624       j = 0;
1625       while (j < w)
1626         {
1627           guchar *s = src + i * src_stride + j * 3;
1628           guchar *d = dest + i * dest_stride + j * 4;
1629 
1630           /* s[0] == s[1] == s[2], they are 255 if the bit was set, 0
1631            * otherwise
1632            */
1633           if (s[0] == 0)
1634             d[3] = 0;   /* transparent */
1635           else
1636             d[3] = 255; /* opaque */
1637 
1638           ++j;
1639         }
1640 
1641       ++i;
1642     }
1643 
1644   return with_alpha;
1645 }
1646 
1647 static GdkColormap*
get_cmap(GdkPixmap * pixmap)1648 get_cmap (GdkPixmap *pixmap)
1649 {
1650   GdkColormap *cmap;
1651 
1652   cmap = gdk_drawable_get_colormap (pixmap);
1653   if (cmap)
1654     g_object_ref (G_OBJECT (cmap));
1655 
1656   if (cmap == NULL)
1657     {
1658       if (gdk_drawable_get_depth (pixmap) == 1)
1659         {
1660           /* try null cmap */
1661           cmap = NULL;
1662         }
1663       else
1664         {
1665           /* Try system cmap */
1666           GdkScreen *screen = gdk_drawable_get_screen (GDK_DRAWABLE (pixmap));
1667           cmap = gdk_screen_get_system_colormap (screen);
1668           g_object_ref (G_OBJECT (cmap));
1669         }
1670     }
1671 
1672   /* Be sure we aren't going to blow up due to visual mismatch */
1673   if (cmap &&
1674 #if GTK_CHECK_VERSION(2,21,0)
1675       (gdk_visual_get_depth (gdk_colormap_get_visual (cmap)) !=
1676        gdk_drawable_get_depth (pixmap))
1677 #else
1678       (gdk_colormap_get_visual (cmap)->depth !=
1679        gdk_drawable_get_depth (pixmap))
1680 #endif
1681      )
1682     {
1683       g_object_unref (G_OBJECT (cmap));
1684       cmap = NULL;
1685     }
1686 
1687   return cmap;
1688 }
1689 
1690 GdkPixbuf*
_wnck_gdk_pixbuf_get_from_pixmap(GdkPixbuf * dest,Pixmap xpixmap,int src_x,int src_y,int dest_x,int dest_y,int width,int height)1691 _wnck_gdk_pixbuf_get_from_pixmap (GdkPixbuf   *dest,
1692                                   Pixmap       xpixmap,
1693                                   int          src_x,
1694                                   int          src_y,
1695                                   int          dest_x,
1696                                   int          dest_y,
1697                                   int          width,
1698                                   int          height)
1699 {
1700   GdkDrawable *drawable;
1701   GdkPixbuf *retval;
1702   GdkColormap *cmap;
1703 
1704   retval = NULL;
1705   cmap = NULL;
1706 
1707   drawable = gdk_xid_table_lookup (xpixmap);
1708 
1709   if (drawable)
1710     g_object_ref (G_OBJECT (drawable));
1711   else
1712     drawable = gdk_pixmap_foreign_new (xpixmap);
1713 
1714   if (drawable)
1715     {
1716       cmap = get_cmap (drawable);
1717 
1718       /* GDK is supposed to do this but doesn't in GTK 2.0.2,
1719        * fixed in 2.0.3
1720        */
1721       if (width < 0)
1722         gdk_drawable_get_size (drawable, &width, NULL);
1723       if (height < 0)
1724         gdk_drawable_get_size (drawable, NULL, &height);
1725 
1726       retval = gdk_pixbuf_get_from_drawable (dest,
1727                                              drawable,
1728                                              cmap,
1729                                              src_x, src_y,
1730                                              dest_x, dest_y,
1731                                              width, height);
1732     }
1733 
1734   if (cmap)
1735     g_object_unref (G_OBJECT (cmap));
1736   if (drawable)
1737     g_object_unref (G_OBJECT (drawable));
1738 
1739   return retval;
1740 }
1741 
1742 static gboolean
try_pixmap_and_mask(Pixmap src_pixmap,Pixmap src_mask,GdkPixbuf ** iconp,int ideal_width,int ideal_height,GdkPixbuf ** mini_iconp,int ideal_mini_width,int ideal_mini_height)1743 try_pixmap_and_mask (Pixmap      src_pixmap,
1744                      Pixmap      src_mask,
1745                      GdkPixbuf **iconp,
1746                      int         ideal_width,
1747                      int         ideal_height,
1748                      GdkPixbuf **mini_iconp,
1749                      int         ideal_mini_width,
1750                      int         ideal_mini_height)
1751 {
1752   GdkPixbuf *unscaled = NULL;
1753   GdkPixbuf *mask = NULL;
1754   int w, h;
1755 
1756   if (src_pixmap == None)
1757     return FALSE;
1758 
1759   _wnck_error_trap_push ();
1760 
1761   get_pixmap_geometry (src_pixmap, &w, &h, NULL);
1762 
1763   unscaled = _wnck_gdk_pixbuf_get_from_pixmap (NULL,
1764                                                src_pixmap,
1765                                                0, 0, 0, 0,
1766                                                w, h);
1767 
1768   if (unscaled && src_mask != None)
1769     {
1770       get_pixmap_geometry (src_mask, &w, &h, NULL);
1771       mask = _wnck_gdk_pixbuf_get_from_pixmap (NULL,
1772                                                src_mask,
1773                                                0, 0, 0, 0,
1774                                                w, h);
1775     }
1776 
1777   _wnck_error_trap_pop ();
1778 
1779   if (mask)
1780     {
1781       GdkPixbuf *masked;
1782 
1783       masked = apply_mask (unscaled, mask);
1784       g_object_unref (G_OBJECT (unscaled));
1785       unscaled = masked;
1786 
1787       g_object_unref (G_OBJECT (mask));
1788       mask = NULL;
1789     }
1790 
1791   if (unscaled)
1792     {
1793       *iconp =
1794         gdk_pixbuf_scale_simple (unscaled,
1795                                  ideal_width > 0 ? ideal_width :
1796                                  gdk_pixbuf_get_width (unscaled),
1797                                  ideal_height > 0 ? ideal_height :
1798                                  gdk_pixbuf_get_height (unscaled),
1799                                  GDK_INTERP_BILINEAR);
1800       *mini_iconp =
1801         gdk_pixbuf_scale_simple (unscaled,
1802                                  ideal_mini_width > 0 ? ideal_mini_width :
1803                                  gdk_pixbuf_get_width (unscaled),
1804                                  ideal_mini_height > 0 ? ideal_mini_height :
1805                                  gdk_pixbuf_get_height (unscaled),
1806                                  GDK_INTERP_BILINEAR);
1807 
1808       g_object_unref (G_OBJECT (unscaled));
1809       return TRUE;
1810     }
1811   else
1812     return FALSE;
1813 }
1814 
1815 static void
get_kwm_win_icon(Window xwindow,Pixmap * pixmap,Pixmap * mask)1816 get_kwm_win_icon (Window  xwindow,
1817                   Pixmap *pixmap,
1818                   Pixmap *mask)
1819 {
1820   Atom type;
1821   int format;
1822   gulong nitems;
1823   gulong bytes_after;
1824   Pixmap *icons;
1825   int err, result;
1826 
1827   *pixmap = None;
1828   *mask = None;
1829 
1830   _wnck_error_trap_push ();
1831   icons = NULL;
1832   result = XGetWindowProperty (_wnck_get_default_display (), xwindow,
1833 			       _wnck_atom_get ("KWM_WIN_ICON"),
1834 			       0, G_MAXLONG,
1835 			       False,
1836 			       _wnck_atom_get ("KWM_WIN_ICON"),
1837 			       &type, &format, &nitems,
1838 			       &bytes_after, (void*)&icons);
1839 
1840   err = _wnck_error_trap_pop ();
1841   if (err != Success ||
1842       result != Success)
1843     return;
1844 
1845   if (type != _wnck_atom_get ("KWM_WIN_ICON"))
1846     {
1847       XFree (icons);
1848       return;
1849     }
1850 
1851   *pixmap = icons[0];
1852   *mask = icons[1];
1853 
1854   XFree (icons);
1855 
1856   return;
1857 }
1858 
1859 typedef enum
1860 {
1861   /* These MUST be in ascending order of preference;
1862    * i.e. if we get _NET_WM_ICON and already have
1863    * WM_HINTS, we prefer _NET_WM_ICON
1864    */
1865   USING_NO_ICON,
1866   USING_FALLBACK_ICON,
1867   USING_KWM_WIN_ICON,
1868   USING_WM_HINTS,
1869   USING_NET_WM_ICON
1870 } IconOrigin;
1871 
1872 struct _WnckIconCache
1873 {
1874   IconOrigin origin;
1875   Pixmap prev_pixmap;
1876   Pixmap prev_mask;
1877   GdkPixbuf *icon;
1878   GdkPixbuf *mini_icon;
1879   int ideal_width;
1880   int ideal_height;
1881   int ideal_mini_width;
1882   int ideal_mini_height;
1883   guint want_fallback : 1;
1884   /* TRUE if these props have changed */
1885   guint wm_hints_dirty : 1;
1886   guint kwm_win_icon_dirty : 1;
1887   guint net_wm_icon_dirty : 1;
1888 };
1889 
1890 WnckIconCache*
_wnck_icon_cache_new(void)1891 _wnck_icon_cache_new (void)
1892 {
1893   WnckIconCache *icon_cache;
1894 
1895   icon_cache = g_slice_new0 (WnckIconCache);
1896 
1897   icon_cache->origin = USING_NO_ICON;
1898   icon_cache->prev_pixmap = None;
1899   icon_cache->icon = NULL;
1900   icon_cache->mini_icon = NULL;
1901   icon_cache->ideal_width = -1; /* won't be a legit width */
1902   icon_cache->ideal_height = -1;
1903   icon_cache->ideal_mini_width = -1;
1904   icon_cache->ideal_mini_height = -1;
1905   icon_cache->want_fallback = TRUE;
1906   icon_cache->wm_hints_dirty = TRUE;
1907   icon_cache->kwm_win_icon_dirty = TRUE;
1908   icon_cache->net_wm_icon_dirty = TRUE;
1909 
1910   return icon_cache;
1911 }
1912 
1913 static void
clear_icon_cache(WnckIconCache * icon_cache,gboolean dirty_all)1914 clear_icon_cache (WnckIconCache *icon_cache,
1915                   gboolean       dirty_all)
1916 {
1917   if (icon_cache->icon)
1918     g_object_unref (G_OBJECT (icon_cache->icon));
1919   icon_cache->icon = NULL;
1920 
1921   if (icon_cache->mini_icon)
1922     g_object_unref (G_OBJECT (icon_cache->mini_icon));
1923   icon_cache->mini_icon = NULL;
1924 
1925   icon_cache->origin = USING_NO_ICON;
1926 
1927   if (dirty_all)
1928     {
1929       icon_cache->wm_hints_dirty = TRUE;
1930       icon_cache->kwm_win_icon_dirty = TRUE;
1931       icon_cache->net_wm_icon_dirty = TRUE;
1932     }
1933 }
1934 
1935 void
_wnck_icon_cache_free(WnckIconCache * icon_cache)1936 _wnck_icon_cache_free (WnckIconCache *icon_cache)
1937 {
1938   clear_icon_cache (icon_cache, FALSE);
1939 
1940   g_slice_free (WnckIconCache, icon_cache);
1941 }
1942 
1943 void
_wnck_icon_cache_property_changed(WnckIconCache * icon_cache,Atom atom)1944 _wnck_icon_cache_property_changed (WnckIconCache *icon_cache,
1945                                    Atom           atom)
1946 {
1947   if (atom == _wnck_atom_get ("_NET_WM_ICON"))
1948     icon_cache->net_wm_icon_dirty = TRUE;
1949   else if (atom == _wnck_atom_get ("KWM_WIN_ICON"))
1950     icon_cache->kwm_win_icon_dirty = TRUE;
1951   else if (atom == _wnck_atom_get ("WM_HINTS"))
1952     icon_cache->wm_hints_dirty = TRUE;
1953 }
1954 
1955 gboolean
_wnck_icon_cache_get_icon_invalidated(WnckIconCache * icon_cache)1956 _wnck_icon_cache_get_icon_invalidated (WnckIconCache *icon_cache)
1957 {
1958   if (icon_cache->origin <= USING_KWM_WIN_ICON &&
1959       icon_cache->kwm_win_icon_dirty)
1960     return TRUE;
1961   else if (icon_cache->origin <= USING_WM_HINTS &&
1962            icon_cache->wm_hints_dirty)
1963     return TRUE;
1964   else if (icon_cache->origin <= USING_NET_WM_ICON &&
1965            icon_cache->net_wm_icon_dirty)
1966     return TRUE;
1967   else if (icon_cache->origin < USING_FALLBACK_ICON &&
1968            icon_cache->want_fallback)
1969     return TRUE;
1970   else if (icon_cache->origin == USING_NO_ICON)
1971     return TRUE;
1972   else if (icon_cache->origin == USING_FALLBACK_ICON &&
1973            !icon_cache->want_fallback)
1974     return TRUE;
1975   else
1976     return FALSE;
1977 }
1978 
1979 void
_wnck_icon_cache_set_want_fallback(WnckIconCache * icon_cache,gboolean setting)1980 _wnck_icon_cache_set_want_fallback (WnckIconCache *icon_cache,
1981                                     gboolean       setting)
1982 {
1983   icon_cache->want_fallback = setting;
1984 }
1985 
1986 gboolean
_wnck_icon_cache_get_is_fallback(WnckIconCache * icon_cache)1987 _wnck_icon_cache_get_is_fallback (WnckIconCache *icon_cache)
1988 {
1989   return icon_cache->origin == USING_FALLBACK_ICON;
1990 }
1991 
1992 static void
replace_cache(WnckIconCache * icon_cache,IconOrigin origin,GdkPixbuf * new_icon,GdkPixbuf * new_mini_icon)1993 replace_cache (WnckIconCache *icon_cache,
1994                IconOrigin     origin,
1995                GdkPixbuf     *new_icon,
1996                GdkPixbuf     *new_mini_icon)
1997 {
1998   clear_icon_cache (icon_cache, FALSE);
1999 
2000   icon_cache->origin = origin;
2001 
2002   if (new_icon)
2003     g_object_ref (G_OBJECT (new_icon));
2004 
2005   icon_cache->icon = new_icon;
2006 
2007   if (new_mini_icon)
2008     g_object_ref (G_OBJECT (new_mini_icon));
2009 
2010   icon_cache->mini_icon = new_mini_icon;
2011 }
2012 
2013 static GdkPixbuf*
scaled_from_pixdata(guchar * pixdata,int w,int h,int new_w,int new_h)2014 scaled_from_pixdata (guchar *pixdata,
2015                      int     w,
2016                      int     h,
2017                      int     new_w,
2018                      int     new_h)
2019 {
2020   GdkPixbuf *src;
2021   GdkPixbuf *dest;
2022 
2023   src = gdk_pixbuf_new_from_data (pixdata,
2024                                   GDK_COLORSPACE_RGB,
2025                                   TRUE,
2026                                   8,
2027                                   w, h, w * 4,
2028                                   free_pixels,
2029                                   NULL);
2030 
2031   if (src == NULL)
2032     return NULL;
2033 
2034   if (w != h)
2035     {
2036       GdkPixbuf *tmp;
2037       int size;
2038 
2039       size = MAX (w, h);
2040 
2041       tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, TRUE, 8, size, size);
2042 
2043       if (tmp != NULL)
2044 	{
2045 	  gdk_pixbuf_fill (tmp, 0);
2046 	  gdk_pixbuf_copy_area (src, 0, 0, w, h,
2047 				tmp,
2048 				(size - w) / 2, (size - h) / 2);
2049 
2050 	  g_object_unref (src);
2051 	  src = tmp;
2052 	}
2053     }
2054 
2055   if (w != new_w || h != new_h)
2056     {
2057       dest = gdk_pixbuf_scale_simple (src, new_w, new_h, GDK_INTERP_BILINEAR);
2058 
2059       g_object_unref (G_OBJECT (src));
2060     }
2061   else
2062     {
2063       dest = src;
2064     }
2065 
2066   return dest;
2067 }
2068 
2069 gboolean
_wnck_read_icons(Window xwindow,WnckIconCache * icon_cache,GdkPixbuf ** iconp,int ideal_width,int ideal_height,GdkPixbuf ** mini_iconp,int ideal_mini_width,int ideal_mini_height)2070 _wnck_read_icons (Window         xwindow,
2071                   WnckIconCache *icon_cache,
2072                   GdkPixbuf    **iconp,
2073                   int            ideal_width,
2074                   int            ideal_height,
2075                   GdkPixbuf    **mini_iconp,
2076                   int            ideal_mini_width,
2077                   int            ideal_mini_height)
2078 {
2079   guchar *pixdata;
2080   int w, h;
2081   guchar *mini_pixdata;
2082   int mini_w, mini_h;
2083   Pixmap pixmap;
2084   Pixmap mask;
2085   XWMHints *hints;
2086 
2087   /* Return value is whether the icon changed */
2088 
2089   g_return_val_if_fail (icon_cache != NULL, FALSE);
2090 
2091   *iconp = NULL;
2092   *mini_iconp = NULL;
2093 
2094   if (ideal_width != icon_cache->ideal_width ||
2095       ideal_height != icon_cache->ideal_height ||
2096       ideal_mini_width != icon_cache->ideal_mini_width ||
2097       ideal_mini_height != icon_cache->ideal_mini_height)
2098     clear_icon_cache (icon_cache, TRUE);
2099 
2100   icon_cache->ideal_width = ideal_width;
2101   icon_cache->ideal_height = ideal_height;
2102   icon_cache->ideal_mini_width = ideal_mini_width;
2103   icon_cache->ideal_mini_height = ideal_mini_height;
2104 
2105   if (!_wnck_icon_cache_get_icon_invalidated (icon_cache))
2106     return FALSE; /* we have no new info to use */
2107 
2108   pixdata = NULL;
2109 
2110   /* Our algorithm here assumes that we can't have for example origin
2111    * < USING_NET_WM_ICON and icon_cache->net_wm_icon_dirty == FALSE
2112    * unless we have tried to read NET_WM_ICON.
2113    *
2114    * Put another way, if an icon origin is not dirty, then we have
2115    * tried to read it at the current size. If it is dirty, then
2116    * we haven't done that since the last change.
2117    */
2118 
2119   if (icon_cache->origin <= USING_NET_WM_ICON &&
2120       icon_cache->net_wm_icon_dirty)
2121 
2122     {
2123       icon_cache->net_wm_icon_dirty = FALSE;
2124 
2125       if (read_rgb_icon (xwindow,
2126                          ideal_width, ideal_height,
2127                          ideal_mini_width, ideal_mini_height,
2128                          &w, &h, &pixdata,
2129                          &mini_w, &mini_h, &mini_pixdata))
2130         {
2131           *iconp = scaled_from_pixdata (pixdata, w, h, ideal_width, ideal_height);
2132 
2133           *mini_iconp = scaled_from_pixdata (mini_pixdata, mini_w, mini_h,
2134                                              ideal_mini_width, ideal_mini_height);
2135 
2136           replace_cache (icon_cache, USING_NET_WM_ICON,
2137                          *iconp, *mini_iconp);
2138 
2139           return TRUE;
2140         }
2141     }
2142 
2143   if (icon_cache->origin <= USING_WM_HINTS &&
2144       icon_cache->wm_hints_dirty)
2145     {
2146       icon_cache->wm_hints_dirty = FALSE;
2147 
2148       _wnck_error_trap_push ();
2149       hints = XGetWMHints (_wnck_get_default_display (), xwindow);
2150       _wnck_error_trap_pop ();
2151       pixmap = None;
2152       mask = None;
2153       if (hints)
2154         {
2155           if (hints->flags & IconPixmapHint)
2156             pixmap = hints->icon_pixmap;
2157           if (hints->flags & IconMaskHint)
2158             mask = hints->icon_mask;
2159 
2160           XFree (hints);
2161           hints = NULL;
2162         }
2163 
2164       /* We won't update if pixmap is unchanged;
2165        * avoids a get_from_drawable() on every geometry
2166        * hints change
2167        */
2168       if ((pixmap != icon_cache->prev_pixmap ||
2169            mask != icon_cache->prev_mask) &&
2170           pixmap != None)
2171         {
2172           if (try_pixmap_and_mask (pixmap, mask,
2173                                    iconp, ideal_width, ideal_height,
2174                                    mini_iconp, ideal_mini_width, ideal_mini_height))
2175             {
2176               icon_cache->prev_pixmap = pixmap;
2177               icon_cache->prev_mask = mask;
2178 
2179               replace_cache (icon_cache, USING_WM_HINTS,
2180                              *iconp, *mini_iconp);
2181 
2182               return TRUE;
2183             }
2184         }
2185     }
2186 
2187   if (icon_cache->origin <= USING_KWM_WIN_ICON &&
2188       icon_cache->kwm_win_icon_dirty)
2189     {
2190       icon_cache->kwm_win_icon_dirty = FALSE;
2191 
2192       get_kwm_win_icon (xwindow, &pixmap, &mask);
2193 
2194       if ((pixmap != icon_cache->prev_pixmap ||
2195            mask != icon_cache->prev_mask) &&
2196           pixmap != None)
2197         {
2198           if (try_pixmap_and_mask (pixmap, mask,
2199                                    iconp, ideal_width, ideal_height,
2200                                    mini_iconp, ideal_mini_width, ideal_mini_height))
2201             {
2202               icon_cache->prev_pixmap = pixmap;
2203               icon_cache->prev_mask = mask;
2204 
2205               replace_cache (icon_cache, USING_KWM_WIN_ICON,
2206                              *iconp, *mini_iconp);
2207 
2208               return TRUE;
2209             }
2210         }
2211     }
2212 
2213   if (icon_cache->want_fallback &&
2214       icon_cache->origin < USING_FALLBACK_ICON)
2215     {
2216       _wnck_get_fallback_icons (iconp,
2217                                 ideal_width,
2218                                 ideal_height,
2219                                 mini_iconp,
2220                                 ideal_mini_width,
2221                                 ideal_mini_height);
2222 
2223       replace_cache (icon_cache, USING_FALLBACK_ICON,
2224                      *iconp, *mini_iconp);
2225 
2226       return TRUE;
2227     }
2228 
2229   if (!icon_cache->want_fallback &&
2230       icon_cache->origin == USING_FALLBACK_ICON)
2231     {
2232       /* Get rid of current icon */
2233       clear_icon_cache (icon_cache, FALSE);
2234 
2235       return TRUE;
2236     }
2237 
2238   /* found nothing new */
2239   return FALSE;
2240 }
2241 
2242 static GdkPixbuf*
default_icon_at_size(int width,int height)2243 default_icon_at_size (int width,
2244                       int height)
2245 {
2246 
2247   GdkPixbuf *base;
2248 
2249   base = gdk_pixbuf_new_from_inline (-1, default_icon_data,
2250                                      FALSE,
2251                                      NULL);
2252 
2253   g_assert (base);
2254 
2255   if ((width < 0 && height < 0) ||
2256       (gdk_pixbuf_get_width (base) == width &&
2257        gdk_pixbuf_get_height (base) == height))
2258     {
2259       return base;
2260     }
2261   else
2262     {
2263       GdkPixbuf *scaled;
2264 
2265       scaled = gdk_pixbuf_scale_simple (base,
2266                                         width > 0 ? width :
2267                                         gdk_pixbuf_get_width (base),
2268                                         height > 0 ? height :
2269                                         gdk_pixbuf_get_height (base),
2270                                         GDK_INTERP_BILINEAR);
2271 
2272       g_object_unref (G_OBJECT (base));
2273 
2274       return scaled;
2275     }
2276 }
2277 
2278 void
_wnck_get_fallback_icons(GdkPixbuf ** iconp,int ideal_width,int ideal_height,GdkPixbuf ** mini_iconp,int ideal_mini_width,int ideal_mini_height)2279 _wnck_get_fallback_icons (GdkPixbuf **iconp,
2280                           int         ideal_width,
2281                           int         ideal_height,
2282                           GdkPixbuf **mini_iconp,
2283                           int         ideal_mini_width,
2284                           int         ideal_mini_height)
2285 {
2286   if (iconp)
2287     *iconp = default_icon_at_size (ideal_width > 0 ? ideal_width :
2288                                    DEFAULT_ICON_WIDTH,
2289                                    ideal_height > 0 ? ideal_height :
2290                                    DEFAULT_ICON_HEIGHT);
2291 
2292   if (mini_iconp)
2293     *mini_iconp = default_icon_at_size (ideal_mini_width > 0 ? ideal_mini_width :
2294                                         DEFAULT_MINI_ICON_WIDTH,
2295                                         ideal_mini_height > 0 ? ideal_mini_height :
2296                                         DEFAULT_MINI_ICON_HEIGHT);
2297 }
2298 
2299 
2300 void
_wnck_get_window_geometry(Screen * screen,Window xwindow,int * xp,int * yp,int * widthp,int * heightp)2301 _wnck_get_window_geometry (Screen *screen,
2302 			   Window  xwindow,
2303                            int    *xp,
2304                            int    *yp,
2305                            int    *widthp,
2306                            int    *heightp)
2307 {
2308   Display *display;
2309   int x, y;
2310   unsigned int width, height, bw, depth;
2311   Window root_window;
2312 
2313   width = 1;
2314   height = 1;
2315 
2316   display = DisplayOfScreen (screen);
2317 
2318   _wnck_error_trap_push ();
2319 
2320   XGetGeometry (display,
2321                 xwindow,
2322                 &root_window,
2323                 &x, &y, &width, &height, &bw, &depth);
2324 
2325   _wnck_error_trap_pop ();
2326 
2327   _wnck_get_window_position (screen, xwindow, xp, yp);
2328 
2329   if (widthp)
2330     *widthp = width;
2331   if (heightp)
2332     *heightp = height;
2333 }
2334 
_wnck_set_window_geometry(Screen * screen,Window xwindow,int gravity_and_flags,int x,int y,int width,int height)2335 void _wnck_set_window_geometry (Screen *screen,
2336                                 Window  xwindow,
2337                                 int     gravity_and_flags,
2338                                 int     x,
2339                                 int     y,
2340                                 int     width,
2341                                 int     height)
2342 {
2343   Display *display;
2344   Window   root;
2345   XEvent   xev;
2346 
2347   display = DisplayOfScreen (screen);
2348   root = RootWindowOfScreen (screen);
2349 
2350   xev.xclient.type = ClientMessage;
2351   xev.xclient.serial = 0;
2352   xev.xclient.send_event = True;
2353   xev.xclient.display = display;
2354   xev.xclient.window = xwindow;
2355   xev.xclient.message_type = _wnck_atom_get ("_NET_MOVERESIZE_WINDOW");
2356   xev.xclient.format = 32;
2357   xev.xclient.data.l[0] = gravity_and_flags;
2358   xev.xclient.data.l[1] = x;
2359   xev.xclient.data.l[2] = y;
2360   xev.xclient.data.l[3] = width;
2361   xev.xclient.data.l[4] = height;
2362 
2363   _wnck_error_trap_push ();
2364   XSendEvent (display,
2365               root,
2366               False,
2367               SubstructureRedirectMask | SubstructureNotifyMask,
2368               &xev);
2369   _wnck_error_trap_pop ();
2370 }
2371 
2372 void
_wnck_get_window_position(Screen * screen,Window xwindow,int * xp,int * yp)2373 _wnck_get_window_position (Screen *screen,
2374 			   Window  xwindow,
2375                            int    *xp,
2376                            int    *yp)
2377 {
2378   Display *display;
2379   Window   root;
2380   int      x, y;
2381   Window   child;
2382 
2383   x = 0;
2384   y = 0;
2385 
2386   display = DisplayOfScreen (screen);
2387   root = RootWindowOfScreen (screen);
2388 
2389   _wnck_error_trap_push ();
2390   XTranslateCoordinates (display,
2391                          xwindow,
2392 			 root,
2393                          0, 0,
2394                          &x, &y, &child);
2395   _wnck_error_trap_pop ();
2396 
2397   if (xp)
2398     *xp = x;
2399   if (yp)
2400     *yp = y;
2401 }
2402 
2403 void
_wnck_set_icon_geometry(Window xwindow,int x,int y,int width,int height)2404 _wnck_set_icon_geometry  (Window xwindow,
2405 			  int    x,
2406 			  int    y,
2407 			  int    width,
2408 			  int    height)
2409 {
2410   gulong data[4];
2411 
2412   data[0] = x;
2413   data[1] = y;
2414   data[2] = width;
2415   data[3] = height;
2416 
2417   _wnck_error_trap_push ();
2418 
2419   XChangeProperty (_wnck_get_default_display (),
2420 		   xwindow,
2421 		   _wnck_atom_get ("_NET_WM_ICON_GEOMETRY"),
2422 		   XA_CARDINAL, 32, PropModeReplace,
2423 		   (guchar *)&data, 4);
2424 
2425   _wnck_error_trap_pop ();
2426 }
2427 
2428 /* orientation of pager */
2429 #define _NET_WM_ORIENTATION_HORZ 0
2430 #define _NET_WM_ORIENTATION_VERT 1
2431 
2432 /* starting corner for counting spaces */
2433 #define _NET_WM_TOPLEFT     0
2434 #define _NET_WM_TOPRIGHT    1
2435 #define _NET_WM_BOTTOMRIGHT 2
2436 #define _NET_WM_BOTTOMLEFT  3
2437 
2438 void
_wnck_set_desktop_layout(Screen * xscreen,int rows,int columns)2439 _wnck_set_desktop_layout (Screen *xscreen,
2440                           int     rows,
2441                           int     columns)
2442 {
2443   Display *display;
2444   Window   root;
2445   gulong   data[4];
2446 
2447   /* FIXME: hack, hack, hack so as not
2448    * to have to add a orientation param
2449    * to wnck_screen_try_set_workspace_layout.
2450    *
2451    * Remove this crack asap.
2452    */
2453   g_assert ((rows == 0) || (columns == 0));
2454 
2455   display = DisplayOfScreen (xscreen);
2456   root = RootWindowOfScreen (xscreen);
2457 
2458   data[0] = (columns == 0) ? _NET_WM_ORIENTATION_HORZ : _NET_WM_ORIENTATION_VERT;
2459   data[1] = columns;
2460   data[2] = rows;
2461   data[3] = _NET_WM_TOPLEFT;
2462 
2463   _wnck_error_trap_push ();
2464 
2465   XChangeProperty (display,
2466                    root,
2467 		   _wnck_atom_get ("_NET_DESKTOP_LAYOUT"),
2468 		   XA_CARDINAL, 32, PropModeReplace,
2469 		   (guchar *)&data, 4);
2470 
2471   _wnck_error_trap_pop ();
2472 }
2473 
2474 typedef struct
2475 {
2476   Window window;
2477   Atom timestamp_prop_atom;
2478 } TimeStampInfo;
2479 
2480 static Bool
timestamp_predicate(Display * display,XEvent * xevent,XPointer arg)2481 timestamp_predicate (Display *display,
2482 		     XEvent  *xevent,
2483 		     XPointer arg)
2484 {
2485   TimeStampInfo *info = (TimeStampInfo *)arg;
2486 
2487   if (xevent->type == PropertyNotify &&
2488       xevent->xproperty.window == info->window &&
2489       xevent->xproperty.atom == info->timestamp_prop_atom)
2490     return True;
2491 
2492   return False;
2493 }
2494 
2495 /**
2496  * get_server_time:
2497  * @display: display from which to get the time
2498  * @window: a #Window, used for communication with the server.
2499  *          The window must have PropertyChangeMask in its
2500  *          events mask or a hang will result.
2501  *
2502  * Routine to get the current X server time stamp.
2503  *
2504  * Return value: the time stamp.
2505  **/
2506 static Time
get_server_time(Window window)2507 get_server_time (Window window)
2508 {
2509   unsigned char c = 'a';
2510   XEvent xevent;
2511   TimeStampInfo info;
2512 
2513   info.timestamp_prop_atom = _wnck_atom_get ("_TIMESTAMP_PROP");
2514   info.window = window;
2515 
2516   XChangeProperty (_wnck_get_default_display (), window,
2517 		   info.timestamp_prop_atom, info.timestamp_prop_atom,
2518 		   8, PropModeReplace, &c, 1);
2519 
2520   XIfEvent (_wnck_get_default_display (), &xevent,
2521 	    timestamp_predicate, (XPointer)&info);
2522 
2523   return xevent.xproperty.time;
2524 }
2525 
2526 typedef struct
2527 {
2528   Display *display;
2529   int screen_number;
2530   int token;
2531   Window window;
2532   Atom selection_atom;
2533   Atom manager_atom;
2534 } LayoutManager;
2535 
2536 static GSList *layout_managers = NULL;
2537 static int next_token = 1;
2538 
2539 static void
_wnck_free_layout_manager(LayoutManager * lm)2540 _wnck_free_layout_manager (LayoutManager *lm)
2541 {
2542   _wnck_error_trap_push ();
2543   XDestroyWindow (lm->display, lm->window);
2544   _wnck_error_trap_pop ();
2545 
2546   g_slice_free (LayoutManager, lm);
2547 
2548   layout_managers = g_slist_remove (layout_managers, lm);
2549 }
2550 
2551 int
_wnck_try_desktop_layout_manager(Screen * xscreen,int current_token)2552 _wnck_try_desktop_layout_manager (Screen *xscreen,
2553                                   int     current_token)
2554 {
2555   Display *display;
2556   Window root;
2557   Atom selection_atom;
2558   Window owner;
2559   GSList *tmp;
2560   int number;
2561   Time timestamp;
2562   XClientMessageEvent xev;
2563   char buffer[256];
2564   LayoutManager *lm;
2565 
2566   display = DisplayOfScreen (xscreen);
2567   root = RootWindowOfScreen (xscreen);
2568   number = XScreenNumberOfScreen (xscreen);
2569 
2570   sprintf (buffer, "_NET_DESKTOP_LAYOUT_S%d", number);
2571   selection_atom = _wnck_atom_get (buffer);
2572 
2573   owner = XGetSelectionOwner (display, selection_atom);
2574 
2575   tmp = layout_managers;
2576   while (tmp != NULL)
2577     {
2578       lm = tmp->data;
2579 
2580       if (display == lm->display &&
2581           number == lm->screen_number)
2582         {
2583           if (current_token == lm->token)
2584             {
2585               if (owner == lm->window)
2586                 return current_token; /* we still have the selection */
2587               else
2588                 { /* we lost the selection */
2589                   _wnck_free_layout_manager (lm);
2590                   break;
2591                 }
2592             }
2593           else
2594             return WNCK_NO_MANAGER_TOKEN; /* someone else has it */
2595         }
2596 
2597       tmp = tmp->next;
2598     }
2599 
2600   if (owner != None)
2601     return WNCK_NO_MANAGER_TOKEN; /* someone else has the selection */
2602 
2603   /* No one has the selection at the moment */
2604 
2605   lm = g_slice_new0 (LayoutManager);
2606 
2607   lm->display = display;
2608   lm->screen_number = number;
2609   lm->token = next_token;
2610   ++next_token;
2611 
2612   lm->selection_atom = selection_atom;
2613   lm->manager_atom = _wnck_atom_get ("MANAGER");
2614 
2615   _wnck_error_trap_push ();
2616 
2617   lm->window = XCreateSimpleWindow (display,
2618                                     root,
2619                                     0, 0, 10, 10, 0,
2620                                     WhitePixel (display, number),
2621                                     WhitePixel (display, number));
2622 
2623   XSelectInput (display, lm->window, PropertyChangeMask);
2624   timestamp = get_server_time (lm->window);
2625 
2626   XSetSelectionOwner (display, lm->selection_atom,
2627 		      lm->window, timestamp);
2628 
2629   _wnck_error_trap_pop ();
2630 
2631   /* Check to see if we managed to claim the selection. */
2632 
2633   if (XGetSelectionOwner (display, lm->selection_atom) !=
2634       lm->window)
2635     {
2636       g_free (lm);
2637       return WNCK_NO_MANAGER_TOKEN;
2638     }
2639 
2640   xev.type = ClientMessage;
2641   xev.window = root;
2642   xev.message_type = lm->manager_atom;
2643   xev.format = 32;
2644   xev.data.l[0] = timestamp;
2645   xev.data.l[1] = lm->selection_atom;
2646   xev.data.l[2] = lm->window;
2647   xev.data.l[3] = 0;	/* manager specific data */
2648   xev.data.l[4] = 0;	/* manager specific data */
2649 
2650   _wnck_error_trap_push ();
2651   XSendEvent (display, root,
2652               False, StructureNotifyMask, (XEvent *)&xev);
2653   _wnck_error_trap_pop ();
2654 
2655   layout_managers = g_slist_prepend (layout_managers,
2656                                      lm);
2657 
2658   return lm->token;
2659 }
2660 
2661 void
_wnck_release_desktop_layout_manager(Screen * xscreen,int current_token)2662 _wnck_release_desktop_layout_manager (Screen *xscreen,
2663                                       int     current_token)
2664 {
2665   Display *display;
2666   GSList *tmp;
2667   int number;
2668   LayoutManager *lm;
2669 
2670   display = DisplayOfScreen (xscreen);
2671   number = XScreenNumberOfScreen (xscreen);
2672 
2673   tmp = layout_managers;
2674   while (tmp != NULL)
2675     {
2676       lm = tmp->data;
2677 
2678       if (display == lm->display &&
2679           number == lm->screen_number)
2680         {
2681           if (current_token == lm->token)
2682             {
2683               _wnck_error_trap_push ();
2684 
2685               /* release selection ownership */
2686               if (XGetSelectionOwner (display, lm->selection_atom) !=
2687                   lm->window)
2688                 {
2689                   Time timestamp;
2690 
2691                   timestamp = get_server_time (lm->window);
2692                   XSetSelectionOwner (display, lm->selection_atom,
2693                                       None, timestamp);
2694                 }
2695 
2696               _wnck_error_trap_pop ();
2697 
2698               _wnck_free_layout_manager (lm);
2699               return;
2700             }
2701         }
2702 
2703       tmp = tmp->next;
2704     }
2705 }
2706 
2707 gboolean
_wnck_desktop_layout_manager_process_event(XEvent * xev)2708 _wnck_desktop_layout_manager_process_event (XEvent *xev)
2709 {
2710   GSList *tmp;
2711   LayoutManager *lm;
2712 
2713   if (xev->type != SelectionClear)
2714     return FALSE;
2715 
2716   tmp = layout_managers;
2717   while (tmp != NULL)
2718     {
2719       lm = tmp->data;
2720 
2721       if (xev->xany.display == lm->display &&
2722           xev->xany.window == lm->window &&
2723           xev->xselectionclear.selection == lm->selection_atom)
2724         {
2725           _wnck_free_layout_manager (lm);
2726           return TRUE;
2727         }
2728 
2729       tmp = tmp->next;
2730     }
2731 
2732   return FALSE;
2733 }
2734