]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkevents-x11.c
Unshift the group when extracting it from the X keyboard event.
[~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 #ifdef USE_XIM
401   static gchar* buf = NULL;
402   static gint buf_len= 0;
403 #else
404   char buf[16];
405 #endif
406   gint return_val;
407   gint xoffset, yoffset;
408   
409   return_val = FALSE;
410   
411   /* Find the GdkWindow that this event occurred in.
412    * 
413    * We handle events with window=None
414    *  specially - they are generated by XFree86's XInput under
415    *  some circumstances.
416    */
417   
418   if (xevent->xany.window == None)
419     {
420       return_val = _gdk_input_window_none_event (event, xevent);
421       
422       if (return_val >= 0)      /* was handled */
423         return return_val;
424       else
425         return_val = FALSE;
426     }
427   
428   window = gdk_window_lookup (xevent->xany.window);
429   /* FIXME: window might be a GdkPixmap!!! */
430   
431   window_private = (GdkWindowObject *) window;
432   
433   if (window != NULL)
434     gdk_window_ref (window);
435
436   if (wmspec_check_window != None &&
437       xevent->xany.window == wmspec_check_window)
438     {
439       if (xevent->type == DestroyNotify)
440         wmspec_check_window = None;
441       
442       /* Eat events on this window unless someone had wrapped
443        * it as a foreign window
444        */
445       if (window == NULL)
446         return FALSE;
447     }
448   
449   event->any.window = window;
450   event->any.send_event = xevent->xany.send_event ? TRUE : FALSE;
451   
452   if (window_private && GDK_WINDOW_DESTROYED (window))
453     {
454       if (xevent->type != DestroyNotify)
455         return FALSE;
456     }
457   else
458     {
459       /* Check for filters for this window
460        */
461       GdkFilterReturn result;
462       result = gdk_event_apply_filters (xevent, event,
463                                         window_private
464                                         ?window_private->filters
465                                         :gdk_default_filters);
466       
467       if (result != GDK_FILTER_CONTINUE)
468         {
469           return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
470           goto done;
471         }
472     }
473
474 #ifdef USE_XIM
475   if (window == NULL && gdk_xim_window && xevent->type == KeyPress &&
476       !GDK_WINDOW_DESTROYED (gdk_xim_window))
477     {
478       /*
479        * If user presses a key in Preedit or Status window, keypress event
480        * is sometimes sent to these windows. These windows are not managed
481        * by GDK, so we redirect KeyPress event to xim_window.
482        *
483        * If someone want to use the window whitch is not managed by GDK
484        * and want to get KeyPress event, he/she must register the filter
485        * function to gdk_default_filters to intercept the event.
486        */
487
488       GdkFilterReturn result;
489
490       window = gdk_xim_window;
491       window_private = (GdkWindowObject *) window;
492       gdk_window_ref (window);
493       event->any.window = window;
494
495       GDK_NOTE (XIM,
496         g_message ("KeyPress event is redirected to xim_window: %#lx",
497                    xevent->xany.window));
498
499       result = gdk_event_apply_filters (xevent, event,
500                                         window_private->filters);
501       if (result != GDK_FILTER_CONTINUE)
502         {
503           return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
504           goto done;
505         }
506     }
507 #endif
508
509   /* We do a "manual" conversion of the XEvent to a
510    *  GdkEvent. The structures are mostly the same so
511    *  the conversion is fairly straightforward. We also
512    *  optionally print debugging info regarding events
513    *  received.
514    */
515
516   return_val = TRUE;
517
518   if (window)
519     {
520       _gdk_windowing_window_get_offsets (window, &xoffset, &yoffset);
521     }
522   else
523     {
524       xoffset = 0;
525       yoffset = 0;
526     }
527
528   switch (xevent->type)
529     {
530     case KeyPress:
531       /* Lookup the string corresponding to the given keysym.
532        */
533       
534 #ifdef USE_XIM
535       if (buf_len == 0) 
536         {
537           buf_len = 128;
538           buf = g_new (gchar, buf_len);
539         }
540       keysym = GDK_VoidSymbol;
541       
542       if (gdk_xim_ic && gdk_xim_ic->xic)
543         {
544           Status status;
545           
546           /* Clear keyval. Depending on status, may not be set */
547           charcount = XmbLookupString(gdk_xim_ic->xic,
548                                       &xevent->xkey, buf, buf_len-1,
549                                       &keysym, &status);
550           if (status == XBufferOverflow)
551             {                     /* retry */
552               /* alloc adequate size of buffer */
553               GDK_NOTE (XIM,
554                         g_message("XIM: overflow (required %i)", charcount));
555               
556               while (buf_len <= charcount)
557                 buf_len *= 2;
558               buf = (gchar *) g_realloc (buf, buf_len);
559               
560               charcount = XmbLookupString (gdk_xim_ic->xic,
561                                            &xevent->xkey, buf, buf_len-1,
562                                            &keysym, &status);
563             }
564           if (status == XLookupNone)
565             {
566               return_val = FALSE;
567               break;
568             }
569         }
570       else
571         charcount = XLookupString (&xevent->xkey, buf, buf_len,
572                                    &keysym, &compose);
573 #else
574       charcount = XLookupString (&xevent->xkey, buf, 16,
575                                  &keysym, &compose);
576 #endif
577       event->key.keyval = keysym;
578       event->key.hardware_keycode = xevent->xkey.keycode;
579       
580       if (charcount > 0 && buf[charcount-1] == '\0')
581         charcount --;
582       else
583         buf[charcount] = '\0';
584       
585 #ifdef G_ENABLE_DEBUG
586       if (gdk_debug_flags & GDK_DEBUG_EVENTS)
587         {
588           g_message ("key press:\twindow: %ld  key: %12s  %d",
589                      xevent->xkey.window,
590                      event->key.keyval ? XKeysymToString (event->key.keyval) : "(none)",
591                      event->key.keyval);
592           if (charcount > 0)
593             g_message ("\t\tlength: %4d string: \"%s\"",
594                        charcount, buf);
595         }
596 #endif /* G_ENABLE_DEBUG */
597
598       /* bits 13 and 14 in the "state" field are the keyboard group */
599 #define KEYBOARD_GROUP_SHIFT 13
600 #define KEYBOARD_GROUP_MASK ((1 << 13) | (1 << 14))
601       
602       event->key.type = GDK_KEY_PRESS;
603       event->key.window = window;
604       event->key.time = xevent->xkey.time;
605       event->key.state = (GdkModifierType) xevent->xkey.state;
606       event->key.string = g_strdup (buf);
607       event->key.length = charcount;
608
609       event->key.group = (xevent->xkey.state & KEYBOARD_GROUP_MASK) >> KEYBOARD_GROUP_SHIFT;
610       
611       break;
612       
613     case KeyRelease:
614       /* Lookup the string corresponding to the given keysym.
615        */
616 #ifdef USE_XIM
617       if (buf_len == 0) 
618         {
619           buf_len = 128;
620           buf = g_new (gchar, buf_len);
621         }
622 #endif
623       keysym = GDK_VoidSymbol;
624       charcount = XLookupString (&xevent->xkey, buf, 16,
625                                  &keysym, &compose);
626       event->key.keyval = keysym;      
627       
628       GDK_NOTE (EVENTS, 
629                 g_message ("key release:\t\twindow: %ld  key: %12s  %d",
630                            xevent->xkey.window,
631                            XKeysymToString (event->key.keyval),
632                            event->key.keyval));
633       
634       event->key.type = GDK_KEY_RELEASE;
635       event->key.window = window;
636       event->key.time = xevent->xkey.time;
637       event->key.state = (GdkModifierType) xevent->xkey.state;
638       event->key.length = 0;
639       event->key.string = NULL;
640       
641       break;
642       
643     case ButtonPress:
644       GDK_NOTE (EVENTS, 
645                 g_message ("button press:\t\twindow: %ld  x,y: %d %d  button: %d",
646                            xevent->xbutton.window,
647                            xevent->xbutton.x, xevent->xbutton.y,
648                            xevent->xbutton.button));
649       
650       if (window_private &&
651           (window_private->extension_events != 0) &&
652           gdk_input_ignore_core)
653         {
654           return_val = FALSE;
655           break;
656         }
657       
658       /* If we get a ButtonPress event where the button is 4 or 5,
659          it's a Scroll event */
660       if (xevent->xbutton.button == 4 || xevent->xbutton.button == 5)
661         {
662           event->scroll.type = GDK_SCROLL;
663           event->scroll.direction = (xevent->xbutton.button == 4) ? 
664             GDK_SCROLL_UP : GDK_SCROLL_DOWN;
665           event->scroll.window = window;
666           event->scroll.time = xevent->xbutton.x;
667           event->scroll.x = xevent->xbutton.x + xoffset;
668           event->scroll.y = xevent->xbutton.y + yoffset;
669           event->scroll.x_root = (gfloat)xevent->xbutton.x_root;
670           event->scroll.y_root = (gfloat)xevent->xbutton.y_root;
671           event->scroll.state = (GdkModifierType) xevent->xbutton.state;
672           event->scroll.device = gdk_core_pointer;
673         }
674       else
675         {
676           event->button.type = GDK_BUTTON_PRESS;
677           event->button.window = window;
678           event->button.time = xevent->xbutton.time;
679           event->button.x = xevent->xbutton.x + xoffset;
680           event->button.y = xevent->xbutton.y + yoffset;
681           event->button.x_root = (gfloat)xevent->xbutton.x_root;
682           event->button.y_root = (gfloat)xevent->xbutton.y_root;
683           event->button.axes = NULL;
684           event->button.state = (GdkModifierType) xevent->xbutton.state;
685           event->button.button = xevent->xbutton.button;
686           event->button.device = gdk_core_pointer;
687           
688           gdk_event_button_generate (event);
689         }
690
691       break;
692       
693     case ButtonRelease:
694       GDK_NOTE (EVENTS, 
695                 g_message ("button release:\twindow: %ld  x,y: %d %d  button: %d",
696                            xevent->xbutton.window,
697                            xevent->xbutton.x, xevent->xbutton.y,
698                            xevent->xbutton.button));
699       
700       if (window_private &&
701           (window_private->extension_events != 0) &&
702           gdk_input_ignore_core)
703         {
704           return_val = FALSE;
705           break;
706         }
707       
708       /* We treat button presses as scroll wheel events, so ignore the release */
709       if (xevent->xbutton.button == 4 || xevent->xbutton.button == 5)
710         {
711           return_val = FALSE;
712           break;
713         }
714
715       event->button.type = GDK_BUTTON_RELEASE;
716       event->button.window = window;
717       event->button.time = xevent->xbutton.time;
718       event->button.x = xevent->xbutton.x + xoffset;
719       event->button.y = xevent->xbutton.y + yoffset;
720       event->button.x_root = (gfloat)xevent->xbutton.x_root;
721       event->button.y_root = (gfloat)xevent->xbutton.y_root;
722       event->button.axes = NULL;
723       event->button.state = (GdkModifierType) xevent->xbutton.state;
724       event->button.button = xevent->xbutton.button;
725       event->button.device = gdk_core_pointer;
726       
727       break;
728       
729     case MotionNotify:
730       GDK_NOTE (EVENTS,
731                 g_message ("motion notify:\t\twindow: %ld  x,y: %d %d  hint: %s", 
732                            xevent->xmotion.window,
733                            xevent->xmotion.x, xevent->xmotion.y,
734                            (xevent->xmotion.is_hint) ? "true" : "false"));
735       
736       if (window_private &&
737           (window_private->extension_events != 0) &&
738           gdk_input_ignore_core)
739         {
740           return_val = FALSE;
741           break;
742         }
743       
744       event->motion.type = GDK_MOTION_NOTIFY;
745       event->motion.window = window;
746       event->motion.time = xevent->xmotion.time;
747       event->motion.x = xevent->xmotion.x + xoffset;
748       event->motion.y = xevent->xmotion.y + yoffset;
749       event->motion.x_root = (gfloat)xevent->xmotion.x_root;
750       event->motion.y_root = (gfloat)xevent->xmotion.y_root;
751       event->motion.axes = NULL;
752       event->motion.state = (GdkModifierType) xevent->xmotion.state;
753       event->motion.is_hint = xevent->xmotion.is_hint;
754       event->motion.device = gdk_core_pointer;
755       
756       break;
757       
758     case EnterNotify:
759       GDK_NOTE (EVENTS,
760                 g_message ("enter notify:\t\twindow: %ld  detail: %d subwin: %ld",
761                            xevent->xcrossing.window,
762                            xevent->xcrossing.detail,
763                            xevent->xcrossing.subwindow));
764       
765       /* Tell XInput stuff about it if appropriate */
766       if (window_private &&
767           !GDK_WINDOW_DESTROYED (window) &&
768           window_private->extension_events != 0)
769         _gdk_input_enter_event (&xevent->xcrossing, window);
770       
771       event->crossing.type = GDK_ENTER_NOTIFY;
772       event->crossing.window = window;
773       
774       /* If the subwindow field of the XEvent is non-NULL, then
775        *  lookup the corresponding GdkWindow.
776        */
777       if (xevent->xcrossing.subwindow != None)
778         event->crossing.subwindow = gdk_window_lookup (xevent->xcrossing.subwindow);
779       else
780         event->crossing.subwindow = NULL;
781       
782       event->crossing.time = xevent->xcrossing.time;
783       event->crossing.x = xevent->xcrossing.x + xoffset;
784       event->crossing.y = xevent->xcrossing.y + yoffset;
785       event->crossing.x_root = xevent->xcrossing.x_root;
786       event->crossing.y_root = xevent->xcrossing.y_root;
787       
788       /* Translate the crossing mode into Gdk terms.
789        */
790       switch (xevent->xcrossing.mode)
791         {
792         case NotifyNormal:
793           event->crossing.mode = GDK_CROSSING_NORMAL;
794           break;
795         case NotifyGrab:
796           event->crossing.mode = GDK_CROSSING_GRAB;
797           break;
798         case NotifyUngrab:
799           event->crossing.mode = GDK_CROSSING_UNGRAB;
800           break;
801         };
802       
803       /* Translate the crossing detail into Gdk terms.
804        */
805       switch (xevent->xcrossing.detail)
806         {
807         case NotifyInferior:
808           event->crossing.detail = GDK_NOTIFY_INFERIOR;
809           break;
810         case NotifyAncestor:
811           event->crossing.detail = GDK_NOTIFY_ANCESTOR;
812           break;
813         case NotifyVirtual:
814           event->crossing.detail = GDK_NOTIFY_VIRTUAL;
815           break;
816         case NotifyNonlinear:
817           event->crossing.detail = GDK_NOTIFY_NONLINEAR;
818           break;
819         case NotifyNonlinearVirtual:
820           event->crossing.detail = GDK_NOTIFY_NONLINEAR_VIRTUAL;
821           break;
822         default:
823           event->crossing.detail = GDK_NOTIFY_UNKNOWN;
824           break;
825         }
826       
827       event->crossing.focus = xevent->xcrossing.focus;
828       event->crossing.state = xevent->xcrossing.state;
829   
830       break;
831       
832     case LeaveNotify:
833       GDK_NOTE (EVENTS, 
834                 g_message ("leave notify:\t\twindow: %ld  detail: %d subwin: %ld",
835                            xevent->xcrossing.window,
836                            xevent->xcrossing.detail, xevent->xcrossing.subwindow));
837       
838       event->crossing.type = GDK_LEAVE_NOTIFY;
839       event->crossing.window = window;
840       
841       /* If the subwindow field of the XEvent is non-NULL, then
842        *  lookup the corresponding GdkWindow.
843        */
844       if (xevent->xcrossing.subwindow != None)
845         event->crossing.subwindow = gdk_window_lookup (xevent->xcrossing.subwindow);
846       else
847         event->crossing.subwindow = NULL;
848       
849       event->crossing.time = xevent->xcrossing.time;
850       event->crossing.x = xevent->xcrossing.x + xoffset;
851       event->crossing.y = xevent->xcrossing.y + yoffset;
852       event->crossing.x_root = xevent->xcrossing.x_root;
853       event->crossing.y_root = xevent->xcrossing.y_root;
854       
855       /* Translate the crossing mode into Gdk terms.
856        */
857       switch (xevent->xcrossing.mode)
858         {
859         case NotifyNormal:
860           event->crossing.mode = GDK_CROSSING_NORMAL;
861           break;
862         case NotifyGrab:
863           event->crossing.mode = GDK_CROSSING_GRAB;
864           break;
865         case NotifyUngrab:
866           event->crossing.mode = GDK_CROSSING_UNGRAB;
867           break;
868         };
869       
870       /* Translate the crossing detail into Gdk terms.
871        */
872       switch (xevent->xcrossing.detail)
873         {
874         case NotifyInferior:
875           event->crossing.detail = GDK_NOTIFY_INFERIOR;
876           break;
877         case NotifyAncestor:
878           event->crossing.detail = GDK_NOTIFY_ANCESTOR;
879           break;
880         case NotifyVirtual:
881           event->crossing.detail = GDK_NOTIFY_VIRTUAL;
882           break;
883         case NotifyNonlinear:
884           event->crossing.detail = GDK_NOTIFY_NONLINEAR;
885           break;
886         case NotifyNonlinearVirtual:
887           event->crossing.detail = GDK_NOTIFY_NONLINEAR_VIRTUAL;
888           break;
889         default:
890           event->crossing.detail = GDK_NOTIFY_UNKNOWN;
891           break;
892         }
893       
894       event->crossing.focus = xevent->xcrossing.focus;
895       event->crossing.state = xevent->xcrossing.state;
896       
897       break;
898       
899     case FocusIn:
900     case FocusOut:
901       /* We only care about focus events that indicate that _this_
902        * window (not a ancestor or child) got or lost the focus
903        */
904       switch (xevent->xfocus.detail)
905         {
906         case NotifyAncestor:
907         case NotifyInferior:
908         case NotifyNonlinear:
909           GDK_NOTE (EVENTS,
910                     g_message ("focus %s:\t\twindow: %ld",
911                                (xevent->xany.type == FocusIn) ? "in" : "out",
912                                xevent->xfocus.window));
913           
914           /* gdk_keyboard_grab() causes following events. These events confuse
915            * the XIM focus, so ignore them.
916            */
917           if (xevent->xfocus.mode == NotifyGrab ||
918               xevent->xfocus.mode == NotifyUngrab)
919             break;
920           
921           event->focus_change.type = GDK_FOCUS_CHANGE;
922           event->focus_change.window = window;
923           event->focus_change.in = (xevent->xany.type == FocusIn);
924
925           break;
926         default:
927           return_val = FALSE;
928         }
929       break;
930       
931     case KeymapNotify:
932       GDK_NOTE (EVENTS,
933                 g_message ("keymap notify"));
934
935       /* Not currently handled */
936       return_val = FALSE;
937       break;
938       
939     case Expose:
940       GDK_NOTE (EVENTS,
941                 g_message ("expose:\t\twindow: %ld  %d  x,y: %d %d  w,h: %d %d%s",
942                            xevent->xexpose.window, xevent->xexpose.count,
943                            xevent->xexpose.x, xevent->xexpose.y,
944                            xevent->xexpose.width, xevent->xexpose.height,
945                            event->any.send_event ? " (send)" : ""));
946       {
947         GdkRectangle expose_rect;
948
949         expose_rect.x = xevent->xexpose.x + xoffset;
950         expose_rect.y = xevent->xexpose.y + yoffset;
951         expose_rect.width = xevent->xexpose.width;
952         expose_rect.height = xevent->xexpose.height;
953
954         if (return_exposes)
955           {
956             event->expose.type = GDK_EXPOSE;
957             event->expose.area = expose_rect;
958             event->expose.window = window;
959             event->expose.count = xevent->xexpose.count;
960
961             return_val = TRUE;
962           }
963         else
964           {
965             _gdk_window_process_expose (window, xevent->xexpose.serial, &expose_rect);
966
967             return_val = FALSE;
968           }
969         
970         return_val = FALSE;
971       }
972         
973       break;
974       
975     case GraphicsExpose:
976       {
977         GdkRectangle expose_rect;
978
979         GDK_NOTE (EVENTS,
980                   g_message ("graphics expose:\tdrawable: %ld",
981                              xevent->xgraphicsexpose.drawable));
982
983         expose_rect.x = xevent->xgraphicsexpose.x + xoffset;
984         expose_rect.y = xevent->xgraphicsexpose.y + yoffset;
985         expose_rect.width = xevent->xgraphicsexpose.width;
986         expose_rect.height = xevent->xgraphicsexpose.height;
987             
988         if (return_exposes)
989           {
990             event->expose.type = GDK_EXPOSE;
991             event->expose.area = expose_rect;
992             event->expose.window = window;
993             event->expose.count = xevent->xgraphicsexpose.count;
994
995             return_val = TRUE;
996           }
997         else
998           {
999             _gdk_window_process_expose (window, xevent->xgraphicsexpose.serial, &expose_rect);
1000             
1001             return_val = FALSE;
1002           }
1003         
1004       }
1005       break;
1006       
1007     case NoExpose:
1008       GDK_NOTE (EVENTS,
1009                 g_message ("no expose:\t\tdrawable: %ld",
1010                            xevent->xnoexpose.drawable));
1011       
1012       event->no_expose.type = GDK_NO_EXPOSE;
1013       event->no_expose.window = window;
1014       
1015       break;
1016       
1017     case VisibilityNotify:
1018 #ifdef G_ENABLE_DEBUG
1019       if (gdk_debug_flags & GDK_DEBUG_EVENTS)
1020         switch (xevent->xvisibility.state)
1021           {
1022           case VisibilityFullyObscured:
1023             g_message ("visibility notify:\twindow: %ld  none",
1024                        xevent->xvisibility.window);
1025             break;
1026           case VisibilityPartiallyObscured:
1027             g_message ("visibility notify:\twindow: %ld  partial",
1028                        xevent->xvisibility.window);
1029             break;
1030           case VisibilityUnobscured:
1031             g_message ("visibility notify:\twindow: %ld  full",
1032                        xevent->xvisibility.window);
1033             break;
1034           }
1035 #endif /* G_ENABLE_DEBUG */
1036       
1037       event->visibility.type = GDK_VISIBILITY_NOTIFY;
1038       event->visibility.window = window;
1039       
1040       switch (xevent->xvisibility.state)
1041         {
1042         case VisibilityFullyObscured:
1043           event->visibility.state = GDK_VISIBILITY_FULLY_OBSCURED;
1044           break;
1045           
1046         case VisibilityPartiallyObscured:
1047           event->visibility.state = GDK_VISIBILITY_PARTIAL;
1048           break;
1049           
1050         case VisibilityUnobscured:
1051           event->visibility.state = GDK_VISIBILITY_UNOBSCURED;
1052           break;
1053         }
1054       
1055       break;
1056       
1057     case CreateNotify:
1058       GDK_NOTE (EVENTS,
1059                 g_message ("create notify:\twindow: %ld  x,y: %d %d     w,h: %d %d  b-w: %d  parent: %ld         ovr: %d",
1060                            xevent->xcreatewindow.window,
1061                            xevent->xcreatewindow.x,
1062                            xevent->xcreatewindow.y,
1063                            xevent->xcreatewindow.width,
1064                            xevent->xcreatewindow.height,
1065                            xevent->xcreatewindow.border_width,
1066                            xevent->xcreatewindow.parent,
1067                            xevent->xcreatewindow.override_redirect));
1068       /* not really handled */
1069       break;
1070       
1071     case DestroyNotify:
1072       GDK_NOTE (EVENTS,
1073                 g_message ("destroy notify:\twindow: %ld",
1074                            xevent->xdestroywindow.window));
1075       
1076       event->any.type = GDK_DESTROY;
1077       event->any.window = window;
1078       
1079       return_val = window_private && !GDK_WINDOW_DESTROYED (window);
1080       
1081       if (window && GDK_WINDOW_XID (window) != GDK_ROOT_WINDOW())
1082         gdk_window_destroy_notify (window);
1083       break;
1084       
1085     case UnmapNotify:
1086       GDK_NOTE (EVENTS,
1087                 g_message ("unmap notify:\t\twindow: %ld",
1088                            xevent->xmap.window));
1089       
1090       event->any.type = GDK_UNMAP;
1091       event->any.window = window;      
1092
1093       /* If we are shown (not withdrawn) and get an unmap, it means we
1094        * were iconified in the X sense. If we are withdrawn, and get
1095        * an unmap, it means we hid the window ourselves, so we
1096        * will have already flipped the iconified bit off.
1097        */
1098       if (GDK_WINDOW_IS_MAPPED (window))
1099         gdk_synthesize_window_state (window,
1100                                      0,
1101                                      GDK_WINDOW_STATE_ICONIFIED);
1102       
1103       if (gdk_xgrab_window == window_private)
1104         gdk_xgrab_window = NULL;
1105       
1106       break;
1107       
1108     case MapNotify:
1109       GDK_NOTE (EVENTS,
1110                 g_message ("map notify:\t\twindow: %ld",
1111                            xevent->xmap.window));
1112       
1113       event->any.type = GDK_MAP;
1114       event->any.window = window;
1115
1116       /* Unset iconified if it was set */
1117       if (((GdkWindowObject*)window)->state & GDK_WINDOW_STATE_ICONIFIED)
1118         gdk_synthesize_window_state (window,
1119                                      GDK_WINDOW_STATE_ICONIFIED,
1120                                      0);
1121       
1122       break;
1123       
1124     case ReparentNotify:
1125       GDK_NOTE (EVENTS,
1126                 g_message ("reparent notify:\twindow: %ld  x,y: %d %d  parent: %ld      ovr: %d",
1127                            xevent->xreparent.window,
1128                            xevent->xreparent.x,
1129                            xevent->xreparent.y,
1130                            xevent->xreparent.parent,
1131                            xevent->xreparent.override_redirect));
1132
1133       /* Not currently handled */
1134       return_val = FALSE;
1135       break;
1136       
1137     case ConfigureNotify:
1138       GDK_NOTE (EVENTS,
1139                 g_message ("configure notify:\twindow: %ld  x,y: %d %d  w,h: %d %d  b-w: %d  above: %ld  ovr: %d%s",
1140                            xevent->xconfigure.window,
1141                            xevent->xconfigure.x,
1142                            xevent->xconfigure.y,
1143                            xevent->xconfigure.width,
1144                            xevent->xconfigure.height,
1145                            xevent->xconfigure.border_width,
1146                            xevent->xconfigure.above,
1147                            xevent->xconfigure.override_redirect,
1148                            !window
1149                            ? " (discarding)"
1150                            : GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD
1151                            ? " (discarding child)"
1152                            : ""));
1153       if (window &&
1154           !GDK_WINDOW_DESTROYED (window) &&
1155           (window_private->extension_events != 0))
1156         _gdk_input_configure_event (&xevent->xconfigure, window);
1157
1158       if (!window || GDK_WINDOW_TYPE (window) == GDK_WINDOW_CHILD)
1159         return_val = FALSE;
1160       else
1161         {
1162           event->configure.type = GDK_CONFIGURE;
1163           event->configure.window = window;
1164           event->configure.width = xevent->xconfigure.width;
1165           event->configure.height = xevent->xconfigure.height;
1166           
1167           if (!xevent->xconfigure.x &&
1168               !xevent->xconfigure.y &&
1169               !GDK_WINDOW_DESTROYED (window))
1170             {
1171               gint tx = 0;
1172               gint ty = 0;
1173               Window child_window = 0;
1174
1175               gdk_error_trap_push ();
1176               if (XTranslateCoordinates (GDK_DRAWABLE_XDISPLAY (window),
1177                                          GDK_DRAWABLE_XID (window),
1178                                          gdk_root_window,
1179                                          0, 0,
1180                                          &tx, &ty,
1181                                          &child_window))
1182                 {
1183                   if (!gdk_error_trap_pop ())
1184                     {
1185                       event->configure.x = tx;
1186                       event->configure.y = ty;
1187                     }
1188                 }
1189               else
1190                 gdk_error_trap_pop ();
1191             }
1192           else
1193             {
1194               event->configure.x = xevent->xconfigure.x;
1195               event->configure.y = xevent->xconfigure.y;
1196             }
1197           window_private->x = event->configure.x;
1198           window_private->y = event->configure.y;
1199           GDK_WINDOW_IMPL_X11 (window_private->impl)->width = xevent->xconfigure.width;
1200           GDK_WINDOW_IMPL_X11 (window_private->impl)->height = xevent->xconfigure.height;
1201           if (window_private->resize_count > 1)
1202             window_private->resize_count -= 1;
1203         }
1204       break;
1205       
1206     case PropertyNotify:
1207       GDK_NOTE (EVENTS,
1208                 gchar *atom = gdk_atom_name (xevent->xproperty.atom);
1209                 g_message ("property notify:\twindow: %ld, atom(%ld): %s%s%s",
1210                            xevent->xproperty.window,
1211                            xevent->xproperty.atom,
1212                            atom ? "\"" : "",
1213                            atom ? atom : "unknown",
1214                            atom ? "\"" : "");
1215                 g_free (atom);
1216                 );
1217       
1218       event->property.type = GDK_PROPERTY_NOTIFY;
1219       event->property.window = window;
1220       event->property.atom = xevent->xproperty.atom;
1221       event->property.time = xevent->xproperty.time;
1222       event->property.state = xevent->xproperty.state;
1223
1224       if (wm_state_atom == 0)
1225         wm_state_atom = gdk_atom_intern ("_NET_WM_STATE", FALSE);
1226
1227       if (wm_desktop_atom == 0)
1228         wm_desktop_atom = gdk_atom_intern ("_NET_WM_DESKTOP", FALSE);
1229       
1230       if (event->property.atom == wm_state_atom ||
1231           event->property.atom == wm_desktop_atom)
1232         {
1233           /* If window state changed, then synthesize those events. */
1234           gdk_check_wm_state_changed (event->property.window);
1235         }
1236       
1237       break;
1238       
1239     case SelectionClear:
1240       GDK_NOTE (EVENTS,
1241                 g_message ("selection clear:\twindow: %ld",
1242                            xevent->xproperty.window));
1243
1244       if (_gdk_selection_filter_clear_event (&xevent->xselectionclear))
1245         {
1246           event->selection.type = GDK_SELECTION_CLEAR;
1247           event->selection.window = window;
1248           event->selection.selection = xevent->xselectionclear.selection;
1249           event->selection.time = xevent->xselectionclear.time;
1250         }
1251       else
1252         return_val = FALSE;
1253           
1254       break;
1255       
1256     case SelectionRequest:
1257       GDK_NOTE (EVENTS,
1258                 g_message ("selection request:\twindow: %ld",
1259                            xevent->xproperty.window));
1260       
1261       event->selection.type = GDK_SELECTION_REQUEST;
1262       event->selection.window = window;
1263       event->selection.selection = xevent->xselectionrequest.selection;
1264       event->selection.target = xevent->xselectionrequest.target;
1265       event->selection.property = xevent->xselectionrequest.property;
1266       event->selection.requestor = xevent->xselectionrequest.requestor;
1267       event->selection.time = xevent->xselectionrequest.time;
1268       
1269       break;
1270       
1271     case SelectionNotify:
1272       GDK_NOTE (EVENTS,
1273                 g_message ("selection notify:\twindow: %ld",
1274                            xevent->xproperty.window));
1275       
1276       
1277       event->selection.type = GDK_SELECTION_NOTIFY;
1278       event->selection.window = window;
1279       event->selection.selection = xevent->xselection.selection;
1280       event->selection.target = xevent->xselection.target;
1281       event->selection.property = xevent->xselection.property;
1282       event->selection.time = xevent->xselection.time;
1283       
1284       break;
1285       
1286     case ColormapNotify:
1287       GDK_NOTE (EVENTS,
1288                 g_message ("colormap notify:\twindow: %ld",
1289                            xevent->xcolormap.window));
1290       
1291       /* Not currently handled */
1292       return_val = FALSE;
1293       break;
1294       
1295     case ClientMessage:
1296       {
1297         GList *tmp_list;
1298         GdkFilterReturn result = GDK_FILTER_CONTINUE;
1299
1300         GDK_NOTE (EVENTS,
1301                   g_message ("client message:\twindow: %ld",
1302                              xevent->xclient.window));
1303         
1304         tmp_list = client_filters;
1305         while (tmp_list)
1306           {
1307             GdkClientFilter *filter = tmp_list->data;
1308             if (filter->type == xevent->xclient.message_type)
1309               {
1310                 result = (*filter->function) (xevent, event, filter->data);
1311                 break;
1312               }
1313             
1314             tmp_list = tmp_list->next;
1315           }
1316
1317         switch (result)
1318           {
1319           case GDK_FILTER_REMOVE:
1320             return_val = FALSE;
1321             break;
1322           case GDK_FILTER_TRANSLATE:
1323             return_val = TRUE;
1324             break;
1325           case GDK_FILTER_CONTINUE:
1326             /* Send unknown ClientMessage's on to Gtk for it to use */
1327             event->client.type = GDK_CLIENT_EVENT;
1328             event->client.window = window;
1329             event->client.message_type = xevent->xclient.message_type;
1330             event->client.data_format = xevent->xclient.format;
1331             memcpy(&event->client.data, &xevent->xclient.data,
1332                    sizeof(event->client.data));
1333           }
1334       }
1335       
1336       break;
1337       
1338     case MappingNotify:
1339       GDK_NOTE (EVENTS,
1340                 g_message ("mapping notify"));
1341       
1342       /* Let XLib know that there is a new keyboard mapping.
1343        */
1344       XRefreshKeyboardMapping (&xevent->xmapping);
1345       ++_gdk_keymap_serial;
1346       return_val = FALSE;
1347       break;
1348
1349 #ifdef HAVE_XKB
1350     case XkbMapNotify:
1351       ++_gdk_keymap_serial;
1352       return_val = FALSE;
1353       break;
1354 #endif
1355       
1356     default:
1357       /* something else - (e.g., a Xinput event) */
1358       
1359       if (window_private &&
1360           !GDK_WINDOW_DESTROYED (window_private) &&
1361           (window_private->extension_events != 0))
1362         return_val = _gdk_input_other_event(event, xevent, window);
1363       else
1364         return_val = FALSE;
1365       
1366       break;
1367     }
1368
1369  done:
1370   if (return_val)
1371     {
1372       if (event->any.window)
1373         gdk_window_ref (event->any.window);
1374       if (((event->any.type == GDK_ENTER_NOTIFY) ||
1375            (event->any.type == GDK_LEAVE_NOTIFY)) &&
1376           (event->crossing.subwindow != NULL))
1377         gdk_window_ref (event->crossing.subwindow);
1378     }
1379   else
1380     {
1381       /* Mark this event as having no resources to be freed */
1382       event->any.window = NULL;
1383       event->any.type = GDK_NOTHING;
1384     }
1385   
1386   if (window)
1387     gdk_window_unref (window);
1388   
1389   return return_val;
1390 }
1391
1392 GdkFilterReturn
1393 gdk_wm_protocols_filter (GdkXEvent *xev,
1394                          GdkEvent  *event,
1395                          gpointer data)
1396 {
1397   XEvent *xevent = (XEvent *)xev;
1398
1399   if ((Atom) xevent->xclient.data.l[0] == gdk_wm_delete_window)
1400     {
1401   /* The delete window request specifies a window
1402    *  to delete. We don't actually destroy the
1403    *  window because "it is only a request". (The
1404    *  window might contain vital data that the
1405    *  program does not want destroyed). Instead
1406    *  the event is passed along to the program,
1407    *  which should then destroy the window.
1408    */
1409       GDK_NOTE (EVENTS,
1410                 g_message ("delete window:\t\twindow: %ld",
1411                            xevent->xclient.window));
1412       
1413       event->any.type = GDK_DELETE;
1414
1415       return GDK_FILTER_TRANSLATE;
1416     }
1417   else if ((Atom) xevent->xclient.data.l[0] == gdk_wm_take_focus)
1418     {
1419     }
1420   else if ((Atom) xevent->xclient.data.l[0] == gdk_atom_intern ("_NET_WM_PING", FALSE))
1421     {
1422       XEvent xev = *xevent;
1423       
1424       xev.xclient.window = gdk_root_window;
1425       XSendEvent (gdk_display, gdk_root_window, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);
1426     }
1427
1428   return GDK_FILTER_REMOVE;
1429 }
1430
1431 #if 0
1432 static Bool
1433 gdk_event_get_type (Display  *display,
1434                     XEvent   *xevent,
1435                     XPointer  arg)
1436 {
1437   GdkEvent event;
1438   GdkPredicate *pred;
1439   
1440   if (gdk_event_translate (&event, xevent, FALSE))
1441     {
1442       pred = (GdkPredicate*) arg;
1443       return (* pred->func) (&event, pred->data);
1444     }
1445   
1446   return FALSE;
1447 }
1448 #endif
1449
1450 void
1451 gdk_events_queue (void)
1452 {
1453   GList *node;
1454   GdkEvent *event;
1455   XEvent xevent;
1456
1457   while (!gdk_event_queue_find_first() && XPending (gdk_display))
1458     {
1459 #ifdef USE_XIM
1460       Window w = None;
1461       
1462       XNextEvent (gdk_display, &xevent);
1463       if (gdk_xim_window)
1464         switch (xevent.type)
1465           {
1466           case KeyPress:
1467           case KeyRelease:
1468           case ButtonPress:
1469           case ButtonRelease:
1470             w = GDK_WINDOW_XWINDOW (gdk_xim_window);
1471             break;
1472           }
1473
1474       if (XFilterEvent (&xevent, w))
1475         continue;
1476 #else
1477       XNextEvent (gdk_display, &xevent);
1478 #endif
1479
1480       switch (xevent.type)
1481         {
1482         case KeyPress:
1483         case KeyRelease:
1484           break;
1485         default:
1486           if (XFilterEvent (&xevent, None))
1487             continue;
1488         }
1489       
1490       event = gdk_event_new ();
1491       
1492       event->any.type = GDK_NOTHING;
1493       event->any.window = NULL;
1494       event->any.send_event = xevent.xany.send_event ? TRUE : FALSE;
1495
1496       ((GdkEventPrivate *)event)->flags |= GDK_EVENT_PENDING;
1497
1498       gdk_event_queue_append (event);
1499       node = gdk_queued_tail;
1500
1501       if (gdk_event_translate (event, &xevent, FALSE))
1502         {
1503           ((GdkEventPrivate *)event)->flags &= ~GDK_EVENT_PENDING;
1504         }
1505       else
1506         {
1507           gdk_event_queue_remove_link (node);
1508           g_list_free_1 (node);
1509           gdk_event_free (event);
1510         }
1511     }
1512 }
1513
1514 static gboolean  
1515 gdk_event_prepare (GSource  *source,
1516                    gint     *timeout)
1517 {
1518   gboolean retval;
1519   
1520   GDK_THREADS_ENTER ();
1521
1522   *timeout = -1;
1523
1524   retval = (gdk_event_queue_find_first () != NULL) || XPending (gdk_display);
1525
1526   GDK_THREADS_LEAVE ();
1527
1528   return retval;
1529 }
1530
1531 static gboolean  
1532 gdk_event_check (GSource  *source) 
1533 {
1534   gboolean retval;
1535   
1536   GDK_THREADS_ENTER ();
1537
1538   if (event_poll_fd.revents & G_IO_IN)
1539     retval = (gdk_event_queue_find_first () != NULL) || XPending (gdk_display);
1540   else
1541     retval = FALSE;
1542
1543   GDK_THREADS_LEAVE ();
1544
1545   return retval;
1546 }
1547
1548 static gboolean  
1549 gdk_event_dispatch (GSource    *source,
1550                     GSourceFunc callback,
1551                     gpointer    user_data)
1552 {
1553   GdkEvent *event;
1554  
1555   GDK_THREADS_ENTER ();
1556
1557   gdk_events_queue();
1558   event = gdk_event_unqueue();
1559
1560   if (event)
1561     {
1562       if (gdk_event_func)
1563         (*gdk_event_func) (event, gdk_event_data);
1564       
1565       gdk_event_free (event);
1566     }
1567   
1568   GDK_THREADS_LEAVE ();
1569
1570   return TRUE;
1571 }
1572
1573 /* Sends a ClientMessage to all toplevel client windows */
1574 gboolean
1575 gdk_event_send_client_message (GdkEvent *event, guint32 xid)
1576 {
1577   XEvent sev;
1578   
1579   g_return_val_if_fail(event != NULL, FALSE);
1580   
1581   /* Set up our event to send, with the exception of its target window */
1582   sev.xclient.type = ClientMessage;
1583   sev.xclient.display = gdk_display;
1584   sev.xclient.format = event->client.data_format;
1585   sev.xclient.window = xid;
1586   memcpy(&sev.xclient.data, &event->client.data, sizeof(sev.xclient.data));
1587   sev.xclient.message_type = event->client.message_type;
1588   
1589   return gdk_send_xevent (xid, False, NoEventMask, &sev);
1590 }
1591
1592 /* Sends a ClientMessage to all toplevel client windows */
1593 gboolean
1594 gdk_event_send_client_message_to_all_recurse (XEvent  *xev, 
1595                                               guint32  xid,
1596                                               guint    level)
1597 {
1598   static GdkAtom wm_state_atom = GDK_NONE;
1599   Atom type = None;
1600   int format;
1601   unsigned long nitems, after;
1602   unsigned char *data;
1603   Window *ret_children, ret_root, ret_parent;
1604   unsigned int ret_nchildren;
1605   gint old_warnings = gdk_error_warnings;
1606   gboolean send = FALSE;
1607   gboolean found = FALSE;
1608   int i;
1609
1610   if (!wm_state_atom)
1611     wm_state_atom = gdk_atom_intern ("WM_STATE", FALSE);
1612
1613   gdk_error_warnings = FALSE;
1614   gdk_error_code = 0;
1615   XGetWindowProperty (gdk_display, xid, wm_state_atom, 0, 0, False, AnyPropertyType,
1616                       &type, &format, &nitems, &after, &data);
1617
1618   if (gdk_error_code)
1619     {
1620       gdk_error_warnings = old_warnings;
1621
1622       return FALSE;
1623     }
1624
1625   if (type)
1626     {
1627       send = TRUE;
1628       XFree (data);
1629     }
1630   else
1631     {
1632       /* OK, we're all set, now let's find some windows to send this to */
1633       if (XQueryTree (gdk_display, xid, &ret_root, &ret_parent,
1634                       &ret_children, &ret_nchildren) != True ||
1635           gdk_error_code)
1636         {
1637           gdk_error_warnings = old_warnings;
1638
1639           return FALSE;
1640         }
1641
1642       for(i = 0; i < ret_nchildren; i++)
1643         if (gdk_event_send_client_message_to_all_recurse (xev, ret_children[i], level + 1))
1644           found = TRUE;
1645
1646       XFree (ret_children);
1647     }
1648
1649   if (send || (!found && (level == 1)))
1650     {
1651       xev->xclient.window = xid;
1652       gdk_send_xevent (xid, False, NoEventMask, xev);
1653     }
1654
1655   gdk_error_warnings = old_warnings;
1656
1657   return (send || found);
1658 }
1659
1660 void
1661 gdk_event_send_clientmessage_toall (GdkEvent *event)
1662 {
1663   XEvent sev;
1664   gint old_warnings = gdk_error_warnings;
1665
1666   g_return_if_fail(event != NULL);
1667   
1668   /* Set up our event to send, with the exception of its target window */
1669   sev.xclient.type = ClientMessage;
1670   sev.xclient.display = gdk_display;
1671   sev.xclient.format = event->client.data_format;
1672   memcpy(&sev.xclient.data, &event->client.data, sizeof(sev.xclient.data));
1673   sev.xclient.message_type = event->client.message_type;
1674
1675   gdk_event_send_client_message_to_all_recurse(&sev, gdk_root_window, 0);
1676
1677   gdk_error_warnings = old_warnings;
1678 }
1679
1680 /*
1681  *--------------------------------------------------------------
1682  * gdk_flush
1683  *
1684  *   Flushes the Xlib output buffer and then waits
1685  *   until all requests have been received and processed
1686  *   by the X server. The only real use for this function
1687  *   is in dealing with XShm.
1688  *
1689  * Arguments:
1690  *
1691  * Results:
1692  *
1693  * Side effects:
1694  *
1695  *--------------------------------------------------------------
1696  */
1697
1698 void
1699 gdk_flush (void)
1700 {
1701   XSync (gdk_display, False);
1702 }
1703
1704 static GdkAtom timestamp_prop_atom = 0;
1705
1706 static Bool
1707 timestamp_predicate (Display *display,
1708                      XEvent  *xevent,
1709                      XPointer arg)
1710 {
1711   Window xwindow = GPOINTER_TO_UINT (arg);
1712
1713   if (xevent->type == PropertyNotify &&
1714       xevent->xproperty.window == xwindow &&
1715       xevent->xproperty.atom == timestamp_prop_atom)
1716     return True;
1717
1718   return False;
1719 }
1720
1721 /**
1722  * gdk_x11_get_server_time:
1723  * @window: a #GdkWindow, used for communication with the server.
1724  *          The window must have GDK_PROPERTY_CHANGE_MASK in its
1725  *          events mask or a hang will result.
1726  * 
1727  * Routine to get the current X server time stamp. 
1728  * 
1729  * Return value: the time stamp.
1730  **/
1731 guint32
1732 gdk_x11_get_server_time (GdkWindow *window)
1733 {
1734   Display *xdisplay;
1735   Window   xwindow;
1736   guchar c = 'a';
1737   XEvent xevent;
1738
1739   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
1740   g_return_val_if_fail (!GDK_WINDOW_DESTROYED (window), 0);
1741
1742   if (!timestamp_prop_atom)
1743     timestamp_prop_atom = gdk_atom_intern ("GDK_TIMESTAMP_PROP", FALSE);
1744
1745   xdisplay = GDK_WINDOW_XDISPLAY (window);
1746   xwindow = GDK_WINDOW_XWINDOW (window);
1747   
1748   XChangeProperty (xdisplay, xwindow,
1749                    timestamp_prop_atom, timestamp_prop_atom,
1750                    8, PropModeReplace, &c, 1);
1751
1752   XIfEvent (xdisplay, &xevent,
1753             timestamp_predicate, GUINT_TO_POINTER(xwindow));
1754
1755   return xevent.xproperty.time;
1756 }
1757
1758
1759 gboolean
1760 gdk_net_wm_supports (GdkAtom property)
1761 {
1762   static GdkAtom wmspec_check_atom = 0;
1763   static GdkAtom wmspec_supported_atom = 0;
1764   static GdkAtom *atoms = NULL;
1765   static gulong n_atoms = 0;
1766   Atom type;
1767   gint format;
1768   gulong nitems;
1769   gulong bytes_after;
1770   Window *xwindow;
1771   gulong i;
1772
1773   if (wmspec_check_window != None)
1774     {
1775       if (atoms == NULL)
1776         return FALSE;
1777
1778       i = 0;
1779       while (i < n_atoms)
1780         {
1781           if (atoms[i] == property)
1782             return TRUE;
1783           
1784           ++i;
1785         }
1786
1787       return FALSE;
1788     }
1789
1790   if (atoms)
1791     XFree (atoms);
1792
1793   atoms = NULL;
1794   n_atoms = 0;
1795   
1796   /* This function is very slow on every call if you are not running a
1797    * spec-supporting WM. For now not optimized, because it isn't in
1798    * any critical code paths, but if you used it somewhere that had to
1799    * be fast you want to avoid "GTK is slow with old WMs" complaints.
1800    * Probably at that point the function should be changed to query
1801    * _NET_SUPPORTING_WM_CHECK only once every 10 seconds or something.
1802    */
1803   
1804   if (wmspec_check_atom == 0)
1805     wmspec_check_atom = gdk_atom_intern ("_NET_SUPPORTING_WM_CHECK", FALSE);
1806       
1807   if (wmspec_supported_atom == 0)
1808     wmspec_supported_atom = gdk_atom_intern ("_NET_SUPPORTED", FALSE);
1809   
1810   XGetWindowProperty (gdk_display, gdk_root_window,
1811                       wmspec_check_atom, 0, G_MAXLONG,
1812                       False, XA_WINDOW, &type, &format, &nitems,
1813                       &bytes_after, (guchar **)&xwindow);
1814
1815   if (type != XA_WINDOW)
1816     return FALSE;
1817
1818   gdk_error_trap_push ();
1819
1820   /* Find out if this WM goes away, so we can reset everything. */
1821   XSelectInput (gdk_display, *xwindow,
1822                 StructureNotifyMask);
1823   
1824   gdk_flush ();
1825   
1826   if (gdk_error_trap_pop ())
1827     {
1828       XFree (xwindow);
1829       return FALSE;
1830     }
1831
1832   XGetWindowProperty (gdk_display, gdk_root_window,
1833                       wmspec_supported_atom, 0, G_MAXLONG,
1834                       False, XA_ATOM, &type, &format, &n_atoms,
1835                       &bytes_after, (guchar **)&atoms);
1836   
1837   if (type != XA_ATOM)
1838     return FALSE;
1839   
1840   wmspec_check_window = *xwindow;
1841   XFree (xwindow);
1842   
1843   /* since wmspec_check_window != None this isn't infinite. ;-) */
1844   return gdk_net_wm_supports (property);
1845 }
1846
1847
1848