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