1 /* GAIL - The GNOME Accessibility Implementation Library
2  * Copyright 2001, 2002, 2003 Sun Microsystems Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 
20 #include "config.h"
21 
22 #include <string.h>
23 
24 #undef GTK_DISABLE_DEPRECATED
25 
26 #include <gtk/gtk.h>
27 
28 #include "gailwindow.h"
29 #include "gailtoplevel.h"
30 #include "gail-private-macros.h"
31 
32 enum {
33   ACTIVATE,
34   CREATE,
35   DEACTIVATE,
36   DESTROY,
37   MAXIMIZE,
38   MINIMIZE,
39   MOVE,
40   RESIZE,
41   RESTORE,
42   LAST_SIGNAL
43 };
44 
45 static void gail_window_class_init (GailWindowClass *klass);
46 
47 static void                  gail_window_init            (GailWindow   *accessible);
48 
49 static void                  gail_window_real_initialize (AtkObject    *obj,
50                                                           gpointer     data);
51 static void                  gail_window_finalize        (GObject      *object);
52 
53 static const gchar*          gail_window_get_name        (AtkObject     *accessible);
54 
55 static AtkObject*            gail_window_get_parent     (AtkObject     *accessible);
56 static gint                  gail_window_get_index_in_parent (AtkObject *accessible);
57 static gboolean              gail_window_real_focus_gtk (GtkWidget     *widget,
58                                                          GdkEventFocus *event);
59 
60 static AtkStateSet*          gail_window_ref_state_set  (AtkObject     *accessible);
61 static AtkRelationSet*       gail_window_ref_relation_set  (AtkObject     *accessible);
62 static void                  gail_window_real_notify_gtk (GObject      *obj,
63                                                           GParamSpec   *pspec);
64 static gint                  gail_window_get_mdi_zorder (AtkComponent  *component);
65 
66 static gboolean              gail_window_state_event_gtk (GtkWidget           *widget,
67                                                           GdkEventWindowState *event);
68 
69 /* atkcomponent.h */
70 static void                  atk_component_interface_init (AtkComponentIface    *iface);
71 
72 static void                  gail_window_get_extents      (AtkComponent         *component,
73                                                            gint                 *x,
74                                                            gint                 *y,
75                                                            gint                 *width,
76                                                            gint                 *height,
77                                                            AtkCoordType         coord_type);
78 static void                  gail_window_get_size         (AtkComponent         *component,
79                                                            gint                 *width,
80                                                            gint                 *height);
81 
82 static guint gail_window_signals [LAST_SIGNAL] = { 0, };
83 
G_DEFINE_TYPE_WITH_CODE(GailWindow,gail_window,GAIL_TYPE_CONTAINER,G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT,atk_component_interface_init))84 G_DEFINE_TYPE_WITH_CODE (GailWindow, gail_window, GAIL_TYPE_CONTAINER,
85                          G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, atk_component_interface_init))
86 
87 static void
88 gail_window_class_init (GailWindowClass *klass)
89 {
90   GailWidgetClass *widget_class;
91   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
92   AtkObjectClass  *class = ATK_OBJECT_CLASS (klass);
93 
94   gobject_class->finalize = gail_window_finalize;
95 
96   widget_class = (GailWidgetClass*)klass;
97   widget_class->focus_gtk = gail_window_real_focus_gtk;
98   widget_class->notify_gtk = gail_window_real_notify_gtk;
99 
100   class->get_name = gail_window_get_name;
101   class->get_parent = gail_window_get_parent;
102   class->get_index_in_parent = gail_window_get_index_in_parent;
103   class->ref_relation_set = gail_window_ref_relation_set;
104   class->ref_state_set = gail_window_ref_state_set;
105   class->initialize = gail_window_real_initialize;
106 
107   gail_window_signals [ACTIVATE] =
108     g_signal_new ("activate",
109                   G_TYPE_FROM_CLASS (klass),
110                   G_SIGNAL_RUN_LAST,
111                   0, /* default signal handler */
112                   NULL, NULL,
113                   g_cclosure_marshal_VOID__VOID,
114                   G_TYPE_NONE, 0);
115   gail_window_signals [CREATE] =
116     g_signal_new ("create",
117                   G_TYPE_FROM_CLASS (klass),
118                   G_SIGNAL_RUN_LAST,
119                   0, /* default signal handler */
120                   NULL, NULL,
121                   g_cclosure_marshal_VOID__VOID,
122                   G_TYPE_NONE, 0);
123   gail_window_signals [DEACTIVATE] =
124     g_signal_new ("deactivate",
125                   G_TYPE_FROM_CLASS (klass),
126                   G_SIGNAL_RUN_LAST,
127                   0, /* default signal handler */
128                   NULL, NULL,
129                   g_cclosure_marshal_VOID__VOID,
130                   G_TYPE_NONE, 0);
131   gail_window_signals [DESTROY] =
132     g_signal_new ("destroy",
133                   G_TYPE_FROM_CLASS (klass),
134                   G_SIGNAL_RUN_LAST,
135                   0, /* default signal handler */
136                   NULL, NULL,
137                   g_cclosure_marshal_VOID__VOID,
138                   G_TYPE_NONE, 0);
139   gail_window_signals [MAXIMIZE] =
140     g_signal_new ("maximize",
141                   G_TYPE_FROM_CLASS (klass),
142                   G_SIGNAL_RUN_LAST,
143                   0, /* default signal handler */
144                   NULL, NULL,
145                   g_cclosure_marshal_VOID__VOID,
146                   G_TYPE_NONE, 0);
147   gail_window_signals [MINIMIZE] =
148     g_signal_new ("minimize",
149                   G_TYPE_FROM_CLASS (klass),
150                   G_SIGNAL_RUN_LAST,
151                   0, /* default signal handler */
152                   NULL, NULL,
153                   g_cclosure_marshal_VOID__VOID,
154                   G_TYPE_NONE, 0);
155   gail_window_signals [MOVE] =
156     g_signal_new ("move",
157                   G_TYPE_FROM_CLASS (klass),
158                   G_SIGNAL_RUN_LAST,
159                   0, /* default signal handler */
160                   NULL, NULL,
161                   g_cclosure_marshal_VOID__VOID,
162                   G_TYPE_NONE, 0);
163   gail_window_signals [RESIZE] =
164     g_signal_new ("resize",
165                   G_TYPE_FROM_CLASS (klass),
166                   G_SIGNAL_RUN_LAST,
167                   0, /* default signal handler */
168                   NULL, NULL,
169                   g_cclosure_marshal_VOID__VOID,
170                   G_TYPE_NONE, 0);
171   gail_window_signals [RESTORE] =
172     g_signal_new ("restore",
173                   G_TYPE_FROM_CLASS (klass),
174                   G_SIGNAL_RUN_LAST,
175                   0, /* default signal handler */
176                   NULL, NULL,
177                   g_cclosure_marshal_VOID__VOID,
178                   G_TYPE_NONE, 0);
179 }
180 
181 static void
gail_window_init(GailWindow * accessible)182 gail_window_init (GailWindow   *accessible)
183 {
184 }
185 
186 static void
gail_window_real_initialize(AtkObject * obj,gpointer data)187 gail_window_real_initialize (AtkObject *obj,
188                              gpointer  data)
189 {
190   GtkWidget *widget = GTK_WIDGET (data);
191   GailWindow *window;
192 
193   /*
194    * A GailWindow can be created for a GtkHandleBox or a GtkWindow
195    */
196   if (!GTK_IS_WINDOW (widget) &&
197       !GTK_IS_HANDLE_BOX (widget))
198     gail_return_if_fail (FALSE);
199 
200   ATK_OBJECT_CLASS (gail_window_parent_class)->initialize (obj, data);
201 
202   window = GAIL_WINDOW (obj);
203   window->name_change_handler = 0;
204   window->previous_name = g_strdup (gtk_window_get_title (GTK_WINDOW (data)));
205 
206   g_signal_connect (data,
207                     "window_state_event",
208                     G_CALLBACK (gail_window_state_event_gtk),
209                     NULL);
210   g_object_set_data (G_OBJECT (obj), "atk-component-layer",
211                      GINT_TO_POINTER (ATK_LAYER_WINDOW));
212 
213   if (GTK_IS_FILE_SELECTION (widget))
214     obj->role = ATK_ROLE_FILE_CHOOSER;
215   else if (GTK_IS_COLOR_SELECTION_DIALOG (widget))
216     obj->role = ATK_ROLE_COLOR_CHOOSER;
217   else if (GTK_IS_FONT_SELECTION_DIALOG (widget))
218     obj->role = ATK_ROLE_FONT_CHOOSER;
219   else if (GTK_IS_MESSAGE_DIALOG (widget))
220     obj->role = ATK_ROLE_ALERT;
221   else if (GTK_IS_DIALOG (widget))
222     obj->role = ATK_ROLE_DIALOG;
223   else
224     {
225       const gchar *name;
226 
227       name = gtk_widget_get_name (widget);
228       if (name && (!strcmp (name, "gtk-tooltip") ||
229                    !strcmp (name, "gtk-tooltips")))
230         obj->role = ATK_ROLE_TOOL_TIP;
231       else if (GTK_IS_PLUG (widget))
232         obj->role = ATK_ROLE_PANEL;
233       else if (GTK_WINDOW (widget)->type == GTK_WINDOW_POPUP)
234         obj->role = ATK_ROLE_WINDOW;
235       else
236         obj->role = ATK_ROLE_FRAME;
237     }
238 
239   /*
240    * Notify that tooltip is showing
241    */
242   if (obj->role == ATK_ROLE_TOOL_TIP &&
243       gtk_widget_get_mapped (widget))
244     atk_object_notify_state_change (obj, ATK_STATE_SHOWING, 1);
245 }
246 
247 static void
gail_window_finalize(GObject * object)248 gail_window_finalize (GObject *object)
249 {
250   GailWindow* window = GAIL_WINDOW (object);
251 
252   if (window->name_change_handler)
253     {
254       g_source_remove (window->name_change_handler);
255       window->name_change_handler = 0;
256     }
257   if (window->previous_name)
258     {
259       g_free (window->previous_name);
260       window->previous_name = NULL;
261     }
262 
263   G_OBJECT_CLASS (gail_window_parent_class)->finalize (object);
264 }
265 
266 static const gchar*
gail_window_get_name(AtkObject * accessible)267 gail_window_get_name (AtkObject *accessible)
268 {
269   const gchar* name;
270 
271   name = ATK_OBJECT_CLASS (gail_window_parent_class)->get_name (accessible);
272   if (name == NULL)
273     {
274       /*
275        * Get the window title if it exists
276        */
277       GtkWidget* widget = GTK_ACCESSIBLE (accessible)->widget;
278 
279       if (widget == NULL)
280         /*
281          * State is defunct
282          */
283         return NULL;
284 
285       gail_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
286 
287       if (GTK_IS_WINDOW (widget))
288         {
289           GtkWindow *window = GTK_WINDOW (widget);
290 
291           name = gtk_window_get_title (window);
292           if (name == NULL &&
293               accessible->role == ATK_ROLE_TOOL_TIP)
294             {
295               GtkWidget *child;
296 
297               child = gtk_bin_get_child (GTK_BIN (window));
298               /* could be some kind of egg notification bubble thingy? */
299 
300               /* Handle new GTK+ GNOME 2.20 tooltips */
301               if (GTK_IS_ALIGNMENT(child))
302                 {
303                   child = gtk_bin_get_child (GTK_BIN (child));
304                   if (GTK_IS_BOX(child))
305                     {
306                       GList *children;
307                       guint count;
308                       children = gtk_container_get_children (GTK_CONTAINER (child));
309                       count = g_list_length (children);
310                       if (count == 2)
311                         {
312                           child = (GtkWidget *) g_list_nth_data (children, 1);
313                         }
314                       g_list_free (children);
315                     }
316                 }
317 
318               if (!GTK_IS_LABEL (child))
319               {
320                   g_message ("ATK_ROLE_TOOLTIP object found, but doesn't look like a tooltip.");
321                   return NULL;
322               }
323               name = gtk_label_get_text (GTK_LABEL (child));
324             }
325         }
326     }
327   return name;
328 }
329 
330 static AtkObject*
gail_window_get_parent(AtkObject * accessible)331 gail_window_get_parent (AtkObject *accessible)
332 {
333   AtkObject* parent;
334 
335   parent = ATK_OBJECT_CLASS (gail_window_parent_class)->get_parent (accessible);
336 
337   return parent;
338 }
339 
340 static gint
gail_window_get_index_in_parent(AtkObject * accessible)341 gail_window_get_index_in_parent (AtkObject *accessible)
342 {
343   GtkWidget* widget = GTK_ACCESSIBLE (accessible)->widget;
344   AtkObject* atk_obj = atk_get_root ();
345   gint index = -1;
346 
347   if (widget == NULL)
348     /*
349      * State is defunct
350      */
351     return -1;
352 
353   gail_return_val_if_fail (GTK_IS_WIDGET (widget), -1);
354 
355   index = ATK_OBJECT_CLASS (gail_window_parent_class)->get_index_in_parent (accessible);
356   if (index != -1)
357     return index;
358 
359   if (GTK_IS_WINDOW (widget))
360     {
361       GtkWindow *window = GTK_WINDOW (widget);
362       if (GAIL_IS_TOPLEVEL (atk_obj))
363         {
364 	  GailToplevel* toplevel = GAIL_TOPLEVEL (atk_obj);
365 	  index = g_list_index (toplevel->window_list, window);
366 	}
367       else
368         {
369 	  int i, sibling_count = atk_object_get_n_accessible_children (atk_obj);
370 	  for (i = 0; i < sibling_count && index == -1; ++i)
371 	    {
372 	      AtkObject *child = atk_object_ref_accessible_child (atk_obj, i);
373 	      if (accessible == child) index = i;
374 	      g_object_unref (G_OBJECT (child));
375 	    }
376 	}
377     }
378   return index;
379 }
380 
381 static gboolean
gail_window_real_focus_gtk(GtkWidget * widget,GdkEventFocus * event)382 gail_window_real_focus_gtk (GtkWidget     *widget,
383                             GdkEventFocus *event)
384 {
385   AtkObject* obj;
386 
387   obj = gtk_widget_get_accessible (widget);
388   atk_object_notify_state_change (obj, ATK_STATE_ACTIVE, event->in);
389 
390   return FALSE;
391 }
392 
393 static AtkRelationSet*
gail_window_ref_relation_set(AtkObject * obj)394 gail_window_ref_relation_set (AtkObject *obj)
395 {
396   GtkWidget *widget;
397   AtkRelationSet *relation_set;
398   AtkObject *array[1];
399   AtkRelation* relation;
400   GtkWidget *current_widget;
401 
402   gail_return_val_if_fail (GAIL_IS_WIDGET (obj), NULL);
403 
404   widget = GTK_ACCESSIBLE (obj)->widget;
405   if (widget == NULL)
406     /*
407      * State is defunct
408      */
409     return NULL;
410 
411   relation_set = ATK_OBJECT_CLASS (gail_window_parent_class)->ref_relation_set (obj);
412 
413   if (atk_object_get_role (obj) == ATK_ROLE_TOOL_TIP)
414     {
415       relation = atk_relation_set_get_relation_by_type (relation_set, ATK_RELATION_POPUP_FOR);
416 
417       if (relation)
418         {
419           atk_relation_set_remove (relation_set, relation);
420         }
421       if (gtk_widget_get_visible(widget) && gtk_tooltips_get_info_from_tip_window (GTK_WINDOW (widget), NULL, &current_widget))
422         {
423           array [0] = gtk_widget_get_accessible (current_widget);
424 
425           relation = atk_relation_new (array, 1, ATK_RELATION_POPUP_FOR);
426           atk_relation_set_add (relation_set, relation);
427           g_object_unref (relation);
428         }
429     }
430   return relation_set;
431 }
432 
433 static AtkStateSet*
gail_window_ref_state_set(AtkObject * accessible)434 gail_window_ref_state_set (AtkObject *accessible)
435 {
436   AtkStateSet *state_set;
437   GtkWidget *widget;
438   GtkWindow *window;
439   GdkWindowState state;
440 
441   state_set = ATK_OBJECT_CLASS (gail_window_parent_class)->ref_state_set (accessible);
442   widget = GTK_ACCESSIBLE (accessible)->widget;
443 
444   if (widget == NULL)
445     return state_set;
446 
447   window = GTK_WINDOW (widget);
448 
449   if (window->has_focus)
450     atk_state_set_add_state (state_set, ATK_STATE_ACTIVE);
451 
452   if (widget->window)
453     {
454       state = gdk_window_get_state (widget->window);
455       if (state & GDK_WINDOW_STATE_ICONIFIED)
456         atk_state_set_add_state (state_set, ATK_STATE_ICONIFIED);
457     }
458   if (gtk_window_get_modal (window))
459     atk_state_set_add_state (state_set, ATK_STATE_MODAL);
460 
461   if (gtk_window_get_resizable (window))
462     atk_state_set_add_state (state_set, ATK_STATE_RESIZABLE);
463 
464   return state_set;
465 }
466 
467 static gboolean
idle_notify_name_change(gpointer data)468 idle_notify_name_change (gpointer data)
469 {
470   GailWindow *window;
471   AtkObject *obj;
472 
473   window = GAIL_WINDOW (data);
474   window->name_change_handler = 0;
475   if (GTK_ACCESSIBLE (window)->widget == NULL)
476     return FALSE;
477 
478   obj = ATK_OBJECT (window);
479   if (obj->name == NULL)
480     {
481     /*
482      * The title has changed so notify a change in accessible-name
483      */
484       g_object_notify (G_OBJECT (obj), "accessible-name");
485     }
486   g_signal_emit_by_name (obj, "visible_data_changed");
487 
488   return FALSE;
489 }
490 
491 static void
gail_window_real_notify_gtk(GObject * obj,GParamSpec * pspec)492 gail_window_real_notify_gtk (GObject		*obj,
493                              GParamSpec		*pspec)
494 {
495   GtkWidget *widget = GTK_WIDGET (obj);
496   AtkObject* atk_obj = gtk_widget_get_accessible (widget);
497   GailWindow *window = GAIL_WINDOW (atk_obj);
498   const gchar *name;
499   gboolean name_changed = FALSE;
500 
501   if (strcmp (pspec->name, "title") == 0)
502     {
503       name = gtk_window_get_title (GTK_WINDOW (widget));
504       if (name)
505         {
506          if (window->previous_name == NULL ||
507              strcmp (name, window->previous_name) != 0)
508            name_changed = TRUE;
509         }
510       else if (window->previous_name != NULL)
511         name_changed = TRUE;
512 
513       if (name_changed)
514         {
515           g_free (window->previous_name);
516           window->previous_name = g_strdup (name);
517 
518           if (window->name_change_handler == 0)
519             window->name_change_handler = gdk_threads_add_idle (idle_notify_name_change, atk_obj);
520         }
521     }
522   else
523     GAIL_WIDGET_CLASS (gail_window_parent_class)->notify_gtk (obj, pspec);
524 }
525 
526 static gboolean
gail_window_state_event_gtk(GtkWidget * widget,GdkEventWindowState * event)527 gail_window_state_event_gtk (GtkWidget           *widget,
528                              GdkEventWindowState *event)
529 {
530   AtkObject* obj;
531 
532   obj = gtk_widget_get_accessible (widget);
533   atk_object_notify_state_change (obj, ATK_STATE_ICONIFIED,
534                          (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) != 0);
535   return FALSE;
536 }
537 
538 static void
atk_component_interface_init(AtkComponentIface * iface)539 atk_component_interface_init (AtkComponentIface *iface)
540 {
541   iface->get_extents = gail_window_get_extents;
542   iface->get_size = gail_window_get_size;
543   iface->get_mdi_zorder = gail_window_get_mdi_zorder;
544 }
545 
546 static void
gail_window_get_extents(AtkComponent * component,gint * x,gint * y,gint * width,gint * height,AtkCoordType coord_type)547 gail_window_get_extents (AtkComponent  *component,
548                          gint          *x,
549                          gint          *y,
550                          gint          *width,
551                          gint          *height,
552                          AtkCoordType  coord_type)
553 {
554   GtkWidget *widget = GTK_ACCESSIBLE (component)->widget;
555   GdkRectangle rect;
556   gint x_toplevel, y_toplevel;
557 
558   if (widget == NULL)
559     /*
560      * State is defunct
561      */
562     return;
563 
564   gail_return_if_fail (GTK_IS_WINDOW (widget));
565 
566   if (!gtk_widget_is_toplevel (widget))
567     {
568       AtkComponentIface *parent_iface;
569 
570       parent_iface = (AtkComponentIface *) g_type_interface_peek_parent (ATK_COMPONENT_GET_IFACE (component));
571       parent_iface->get_extents (component, x, y, width, height, coord_type);
572       return;
573     }
574 
575   gdk_window_get_frame_extents (widget->window, &rect);
576 
577   *width = rect.width;
578   *height = rect.height;
579   if (!gtk_widget_is_drawable (widget))
580     {
581       *x = G_MININT;
582       *y = G_MININT;
583       return;
584     }
585   *x = rect.x;
586   *y = rect.y;
587   if (coord_type == ATK_XY_WINDOW)
588     {
589       gdk_window_get_origin (widget->window, &x_toplevel, &y_toplevel);
590       *x -= x_toplevel;
591       *y -= y_toplevel;
592     }
593 }
594 
595 static void
gail_window_get_size(AtkComponent * component,gint * width,gint * height)596 gail_window_get_size (AtkComponent *component,
597                       gint         *width,
598                       gint         *height)
599 {
600   GtkWidget *widget = GTK_ACCESSIBLE (component)->widget;
601   GdkRectangle rect;
602 
603   if (widget == NULL)
604     /*
605      * State is defunct
606      */
607     return;
608 
609   gail_return_if_fail (GTK_IS_WINDOW (widget));
610 
611   if (!gtk_widget_is_toplevel (widget))
612     {
613       AtkComponentIface *parent_iface;
614 
615       parent_iface = (AtkComponentIface *) g_type_interface_peek_parent (ATK_COMPONENT_GET_IFACE (component));
616       parent_iface->get_size (component, width, height);
617       return;
618     }
619   gdk_window_get_frame_extents (widget->window, &rect);
620 
621   *width = rect.width;
622   *height = rect.height;
623 }
624 
625 #if defined (GDK_WINDOWING_X11)
626 
627 #include <X11/Xlib.h>
628 #include <X11/Xatom.h>
629 #include <gdk/x11/gdkx.h>
630 
631 /* _NET_CLIENT_LIST_STACKING monitoring */
632 
633 typedef struct {
634   Window     *stacked_windows;
635   int         stacked_windows_len;
636   GdkWindow  *root_window;
637   guint       update_handler;
638   int        *desktop;
639   guint       update_desktop_handler;
640   gboolean   *desktop_changed;
641 
642   guint       screen_initialized : 1;
643   guint       update_stacked_windows : 1;
644 } GailScreenInfo;
645 
646 static GailScreenInfo *gail_screens = NULL;
647 static int             num_screens = 0;
648 static Atom            _net_client_list_stacking = None;
649 static Atom            _net_wm_desktop = None;
650 
651 static gint
get_window_desktop(Window window)652 get_window_desktop (Window window)
653 {
654   Atom            ret_type;
655   int             format;
656   gulong          nitems;
657   gulong          bytes_after;
658   guchar         *cardinals;
659   int             error;
660   int             result;
661   int             desktop;
662 
663   if (_net_wm_desktop == None)
664     _net_wm_desktop =
665 		XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_WM_DESKTOP", False);
666 
667   gdk_error_trap_push ();
668   result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), window, _net_wm_desktop,
669                                0, G_MAXLONG,
670                                False, XA_CARDINAL,
671                                &ret_type, &format, &nitems,
672                                &bytes_after, &cardinals);
673   error = gdk_error_trap_pop();
674   /* nitems < 1 will occur if the property is not set */
675   if (error != Success || result != Success || nitems < 1)
676     return -1;
677 
678   desktop = *cardinals;
679 
680   XFree (cardinals);
681   if (nitems != 1)
682     return -1;
683   return desktop;
684 }
685 
686 static void
free_screen_info(GailScreenInfo * info)687 free_screen_info (GailScreenInfo *info)
688 {
689   if (info->stacked_windows)
690     XFree (info->stacked_windows);
691   if (info->desktop)
692     g_free (info->desktop);
693   if (info->desktop_changed)
694     g_free (info->desktop_changed);
695 
696   info->stacked_windows = NULL;
697   info->stacked_windows_len = 0;
698   info->desktop = NULL;
699   info->desktop_changed = NULL;
700 }
701 
702 static gboolean
get_stacked_windows(GailScreenInfo * info)703 get_stacked_windows (GailScreenInfo *info)
704 {
705   Atom    ret_type;
706   int     format;
707   gulong  nitems;
708   gulong  bytes_after;
709   guchar *data;
710   int     error;
711   int     result;
712   int     i;
713   int     j;
714   int    *desktops;
715   gboolean *desktops_changed;
716 
717   if (_net_client_list_stacking == None)
718     _net_client_list_stacking =
719 		XInternAtom (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), "_NET_CLIENT_LIST_STACKING", False);
720 
721   gdk_error_trap_push ();
722   ret_type = None;
723   result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
724                                GDK_WINDOW_XWINDOW (info->root_window),
725                                _net_client_list_stacking,
726                                0, G_MAXLONG,
727                                False, XA_WINDOW, &ret_type, &format, &nitems,
728                                &bytes_after, &data);
729   error = gdk_error_trap_pop ();
730   /* nitems < 1 will occur if the property is not set */
731   if (error != Success || result != Success || nitems < 1)
732     {
733       free_screen_info (info);
734       return FALSE;
735     }
736 
737   if (ret_type != XA_WINDOW)
738     {
739       XFree (data);
740       free_screen_info (info);
741       return FALSE;
742     }
743 
744   desktops = g_malloc0 (nitems * sizeof (int));
745   desktops_changed = g_malloc0 (nitems * sizeof (gboolean));
746   for (i = 0; i < nitems; i++)
747     {
748       gboolean window_found = FALSE;
749 
750       for (j = 0; j < info->stacked_windows_len; j++)
751         {
752           if (info->stacked_windows [j] == data [i])
753             {
754               desktops [i] = info->desktop [j];
755               desktops_changed [i] = info->desktop_changed [j];
756               window_found = TRUE;
757               break;
758             }
759         }
760       if (!window_found)
761         {
762           desktops [i] = get_window_desktop (data [i]);
763           desktops_changed [i] = FALSE;
764         }
765     }
766   free_screen_info (info);
767   info->stacked_windows = (Window*) data;
768   info->stacked_windows_len = nitems;
769   info->desktop = desktops;
770   info->desktop_changed = desktops_changed;
771 
772   return TRUE;
773 }
774 
775 static gboolean
update_screen_info(gpointer data)776 update_screen_info (gpointer data)
777 {
778   int screen_n = GPOINTER_TO_INT (data);
779 
780   gail_screens [screen_n].update_handler = 0;
781   gail_screens [screen_n].update_stacked_windows = FALSE;
782 
783   get_stacked_windows (&gail_screens [screen_n]);
784 
785   return FALSE;
786 }
787 
788 static gboolean
update_desktop_info(gpointer data)789 update_desktop_info (gpointer data)
790 {
791   int screen_n = GPOINTER_TO_INT (data);
792   GailScreenInfo *info;
793   int i;
794 
795   info = &gail_screens [screen_n];
796   info->update_desktop_handler = 0;
797 
798   for (i = 0; i < info->stacked_windows_len; i++)
799     {
800       if (info->desktop_changed [i])
801         {
802           info->desktop [i] = get_window_desktop (info->stacked_windows [i]);
803           info->desktop_changed [i] = FALSE;
804         }
805     }
806 
807   return FALSE;
808 }
809 
810 static GdkFilterReturn
filter_func(GdkXEvent * gdkxevent,GdkEvent * event,gpointer data)811 filter_func (GdkXEvent *gdkxevent,
812 	     GdkEvent  *event,
813 	     gpointer   data)
814 {
815   XEvent *xevent = gdkxevent;
816 
817   if (xevent->type == PropertyNotify)
818     {
819       if (xevent->xproperty.atom == _net_client_list_stacking)
820         {
821           int     screen_n;
822           GdkWindow *window;
823 
824           window = event->any.window;
825 
826           if (window)
827             {
828               screen_n = gdk_screen_get_number (gdk_window_get_screen (window));
829 
830               gail_screens [screen_n].update_stacked_windows = TRUE;
831               if (!gail_screens [screen_n].update_handler)
832                 {
833                   gail_screens [screen_n].update_handler = gdk_threads_add_idle (update_screen_info,
834 	        						                 GINT_TO_POINTER (screen_n));
835                 }
836             }
837         }
838       else if (xevent->xproperty.atom == _net_wm_desktop)
839         {
840           int     i;
841           int     j;
842           GailScreenInfo *info;
843 
844           for (i = 0; i < num_screens; i++)
845             {
846               info = &gail_screens [i];
847               for (j = 0; j < info->stacked_windows_len; j++)
848                 {
849                   if (xevent->xany.window == info->stacked_windows [j])
850                     {
851                       info->desktop_changed [j] = TRUE;
852                       if (!info->update_desktop_handler)
853                         {
854                           info->update_desktop_handler = gdk_threads_add_idle (update_desktop_info,
855                                                                                GINT_TO_POINTER (i));
856                         }
857                       break;
858                     }
859                 }
860             }
861         }
862     }
863   return GDK_FILTER_CONTINUE;
864 }
865 
866 static void
display_closed(GdkDisplay * display,gboolean is_error)867 display_closed (GdkDisplay *display,
868 		gboolean    is_error)
869 {
870   int i;
871 
872   for (i = 0; i < num_screens; i++)
873     {
874       if (gail_screens [i].update_handler)
875 	{
876 	  g_source_remove (gail_screens [i].update_handler);
877 	  gail_screens [i].update_handler = 0;
878 	}
879 
880       if (gail_screens [i].update_desktop_handler)
881 	{
882 	  g_source_remove (gail_screens [i].update_desktop_handler);
883 	  gail_screens [i].update_desktop_handler = 0;
884 	}
885 
886       free_screen_info (&gail_screens [i]);
887     }
888 
889   g_free (gail_screens);
890   gail_screens = NULL;
891   num_screens = 0;
892 }
893 
894 static void
init_gail_screens(void)895 init_gail_screens (void)
896 {
897   GdkDisplay *display;
898 
899   display = gdk_display_get_default ();
900 
901   num_screens = gdk_display_get_n_screens (display);
902 
903   gail_screens = g_new0 (GailScreenInfo, num_screens);
904   gdk_window_add_filter (NULL, filter_func, NULL);
905 
906   g_signal_connect (display, "closed", G_CALLBACK (display_closed), NULL);
907 }
908 
909 static void
init_gail_screen(GdkScreen * screen,int screen_n)910 init_gail_screen (GdkScreen *screen,
911                   int        screen_n)
912 {
913   XWindowAttributes attrs;
914 
915   gail_screens [screen_n].root_window = gdk_screen_get_root_window (screen);
916 
917   get_stacked_windows (&gail_screens [screen_n]);
918 
919   XGetWindowAttributes (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
920 			GDK_WINDOW_XWINDOW (gail_screens [screen_n].root_window),
921 			&attrs);
922 
923   XSelectInput (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
924 		GDK_WINDOW_XWINDOW (gail_screens [screen_n].root_window),
925 		attrs.your_event_mask | PropertyChangeMask);
926 
927   gail_screens [screen_n].screen_initialized = TRUE;
928 }
929 
930 static GailScreenInfo *
get_screen_info(GdkScreen * screen)931 get_screen_info (GdkScreen *screen)
932 {
933   int screen_n;
934 
935   gail_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
936 
937   screen_n = gdk_screen_get_number (screen);
938 
939   if (gail_screens && gail_screens [screen_n].screen_initialized)
940     return &gail_screens [screen_n];
941 
942   if (!gail_screens)
943     init_gail_screens ();
944 
945   g_assert (gail_screens != NULL);
946 
947   init_gail_screen (screen, screen_n);
948 
949   g_assert (gail_screens [screen_n].screen_initialized);
950 
951   return &gail_screens [screen_n];
952 }
953 
954 static gint
get_window_zorder(GdkWindow * window)955 get_window_zorder (GdkWindow *window)
956 {
957   GailScreenInfo *info;
958   Window          xid;
959   int             i;
960   int             zorder;
961   int             w_desktop;
962 
963   gail_return_val_if_fail (GDK_IS_WINDOW (window), -1);
964 
965   info = get_screen_info (gdk_window_get_screen (window));
966 
967   gail_return_val_if_fail (info->stacked_windows != NULL, -1);
968 
969   xid = GDK_WINDOW_XID (window);
970 
971   w_desktop = -1;
972   for (i = 0; i < info->stacked_windows_len; i++)
973     {
974       if (info->stacked_windows [i] == xid)
975         {
976           w_desktop = info->desktop[i];
977           break;
978         }
979     }
980   if (w_desktop < 0)
981     return w_desktop;
982 
983   zorder = 0;
984   for (i = 0; i < info->stacked_windows_len; i++)
985     {
986       if (info->stacked_windows [i] == xid)
987         {
988           return zorder;
989         }
990       else
991         {
992           if (info->desktop[i] == w_desktop)
993             zorder++;
994         }
995      }
996 
997   return -1;
998 }
999 
1000 static gint
gail_window_get_mdi_zorder(AtkComponent * component)1001 gail_window_get_mdi_zorder (AtkComponent *component)
1002 {
1003   GtkWidget *widget = GTK_ACCESSIBLE (component)->widget;
1004 
1005   if (widget == NULL)
1006     /*
1007      * State is defunct
1008      */
1009     return -1;
1010 
1011   gail_return_val_if_fail (GTK_IS_WINDOW (widget), -1);
1012 
1013   return get_window_zorder (widget->window);
1014 }
1015 
1016 #elif defined (GDK_WINDOWING_WIN32)
1017 
1018 static gint
gail_window_get_mdi_zorder(AtkComponent * component)1019 gail_window_get_mdi_zorder (AtkComponent *component)
1020 {
1021   GtkWidget *widget = GTK_ACCESSIBLE (component)->widget;
1022 
1023   if (widget == NULL)
1024     /*
1025      * State is defunct
1026      */
1027     return -1;
1028 
1029   gail_return_val_if_fail (GTK_IS_WINDOW (widget), -1);
1030 
1031   return 0;			/* Punt, FIXME */
1032 }
1033 
1034 #else
1035 
1036 static gint
gail_window_get_mdi_zorder(AtkComponent * component)1037 gail_window_get_mdi_zorder (AtkComponent *component)
1038 {
1039   GtkWidget *widget = GTK_ACCESSIBLE (component)->widget;
1040 
1041   if (widget == NULL)
1042     /*
1043      * State is defunct
1044      */
1045     return -1;
1046 
1047   gail_return_val_if_fail (GTK_IS_WINDOW (widget), -1);
1048 
1049   return 0;			/* Punt, FIXME */
1050 }
1051 
1052 #endif
1053