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