]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkevents-x11.c
*** empty log message ***
[~andy/gtk] / gdk / x11 / gdkevents-x11.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "gdk.h"
28 #include "gdkprivate-x11.h"
29 #include "gdkinternals.h"
30 #include "gdkx.h"
31
32 #include "gdkkeysyms.h"
33
34 #if HAVE_CONFIG_H
35 #  include <config.h>
36 #  if STDC_HEADERS
37 #    include <string.h>
38 #  endif
39 #endif
40
41 #include "gdkinputprivate.h"
42
43 #ifdef HAVE_XKB
44 #include <X11/XKBlib.h>
45 #endif
46
47 #include <X11/Xatom.h>
48
49 typedef struct _GdkIOClosure GdkIOClosure;
50 typedef struct _GdkEventPrivate GdkEventPrivate;
51
52 #define DOUBLE_CLICK_TIME      250
53 #define TRIPLE_CLICK_TIME      500
54 #define DOUBLE_CLICK_DIST      5
55 #define TRIPLE_CLICK_DIST      5
56
57 typedef enum
58 {
59   /* Following flag is set for events on the event queue during
60    * translation and cleared afterwards.
61    */
62   GDK_EVENT_PENDING = 1 << 0
63 } GdkEventFlags;
64
65 struct _GdkIOClosure
66 {
67   GdkInputFunction function;
68   GdkInputCondition condition;
69   GdkDestroyNotify notify;
70   gpointer data;
71 };
72
73 struct _GdkEventPrivate
74 {
75   GdkEvent event;
76   guint    flags;
77 };
78
79 /* 
80  * Private function declarations
81  */
82
83 static gint      gdk_event_apply_filters (XEvent   *xevent,
84                                           GdkEvent *event,
85                                           GList    *filters);
86 static gint      gdk_event_translate     (GdkEvent *event, 
87                                           XEvent   *xevent,
88                                           gboolean  return_exposes);
89 #if 0
90 static Bool      gdk_event_get_type     (Display   *display, 
91                                          XEvent    *xevent, 
92                                          XPointer   arg);
93 #endif
94
95 static gboolean gdk_event_prepare  (GSource     *source,
96                                     gint        *timeout);
97 static gboolean gdk_event_check    (GSource     *source);
98 static gboolean gdk_event_dispatch (GSource     *source,
99                                     GSourceFunc  callback,
100                                     gpointer     user_data);
101
102 GdkFilterReturn gdk_wm_protocols_filter (GdkXEvent *xev,
103                                          GdkEvent  *event,
104                                          gpointer   data);
105
106 /* Private variable declarations
107  */
108
109 static int connection_number = 0;           /* The file descriptor number of our
110                                              *  connection to the X server. This
111                                              *  is used so that we may determine
112                                              *  when events are pending by using
113                                              *  the "select" system call.
114                                              */
115 static GList *client_filters;               /* Filters for client messages */
116
117 static GSourceFuncs event_funcs = {
118   gdk_event_prepare,
119   gdk_event_check,
120   gdk_event_dispatch,
121   NULL
122 };
123
124 static GPollFD event_poll_fd;
125
126 static Window wmspec_check_window = None;
127
128 /*********************************************
129  * Functions for maintaining the event queue *
130  *********************************************/
131
132 void 
133 gdk_events_init (void)
134 {
135   GSource *source;
136   
137   connection_number = ConnectionNumber (gdk_display);
138   GDK_NOTE (MISC,
139             g_message ("connection number: %d", connection_number));
140
141
142   source = g_source_new (&event_funcs, sizeof (GSource));
143   g_source_set_priority (source, GDK_PRIORITY_EVENTS);
144   
145   event_poll_fd.fd = connection_number;
146   event_poll_fd.events = G_IO_IN;
147   
148   g_source_add_poll (source, &event_poll_fd);
149   g_source_set_can_recurse (source, TRUE);
150   g_source_attach (source, NULL);
151
152   gdk_add_client_message_filter (gdk_wm_protocols, 
153                                  gdk_wm_protocols_filter, NULL);
154 }
155
156 /*
157  *--------------------------------------------------------------
158  * gdk_events_pending
159  *
160  *   Returns if events are pending on the queue.
161  *
162  * Arguments:
163  *
164  * Results:
165  *   Returns TRUE if events are pending
166  *
167  * Side effects:
168  *
169  *--------------------------------------------------------------
170  */
171
172 gboolean
173 gdk_events_pending (void)
174 {
175   return (gdk_event_queue_find_first() || XPending (gdk_display));
176 }
177
178 /*
179  *--------------------------------------------------------------
180  * gdk_event_get_graphics_expose
181  *
182  *   Waits for a GraphicsExpose or NoExpose event
183  *
184  * Arguments:
185  *
186  * Results: 
187  *   For GraphicsExpose events, returns a pointer to the event
188  *   converted into a GdkEvent Otherwise, returns NULL.
189  *
190  * Side effects:
191  *
192  *-------------------------------------------------------------- */
193
194 static Bool
195 graphics_expose_predicate (Display  *display,
196                            XEvent   *xevent,
197                            XPointer  arg)
198 {
199   if (xevent->xany.window == GDK_DRAWABLE_XID (arg) &&
200       (xevent->xany.type == GraphicsExpose ||
201        xevent->xany.type == NoExpose))
202     return True;
203   else
204     return False;
205 }
206
207 GdkEvent*
208 gdk_event_get_graphics_expose (GdkWindow *window)
209 {
210   XEvent xevent;
211   GdkEvent *event;
212   
213   g_return_val_if_fail (window != NULL, NULL);
214   
215   XIfEvent (gdk_display, &xevent, graphics_expose_predicate, (XPointer) window);
216   
217   if (xevent.xany.type == GraphicsExpose)
218     {
219       event = gdk_event_new ();
220       
221       if (gdk_event_translate (event, &xevent, TRUE))
222         return event;
223       else
224         gdk_event_free (event);
225     }
226   
227   return NULL;  
228 }
229
230 static gint
231 gdk_event_apply_filters (XEvent *xevent,
232                          GdkEvent *event,
233                          GList *filters)
234 {
235   GList *tmp_list;
236   GdkFilterReturn result;
237   
238   tmp_list = filters;
239   
240   while (tmp_list)
241     {
242       GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
243       
244       tmp_list = tmp_list->next;
245       result = filter->function (xevent, event, filter->data);
246       if (result !=  GDK_FILTER_CONTINUE)
247         return result;
248     }
249   
250   return GDK_FILTER_CONTINUE;
251 }
252
253 void 
254 gdk_add_client_message_filter (GdkAtom       message_type,
255                                GdkFilterFunc func,
256                                gpointer      data)
257 {
258   GdkClientFilter *filter = g_new (GdkClientFilter, 1);
259
260   filter->type = message_type;
261   filter->function = func;
262   filter->data = data;
263   
264   client_filters = g_list_prepend (client_filters, filter);
265 }
266
267 static GdkAtom wm_state_atom = 0;
268 static GdkAtom wm_desktop_atom = 0;
269
270 static void
271 gdk_check_wm_state_changed (GdkWindow *window)
272 {  
273   Atom type;
274   gint format;
275   gulong nitems;
276   gulong bytes_after;
277   GdkAtom *atoms = NULL;
278   gulong i;
279   GdkAtom sticky_atom;
280   GdkAtom maxvert_atom;
281   GdkAtom maxhorz_atom;
282   gboolean found_sticky, found_maxvert, found_maxhorz;
283   GdkWindowState old_state;
284   
285   if (GDK_WINDOW_DESTROYED (window))
286     return;
287   
288   if (wm_state_atom == 0)
289     wm_state_atom = gdk_atom_intern ("_NET_WM_STATE", FALSE);
290
291   if (wm_desktop_atom == 0)
292     wm_desktop_atom = gdk_atom_intern ("_NET_WM_DESKTOP", FALSE);
293   
294   XGetWindowProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window),
295                       wm_state_atom, 0, G_MAXLONG,
296                       False, XA_ATOM, &type, &format, &nitems,
297                       &bytes_after, (guchar **)&atoms);
298
299   if (type != None)
300     {
301
302       sticky_atom = gdk_atom_intern ("_NET_WM_STATE_STICKY", FALSE);
303       maxvert_atom = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_VERT", FALSE);
304       maxhorz_atom = gdk_atom_intern ("_NET_WM_STATE_MAXIMIZED_HORZ", FALSE);    
305
306       found_sticky = FALSE;
307       found_maxvert = FALSE;
308       found_maxhorz = FALSE;
309   
310       i = 0;
311       while (i < nitems)
312         {
313           if (atoms[i] == sticky_atom)
314             found_sticky = TRUE;
315           else if (atoms[i] == maxvert_atom)
316             found_maxvert = TRUE;
317           else if (atoms[i] == maxhorz_atom)
318             found_maxhorz = TRUE;
319
320           ++i;
321         }
322
323       XFree (atoms);
324     }
325   else
326     {
327       found_sticky = FALSE;
328       found_maxvert = FALSE;
329       found_maxhorz = FALSE;
330     }
331
332   /* For found_sticky to remain TRUE, we have to also be on desktop
333    * 0xFFFFFFFF
334    */
335
336   if (found_sticky)
337     {
338       gulong *desktop;
339       
340       XGetWindowProperty (GDK_WINDOW_XDISPLAY (window), GDK_WINDOW_XID (window),
341                           wm_desktop_atom, 0, G_MAXLONG,
342                           False, XA_CARDINAL, &type, &format, &nitems,
343                           &bytes_after, (guchar **)&desktop);
344
345       if (type != None)
346         {
347           if (*desktop != 0xFFFFFFFF)
348             found_sticky = FALSE;
349           XFree (desktop);
350         }
351     }
352           
353   old_state = gdk_window_get_state (window);
354
355   if (old_state & GDK_WINDOW_STATE_STICKY)
356     {
357       if (!found_sticky)
358         gdk_synthesize_window_state (window,
359                                      GDK_WINDOW_STATE_STICKY,
360                                      0);
361     }
362   else
363     {
364       if (found_sticky)
365         gdk_synthesize_window_state (window,
366                                      0,
367                                      GDK_WINDOW_STATE_STICKY);
368     }
369
370   /* Our "maximized" means both vertical and horizontal; if only one,
371    * we don't expose that via GDK
372    */
373   if (old_state & GDK_WINDOW_STATE_MAXIMIZED)
374     {
375       if (!(found_maxvert && found_maxhorz))
376         gdk_synthesize_window_state (window,
377                                      GDK_WINDOW_STATE_MAXIMIZED,
378                                      0);
379     }
380   else
381     {
382       if (found_maxvert && found_maxhorz)
383         gdk_synthesize_window_state (window,
384                                      0,
385                                      GDK_WINDOW_STATE_MAXIMIZED);
386     }
387 }
388
389 static gint
390 gdk_event_translate (GdkEvent *event,
391                      XEvent   *xevent,
392                      gboolean  return_exposes)
393 {
394   
395   GdkWindow *window;
396   GdkWindowObject *window_private;
397   static XComposeStatus compose;
398   KeySym keysym;
399   int charcount;
400   char buf[16];
401   gint return_val;
402   gint xoffset, yoffset;
403   
404   return_val = FALSE;
405   
406   /* Find the GdkWindow that this event occurred in.
407    * 
408    * We handle events with window=None
409    *  specially - they are generated by XFree86's XInput under
410    *  some circumstances.
411    */
412   
413   if (xevent->xany.window == None)
414     {
415       return_val = _gdk_input_window_none_event (event, xevent);
416       
417       if (return_val >= 0)      /* was handled */
418         return return_val;
419       else
420         return_val = FALSE;
421     }
422   
423   window = gdk_window_lookup (xevent->xany.window);
424   /* FIXME: window might be a GdkPixmap!!! */
425   
426   window_private = (GdkWindowObject *) window;
427   
428   if (window != NULL)
429     gdk_window_ref (window);
430
431   if (_gdk_moveresize_window &&
432       (xevent->xany.type == MotionNotify ||
433        xevent->xany.type == ButtonRelease))
434     {
435       _gdk_moveresize_handle_event (xevent);
436       gdk_window_unref (window);
437       return FALSE;
438     }
439     
440   if (wmspec_check_window != None &&
441       xevent->xany.window == wmspec_check_window)
442     {
443       if (xevent->type == DestroyNotify)
444         wmspec_check_window = None;
445       
446       /* Eat events on this window unless someone had wrapped
447        * it as a foreign window
448        */
449       if (window == NULL)
450         return FALSE;
451     }
452   
453   event->any.window = window;
454   event->any.send_event = xevent->xany.send_event ? TRUE : FALSE;
455   
456   if (window_private && GDK_WINDOW_DESTROYED (window))
457     {
458       if (xevent->type != DestroyNotify)
459         return FALSE;
460     }
461   else
462     {
463       /* Check for filters for this window
464        */
465       GdkFilterReturn result;
466       result = gdk_event_apply_filters (xevent, event,
467                                         window_private
468                                         ?window_private->filters
469                                         :gdk_default_filters);
470       
471       if (result != GDK_FILTER_CONTINUE)
472         {
473           return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
474           goto done;
475         }
476     }
477
478   /* We do a "manual" conversion of the XEvent to a
479    *  GdkEvent. The structures are mostly the same so
480    *  the conversion is fairly straightforward. We also
481    *  optionally print debugging info regarding events
482    *  received.
483    */
484
485   return_val = TRUE;
486
487   if (window)
488     {
489       _gdk_windowing_window_get_offsets (window, &xoffset, &yoffset);
490     }
491   else
492     {
493       xoffset = 0;
494       yoffset = 0;
495     }
496
497   switch (xevent->type)
498     {
499     case KeyPress:
500       /* Lookup the string corresponding to the given keysym.
501        */
502
503       charcount = XLookupString (&xevent->xkey, buf, 16,
504                                  &keysym, &compose);
505       event->key.keyval = keysym;
506       event->key.hardware_keycode = xevent->xkey.keycode;
507       
508       if (charcount > 0 && buf[charcount-1] == '\0')
509         charcount --;
510       else
511         buf[charcount] = '\0';
512       
513 #ifdef G_ENABLE_DEBUG
514       if (gdk_debug_flags & GDK_DEBUG_EVENTS)
515         {
516           g_message ("key press:\twindow: %ld  key: %12s  %d",
517                      xevent->xkey.window,
518                      event->key.keyval ? XKeysymToString (event->key.keyval) : "(none)",
519                      event->key.keyval);
520           if (charcount > 0)
521             g_message ("\t\tlength: %4d string: \"%s\"",
522                        charcount, buf);
523         }
524 #endif /* G_ENABLE_DEBUG */
525
526       /* bits 13 and 14 in the "state" field are the keyboard group */
527 #define KEYBOARD_GROUP_SHIFT 13
528 #define KEYBOARD_GROUP_MASK ((1 << 13) | (1 << 14))
529       
530       event->key.type = GDK_KEY_PRESS;
531       event->key.window = window;
532       event->key.time = xevent->xkey.time;
533       event->key.state = (GdkModifierType) xevent->xkey.state;
534       event->key.string = g_strdup (buf);
535       event->key.length = charcount;
536
537       event->key.group = (xevent->xkey.state & KEYBOARD_GROUP_MASK) >> KEYBOARD_GROUP_SHIFT;
538       
539       break;
540       
541     case KeyRelease:
542       /* Lookup the string corresponding to the given keysym.
543        */
544
545       /* Emulate detectable auto-repeat by checking to see
546        * if the next event is a key press with the same
547        * keycode and timestamp, and if so, ignoring the event.
548        */
549
550       if (!_gdk_have_xkb_autorepeat && XPending (gdk_display))
551         {
552           XEvent next_event;
553
554           XPeekEvent (gdk_display, &next_event);
555
556           if (next_event.type == KeyPress &&
557               next_event.xkey.keycode == xevent->xkey.keycode &&
558               next_event.xkey.time == xevent->xkey.time)
559             break;
560         }
561       
562       keysym = GDK_VoidSymbol;
563       charcount = XLookupString (&xevent->xkey, buf, 16,
564                                  &keysym, &compose);
565       event->key.keyval = keysym;      
566       
567       GDK_NOTE (EVENTS, 
568                 g_message ("key release:\t\twindow: %ld  key: %12s  %d",
569                            xevent->xkey.window,
570                            XKeysymToString (event->key.keyval),
571                            event->key.keyval));
572       
573       event->key.type = GDK_KEY_RELEASE;
574       event->key.window = window;
575       event->key.time = xevent->xkey.time;
576       event->key.state = (GdkModifierType) xevent->xkey.state;
577       event->key.length = 0;
578       event->key.string = NULL;
579       
580       break;
581       
582     case ButtonPress:
583       GDK_NOTE (EVENTS, 
584                 g_message ("button press:\t\twindow: %ld  x,y: %d %d  button: %d",
585                            xevent->xbutton.window,
586                            xevent->xbutton.x, xevent->xbutton.y,
587                            xevent->xbutton.button));
588       
589       if (window_private &&
590           (window_private->extension_events != 0) &&
591           gdk_input_ignore_core)
592         {
593           return_val = FALSE;
594           break;
595         }
596       
597       /* If we get a ButtonPress event where the button is 4 or 5,
598          it's a Scroll event */
599       if (xevent->xbutton.button == 4 || xevent->xbutton.button == 5)
600         {
601           event->scroll.type = GDK_SCROLL;
602           event->scroll.direction = (xevent->xbutton.button == 4) ? 
603             GDK_SCROLL_UP : GDK_SCROLL_DOWN;
604           event->scroll.window = window;
605           event->scroll.time = xevent->xbutton.x;
606           event->scroll.x = xevent->xbutton.x + xoffset;
607           event->scroll.y = xevent->xbutton.y + yoffset;
608           event->scroll.x_root = (gfloat)xevent->xbutton.x_root;
609           event->scroll.y_root = (gfloat)xevent->xbutton.y_root;
610           event->scroll.state = (GdkModifierType) xevent->xbutton.state;
611           event->scroll.device = gdk_core_pointer;
612         }
613       else
614         {
615           event->button.type = GDK_BUTTON_PRESS;
616           event->button.window = window;
617           event->button.time = xevent->xbutton.time;
618           event->button.x = xevent->xbutton.x + xoffset;
619           event->button.y = xevent->xbutton.y + yoffset;
620           event->button.x_root = (gfloat)xevent->xbutton.x_root;
621           event->button.y_root = (gfloat)xevent->xbutton.y_root;
622           event->button.axes = NULL;
623           event->button.state = (GdkModifierType) xevent->xbutton.state;
624           event->button.button = xevent->xbutton.button;
625           event->button.device = gdk_core_pointer;
626           
627           gdk_event_button_generate (event);
628         }
629
630       break;
631       
632     case ButtonRelease:
633       GDK_NOTE (EVENTS, 
634                 g_message ("button release:\twindow: %ld  x,y: %d %d  button: %d",
635                            xevent->xbutton.window,
636                            xevent->xbutton.x, xevent->xbutton.y,
637                            xevent->xbutton.button));
638       
639       if (window_private &&
640           (window_private->extension_events != 0) &&
641           gdk_input_ignore_core)
642         {
643           return_val = FALSE;
644           break;
645         }
646       
647       /* We treat button presses as scroll wheel events, so ignore the release */
648       if (xevent->xbutton.button == 4 || xevent->xbutton.button == 5)
649         {
650           return_val = FALSE;
651           break;
652         }
653
654       event->button.type = GDK_BUTTON_RELEASE;
655       event->button.window = window;
656       event->button.time = xevent->xbutton.time;
657       event->button.x = xevent->xbutton.x + xoffset;
658       event->button.y = xevent->xbutton.y + yoffset;
659       event->button.x_root = (gfloat)xevent->xbutton.x_root;
660       event->button.y_root = (gfloat)xevent->xbutton.y_root;
661       event->button.axes = NULL;
662       event->button.state = (GdkModifierType) xevent->xbutton.state;
663       event->button.button = xevent->xbutton.button;
664       event->button.device = gdk_core_pointer;
665       
666       break;
667       
668     case MotionNotify:
669       GDK_NOTE (EVENTS,
670                 g_message ("motion notify:\t\twindow: %ld  x,y: %d %d  hint: %s", 
671                            xevent->xmotion.window,
672                            xevent->xmotion.x, xevent->xmotion.y,
673                            (xevent->xmotion.is_hint) ? "true" : "false"));
674       
675       if (window_private &&
676           (window_private->extension_events != 0) &&
677           gdk_input_ignore_core)
678         {
679           return_val = FALSE;
680           break;
681         }
682       
683       event->motion.type = GDK_MOTION_NOTIFY;
684       event->motion.window = window;
685       event->motion.time = xevent->xmotion.time;
686       event->motion.x = xevent->xmotion.x + xoffset;
687       event->motion.y = xevent->xmotion.y + yoffset;
688       event->motion.x_root = (gfloat)xevent->xmotion.x_root;
689       event->motion.y_root = (gfloat)xevent->xmotion.y_root;
690       event->motion.axes = NULL;
691       event->motion.state = (GdkModifierType) xevent->xmotion.state;
692       event->motion.is_hint = xevent->xmotion.is_hint;
693       event->motion.device = gdk_core_pointer;
694       
695       break;
696       
697     case EnterNotify:
698       GDK_NOTE (EVENTS,
699                 g_message ("enter notify:\t\twindow: %ld  detail: %d subwin: %ld",
700                            xevent->xcrossing.window,
701                            xevent->xcrossing.detail,
702                            xevent->xcrossing.subwindow));
703       
704       /* Tell XInput stuff about it if appropriate */
705       if (window_private &&
706           !GDK_WINDOW_DESTROYED (window) &&
707           window_private->extension_events != 0)
708         _gdk_input_enter_event (&xevent->xcrossing, window);
709       
710       event->crossing.type = GDK_ENTER_NOTIFY;
711       event->crossing.window = window;
712       
713       /* If the subwindow field of the XEvent is non-NULL, then
714        *  lookup the corresponding GdkWindow.
715        */
716       if (xevent->xcrossing.subwindow != None)
717         event->crossing.subwindow = gdk_window_lookup (xevent->xcrossing.subwindow);
718       else
719         event->crossing.subwindow = NULL;
720       
721       event->crossing.time = xevent->xcrossing.time;
722       event->crossing.x = xevent->xcrossing.x + xoffset;
723       event->crossing.y = xevent->xcrossing.y + yoffset;
724       event->crossing.x_root = xevent->xcrossing.x_root;
725       event->crossing.y_root = xevent->xcrossing.y_root;
726       
727       /* Translate the crossing mode into Gdk terms.
728        */
729       switch (xevent->xcrossing.mode)
730         {
731         case NotifyNormal:
732           event->crossing.mode = GDK_CROSSING_NORMAL;
733           break;
734         case NotifyGrab:
735           event->crossing.mode = GDK_CROSSING_GRAB;
736           break;
737         case NotifyUngrab:
738           event->crossing.mode = GDK_CROSSING_UNGRAB;
739           break;
740         };
741       
742       /* Translate the crossing detail into Gdk terms.
743        */
744       switch (xevent->xcrossing.detail)
745         {
746         case NotifyInferior:
747           event->crossing.detail = GDK_NOTIFY_INFERIOR;
748           break;
749         case NotifyAncestor:
750           event->crossing.detail = GDK_NOTIFY_ANCESTOR;
751           break;
752         case NotifyVirtual:
753           event->crossing.detail = GDK_NOTIFY_VIRTUAL;
754           break;
755         case NotifyNonlinear:
756           event->crossing.detail = GDK_NOTIFY_NONLINEAR;
757           break;
758         case NotifyNonlinearVirtual:
759           event->crossing.detail = GDK_NOTIFY_NONLINEAR_VIRTUAL;
760           break;
761         default:
762           event->crossing.detail = GDK_NOTIFY_UNKNOWN;
763           break;
764         }
765       
766       event->crossing.focus = xevent->xcrossing.focus;
767       event->crossing.state = xevent->xcrossing.state;
768   
769       break;
770       
771     case LeaveNotify:
772       GDK_NOTE (EVENTS, 
773                 g_message ("leave notify:\t\twindow: %ld  detail: %d subwin: %ld",
774                            xevent->xcrossing.window,
775                            xevent->xcrossing.detail, xevent->xcrossing.subwindow));
776       
777       event->crossing.type = GDK_LEAVE_NOTIFY;
778       event->crossing.window = window;
779       
780       /* If the subwindow field of the XEvent is non-NULL, then
781        *  lookup the corresponding GdkWindow.
782        */
783       if (xevent->xcrossing.subwindow != None)
784         event->crossing.subwindow = gdk_window_lookup (xevent->xcrossing.subwindow);
785       else
786         event->crossing.subwindow = NULL;
787       
788       event->crossing.time = xevent->xcrossing.time;
789       event->crossing.x = xevent->xcrossing.x + xoffset;
790       event->crossing.y = xevent->xcrossing.y + yoffset;
791       event->crossing.x_root = xevent->xcrossing.x_root;
792       event->crossing.y_root = xevent->xcrossing.y_root;
793       
794       /* Translate the crossing mode into Gdk terms.
795        */
796       switch (xevent->xcrossing.mode)
797         {
798         case NotifyNormal:
799           event->crossing.mode = GDK_CROSSING_NORMAL;
800           break;
801         case NotifyGrab:
802           event->crossing.mode = GDK_CROSSING_GRAB;
803           break;
804         case NotifyUngrab:
805           event->crossing.mode = GDK_CROSSING_UNGRAB;
806           break;
807         };
808       
809       /* Translate the crossing detail into Gdk terms.
810        */
811       switch (xevent->xcrossing.detail)
812         {
813         case NotifyInferior:
814           event->crossing.detail = GDK_NOTIFY_INFERIOR;
815           break;
816         case NotifyAncestor:
817           event->crossing.detail = GDK_NOTIFY_ANCESTOR;
818           break;
819         case NotifyVirtual:
820           event->crossing.detail = GDK_NOTIFY_VIRTUAL;
821           break;
822         case NotifyNonlinear:
823           event->crossing.detail = GDK_NOTIFY_NONLINEAR;
824           break;
825         case NotifyNonlinearVirtual:
826           event->crossing.detail = GDK_NOTIFY_NONLINEAR_VIRTUAL;
827           break;
828         default:
829           event->crossing.detail = GDK_NOTIFY_UNKNOWN;
830           break;
831         }
832       
833       event->crossing.focus = xevent->xcrossing.focus;
834       event->crossing.state = xevent->xcrossing.state;
835       
836       break;
837       
838     case FocusIn:
839     case FocusOut:
840       /* We only care about focus events that indicate that _this_
841        * window (not a ancestor or child) got or lost the focus
842        */
843       switch (xevent->xfocus.detail)
844         {
845         case NotifyAncestor:
846         case NotifyInferior:
847         case NotifyNonlinear:
848           GDK_NOTE (EVENTS,
849                     g_message ("focus %s:\t\twindow: %ld",
850                                (xevent->xany.type == FocusIn) ? "in" : "out",
851                                xevent->xfocus.window));
852           
853           /* gdk_keyboard_grab() causes following events. These events confuse
854            * the XIM focus, so ignore them.
855            */
856           if (xevent->xfocus.mode == NotifyGrab ||
857               xevent->xfocus.mode == NotifyUngrab)
858             break;
859           
860           event->focus_change.type = GDK_FOCUS_CHANGE;
861           event->focus_change.window = window;
862           event->focus_change.in = (xevent->xany.type == FocusIn);
863
864           break;
865         default:
866           return_val = FALSE;
867         }
868       break;
869       
870     case KeymapNotify:
871       GDK_NOTE (EVENTS,
872                 g_message ("keymap notify"));
873
874       /* Not currently handled */
875       return_val = FALSE;
876       break;
877       
878     case Expose:
879       GDK_NOTE (EVENTS,
880                 g_message ("expose:\t\twindow: %ld  %d  x,y: %d %d  w,h: %d %d%s",
881                            xevent->xexpose.window, xevent->xexpose.count,
882                            xevent->xexpose.x, xevent->xexpose.y,
883                            xevent->xexpose.width, xevent->xexpose.height,
884                            event->any.send_event ? " (send)" : ""));
885       {
886         GdkRectangle expose_rect;
887
888         expose_rect.x = xevent->xexpose.x + xoffset;
889         expose_rect.y = xevent->xexpose.y + yoffset;
890         expose_rect.width = xevent->xexpose.width;
891         expose_rect.height = xevent->xexpose.height;
892
893         if (return_exposes)
894           {
895             event->expose.type = GDK_EXPOSE;
896             event->expose.area = expose_rect;
897             event->expose.region = gdk_region_rectangle (&expose_rect);
898             event->expose.window = window;
899             event->expose.count = xevent->xexpose.count;
900
901             return_val = TRUE;
902           }
903         else
904           {
905             _gdk_window_process_expose (window, xevent->xexpose.serial, &expose_rect);
906
907             return_val = FALSE;
908           }
909         
910         return_val = FALSE;
911       }
912         
913       break;
914       
915     case GraphicsExpose:
916       {
917         GdkRectangle expose_rect;
918
919         GDK_NOTE (EVENTS,
920                   g_message ("graphics expose:\tdrawable: %ld",
921                              xevent->xgraphicsexpose.drawable));
922
923         expose_rect.x = xevent->xgraphicsexpose.x + xoffset;
924         expose_rect.y = xevent->xgraphicsexpose.y + yoffset;
925         expose_rect.width = xevent->xgraphicsexpose.width;
926         expose_rect.height = xevent->xgraphicsexpose.height;
927             
928         if (return_exposes)
929           {
930             event->expose.type = GDK_EXPOSE;
931             event->expose.area = expose_rect;
932             event->expose.region = gdk_region_rectangle (&expose_rect);
933             event->expose.window = window;
934             event->expose.count = xevent->xgraphicsexpose.count;
935
936             return_val = TRUE;
937           }
938         else
939           {
940             _gdk_window_process_expose (window, xevent->xgraphicsexpose.serial, &expose_rect);
941             
942             return_val = FALSE;
943           }
944         
945       }
946       break;
947       
948     case NoExpose:
949       GDK_NOTE (EVENTS,
950                 g_message ("no expose:\t\tdrawable: %ld",
951                            xevent->xnoexpose.drawable));
952       
953       event->no_expose.type = GDK_NO_EXPOSE;
954       event->no_expose.window = window;
955       
956       break;
957       
958     case VisibilityNotify:
959 #ifdef G_ENABLE_DEBUG
960       if (gdk_debug_flags & GDK_DEBUG_EVENTS)
961         switch (xevent->xvisibility.state)
962           {
963           case VisibilityFullyObscured:
964             g_message ("visibility notify:\twindow: %ld  none",
965                        xevent->xvisibility.window);
966             break;
967           case VisibilityPartiallyObscured:
968             g_message ("visibility notify:\twindow: %ld  partial",
969                        xevent->xvisibility.window);
970             break;
971           case VisibilityUnobscured:
972             g_message ("visibility notify:\twindow: %ld  full",
973                        xevent->xvisibility.window);
974             break;
975           }
976 #endif /* G_ENABLE_DEBUG */
977       
978       event->visibility.type = GDK_VISIBILITY_NOTIFY;
979       event->visibility.window = window;
980       
981       switch (xevent->xvisibility.state)
982         {
983         case VisibilityFullyObscured:
984           event->visibility.state = GDK_VISIBILITY_FULLY_OBSCURED;
985           break;
986           
987         case VisibilityPartiallyObscured:
988           event->visibility.state = GDK_VISIBILITY_PARTIAL;
989           break;
990           
991         case VisibilityUnobscured:
992           event->visibility.state = GDK_VISIBILITY_UNOBSCURED;
993           break;
994         }
995       
996       break;
997       
998     case CreateNotify:
999       GDK_NOTE (EVENTS,
1000                 g_message ("create notify:\twindow: %ld  x,y: %d %d     w,h: %d %d  b-w: %d  parent: %ld         ovr: %d",
1001                            xevent->xcreatewindow.window,
1002                            xevent->xcreatewindow.x,
1003                            xevent->xcreatewindow.y,
1004                            xevent->xcreatewindow.width,
1005                            xevent->xcreatewindow.height,
1006                            xevent->xcreatewindow.border_width,
1007                            xevent->xcreatewindow.parent,
1008                            xevent->xcreatewindow.override_redirect));
1009       /* not really handled */
1010       break;
1011       
1012     case DestroyNotify:
1013       GDK_NOTE (EVENTS,
1014                 g_message ("destroy notify:\twindow: %ld",
1015                            xevent->xdestroywindow.window));
1016       
1017       event->any.type = GDK_DESTROY;
1018       event->any.window = window;
1019       
1020       return_val = window_private && !GDK_WINDOW_DESTROYED (window);
1021       
1022       if (window && GDK_WINDOW_XID (window) != GDK_ROOT_WINDOW())
1023         gdk_window_destroy_notify (window);
1024       break;
1025       
1026     case UnmapNotify:
1027       GDK_NOTE (EVENTS,
1028                 g_message ("unmap notify:\t\twindow: %ld",
1029                            xevent->xmap.window));
1030       
1031       event->any.type = GDK_UNMAP;
1032       event->any.window = window;      
1033
1034       /* If we are shown (not withdrawn) and get an unmap, it means we
1035        * were iconified in the X sense. If we are withdrawn, and get
1036        * an unmap, it means we hid the window ourselves, so we
1037        * will have already flipped the iconified bit off.
1038        */
1039       if (GDK_WINDOW_IS_MAPPED (window))
1040         gdk_synthesize_window_state (window,
1041                                      0,
1042                                      GDK_WINDOW_STATE_ICONIFIED);
1043       
1044       if (gdk_xgrab_window == window_private)
1045         gdk_xgrab_window = NULL;
1046       
1047       break;
1048       
1049     case MapNotify:
1050       GDK_NOTE (EVENTS,
1051                 g_message ("map notify:\t\twindow: %ld",
1052                            xevent->xmap.window));
1053       
1054       event->any.type = GDK_MAP;
1055       event->any.window = window;
1056
1057       /* Unset iconified if it was set */
1058       if (((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_ICONIFIED)
1059         gdk_synthesize_window_state (window,
1060                                      GDK_WINDOW_STATE_ICONIFIED,
1061                                      0);
1062       
1063       break;
1064       
1065     case ReparentNotify:
1066       GDK_NOTE (EVENTS,
1067                 g_message ("reparent notify:\twindow: %ld  x,y: %d %d  parent: %ld      ovr: %d",
1068                            xevent->xreparent.window,
1069                            xevent->xreparent.x,
1070                            xevent->xreparent.y,
1071                            xevent->xreparent.parent,
1072                            xevent->xreparent.override_redirect));
1073
1074       /* Not currently handled */
1075       return_val = FALSE;
1076       break;
1077       
1078     case ConfigureNotify:
1079       GDK_NOTE (EVENTS,
1080                 g_message ("configure notify:\twindow: %ld  x,y: %d %d  w,h: %d %d  b-w: %d  above: %ld  ovr: %d%s",
1081                            xevent->xconfigure.window,
1082                            xevent->xconfigure.x,
1083                            xevent->xconfigure.y,
1084                            xevent->xconfigure.width,
1085                            xevent->xconfigure.height,
1086                            xevent->xconfigure.border_width,
1087                            xevent->xconfigure.above,
1088                            xevent->xconfigure.override_redirect,
1089                            !window
1090                            ? " (discarding)"
1091                            : GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD
1092                            ? " (discarding child)"
1093                            : ""));
1094       if (window &&
1095           !GDK_WINDOW_DESTROYED (window) &&
1096           (window_private->extension_events != 0))
1097         _gdk_input_configure_event (&xevent->xconfigure, window);
1098
1099       if (!window || GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
1100         return_val = FALSE;
1101       else
1102         {
1103           event->configure.type = GDK_CONFIGURE;
1104           event->configure.window = window;
1105           event->configure.width = xevent->xconfigure.width;
1106           event->configure.height = xevent->xconfigure.height;
1107           
1108           if (!xevent->xconfigure.x &&
1109               !xevent->xconfigure.y &&
1110               !GDK_WINDOW_DESTROYED (window))
1111             {
1112               gint tx = 0;
1113               gint ty = 0;
1114               Window child_window = 0;
1115
1116               gdk_error_trap_push ();
1117               if (XTranslateCoordinates (GDK_DRAWABLE_XDISPLAY (window),
1118                                          GDK_DRAWABLE_XID (window),
1119                                          gdk_root_window,
1120                                          0, 0,
1121                                          &tx, &ty,
1122                                          &child_window))
1123                 {
1124                   if (!gdk_error_trap_pop ())
1125                     {
1126                       event->configure.x = tx;
1127                       event->configure.y = ty;
1128                     }
1129                 }
1130               else
1131                 gdk_error_trap_pop ();
1132             }
1133           else
1134             {
1135               event->configure.x = xevent->xconfigure.x;
1136               event->configure.y = xevent->xconfigure.y;
1137             }
1138           window_private->x = event->configure.x;
1139           window_private->y = event->configure.y;
1140           GDK_WINDOW_IMPL_X11 (window_private->impl)->width = xevent->xconfigure.width;
1141           GDK_WINDOW_IMPL_X11 (window_private->impl)->height = xevent->xconfigure.height;
1142           if (window_private->resize_count >= 1)
1143             {
1144               window_private->resize_count -= 1;
1145
1146               if (window_private->resize_count == 0 &&
1147                   window == _gdk_moveresize_window)
1148                 _gdk_moveresize_configure_done ();
1149             }
1150         }
1151       break;
1152       
1153     case PropertyNotify:
1154       GDK_NOTE (EVENTS,
1155                 gchar *atom = gdk_atom_name (xevent->xproperty.atom);
1156                 g_message ("property notify:\twindow: %ld, atom(%ld): %s%s%s",
1157                            xevent->xproperty.window,
1158                            xevent->xproperty.atom,
1159                            atom ? "\"" : "",
1160                            atom ? atom : "unknown",
1161                            atom ? "\"" : "");
1162                 g_free (atom);
1163                 );
1164       
1165       event->property.type = GDK_PROPERTY_NOTIFY;
1166       event->property.window = window;
1167       event->property.atom = xevent->xproperty.atom;
1168       event->property.time = xevent->xproperty.time;
1169       event->property.state = xevent->xproperty.state;
1170
1171       if (wm_state_atom == 0)
1172         wm_state_atom = gdk_atom_intern ("_NET_WM_STATE", FALSE);
1173
1174       if (wm_desktop_atom == 0)
1175         wm_desktop_atom = gdk_atom_intern ("_NET_WM_DESKTOP", FALSE);
1176       
1177       if (event->property.atom == wm_state_atom ||
1178           event->property.atom == wm_desktop_atom)
1179         {
1180           /* If window state changed, then synthesize those events. */
1181           gdk_check_wm_state_changed (event->property.window);
1182         }
1183       
1184       break;
1185       
1186     case SelectionClear:
1187       GDK_NOTE (EVENTS,
1188                 g_message ("selection clear:\twindow: %ld",
1189                            xevent->xproperty.window));
1190
1191       if (_gdk_selection_filter_clear_event (&xevent->xselectionclear))
1192         {
1193           event->selection.type = GDK_SELECTION_CLEAR;
1194           event->selection.window = window;
1195           event->selection.selection = xevent->xselectionclear.selection;
1196           event->selection.time = xevent->xselectionclear.time;
1197         }
1198       else
1199         return_val = FALSE;
1200           
1201       break;
1202       
1203     case SelectionRequest:
1204       GDK_NOTE (EVENTS,
1205                 g_message ("selection request:\twindow: %ld",
1206                            xevent->xproperty.window));
1207       
1208       event->selection.type = GDK_SELECTION_REQUEST;
1209       event->selection.window = window;
1210       event->selection.selection = xevent->xselectionrequest.selection;
1211       event->selection.target = xevent->xselectionrequest.target;
1212       event->selection.property = xevent->xselectionrequest.property;
1213       event->selection.requestor = xevent->xselectionrequest.requestor;
1214       event->selection.time = xevent->xselectionrequest.time;
1215       
1216       break;
1217       
1218     case SelectionNotify:
1219       GDK_NOTE (EVENTS,
1220                 g_message ("selection notify:\twindow: %ld",
1221                            xevent->xproperty.window));
1222       
1223       
1224       event->selection.type = GDK_SELECTION_NOTIFY;
1225       event->selection.window = window;
1226       event->selection.selection = xevent->xselection.selection;
1227       event->selection.target = xevent->xselection.target;
1228       event->selection.property = xevent->xselection.property;
1229       event->selection.time = xevent->xselection.time;
1230       
1231       break;
1232       
1233     case ColormapNotify:
1234       GDK_NOTE (EVENTS,
1235                 g_message ("colormap notify:\twindow: %ld",
1236                            xevent->xcolormap.window));
1237       
1238       /* Not currently handled */
1239       return_val = FALSE;
1240       break;
1241       
1242     case ClientMessage:
1243       {
1244         GList *tmp_list;
1245         GdkFilterReturn result = GDK_FILTER_CONTINUE;
1246
1247         GDK_NOTE (EVENTS,
1248                   g_message ("client message:\twindow: %ld",
1249                              xevent->xclient.window));
1250         
1251         tmp_list = client_filters;
1252         while (tmp_list)
1253           {
1254             GdkClientFilter *filter = tmp_list->data;
1255             if (filter->type == xevent->xclient.message_type)
1256               {
1257                 result = (*filter->function) (xevent, event, filter->data);
1258                 break;
1259               }
1260             
1261             tmp_list = tmp_list->next;
1262           }
1263
1264         switch (result)
1265           {
1266           case GDK_FILTER_REMOVE:
1267             return_val = FALSE;
1268             break;
1269           case GDK_FILTER_TRANSLATE:
1270             return_val = TRUE;
1271             break;
1272           case GDK_FILTER_CONTINUE:
1273             /* Send unknown ClientMessage's on to Gtk for it to use */
1274             event->client.type = GDK_CLIENT_EVENT;
1275             event->client.window = window;
1276             event->client.message_type = xevent->xclient.message_type;
1277             event->client.data_format = xevent->xclient.format;
1278             memcpy(&event->client.data, &xevent->xclient.data,
1279                    sizeof(event->client.data));
1280           }
1281       }
1282       
1283       break;
1284       
1285     case MappingNotify:
1286       GDK_NOTE (EVENTS,
1287                 g_message ("mapping notify"));
1288       
1289       /* Let XLib know that there is a new keyboard mapping.
1290        */
1291       XRefreshKeyboardMapping (&xevent->xmapping);
1292       ++_gdk_keymap_serial;
1293       return_val = FALSE;
1294       break;
1295
1296 #ifdef HAVE_XKB
1297     case XkbMapNotify:
1298       ++_gdk_keymap_serial;
1299       return_val = FALSE;
1300       break;
1301 #endif
1302       
1303     default:
1304       /* something else - (e.g., a Xinput event) */
1305       
1306       if (window_private &&
1307           !GDK_WINDOW_DESTROYED (window_private) &&
1308           (window_private->extension_events != 0))
1309         return_val = _gdk_input_other_event(event, xevent, window);
1310       else
1311         return_val = FALSE;
1312       
1313       break;
1314     }
1315
1316  done:
1317   if (return_val)
1318     {
1319       if (event->any.window)
1320         gdk_window_ref (event->any.window);
1321       if (((event->any.type == GDK_ENTER_NOTIFY) ||
1322            (event->any.type == GDK_LEAVE_NOTIFY)) &&
1323           (event->crossing.subwindow != NULL))
1324         gdk_window_ref (event->crossing.subwindow);
1325     }
1326   else
1327     {
1328       /* Mark this event as having no resources to be freed */
1329       event->any.window = NULL;
1330       event->any.type = GDK_NOTHING;
1331     }
1332   
1333   if (window)
1334     gdk_window_unref (window);
1335   
1336   return return_val;
1337 }
1338
1339 GdkFilterReturn
1340 gdk_wm_protocols_filter (GdkXEvent *xev,
1341                          GdkEvent  *event,
1342                          gpointer data)
1343 {
1344   XEvent *xevent = (XEvent *)xev;
1345
1346   if ((Atom) xevent->xclient.data.l[0] == gdk_wm_delete_window)
1347     {
1348   /* The delete window request specifies a window
1349    *  to delete. We don't actually destroy the
1350    *  window because "it is only a request". (The
1351    *  window might contain vital data that the
1352    *  program does not want destroyed). Instead
1353    *  the event is passed along to the program,
1354    *  which should then destroy the window.
1355    */
1356       GDK_NOTE (EVENTS,
1357                 g_message ("delete window:\t\twindow: %ld",
1358                            xevent->xclient.window));
1359       
1360       event->any.type = GDK_DELETE;
1361
1362       return GDK_FILTER_TRANSLATE;
1363     }
1364   else if ((Atom) xevent->xclient.data.l[0] == gdk_wm_take_focus)
1365     {
1366     }
1367   else if ((Atom) xevent->xclient.data.l[0] == gdk_atom_intern ("_NET_WM_PING", FALSE))
1368     {
1369       XEvent xev = *xevent;
1370       
1371       xev.xclient.window = gdk_root_window;
1372       XSendEvent (gdk_display, gdk_root_window, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
1373     }
1374
1375   return GDK_FILTER_REMOVE;
1376 }
1377
1378 #if 0
1379 static Bool
1380 gdk_event_get_type (Display  *display,
1381                     XEvent   *xevent,
1382                     XPointer  arg)
1383 {
1384   GdkEvent event;
1385   GdkPredicate *pred;
1386   
1387   if (gdk_event_translate (&event, xevent, FALSE))
1388     {
1389       pred = (GdkPredicate*) arg;
1390       return (* pred->func) (&event, pred->data);
1391     }
1392   
1393   return FALSE;
1394 }
1395 #endif
1396
1397 void
1398 gdk_events_queue (void)
1399 {
1400   GList *node;
1401   GdkEvent *event;
1402   XEvent xevent;
1403
1404   while (!gdk_event_queue_find_first() && XPending (gdk_display))
1405     {
1406       XNextEvent (gdk_display, &xevent);
1407
1408       switch (xevent.type)
1409         {
1410         case KeyPress:
1411         case KeyRelease:
1412           break;
1413         default:
1414           if (XFilterEvent (&xevent, None))
1415             continue;
1416         }
1417       
1418       event = gdk_event_new ();
1419       
1420       event->any.type = GDK_NOTHING;
1421       event->any.window = NULL;
1422       event->any.send_event = xevent.xany.send_event ? TRUE : FALSE;
1423
1424       ((GdkEventPrivate *)event)->flags |= GDK_EVENT_PENDING;
1425
1426       gdk_event_queue_append (event);
1427       node = gdk_queued_tail;
1428
1429       if (gdk_event_translate (event, &xevent, FALSE))
1430         {
1431           ((GdkEventPrivate *)event)->flags &= ~GDK_EVENT_PENDING;
1432         }
1433       else
1434         {
1435           gdk_event_queue_remove_link (node);
1436           g_list_free_1 (node);
1437           gdk_event_free (event);
1438         }
1439     }
1440 }
1441
1442 static gboolean  
1443 gdk_event_prepare (GSource  *source,
1444                    gint     *timeout)
1445 {
1446   gboolean retval;
1447   
1448   GDK_THREADS_ENTER ();
1449
1450   *timeout = -1;
1451
1452   retval = (gdk_event_queue_find_first () != NULL) || XPending (gdk_display);
1453
1454   GDK_THREADS_LEAVE ();
1455
1456   return retval;
1457 }
1458
1459 static gboolean  
1460 gdk_event_check (GSource  *source) 
1461 {
1462   gboolean retval;
1463   
1464   GDK_THREADS_ENTER ();
1465
1466   if (event_poll_fd.revents & G_IO_IN)
1467     retval = (gdk_event_queue_find_first () != NULL) || XPending (gdk_display);
1468   else
1469     retval = FALSE;
1470
1471   GDK_THREADS_LEAVE ();
1472
1473   return retval;
1474 }
1475
1476 static gboolean  
1477 gdk_event_dispatch (GSource    *source,
1478                     GSourceFunc callback,
1479                     gpointer    user_data)
1480 {
1481   GdkEvent *event;
1482  
1483   GDK_THREADS_ENTER ();
1484
1485   gdk_events_queue();
1486   event = gdk_event_unqueue();
1487
1488   if (event)
1489     {
1490       if (gdk_event_func)
1491         (*gdk_event_func) (event, gdk_event_data);
1492       
1493       gdk_event_free (event);
1494     }
1495   
1496   GDK_THREADS_LEAVE ();
1497
1498   return TRUE;
1499 }
1500
1501 /* Sends a ClientMessage to all toplevel client windows */
1502 gboolean
1503 gdk_event_send_client_message (GdkEvent *event, guint32 xid)
1504 {
1505   XEvent sev;
1506   
1507   g_return_val_if_fail(event != NULL, FALSE);
1508   
1509   /* Set up our event to send, with the exception of its target window */
1510   sev.xclient.type = ClientMessage;
1511   sev.xclient.display = gdk_display;
1512   sev.xclient.format = event->client.data_format;
1513   sev.xclient.window = xid;
1514   memcpy(&sev.xclient.data, &event->client.data, sizeof(sev.xclient.data));
1515   sev.xclient.message_type = event->client.message_type;
1516   
1517   return gdk_send_xevent (xid, False, NoEventMask, &sev);
1518 }
1519
1520 /* Sends a ClientMessage to all toplevel client windows */
1521 gboolean
1522 gdk_event_send_client_message_to_all_recurse (XEvent  *xev, 
1523                                               guint32  xid,
1524                                               guint    level)
1525 {
1526   static GdkAtom wm_state_atom = GDK_NONE;
1527   Atom type = None;
1528   int format;
1529   unsigned long nitems, after;
1530   unsigned char *data;
1531   Window *ret_children, ret_root, ret_parent;
1532   unsigned int ret_nchildren;
1533   gint old_warnings = gdk_error_warnings;
1534   gboolean send = FALSE;
1535   gboolean found = FALSE;
1536   int i;
1537
1538   if (!wm_state_atom)
1539     wm_state_atom = gdk_atom_intern ("WM_STATE", FALSE);
1540
1541   gdk_error_warnings = FALSE;
1542   gdk_error_code = 0;
1543   XGetWindowProperty (gdk_display, xid, wm_state_atom, 0, 0, False, AnyPropertyType,
1544                       &type, &format, &nitems, &after, &data);
1545
1546   if (gdk_error_code)
1547     {
1548       gdk_error_warnings = old_warnings;
1549
1550       return FALSE;
1551     }
1552
1553   if (type)
1554     {
1555       send = TRUE;
1556       XFree (data);
1557     }
1558   else
1559     {
1560       /* OK, we're all set, now let's find some windows to send this to */
1561       if (XQueryTree (gdk_display, xid, &ret_root, &ret_parent,
1562                       &ret_children, &ret_nchildren) != True ||
1563           gdk_error_code)
1564         {
1565           gdk_error_warnings = old_warnings;
1566
1567           return FALSE;
1568         }
1569
1570       for(i = 0; i < ret_nchildren; i++)
1571         if (gdk_event_send_client_message_to_all_recurse (xev, ret_children[i], level + 1))
1572           found = TRUE;
1573
1574       XFree (ret_children);
1575     }
1576
1577   if (send || (!found && (level == 1)))
1578     {
1579       xev->xclient.window = xid;
1580       gdk_send_xevent (xid, False, NoEventMask, xev);
1581     }
1582
1583   gdk_error_warnings = old_warnings;
1584
1585   return (send || found);
1586 }
1587
1588 void
1589 gdk_event_send_clientmessage_toall (GdkEvent *event)
1590 {
1591   XEvent sev;
1592   gint old_warnings = gdk_error_warnings;
1593
1594   g_return_if_fail(event != NULL);
1595   
1596   /* Set up our event to send, with the exception of its target window */
1597   sev.xclient.type = ClientMessage;
1598   sev.xclient.display = gdk_display;
1599   sev.xclient.format = event->client.data_format;
1600   memcpy(&sev.xclient.data, &event->client.data, sizeof(sev.xclient.data));
1601   sev.xclient.message_type = event->client.message_type;
1602
1603   gdk_event_send_client_message_to_all_recurse(&sev, gdk_root_window, 0);
1604
1605   gdk_error_warnings = old_warnings;
1606 }
1607
1608 /*
1609  *--------------------------------------------------------------
1610  * gdk_flush
1611  *
1612  *   Flushes the Xlib output buffer and then waits
1613  *   until all requests have been received and processed
1614  *   by the X server. The only real use for this function
1615  *   is in dealing with XShm.
1616  *
1617  * Arguments:
1618  *
1619  * Results:
1620  *
1621  * Side effects:
1622  *
1623  *--------------------------------------------------------------
1624  */
1625
1626 void
1627 gdk_flush (void)
1628 {
1629   XSync (gdk_display, False);
1630 }
1631
1632 static GdkAtom timestamp_prop_atom = 0;
1633
1634 static Bool
1635 timestamp_predicate (Display *display,
1636                      XEvent  *xevent,
1637                      XPointer arg)
1638 {
1639   Window xwindow = GPOINTER_TO_UINT (arg);
1640
1641   if (xevent->type == PropertyNotify &&
1642       xevent->xproperty.window == xwindow &&
1643       xevent->xproperty.atom == timestamp_prop_atom)
1644     return True;
1645
1646   return False;
1647 }
1648
1649 /**
1650  * gdk_x11_get_server_time:
1651  * @window: a #GdkWindow, used for communication with the server.
1652  *          The window must have GDK_PROPERTY_CHANGE_MASK in its
1653  *          events mask or a hang will result.
1654  * 
1655  * Routine to get the current X server time stamp. 
1656  * 
1657  * Return value: the time stamp.
1658  **/
1659 guint32
1660 gdk_x11_get_server_time (GdkWindow *window)
1661 {
1662   Display *xdisplay;
1663   Window   xwindow;
1664   guchar c = 'a';
1665   XEvent xevent;
1666
1667   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
1668   g_return_val_if_fail (!GDK_WINDOW_DESTROYED (window), 0);
1669
1670   if (!timestamp_prop_atom)
1671     timestamp_prop_atom = gdk_atom_intern ("GDK_TIMESTAMP_PROP", FALSE);
1672
1673   xdisplay = GDK_WINDOW_XDISPLAY (window);
1674   xwindow = GDK_WINDOW_XWINDOW (window);
1675   
1676   XChangeProperty (xdisplay, xwindow,
1677                    timestamp_prop_atom, timestamp_prop_atom,
1678                    8, PropModeReplace, &c, 1);
1679
1680   XIfEvent (xdisplay, &xevent,
1681             timestamp_predicate, GUINT_TO_POINTER(xwindow));
1682
1683   return xevent.xproperty.time;
1684 }
1685
1686
1687 gboolean
1688 gdk_net_wm_supports (GdkAtom property)
1689 {
1690   static GdkAtom wmspec_check_atom = 0;
1691   static GdkAtom wmspec_supported_atom = 0;
1692   static GdkAtom *atoms = NULL;
1693   static gulong n_atoms = 0;
1694   Atom type;
1695   gint format;
1696   gulong nitems;
1697   gulong bytes_after;
1698   Window *xwindow;
1699   gulong i;
1700
1701   if (wmspec_check_window != None)
1702     {
1703       if (atoms == NULL)
1704         return FALSE;
1705
1706       i = 0;
1707       while (i < n_atoms)
1708         {
1709           if (atoms[i] == property)
1710             return TRUE;
1711           
1712           ++i;
1713         }
1714
1715       return FALSE;
1716     }
1717
1718   if (atoms)
1719     XFree (atoms);
1720
1721   atoms = NULL;
1722   n_atoms = 0;
1723   
1724   /* This function is very slow on every call if you are not running a
1725    * spec-supporting WM. For now not optimized, because it isn't in
1726    * any critical code paths, but if you used it somewhere that had to
1727    * be fast you want to avoid "GTK is slow with old WMs" complaints.
1728    * Probably at that point the function should be changed to query
1729    * _NET_SUPPORTING_WM_CHECK only once every 10 seconds or something.
1730    */
1731   
1732   if (wmspec_check_atom == 0)
1733     wmspec_check_atom = gdk_atom_intern ("_NET_SUPPORTING_WM_CHECK", FALSE);
1734       
1735   if (wmspec_supported_atom == 0)
1736     wmspec_supported_atom = gdk_atom_intern ("_NET_SUPPORTED", FALSE);
1737   
1738   XGetWindowProperty (gdk_display, gdk_root_window,
1739                       wmspec_check_atom, 0, G_MAXLONG,
1740                       False, XA_WINDOW, &type, &format, &nitems,
1741                       &bytes_after, (guchar **)&xwindow);
1742
1743   if (type != XA_WINDOW)
1744     return FALSE;
1745
1746   gdk_error_trap_push ();
1747
1748   /* Find out if this WM goes away, so we can reset everything. */
1749   XSelectInput (gdk_display, *xwindow,
1750                 StructureNotifyMask);
1751   
1752   gdk_flush ();
1753   
1754   if (gdk_error_trap_pop ())
1755     {
1756       XFree (xwindow);
1757       return FALSE;
1758     }
1759
1760   XGetWindowProperty (gdk_display, gdk_root_window,
1761                       wmspec_supported_atom, 0, G_MAXLONG,
1762                       False, XA_ATOM, &type, &format, &n_atoms,
1763                       &bytes_after, (guchar **)&atoms);
1764   
1765   if (type != XA_ATOM)
1766     return FALSE;
1767   
1768   wmspec_check_window = *xwindow;
1769   XFree (xwindow);
1770   
1771   /* since wmspec_check_window != None this isn't infinite. ;-) */
1772   return gdk_net_wm_supports (property);
1773 }
1774
1775
1776