]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkevents-x11.c
Fix a reference to window_private->destroyed.
[~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 Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library 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-1999.  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.h"
29 #include "gdkx.h"
30
31 #include "gdkkeysyms.h"
32
33 #if HAVE_CONFIG_H
34 #  include <config.h>
35 #  if STDC_HEADERS
36 #    include <string.h>
37 #  endif
38 #endif
39
40 #include "gdkinputprivate.h"
41
42 typedef struct _GdkIOClosure GdkIOClosure;
43 typedef struct _GdkEventPrivate GdkEventPrivate;
44
45 #define DOUBLE_CLICK_TIME      250
46 #define TRIPLE_CLICK_TIME      500
47 #define DOUBLE_CLICK_DIST      5
48 #define TRIPLE_CLICK_DIST      5
49
50 typedef enum
51 {
52   /* Following flag is set for events on the event queue during
53    * translation and cleared afterwards.
54    */
55   GDK_EVENT_PENDING = 1 << 0
56 } GdkEventFlags;
57
58 struct _GdkIOClosure
59 {
60   GdkInputFunction function;
61   GdkInputCondition condition;
62   GdkDestroyNotify notify;
63   gpointer data;
64 };
65
66 struct _GdkEventPrivate
67 {
68   GdkEvent event;
69   guint    flags;
70 };
71
72 /* 
73  * Private function declarations
74  */
75
76 static GdkEvent *gdk_event_new          (void);
77 static gint      gdk_event_apply_filters (XEvent   *xevent,
78                                           GdkEvent *event,
79                                           GList    *filters);
80 static gint      gdk_event_translate     (GdkEvent *event, 
81                                           XEvent   *xevent);
82 #if 0
83 static Bool      gdk_event_get_type     (Display   *display, 
84                                          XEvent    *xevent, 
85                                          XPointer   arg);
86 #endif
87 static void      gdk_events_queue       (void);
88 static GdkEvent* gdk_event_unqueue      (void);
89
90 static gboolean  gdk_event_prepare      (gpointer   source_data, 
91                                          GTimeVal  *current_time,
92                                          gint      *timeout,
93                                          gpointer   user_data);
94 static gboolean  gdk_event_check        (gpointer   source_data,
95                                          GTimeVal  *current_time,
96                                          gpointer   user_data);
97 static gboolean  gdk_event_dispatch     (gpointer   source_data,
98                                          GTimeVal  *current_time,
99                                          gpointer   user_data);
100
101 static void      gdk_synthesize_click   (GdkEvent  *event, 
102                                          gint       nclicks);
103
104 GdkFilterReturn gdk_wm_protocols_filter (GdkXEvent *xev,
105                                          GdkEvent  *event,
106                                          gpointer   data);
107
108 /* Private variable declarations
109  */
110
111 static int connection_number = 0;           /* The file descriptor number of our
112                                              *  connection to the X server. This
113                                              *  is used so that we may determine
114                                              *  when events are pending by using
115                                              *  the "select" system call.
116                                              */
117 static guint32 button_click_time[2];        /* The last 2 button click times. Used
118                                              *  to determine if the latest button click
119                                              *  is part of a double or triple click.
120                                              */
121 static GdkWindow *button_window[2];         /* The last 2 windows to receive button presses.
122                                              *  Also used to determine if the latest button
123                                              *  click is part of a double or triple click.
124                                              */
125 static guint button_number[2];              /* The last 2 buttons to be pressed.
126                                              */
127 static GdkEventFunc   event_func = NULL;    /* Callback for events */
128 static gpointer       event_data = NULL;
129 static GDestroyNotify event_notify = NULL;
130
131 static GList *client_filters;               /* Filters for client messages */
132
133 /* FIFO's for event queue, and for events put back using
134  * gdk_event_put().
135  */
136 static GList *queued_events = NULL;
137 static GList *queued_tail = NULL;
138
139 static GSourceFuncs event_funcs = {
140   gdk_event_prepare,
141   gdk_event_check,
142   gdk_event_dispatch,
143   (GDestroyNotify)g_free
144 };
145
146 GPollFD event_poll_fd;
147
148 /*********************************************
149  * Functions for maintaining the event queue *
150  *********************************************/
151
152 /*************************************************************
153  * gdk_event_queue_find_first:
154  *     Find the first event on the queue that is not still
155  *     being filled in.
156  *   arguments:
157  *     
158  *   results:
159  *     Pointer to the list node for that event, or NULL
160  *************************************************************/
161
162 static GList*
163 gdk_event_queue_find_first (void)
164 {
165   GList *tmp_list = queued_events;
166
167   while (tmp_list)
168     {
169       GdkEventPrivate *event = tmp_list->data;
170       if (!(event->flags & GDK_EVENT_PENDING))
171         return tmp_list;
172
173       tmp_list = g_list_next (tmp_list);
174     }
175
176   return NULL;
177 }
178
179 /*************************************************************
180  * gdk_event_queue_remove_link:
181  *     Remove a specified list node from the event queue.
182  *   arguments:
183  *     node: Node to remove.
184  *   results:
185  *************************************************************/
186
187 static void
188 gdk_event_queue_remove_link (GList *node)
189 {
190   if (node->prev)
191     node->prev->next = node->next;
192   else
193     queued_events = node->next;
194   
195   if (node->next)
196     node->next->prev = node->prev;
197   else
198     queued_tail = node->prev;
199   
200 }
201
202 /*************************************************************
203  * gdk_event_queue_append:
204  *     Append an event onto the tail of the event queue.
205  *   arguments:
206  *     event: Event to append.
207  *   results:
208  *************************************************************/
209
210 static void
211 gdk_event_queue_append (GdkEvent *event)
212 {
213   queued_tail = g_list_append (queued_tail, event);
214   
215   if (!queued_events)
216     queued_events = queued_tail;
217   else
218     queued_tail = queued_tail->next;
219 }
220
221 void 
222 gdk_events_init (void)
223 {
224   connection_number = ConnectionNumber (gdk_display);
225   GDK_NOTE (MISC,
226             g_message ("connection number: %d", connection_number));
227
228   g_source_add (GDK_PRIORITY_EVENTS, TRUE, &event_funcs, NULL, NULL, NULL);
229
230   event_poll_fd.fd = connection_number;
231   event_poll_fd.events = G_IO_IN;
232   
233   g_main_add_poll (&event_poll_fd, GDK_PRIORITY_EVENTS);
234
235   button_click_time[0] = 0;
236   button_click_time[1] = 0;
237   button_window[0] = NULL;
238   button_window[1] = NULL;
239   button_number[0] = -1;
240   button_number[1] = -1;
241
242   gdk_add_client_message_filter (gdk_wm_protocols, 
243                                  gdk_wm_protocols_filter, NULL);
244 }
245
246 /*
247  *--------------------------------------------------------------
248  * gdk_events_pending
249  *
250  *   Returns if events are pending on the queue.
251  *
252  * Arguments:
253  *
254  * Results:
255  *   Returns TRUE if events are pending
256  *
257  * Side effects:
258  *
259  *--------------------------------------------------------------
260  */
261
262 gboolean
263 gdk_events_pending (void)
264 {
265   return (gdk_event_queue_find_first() || XPending (gdk_display));
266 }
267
268 /*
269  *--------------------------------------------------------------
270  * gdk_event_get_graphics_expose
271  *
272  *   Waits for a GraphicsExpose or NoExpose event
273  *
274  * Arguments:
275  *
276  * Results: 
277  *   For GraphicsExpose events, returns a pointer to the event
278  *   converted into a GdkEvent Otherwise, returns NULL.
279  *
280  * Side effects:
281  *
282  *-------------------------------------------------------------- */
283
284 static Bool
285 graphics_expose_predicate (Display  *display,
286                            XEvent   *xevent,
287                            XPointer  arg)
288 {
289   if (xevent->xany.window == GDK_DRAWABLE_XID (arg) &&
290       (xevent->xany.type == GraphicsExpose ||
291        xevent->xany.type == NoExpose))
292     return True;
293   else
294     return False;
295 }
296
297 GdkEvent*
298 gdk_event_get_graphics_expose (GdkWindow *window)
299 {
300   XEvent xevent;
301   GdkEvent *event;
302   
303   g_return_val_if_fail (window != NULL, NULL);
304   
305   XIfEvent (gdk_display, &xevent, graphics_expose_predicate, (XPointer) window);
306   
307   if (xevent.xany.type == GraphicsExpose)
308     {
309       event = gdk_event_new ();
310       
311       if (gdk_event_translate (event, &xevent))
312         return event;
313       else
314         gdk_event_free (event);
315     }
316   
317   return NULL;  
318 }
319
320 /************************
321  * Exposure compression *
322  ************************/
323
324 /*
325  * The following implements simple exposure compression. It is
326  * modelled after the way Xt does exposure compression - in
327  * particular compress_expose = XtExposeCompressMultiple.
328  * It compress consecutive sequences of exposure events,
329  * but not sequences that cross other events. (This is because
330  * if it crosses a ConfigureNotify, we could screw up and
331  * mistakenly compress the exposures generated for the new
332  * size - could we just check for ConfigureNotify?)
333  *
334  * Xt compresses to a region / bounding rectangle, we compress
335  * to two rectangles, and try find the two rectangles of minimal
336  * area for this - this is supposed to handle the typical
337  * L-shaped regions generated by OpaqueMove.
338  */
339
340 /* Given three rectangles, find the two rectangles that cover
341  * them with the smallest area.
342  */
343 static void
344 gdk_add_rect_to_rects (GdkRectangle *rect1,
345                        GdkRectangle *rect2, 
346                        GdkRectangle *new_rect)
347 {
348   GdkRectangle t1, t2, t3;
349   gint size1, size2, size3;
350
351   gdk_rectangle_union (rect1, rect2, &t1);
352   gdk_rectangle_union (rect1, new_rect, &t2);
353   gdk_rectangle_union (rect2, new_rect, &t3);
354
355   size1 = t1.width * t1.height + new_rect->width * new_rect->height;
356   size2 = t2.width * t2.height + rect2->width * rect2->height;
357   size3 = t1.width * t1.height + rect1->width * rect1->height;
358
359   if (size1 < size2)
360     {
361       if (size1 < size3)
362         {
363           *rect1 = t1;
364           *rect2 = *new_rect;
365         }
366       else
367         *rect2 = t3;
368     }
369   else
370     {
371       if (size2 < size3)
372         *rect1 = t2;
373       else
374         *rect2 = t3;
375     }
376 }
377
378 typedef struct _GdkExposeInfo GdkExposeInfo;
379
380 struct _GdkExposeInfo
381 {
382   Window window;
383   gboolean seen_nonmatching;
384 };
385
386 static Bool
387 expose_predicate (Display *display,
388                   XEvent  *xevent,
389                   XPointer arg)
390 {
391   GdkExposeInfo *info = (GdkExposeInfo*) arg;
392
393   /* Compressing across GravityNotify events is safe, because
394    * we completely ignore them, so they can't change what
395    * we are going to draw. Compressing across GravityNotify
396    * events is necessay because during window-unshading animation
397    * we'll get a whole bunch of them interspersed with
398    * expose events.
399    */
400   if (xevent->xany.type != Expose && 
401       xevent->xany.type != GravityNotify)
402     {
403       info->seen_nonmatching = TRUE;
404     }
405
406   if (info->seen_nonmatching ||
407       xevent->xany.type != Expose ||
408       xevent->xany.window != info->window)
409     return FALSE;
410   else
411     return TRUE;
412 }
413
414 void
415 gdk_compress_exposures (XEvent    *xevent,
416                         GdkWindow *window)
417 {
418   gint nrects = 1;
419   gint count = 0;
420   GdkRectangle rect1;
421   GdkRectangle rect2;
422   GdkRectangle tmp_rect;
423   XEvent tmp_event;
424   GdkFilterReturn result;
425   GdkExposeInfo info;
426   GdkEvent event;
427
428   info.window = xevent->xany.window;
429   info.seen_nonmatching = FALSE;
430   
431   rect1.x = xevent->xexpose.x;
432   rect1.y = xevent->xexpose.y;
433   rect1.width = xevent->xexpose.width;
434   rect1.height = xevent->xexpose.height;
435
436   event.any.type = GDK_EXPOSE;
437   event.any.window = None;
438   event.any.send_event = FALSE;
439   
440   while (1)
441     {
442       if (count == 0)
443         {
444           if (!XCheckIfEvent (gdk_display, 
445                               &tmp_event, 
446                               expose_predicate, 
447                               (XPointer)&info))
448             break;
449         }
450       else
451         XIfEvent (gdk_display, 
452                   &tmp_event, 
453                   expose_predicate, 
454                   (XPointer)&info);
455
456       event.any.window = window;
457       
458       /* We apply filters here, and if it was filtered, completely
459        * ignore the return
460        */
461       result = gdk_event_apply_filters (xevent, &event,
462                                         window ? 
463                                           ((GdkWindowPrivate *)window)->filters
464                                           : gdk_default_filters);
465       
466       if (result != GDK_FILTER_CONTINUE)
467         {
468           if (result == GDK_FILTER_TRANSLATE)
469             gdk_event_put (&event);
470           continue;
471         }
472
473       if (nrects == 1)
474         {
475           rect2.x = tmp_event.xexpose.x;
476           rect2.y = tmp_event.xexpose.y;
477           rect2.width = tmp_event.xexpose.width;
478           rect2.height = tmp_event.xexpose.height;
479
480           nrects++;
481         }
482       else
483         {
484           tmp_rect.x = tmp_event.xexpose.x;
485           tmp_rect.y = tmp_event.xexpose.y;
486           tmp_rect.width = tmp_event.xexpose.width;
487           tmp_rect.height = tmp_event.xexpose.height;
488
489           gdk_add_rect_to_rects (&rect1, &rect2, &tmp_rect);
490         }
491
492       count = tmp_event.xexpose.count;
493     }
494
495   if (nrects == 2)
496     {
497       gdk_rectangle_union (&rect1, &rect2, &tmp_rect);
498
499       if ((tmp_rect.width * tmp_rect.height) <
500           2 * (rect1.height * rect1.width +
501                rect2.height * rect2.width))
502         {
503           rect1 = tmp_rect;
504           nrects = 1;
505         }
506     }
507
508   if (nrects == 2)
509     {
510       event.expose.type = GDK_EXPOSE;
511       event.expose.window = window;
512       event.expose.area.x = rect2.x;
513       event.expose.area.y = rect2.y;
514       event.expose.area.width = rect2.width;
515       event.expose.area.height = rect2.height;
516       event.expose.count = 0;
517
518       gdk_event_put (&event);
519     }
520
521   xevent->xexpose.count = nrects - 1;
522   xevent->xexpose.x = rect1.x;
523   xevent->xexpose.y = rect1.y;
524   xevent->xexpose.width = rect1.width;
525   xevent->xexpose.height = rect1.height;
526 }
527
528 /*************************************************************
529  * gdk_event_handler_set:
530  *     
531  *   arguments:
532  *     func: Callback function to be called for each event.
533  *     data: Data supplied to the function
534  *     notify: function called when function is no longer needed
535  * 
536  *   results:
537  *************************************************************/
538
539 void 
540 gdk_event_handler_set (GdkEventFunc   func,
541                        gpointer       data,
542                        GDestroyNotify notify)
543 {
544   if (event_notify)
545     (*event_notify) (event_data);
546
547   event_func = func;
548   event_data = data;
549   event_notify = notify;
550 }
551
552 /*
553  *--------------------------------------------------------------
554  * gdk_event_get
555  *
556  *   Gets the next event.
557  *
558  * Arguments:
559  *
560  * Results:
561  *   If an event is waiting that we care about, returns 
562  *   a pointer to that event, to be freed with gdk_event_free.
563  *   Otherwise, returns NULL.
564  *
565  * Side effects:
566  *
567  *--------------------------------------------------------------
568  */
569
570 GdkEvent*
571 gdk_event_get (void)
572 {
573   gdk_events_queue ();
574
575   return gdk_event_unqueue ();
576 }
577
578 /*
579  *--------------------------------------------------------------
580  * gdk_event_peek
581  *
582  *   Gets the next event.
583  *
584  * Arguments:
585  *
586  * Results:
587  *   If an event is waiting that we care about, returns 
588  *   a copy of that event, but does not remove it from
589  *   the queue. The pointer is to be freed with gdk_event_free.
590  *   Otherwise, returns NULL.
591  *
592  * Side effects:
593  *
594  *--------------------------------------------------------------
595  */
596
597 GdkEvent*
598 gdk_event_peek (void)
599 {
600   GList *tmp_list;
601
602   tmp_list = gdk_event_queue_find_first ();
603   
604   if (tmp_list)
605     return gdk_event_copy (tmp_list->data);
606   else
607     return NULL;
608 }
609
610 void
611 gdk_event_put (GdkEvent *event)
612 {
613   GdkEvent *new_event;
614   
615   g_return_if_fail (event != NULL);
616   
617   new_event = gdk_event_copy (event);
618
619   gdk_event_queue_append (new_event);
620 }
621
622 /*
623  *--------------------------------------------------------------
624  * gdk_event_copy
625  *
626  *   Copy a event structure into new storage.
627  *
628  * Arguments:
629  *   "event" is the event struct to copy.
630  *
631  * Results:
632  *   A new event structure.  Free it with gdk_event_free.
633  *
634  * Side effects:
635  *   The reference count of the window in the event is increased.
636  *
637  *--------------------------------------------------------------
638  */
639
640 static GMemChunk *event_chunk = NULL;
641
642 static GdkEvent*
643 gdk_event_new (void)
644 {
645   GdkEventPrivate *new_event;
646   
647   if (event_chunk == NULL)
648     event_chunk = g_mem_chunk_new ("events",
649                                    sizeof (GdkEventPrivate),
650                                    4096,
651                                    G_ALLOC_AND_FREE);
652   
653   new_event = g_chunk_new (GdkEventPrivate, event_chunk);
654   new_event->flags = 0;
655   
656   return (GdkEvent*) new_event;
657 }
658
659 GdkEvent*
660 gdk_event_copy (GdkEvent *event)
661 {
662   GdkEvent *new_event;
663   
664   g_return_val_if_fail (event != NULL, NULL);
665   
666   new_event = gdk_event_new ();
667   
668   *new_event = *event;
669   gdk_window_ref (new_event->any.window);
670   
671   switch (event->any.type)
672     {
673     case GDK_KEY_PRESS:
674     case GDK_KEY_RELEASE:
675       new_event->key.string = g_strdup (event->key.string);
676       break;
677       
678     case GDK_ENTER_NOTIFY:
679     case GDK_LEAVE_NOTIFY:
680       if (event->crossing.subwindow != NULL)
681         gdk_window_ref (event->crossing.subwindow);
682       break;
683       
684     case GDK_DRAG_ENTER:
685     case GDK_DRAG_LEAVE:
686     case GDK_DRAG_MOTION:
687     case GDK_DRAG_STATUS:
688     case GDK_DROP_START:
689     case GDK_DROP_FINISHED:
690       gdk_drag_context_ref (event->dnd.context);
691       break;
692       
693     default:
694       break;
695     }
696   
697   return new_event;
698 }
699
700 /*
701  *--------------------------------------------------------------
702  * gdk_event_free
703  *
704  *   Free a event structure obtained from gdk_event_copy.  Do not use
705  *   with other event structures.
706  *
707  * Arguments:
708  *   "event" is the event struct to free.
709  *
710  * Results:
711  *
712  * Side effects:
713  *   The reference count of the window in the event is decreased and
714  *   might be freed, too.
715  *
716  *-------------------------------------------------------------- */
717
718 void
719 gdk_event_free (GdkEvent *event)
720 {
721   g_return_if_fail (event != NULL);
722
723   g_assert (event_chunk != NULL); /* paranoid */
724   
725   if (event->any.window)
726     gdk_window_unref (event->any.window);
727   
728   switch (event->any.type)
729     {
730     case GDK_KEY_PRESS:
731     case GDK_KEY_RELEASE:
732       g_free (event->key.string);
733       break;
734       
735     case GDK_ENTER_NOTIFY:
736     case GDK_LEAVE_NOTIFY:
737       if (event->crossing.subwindow != NULL)
738         gdk_window_unref (event->crossing.subwindow);
739       break;
740       
741     case GDK_DRAG_ENTER:
742     case GDK_DRAG_LEAVE:
743     case GDK_DRAG_MOTION:
744     case GDK_DRAG_STATUS:
745     case GDK_DROP_START:
746     case GDK_DROP_FINISHED:
747       gdk_drag_context_unref (event->dnd.context);
748       break;
749       
750     default:
751       break;
752     }
753   
754   g_mem_chunk_free (event_chunk, event);
755 }
756
757 /*
758  *--------------------------------------------------------------
759  * gdk_event_get_time:
760  *    Get the timestamp from an event.
761  *   arguments:
762  *     event:
763  *   results:
764  *    The event's time stamp, if it has one, otherwise
765  *    GDK_CURRENT_TIME.
766  *--------------------------------------------------------------
767  */
768
769 guint32
770 gdk_event_get_time (GdkEvent *event)
771 {
772   if (event)
773     switch (event->type)
774       {
775       case GDK_MOTION_NOTIFY:
776         return event->motion.time;
777       case GDK_BUTTON_PRESS:
778       case GDK_2BUTTON_PRESS:
779       case GDK_3BUTTON_PRESS:
780       case GDK_BUTTON_RELEASE:
781         return event->button.time;
782       case GDK_KEY_PRESS:
783       case GDK_KEY_RELEASE:
784         return event->key.time;
785       case GDK_ENTER_NOTIFY:
786       case GDK_LEAVE_NOTIFY:
787         return event->crossing.time;
788       case GDK_PROPERTY_NOTIFY:
789         return event->property.time;
790       case GDK_SELECTION_CLEAR:
791       case GDK_SELECTION_REQUEST:
792       case GDK_SELECTION_NOTIFY:
793         return event->selection.time;
794       case GDK_PROXIMITY_IN:
795       case GDK_PROXIMITY_OUT:
796         return event->proximity.time;
797       case GDK_DRAG_ENTER:
798       case GDK_DRAG_LEAVE:
799       case GDK_DRAG_MOTION:
800       case GDK_DRAG_STATUS:
801       case GDK_DROP_START:
802       case GDK_DROP_FINISHED:
803         return event->dnd.time;
804       default:                  /* use current time */
805         break;
806       }
807   
808   return GDK_CURRENT_TIME;
809 }
810
811 /*
812  *--------------------------------------------------------------
813  * gdk_set_show_events
814  *
815  *   Turns on/off the showing of events.
816  *
817  * Arguments:
818  *   "show_events" is a boolean describing whether or
819  *   not to show the events gdk receives.
820  *
821  * Results:
822  *
823  * Side effects:
824  *   When "show_events" is TRUE, calls to "gdk_event_get"
825  *   will output debugging informatin regarding the event
826  *   received to stdout.
827  *
828  *--------------------------------------------------------------
829  */
830
831 void
832 gdk_set_show_events (gint show_events)
833 {
834   if (show_events)
835     gdk_debug_flags |= GDK_DEBUG_EVENTS;
836   else
837     gdk_debug_flags &= ~GDK_DEBUG_EVENTS;
838 }
839
840 gint
841 gdk_get_show_events (void)
842 {
843   return gdk_debug_flags & GDK_DEBUG_EVENTS;
844 }
845
846 static void
847 gdk_io_destroy (gpointer data)
848 {
849   GdkIOClosure *closure = data;
850
851   if (closure->notify)
852     closure->notify (closure->data);
853
854   g_free (closure);
855 }
856
857 /* What do we do with G_IO_NVAL?
858  */
859 #define READ_CONDITION (G_IO_IN | G_IO_HUP | G_IO_ERR)
860 #define WRITE_CONDITION (G_IO_OUT | G_IO_ERR)
861 #define EXCEPTION_CONDITION (G_IO_PRI)
862
863 static gboolean  
864 gdk_io_invoke (GIOChannel   *source,
865                GIOCondition  condition,
866                gpointer      data)
867 {
868   GdkIOClosure *closure = data;
869   GdkInputCondition gdk_cond = 0;
870
871   if (condition & READ_CONDITION)
872     gdk_cond |= GDK_INPUT_READ;
873   if (condition & WRITE_CONDITION)
874     gdk_cond |= GDK_INPUT_WRITE;
875   if (condition & EXCEPTION_CONDITION)
876     gdk_cond |= GDK_INPUT_EXCEPTION;
877
878   if (closure->condition & gdk_cond)
879     closure->function (closure->data, g_io_channel_unix_get_fd (source), gdk_cond);
880
881   return TRUE;
882 }
883
884 gint
885 gdk_input_add_full (gint              source,
886                     GdkInputCondition condition,
887                     GdkInputFunction  function,
888                     gpointer          data,
889                     GdkDestroyNotify  destroy)
890 {
891   guint result;
892   GdkIOClosure *closure = g_new (GdkIOClosure, 1);
893   GIOChannel *channel;
894   GIOCondition cond = 0;
895
896   closure->function = function;
897   closure->condition = condition;
898   closure->notify = destroy;
899   closure->data = data;
900
901   if (condition & GDK_INPUT_READ)
902     cond |= READ_CONDITION;
903   if (condition & GDK_INPUT_WRITE)
904     cond |= WRITE_CONDITION;
905   if (condition & GDK_INPUT_EXCEPTION)
906     cond |= EXCEPTION_CONDITION;
907
908   channel = g_io_channel_unix_new (source);
909   result = g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, cond, 
910                                 gdk_io_invoke,
911                                 closure, gdk_io_destroy);
912   g_io_channel_unref (channel);
913
914   return result;
915 }
916
917 gint
918 gdk_input_add (gint              source,
919                GdkInputCondition condition,
920                GdkInputFunction  function,
921                gpointer          data)
922 {
923   return gdk_input_add_full (source, condition, function, data, NULL);
924 }
925
926 void
927 gdk_input_remove (gint tag)
928 {
929   g_source_remove (tag);
930 }
931
932 static gint
933 gdk_event_apply_filters (XEvent *xevent,
934                          GdkEvent *event,
935                          GList *filters)
936 {
937   GList *tmp_list;
938   GdkFilterReturn result;
939   
940   tmp_list = filters;
941   
942   while (tmp_list)
943     {
944       GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
945       
946       tmp_list = tmp_list->next;
947       result = filter->function (xevent, event, filter->data);
948       if (result !=  GDK_FILTER_CONTINUE)
949         return result;
950     }
951   
952   return GDK_FILTER_CONTINUE;
953 }
954
955 void 
956 gdk_add_client_message_filter (GdkAtom       message_type,
957                                GdkFilterFunc func,
958                                gpointer      data)
959 {
960   GdkClientFilter *filter = g_new (GdkClientFilter, 1);
961
962   filter->type = message_type;
963   filter->function = func;
964   filter->data = data;
965   
966   client_filters = g_list_prepend (client_filters, filter);
967 }
968
969 static gint
970 gdk_event_translate (GdkEvent *event,
971                      XEvent   *xevent)
972 {
973   
974   GdkWindow *window;
975   GdkWindowPrivate *window_private;
976   static XComposeStatus compose;
977   KeySym keysym;
978   int charcount;
979 #ifdef USE_XIM
980   static gchar* buf = NULL;
981   static gint buf_len= 0;
982 #else
983   char buf[16];
984 #endif
985   gint return_val;
986   
987   return_val = FALSE;
988   
989   /* Find the GdkWindow that this event occurred in.
990    * 
991    * We handle events with window=None
992    *  specially - they are generated by XFree86's XInput under
993    *  some circumstances.
994    */
995   
996   if ((xevent->xany.window == None) &&
997       gdk_input_vtable.window_none_event)
998     {
999       return_val = gdk_input_vtable.window_none_event (event,xevent);
1000       
1001       if (return_val >= 0)      /* was handled */
1002         return return_val;
1003       else
1004         return_val = FALSE;
1005     }
1006   
1007   window = gdk_window_lookup (xevent->xany.window);
1008   /* FIXME: window might be a GdkPixmap!!! */
1009   
1010   window_private = (GdkWindowPrivate *) window;
1011   
1012   if (window != NULL)
1013     gdk_window_ref (window);
1014   
1015   event->any.window = window;
1016   event->any.send_event = xevent->xany.send_event ? TRUE : FALSE;
1017   
1018   if (window_private && GDK_DRAWABLE_DESTROYED (window))
1019     {
1020       if (xevent->type != DestroyNotify)
1021         return FALSE;
1022     }
1023   else
1024     {
1025       /* Check for filters for this window
1026        */
1027       GdkFilterReturn result;
1028       result = gdk_event_apply_filters (xevent, event,
1029                                         window_private
1030                                         ?window_private->filters
1031                                         :gdk_default_filters);
1032       
1033       if (result != GDK_FILTER_CONTINUE)
1034         {
1035           return (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
1036         }
1037     }
1038
1039 #ifdef USE_XIM
1040   if (window == NULL && gdk_xim_window && xevent->type == KeyPress &&
1041       !GDK_DRAWABLE_DESTROYED (gdk_xim_window))
1042     {
1043       /*
1044        * If user presses a key in Preedit or Status window, keypress event
1045        * is sometimes sent to these windows. These windows are not managed
1046        * by GDK, so we redirect KeyPress event to xim_window.
1047        *
1048        * If someone want to use the window whitch is not managed by GDK
1049        * and want to get KeyPress event, he/she must register the filter
1050        * function to gdk_default_filters to intercept the event.
1051        */
1052
1053       GdkFilterReturn result;
1054
1055       window = gdk_xim_window;
1056       window_private = (GdkWindowPrivate *) window;
1057       gdk_window_ref (window);
1058       event->any.window = window;
1059
1060       GDK_NOTE (XIM,
1061         g_message ("KeyPress event is redirected to xim_window: %#lx",
1062                    xevent->xany.window));
1063
1064       result = gdk_event_apply_filters (xevent, event,
1065                                         window_private->filters);
1066       if (result != GDK_FILTER_CONTINUE)
1067         return (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
1068     }
1069 #endif
1070
1071   /* We do a "manual" conversion of the XEvent to a
1072    *  GdkEvent. The structures are mostly the same so
1073    *  the conversion is fairly straightforward. We also
1074    *  optionally print debugging info regarding events
1075    *  received.
1076    */
1077
1078   return_val = TRUE;
1079
1080   switch (xevent->type)
1081     {
1082     case KeyPress:
1083       /* Lookup the string corresponding to the given keysym.
1084        */
1085       
1086 #ifdef USE_XIM
1087       if (buf_len == 0) 
1088         {
1089           buf_len = 128;
1090           buf = g_new (gchar, buf_len);
1091         }
1092       keysym = GDK_VoidSymbol;
1093       
1094       if (gdk_xim_ic && gdk_xim_ic->xic)
1095         {
1096           Status status;
1097           
1098           /* Clear keyval. Depending on status, may not be set */
1099           charcount = XmbLookupString(gdk_xim_ic->xic,
1100                                       &xevent->xkey, buf, buf_len-1,
1101                                       &keysym, &status);
1102           if (status == XBufferOverflow)
1103             {                     /* retry */
1104               /* alloc adequate size of buffer */
1105               GDK_NOTE (XIM,
1106                         g_message("XIM: overflow (required %i)", charcount));
1107               
1108               while (buf_len <= charcount)
1109                 buf_len *= 2;
1110               buf = (gchar *) g_realloc (buf, buf_len);
1111               
1112               charcount = XmbLookupString (gdk_xim_ic->xic,
1113                                            &xevent->xkey, buf, buf_len-1,
1114                                            &keysym, &status);
1115             }
1116           if (status == XLookupNone)
1117             {
1118               return_val = FALSE;
1119               break;
1120             }
1121         }
1122       else
1123         charcount = XLookupString (&xevent->xkey, buf, buf_len,
1124                                    &keysym, &compose);
1125 #else
1126       charcount = XLookupString (&xevent->xkey, buf, 16,
1127                                  &keysym, &compose);
1128 #endif
1129       event->key.keyval = keysym;
1130       
1131       if (charcount > 0 && buf[charcount-1] == '\0')
1132         charcount --;
1133       else
1134         buf[charcount] = '\0';
1135       
1136       /* Print debugging info. */
1137       
1138 #ifdef G_ENABLE_DEBUG
1139       if (gdk_debug_flags & GDK_DEBUG_EVENTS)
1140         {
1141           g_message ("key press:\twindow: %ld  key: %12s  %d",
1142                      xevent->xkey.window,
1143                      event->key.keyval ? XKeysymToString (event->key.keyval) : "(none)",
1144                      event->key.keyval);
1145           if (charcount > 0)
1146             g_message ("\t\tlength: %4d string: \"%s\"",
1147                        charcount, buf);
1148         }
1149 #endif /* G_ENABLE_DEBUG */
1150       
1151       event->key.type = GDK_KEY_PRESS;
1152       event->key.window = window;
1153       event->key.time = xevent->xkey.time;
1154       event->key.state = (GdkModifierType) xevent->xkey.state;
1155       event->key.string = g_strdup (buf);
1156       event->key.length = charcount;
1157       
1158       break;
1159       
1160     case KeyRelease:
1161       /* Lookup the string corresponding to the given keysym.
1162        */
1163 #ifdef USE_XIM
1164       if (buf_len == 0) 
1165         {
1166           buf_len = 128;
1167           buf = g_new (gchar, buf_len);
1168         }
1169 #endif
1170       keysym = GDK_VoidSymbol;
1171       charcount = XLookupString (&xevent->xkey, buf, 16,
1172                                  &keysym, &compose);
1173       event->key.keyval = keysym;      
1174       
1175       /* Print debugging info.
1176        */
1177       GDK_NOTE (EVENTS, 
1178                 g_message ("key release:\t\twindow: %ld  key: %12s  %d",
1179                            xevent->xkey.window,
1180                            XKeysymToString (event->key.keyval),
1181                            event->key.keyval));
1182       
1183       event->key.type = GDK_KEY_RELEASE;
1184       event->key.window = window;
1185       event->key.time = xevent->xkey.time;
1186       event->key.state = (GdkModifierType) xevent->xkey.state;
1187       event->key.length = 0;
1188       event->key.string = NULL;
1189       
1190       break;
1191       
1192     case ButtonPress:
1193       /* Print debugging info.
1194        */
1195       GDK_NOTE (EVENTS, 
1196                 g_message ("button press:\t\twindow: %ld  x,y: %d %d  button: %d",
1197                            xevent->xbutton.window,
1198                            xevent->xbutton.x, xevent->xbutton.y,
1199                            xevent->xbutton.button));
1200       
1201       if (window_private &&
1202           (window_private->extension_events != 0) &&
1203           gdk_input_ignore_core)
1204         {
1205           return_val = FALSE;
1206           break;
1207         }
1208       
1209       event->button.type = GDK_BUTTON_PRESS;
1210       event->button.window = window;
1211       event->button.time = xevent->xbutton.time;
1212       event->button.x = xevent->xbutton.x;
1213       event->button.y = xevent->xbutton.y;
1214       event->button.x_root = (gfloat)xevent->xbutton.x_root;
1215       event->button.y_root = (gfloat)xevent->xbutton.y_root;
1216       event->button.pressure = 0.5;
1217       event->button.xtilt = 0;
1218       event->button.ytilt = 0;
1219       event->button.state = (GdkModifierType) xevent->xbutton.state;
1220       event->button.button = xevent->xbutton.button;
1221       event->button.source = GDK_SOURCE_MOUSE;
1222       event->button.deviceid = GDK_CORE_POINTER;
1223       
1224       if ((event->button.time < (button_click_time[1] + TRIPLE_CLICK_TIME)) &&
1225           (event->button.window == button_window[1]) &&
1226           (event->button.button == button_number[1]))
1227         {
1228           gdk_synthesize_click (event, 3);
1229           
1230           button_click_time[1] = 0;
1231           button_click_time[0] = 0;
1232           button_window[1] = NULL;
1233           button_window[0] = 0;
1234           button_number[1] = -1;
1235           button_number[0] = -1;
1236         }
1237       else if ((event->button.time < (button_click_time[0] + DOUBLE_CLICK_TIME)) &&
1238                (event->button.window == button_window[0]) &&
1239                (event->button.button == button_number[0]))
1240         {
1241           gdk_synthesize_click (event, 2);
1242           
1243           button_click_time[1] = button_click_time[0];
1244           button_click_time[0] = event->button.time;
1245           button_window[1] = button_window[0];
1246           button_window[0] = event->button.window;
1247           button_number[1] = button_number[0];
1248           button_number[0] = event->button.button;
1249         }
1250       else
1251         {
1252           button_click_time[1] = 0;
1253           button_click_time[0] = event->button.time;
1254           button_window[1] = NULL;
1255           button_window[0] = event->button.window;
1256           button_number[1] = -1;
1257           button_number[0] = event->button.button;
1258         }
1259
1260       break;
1261       
1262     case ButtonRelease:
1263       /* Print debugging info.
1264        */
1265       GDK_NOTE (EVENTS, 
1266                 g_message ("button release:\twindow: %ld  x,y: %d %d  button: %d",
1267                            xevent->xbutton.window,
1268                            xevent->xbutton.x, xevent->xbutton.y,
1269                            xevent->xbutton.button));
1270       
1271       if (window_private &&
1272           (window_private->extension_events != 0) &&
1273           gdk_input_ignore_core)
1274         {
1275           return_val = FALSE;
1276           break;
1277         }
1278       
1279       event->button.type = GDK_BUTTON_RELEASE;
1280       event->button.window = window;
1281       event->button.time = xevent->xbutton.time;
1282       event->button.x = xevent->xbutton.x;
1283       event->button.y = xevent->xbutton.y;
1284       event->button.x_root = (gfloat)xevent->xbutton.x_root;
1285       event->button.y_root = (gfloat)xevent->xbutton.y_root;
1286       event->button.pressure = 0.5;
1287       event->button.xtilt = 0;
1288       event->button.ytilt = 0;
1289       event->button.state = (GdkModifierType) xevent->xbutton.state;
1290       event->button.button = xevent->xbutton.button;
1291       event->button.source = GDK_SOURCE_MOUSE;
1292       event->button.deviceid = GDK_CORE_POINTER;
1293       
1294       break;
1295       
1296     case MotionNotify:
1297       /* Print debugging info.
1298        */
1299       GDK_NOTE (EVENTS,
1300                 g_message ("motion notify:\t\twindow: %ld  x,y: %d %d  hint: %s", 
1301                            xevent->xmotion.window,
1302                            xevent->xmotion.x, xevent->xmotion.y,
1303                            (xevent->xmotion.is_hint) ? "true" : "false"));
1304       
1305       if (window_private &&
1306           (window_private->extension_events != 0) &&
1307           gdk_input_ignore_core)
1308         {
1309           return_val = FALSE;
1310           break;
1311         }
1312       
1313       event->motion.type = GDK_MOTION_NOTIFY;
1314       event->motion.window = window;
1315       event->motion.time = xevent->xmotion.time;
1316       event->motion.x = xevent->xmotion.x;
1317       event->motion.y = xevent->xmotion.y;
1318       event->motion.x_root = (gfloat)xevent->xmotion.x_root;
1319       event->motion.y_root = (gfloat)xevent->xmotion.y_root;
1320       event->motion.pressure = 0.5;
1321       event->motion.xtilt = 0;
1322       event->motion.ytilt = 0;
1323       event->motion.state = (GdkModifierType) xevent->xmotion.state;
1324       event->motion.is_hint = xevent->xmotion.is_hint;
1325       event->motion.source = GDK_SOURCE_MOUSE;
1326       event->motion.deviceid = GDK_CORE_POINTER;
1327       
1328       break;
1329       
1330     case EnterNotify:
1331       /* Print debugging info.
1332        */
1333       GDK_NOTE (EVENTS,
1334                 g_message ("enter notify:\t\twindow: %ld  detail: %d subwin: %ld",
1335                            xevent->xcrossing.window,
1336                            xevent->xcrossing.detail,
1337                            xevent->xcrossing.subwindow));
1338       
1339       /* Tell XInput stuff about it if appropriate */
1340       if (window_private &&
1341           !GDK_DRAWABLE_DESTROYED (window) &&
1342           (window_private->extension_events != 0) &&
1343           gdk_input_vtable.enter_event)
1344         gdk_input_vtable.enter_event (&xevent->xcrossing, window);
1345       
1346       event->crossing.type = GDK_ENTER_NOTIFY;
1347       event->crossing.window = window;
1348       
1349       /* If the subwindow field of the XEvent is non-NULL, then
1350        *  lookup the corresponding GdkWindow.
1351        */
1352       if (xevent->xcrossing.subwindow != None)
1353         event->crossing.subwindow = gdk_window_lookup (xevent->xcrossing.subwindow);
1354       else
1355         event->crossing.subwindow = NULL;
1356       
1357       event->crossing.time = xevent->xcrossing.time;
1358       event->crossing.x = xevent->xcrossing.x;
1359       event->crossing.y = xevent->xcrossing.y;
1360       event->crossing.x_root = xevent->xcrossing.x_root;
1361       event->crossing.y_root = xevent->xcrossing.y_root;
1362       
1363       /* Translate the crossing mode into Gdk terms.
1364        */
1365       switch (xevent->xcrossing.mode)
1366         {
1367         case NotifyNormal:
1368           event->crossing.mode = GDK_CROSSING_NORMAL;
1369           break;
1370         case NotifyGrab:
1371           event->crossing.mode = GDK_CROSSING_GRAB;
1372           break;
1373         case NotifyUngrab:
1374           event->crossing.mode = GDK_CROSSING_UNGRAB;
1375           break;
1376         };
1377       
1378       /* Translate the crossing detail into Gdk terms.
1379        */
1380       switch (xevent->xcrossing.detail)
1381         {
1382         case NotifyInferior:
1383           event->crossing.detail = GDK_NOTIFY_INFERIOR;
1384           break;
1385         case NotifyAncestor:
1386           event->crossing.detail = GDK_NOTIFY_ANCESTOR;
1387           break;
1388         case NotifyVirtual:
1389           event->crossing.detail = GDK_NOTIFY_VIRTUAL;
1390           break;
1391         case NotifyNonlinear:
1392           event->crossing.detail = GDK_NOTIFY_NONLINEAR;
1393           break;
1394         case NotifyNonlinearVirtual:
1395           event->crossing.detail = GDK_NOTIFY_NONLINEAR_VIRTUAL;
1396           break;
1397         default:
1398           event->crossing.detail = GDK_NOTIFY_UNKNOWN;
1399           break;
1400         }
1401       
1402       event->crossing.focus = xevent->xcrossing.focus;
1403       event->crossing.state = xevent->xcrossing.state;
1404   
1405       break;
1406       
1407     case LeaveNotify:
1408       /* Print debugging info.
1409        */
1410       GDK_NOTE (EVENTS, 
1411                 g_message ("leave notify:\t\twindow: %ld  detail: %d subwin: %ld",
1412                            xevent->xcrossing.window,
1413                            xevent->xcrossing.detail, xevent->xcrossing.subwindow));
1414       
1415       event->crossing.type = GDK_LEAVE_NOTIFY;
1416       event->crossing.window = window;
1417       
1418       /* If the subwindow field of the XEvent is non-NULL, then
1419        *  lookup the corresponding GdkWindow.
1420        */
1421       if (xevent->xcrossing.subwindow != None)
1422         event->crossing.subwindow = gdk_window_lookup (xevent->xcrossing.subwindow);
1423       else
1424         event->crossing.subwindow = NULL;
1425       
1426       event->crossing.time = xevent->xcrossing.time;
1427       event->crossing.x = xevent->xcrossing.x;
1428       event->crossing.y = xevent->xcrossing.y;
1429       event->crossing.x_root = xevent->xcrossing.x_root;
1430       event->crossing.y_root = xevent->xcrossing.y_root;
1431       
1432       /* Translate the crossing mode into Gdk terms.
1433        */
1434       switch (xevent->xcrossing.mode)
1435         {
1436         case NotifyNormal:
1437           event->crossing.mode = GDK_CROSSING_NORMAL;
1438           break;
1439         case NotifyGrab:
1440           event->crossing.mode = GDK_CROSSING_GRAB;
1441           break;
1442         case NotifyUngrab:
1443           event->crossing.mode = GDK_CROSSING_UNGRAB;
1444           break;
1445         };
1446       
1447       /* Translate the crossing detail into Gdk terms.
1448        */
1449       switch (xevent->xcrossing.detail)
1450         {
1451         case NotifyInferior:
1452           event->crossing.detail = GDK_NOTIFY_INFERIOR;
1453           break;
1454         case NotifyAncestor:
1455           event->crossing.detail = GDK_NOTIFY_ANCESTOR;
1456           break;
1457         case NotifyVirtual:
1458           event->crossing.detail = GDK_NOTIFY_VIRTUAL;
1459           break;
1460         case NotifyNonlinear:
1461           event->crossing.detail = GDK_NOTIFY_NONLINEAR;
1462           break;
1463         case NotifyNonlinearVirtual:
1464           event->crossing.detail = GDK_NOTIFY_NONLINEAR_VIRTUAL;
1465           break;
1466         default:
1467           event->crossing.detail = GDK_NOTIFY_UNKNOWN;
1468           break;
1469         }
1470       
1471       event->crossing.focus = xevent->xcrossing.focus;
1472       event->crossing.state = xevent->xcrossing.state;
1473       
1474       break;
1475       
1476     case FocusIn:
1477     case FocusOut:
1478       /* We only care about focus events that indicate that _this_
1479        * window (not a ancestor or child) got or lost the focus
1480        */
1481       switch (xevent->xfocus.detail)
1482         {
1483         case NotifyAncestor:
1484         case NotifyInferior:
1485         case NotifyNonlinear:
1486           /* Print debugging info.
1487            */
1488           GDK_NOTE (EVENTS,
1489                     g_message ("focus %s:\t\twindow: %ld",
1490                                (xevent->xany.type == FocusIn) ? "in" : "out",
1491                                xevent->xfocus.window));
1492           
1493           /* gdk_keyboard_grab() causes following events. These events confuse
1494            * the XIM focus, so ignore them.
1495            */
1496           if (xevent->xfocus.mode == NotifyGrab ||
1497               xevent->xfocus.mode == NotifyUngrab)
1498             break;
1499           
1500           event->focus_change.type = GDK_FOCUS_CHANGE;
1501           event->focus_change.window = window;
1502           event->focus_change.in = (xevent->xany.type == FocusIn);
1503
1504           break;
1505         default:
1506           return_val = FALSE;
1507         }
1508       break;
1509       
1510     case KeymapNotify:
1511       /* Print debugging info.
1512        */
1513       GDK_NOTE (EVENTS,
1514                 g_message ("keymap notify"));
1515
1516       /* Not currently handled */
1517       return_val = FALSE;
1518       break;
1519       
1520     case Expose:
1521       /* Print debugging info.
1522        */
1523       GDK_NOTE (EVENTS,
1524                 g_message ("expose:\t\twindow: %ld  %d  x,y: %d %d  w,h: %d %d%s",
1525                            xevent->xexpose.window, xevent->xexpose.count,
1526                            xevent->xexpose.x, xevent->xexpose.y,
1527                            xevent->xexpose.width, xevent->xexpose.height,
1528                            event->any.send_event ? " (send)" : ""));
1529       gdk_compress_exposures (xevent, window);
1530       
1531       event->expose.type = GDK_EXPOSE;
1532       event->expose.window = window;
1533       event->expose.area.x = xevent->xexpose.x;
1534       event->expose.area.y = xevent->xexpose.y;
1535       event->expose.area.width = xevent->xexpose.width;
1536       event->expose.area.height = xevent->xexpose.height;
1537       event->expose.count = xevent->xexpose.count;
1538       
1539       break;
1540       
1541     case GraphicsExpose:
1542       /* Print debugging info.
1543        */
1544       GDK_NOTE (EVENTS,
1545                 g_message ("graphics expose:\tdrawable: %ld",
1546                            xevent->xgraphicsexpose.drawable));
1547       
1548       event->expose.type = GDK_EXPOSE;
1549       event->expose.window = window;
1550       event->expose.area.x = xevent->xgraphicsexpose.x;
1551       event->expose.area.y = xevent->xgraphicsexpose.y;
1552       event->expose.area.width = xevent->xgraphicsexpose.width;
1553       event->expose.area.height = xevent->xgraphicsexpose.height;
1554       event->expose.count = xevent->xexpose.count;
1555       
1556       break;
1557       
1558     case NoExpose:
1559       /* Print debugging info.
1560        */
1561       GDK_NOTE (EVENTS,
1562                 g_message ("no expose:\t\tdrawable: %ld",
1563                            xevent->xnoexpose.drawable));
1564       
1565       event->no_expose.type = GDK_NO_EXPOSE;
1566       event->no_expose.window = window;
1567       
1568       break;
1569       
1570     case VisibilityNotify:
1571       /* Print debugging info.
1572        */
1573 #ifdef G_ENABLE_DEBUG
1574       if (gdk_debug_flags & GDK_DEBUG_EVENTS)
1575         switch (xevent->xvisibility.state)
1576           {
1577           case VisibilityFullyObscured:
1578             g_message ("visibility notify:\twindow: %ld  none",
1579                        xevent->xvisibility.window);
1580             break;
1581           case VisibilityPartiallyObscured:
1582             g_message ("visibility notify:\twindow: %ld  partial",
1583                        xevent->xvisibility.window);
1584             break;
1585           case VisibilityUnobscured:
1586             g_message ("visibility notify:\twindow: %ld  full",
1587                        xevent->xvisibility.window);
1588             break;
1589           }
1590 #endif /* G_ENABLE_DEBUG */
1591       
1592       event->visibility.type = GDK_VISIBILITY_NOTIFY;
1593       event->visibility.window = window;
1594       
1595       switch (xevent->xvisibility.state)
1596         {
1597         case VisibilityFullyObscured:
1598           event->visibility.state = GDK_VISIBILITY_FULLY_OBSCURED;
1599           break;
1600           
1601         case VisibilityPartiallyObscured:
1602           event->visibility.state = GDK_VISIBILITY_PARTIAL;
1603           break;
1604           
1605         case VisibilityUnobscured:
1606           event->visibility.state = GDK_VISIBILITY_UNOBSCURED;
1607           break;
1608         }
1609       
1610       break;
1611       
1612     case CreateNotify:
1613       GDK_NOTE (EVENTS,
1614                 g_message ("create notify:\twindow: %ld  x,y: %d %d     w,h: %d %d  b-w: %d  parent: %ld         ovr: %d",
1615                            xevent->xcreatewindow.window,
1616                            xevent->xcreatewindow.x,
1617                            xevent->xcreatewindow.y,
1618                            xevent->xcreatewindow.width,
1619                            xevent->xcreatewindow.height,
1620                            xevent->xcreatewindow.border_width,
1621                            xevent->xcreatewindow.parent,
1622                            xevent->xcreatewindow.override_redirect));
1623       /* not really handled */
1624       break;
1625       
1626     case DestroyNotify:
1627       /* Print debugging info.
1628        */
1629       GDK_NOTE (EVENTS,
1630                 g_message ("destroy notify:\twindow: %ld",
1631                            xevent->xdestroywindow.window));
1632       
1633       event->any.type = GDK_DESTROY;
1634       event->any.window = window;
1635       
1636       return_val = window_private && !GDK_DRAWABLE_DESTROYED (window);
1637       
1638       if (window && GDK_DRAWABLE_XID (window) != GDK_ROOT_WINDOW())
1639         gdk_window_destroy_notify (window);
1640       break;
1641       
1642     case UnmapNotify:
1643       /* Print debugging info.
1644        */
1645       GDK_NOTE (EVENTS,
1646                 g_message ("unmap notify:\t\twindow: %ld",
1647                            xevent->xmap.window));
1648       
1649       event->any.type = GDK_UNMAP;
1650       event->any.window = window;
1651       
1652       if (gdk_xgrab_window == window_private)
1653         gdk_xgrab_window = NULL;
1654       
1655       break;
1656       
1657     case MapNotify:
1658       /* Print debugging info.
1659        */
1660       GDK_NOTE (EVENTS,
1661                 g_message ("map notify:\t\twindow: %ld",
1662                            xevent->xmap.window));
1663       
1664       event->any.type = GDK_MAP;
1665       event->any.window = window;
1666       
1667       break;
1668       
1669     case ReparentNotify:
1670       /* Print debugging info.
1671        */
1672       GDK_NOTE (EVENTS,
1673                 g_message ("reparent notify:\twindow: %ld  x,y: %d %d  parent: %ld      ovr: %d",
1674                            xevent->xreparent.window,
1675                            xevent->xreparent.x,
1676                            xevent->xreparent.y,
1677                            xevent->xreparent.parent,
1678                            xevent->xreparent.override_redirect));
1679
1680       /* Not currently handled */
1681       return_val = FALSE;
1682       break;
1683       
1684     case ConfigureNotify:
1685       /* Print debugging info.
1686        */
1687       GDK_NOTE (EVENTS,
1688                 g_message ("configure notify:\twindow: %ld  x,y: %d %d  w,h: %d %d  b-w: %d  above: %ld  ovr: %d%s",
1689                            xevent->xconfigure.window,
1690                            xevent->xconfigure.x,
1691                            xevent->xconfigure.y,
1692                            xevent->xconfigure.width,
1693                            xevent->xconfigure.height,
1694                            xevent->xconfigure.border_width,
1695                            xevent->xconfigure.above,
1696                            xevent->xconfigure.override_redirect,
1697                            !window
1698                            ? " (discarding)"
1699                            : GDK_DRAWABLE_TYPE (window) == GDK_WINDOW_CHILD
1700                            ? " (discarding child)"
1701                            : ""));
1702       if (window &&
1703           !GDK_DRAWABLE_DESTROYED (window) &&
1704           (window_private->extension_events != 0) &&
1705           gdk_input_vtable.configure_event)
1706         gdk_input_vtable.configure_event (&xevent->xconfigure, window);
1707
1708       if (!window || GDK_DRAWABLE_TYPE (window) == GDK_WINDOW_CHILD)
1709         return_val = FALSE;
1710       else
1711         {
1712           event->configure.type = GDK_CONFIGURE;
1713           event->configure.window = window;
1714           event->configure.width = xevent->xconfigure.width;
1715           event->configure.height = xevent->xconfigure.height;
1716           
1717           if (!xevent->xconfigure.x &&
1718               !xevent->xconfigure.y &&
1719               !GDK_DRAWABLE_DESTROYED (window))
1720             {
1721               gint tx = 0;
1722               gint ty = 0;
1723               Window child_window = 0;
1724
1725               gdk_error_trap_push ();
1726               if (XTranslateCoordinates (GDK_DRAWABLE_XDISPLAY (window),
1727                                          GDK_DRAWABLE_XID (window),
1728                                          gdk_root_window,
1729                                          0, 0,
1730                                          &tx, &ty,
1731                                          &child_window))
1732                 {
1733                   if (!gdk_error_trap_pop ())
1734                     {
1735                       event->configure.x = tx;
1736                       event->configure.y = ty;
1737                     }
1738                 }
1739               else
1740                 gdk_error_trap_pop ();
1741             }
1742           else
1743             {
1744               event->configure.x = xevent->xconfigure.x;
1745               event->configure.y = xevent->xconfigure.y;
1746             }
1747           window_private->x = event->configure.x;
1748           window_private->y = event->configure.y;
1749           window_private->drawable.width = xevent->xconfigure.width;
1750           window_private->drawable.height = xevent->xconfigure.height;
1751           if (window_private->resize_count > 1)
1752             window_private->resize_count -= 1;
1753         }
1754       break;
1755       
1756     case PropertyNotify:
1757       /* Print debugging info.
1758        */
1759       GDK_NOTE (EVENTS,
1760                 gchar *atom = gdk_atom_name (xevent->xproperty.atom);
1761                 g_message ("property notify:\twindow: %ld, atom(%ld): %s%s%s",
1762                            xevent->xproperty.window,
1763                            xevent->xproperty.atom,
1764                            atom ? "\"" : "",
1765                            atom ? atom : "unknown",
1766                            atom ? "\"" : "");
1767                 g_free (atom);
1768                 );
1769       
1770       event->property.type = GDK_PROPERTY_NOTIFY;
1771       event->property.window = window;
1772       event->property.atom = xevent->xproperty.atom;
1773       event->property.time = xevent->xproperty.time;
1774       event->property.state = xevent->xproperty.state;
1775       
1776       break;
1777       
1778     case SelectionClear:
1779       GDK_NOTE (EVENTS,
1780                 g_message ("selection clear:\twindow: %ld",
1781                            xevent->xproperty.window));
1782       
1783       event->selection.type = GDK_SELECTION_CLEAR;
1784       event->selection.window = window;
1785       event->selection.selection = xevent->xselectionclear.selection;
1786       event->selection.time = xevent->xselectionclear.time;
1787       
1788       break;
1789       
1790     case SelectionRequest:
1791       GDK_NOTE (EVENTS,
1792                 g_message ("selection request:\twindow: %ld",
1793                            xevent->xproperty.window));
1794       
1795       event->selection.type = GDK_SELECTION_REQUEST;
1796       event->selection.window = window;
1797       event->selection.selection = xevent->xselectionrequest.selection;
1798       event->selection.target = xevent->xselectionrequest.target;
1799       event->selection.property = xevent->xselectionrequest.property;
1800       event->selection.requestor = xevent->xselectionrequest.requestor;
1801       event->selection.time = xevent->xselectionrequest.time;
1802       
1803       break;
1804       
1805     case SelectionNotify:
1806       GDK_NOTE (EVENTS,
1807                 g_message ("selection notify:\twindow: %ld",
1808                            xevent->xproperty.window));
1809       
1810       
1811       event->selection.type = GDK_SELECTION_NOTIFY;
1812       event->selection.window = window;
1813       event->selection.selection = xevent->xselection.selection;
1814       event->selection.target = xevent->xselection.target;
1815       event->selection.property = xevent->xselection.property;
1816       event->selection.time = xevent->xselection.time;
1817       
1818       break;
1819       
1820     case ColormapNotify:
1821       /* Print debugging info.
1822        */
1823       GDK_NOTE (EVENTS,
1824                 g_message ("colormap notify:\twindow: %ld",
1825                            xevent->xcolormap.window));
1826       
1827       /* Not currently handled */
1828       return_val = FALSE;
1829       break;
1830       
1831     case ClientMessage:
1832       {
1833         GList *tmp_list;
1834         GdkFilterReturn result = GDK_FILTER_CONTINUE;
1835
1836         /* Print debugging info.
1837          */
1838         GDK_NOTE (EVENTS,
1839                   g_message ("client message:\twindow: %ld",
1840                              xevent->xclient.window));
1841         
1842         tmp_list = client_filters;
1843         while (tmp_list)
1844           {
1845             GdkClientFilter *filter = tmp_list->data;
1846             if (filter->type == xevent->xclient.message_type)
1847               {
1848                 result = (*filter->function) (xevent, event, filter->data);
1849                 break;
1850               }
1851             
1852             tmp_list = tmp_list->next;
1853           }
1854
1855         switch (result)
1856           {
1857           case GDK_FILTER_REMOVE:
1858             return_val = FALSE;
1859             break;
1860           case GDK_FILTER_TRANSLATE:
1861             return_val = TRUE;
1862             break;
1863           case GDK_FILTER_CONTINUE:
1864             /* Send unknown ClientMessage's on to Gtk for it to use */
1865             event->client.type = GDK_CLIENT_EVENT;
1866             event->client.window = window;
1867             event->client.message_type = xevent->xclient.message_type;
1868             event->client.data_format = xevent->xclient.format;
1869             memcpy(&event->client.data, &xevent->xclient.data,
1870                    sizeof(event->client.data));
1871           }
1872       }
1873       
1874       break;
1875       
1876     case MappingNotify:
1877       /* Print debugging info.
1878        */
1879       GDK_NOTE (EVENTS,
1880                 g_message ("mapping notify"));
1881       
1882       /* Let XLib know that there is a new keyboard mapping.
1883        */
1884       XRefreshKeyboardMapping (&xevent->xmapping);
1885       return_val = FALSE;
1886       break;
1887       
1888     default:
1889       /* something else - (e.g., a Xinput event) */
1890       
1891       if (window_private &&
1892           !window_private->drawable.destroyed &&
1893           (window_private->extension_events != 0) &&
1894           gdk_input_vtable.other_event)
1895         return_val = gdk_input_vtable.other_event(event, xevent, window);
1896       else
1897         return_val = FALSE;
1898       
1899       break;
1900     }
1901   
1902   if (return_val)
1903     {
1904       if (event->any.window)
1905         gdk_window_ref (event->any.window);
1906       if (((event->any.type == GDK_ENTER_NOTIFY) ||
1907            (event->any.type == GDK_LEAVE_NOTIFY)) &&
1908           (event->crossing.subwindow != NULL))
1909         gdk_window_ref (event->crossing.subwindow);
1910     }
1911   else
1912     {
1913       /* Mark this event as having no resources to be freed */
1914       event->any.window = NULL;
1915       event->any.type = GDK_NOTHING;
1916     }
1917   
1918   if (window)
1919     gdk_window_unref (window);
1920   
1921   return return_val;
1922 }
1923
1924 GdkFilterReturn
1925 gdk_wm_protocols_filter (GdkXEvent *xev,
1926                          GdkEvent  *event,
1927                          gpointer data)
1928 {
1929   XEvent *xevent = (XEvent *)xev;
1930
1931   if ((Atom) xevent->xclient.data.l[0] == gdk_wm_delete_window)
1932     {
1933   /* The delete window request specifies a window
1934    *  to delete. We don't actually destroy the
1935    *  window because "it is only a request". (The
1936    *  window might contain vital data that the
1937    *  program does not want destroyed). Instead
1938    *  the event is passed along to the program,
1939    *  which should then destroy the window.
1940    */
1941       GDK_NOTE (EVENTS,
1942                 g_message ("delete window:\t\twindow: %ld",
1943                            xevent->xclient.window));
1944       
1945       event->any.type = GDK_DELETE;
1946
1947       return GDK_FILTER_TRANSLATE;
1948     }
1949   else if ((Atom) xevent->xclient.data.l[0] == gdk_wm_take_focus)
1950     {
1951     }
1952
1953   return GDK_FILTER_REMOVE;
1954 }
1955
1956 #if 0
1957 static Bool
1958 gdk_event_get_type (Display  *display,
1959                     XEvent   *xevent,
1960                     XPointer  arg)
1961 {
1962   GdkEvent event;
1963   GdkPredicate *pred;
1964   
1965   if (gdk_event_translate (&event, xevent))
1966     {
1967       pred = (GdkPredicate*) arg;
1968       return (* pred->func) (&event, pred->data);
1969     }
1970   
1971   return FALSE;
1972 }
1973 #endif
1974
1975 static void
1976 gdk_events_queue (void)
1977 {
1978   GList *node;
1979   GdkEvent *event;
1980   XEvent xevent;
1981
1982   while (!gdk_event_queue_find_first() && XPending (gdk_display))
1983     {
1984 #ifdef USE_XIM
1985       Window w = None;
1986       
1987       XNextEvent (gdk_display, &xevent);
1988       if (gdk_xim_window)
1989         switch (xevent.type)
1990           {
1991           case KeyPress:
1992           case KeyRelease:
1993           case ButtonPress:
1994           case ButtonRelease:
1995             w = GDK_WINDOW_XWINDOW (gdk_xim_window);
1996             break;
1997           }
1998       
1999       if (XFilterEvent (&xevent, w))
2000         continue;
2001 #else
2002       XNextEvent (gdk_display, &xevent);
2003 #endif
2004       
2005       event = gdk_event_new ();
2006       
2007       event->any.type = GDK_NOTHING;
2008       event->any.window = NULL;
2009       event->any.send_event = xevent.xany.send_event ? TRUE : FALSE;
2010
2011       ((GdkEventPrivate *)event)->flags |= GDK_EVENT_PENDING;
2012
2013       gdk_event_queue_append (event);
2014       node = queued_tail;
2015
2016       if (gdk_event_translate (event, &xevent))
2017         {
2018           ((GdkEventPrivate *)event)->flags &= ~GDK_EVENT_PENDING;
2019         }
2020       else
2021         {
2022           gdk_event_queue_remove_link (node);
2023           g_list_free_1 (node);
2024           gdk_event_free (event);
2025         }
2026     }
2027 }
2028
2029 static gboolean  
2030 gdk_event_prepare (gpointer  source_data, 
2031                    GTimeVal *current_time,
2032                    gint     *timeout,
2033                    gpointer  user_data)
2034 {
2035   gboolean retval;
2036   
2037   GDK_THREADS_ENTER ();
2038
2039   *timeout = -1;
2040
2041   retval = (gdk_event_queue_find_first () != NULL) || XPending (gdk_display);
2042
2043   GDK_THREADS_LEAVE ();
2044
2045   return retval;
2046 }
2047
2048 static gboolean  
2049 gdk_event_check (gpointer  source_data,
2050                  GTimeVal *current_time,
2051                  gpointer  user_data)
2052 {
2053   gboolean retval;
2054   
2055   GDK_THREADS_ENTER ();
2056
2057   if (event_poll_fd.revents & G_IO_IN)
2058     retval = (gdk_event_queue_find_first () != NULL) || XPending (gdk_display);
2059   else
2060     retval = FALSE;
2061
2062   GDK_THREADS_LEAVE ();
2063
2064   return retval;
2065 }
2066
2067 static GdkEvent*
2068 gdk_event_unqueue (void)
2069 {
2070   GdkEvent *event = NULL;
2071   GList *tmp_list;
2072
2073   tmp_list = gdk_event_queue_find_first ();
2074
2075   if (tmp_list)
2076     {
2077       event = tmp_list->data;
2078       gdk_event_queue_remove_link (tmp_list);
2079       g_list_free_1 (tmp_list);
2080     }
2081
2082   return event;
2083 }
2084
2085 static gboolean  
2086 gdk_event_dispatch (gpointer  source_data,
2087                     GTimeVal *current_time,
2088                     gpointer  user_data)
2089 {
2090   GdkEvent *event;
2091  
2092   GDK_THREADS_ENTER ();
2093
2094   gdk_events_queue();
2095   event = gdk_event_unqueue();
2096
2097   if (event)
2098     {
2099       if (event_func)
2100         (*event_func) (event, event_data);
2101       
2102       gdk_event_free (event);
2103     }
2104   
2105   GDK_THREADS_LEAVE ();
2106
2107   return TRUE;
2108 }
2109
2110 static void
2111 gdk_synthesize_click (GdkEvent *event,
2112                       gint      nclicks)
2113 {
2114   GdkEvent temp_event;
2115   
2116   g_return_if_fail (event != NULL);
2117   
2118   temp_event = *event;
2119   temp_event.type = (nclicks == 2) ? GDK_2BUTTON_PRESS : GDK_3BUTTON_PRESS;
2120   
2121   gdk_event_put (&temp_event);
2122 }
2123
2124 /* Sends a ClientMessage to all toplevel client windows */
2125 gboolean
2126 gdk_event_send_client_message (GdkEvent *event, guint32 xid)
2127 {
2128   XEvent sev;
2129   
2130   g_return_val_if_fail(event != NULL, FALSE);
2131   
2132   /* Set up our event to send, with the exception of its target window */
2133   sev.xclient.type = ClientMessage;
2134   sev.xclient.display = gdk_display;
2135   sev.xclient.format = event->client.data_format;
2136   sev.xclient.window = xid;
2137   memcpy(&sev.xclient.data, &event->client.data, sizeof(sev.xclient.data));
2138   sev.xclient.message_type = event->client.message_type;
2139   
2140   return gdk_send_xevent (xid, False, NoEventMask, &sev);
2141 }
2142
2143 /* Sends a ClientMessage to all toplevel client windows */
2144 gboolean
2145 gdk_event_send_client_message_to_all_recurse (XEvent  *xev, 
2146                                               guint32  xid,
2147                                               guint    level)
2148 {
2149   static GdkAtom wm_state_atom = GDK_NONE;
2150   Atom type = None;
2151   int format;
2152   unsigned long nitems, after;
2153   unsigned char *data;
2154   Window *ret_children, ret_root, ret_parent;
2155   unsigned int ret_nchildren;
2156   gint old_warnings = gdk_error_warnings;
2157   gboolean send = FALSE;
2158   gboolean found = FALSE;
2159   int i;
2160
2161   if (!wm_state_atom)
2162     wm_state_atom = gdk_atom_intern ("WM_STATE", FALSE);
2163
2164   gdk_error_warnings = FALSE;
2165   gdk_error_code = 0;
2166   XGetWindowProperty (gdk_display, xid, wm_state_atom, 0, 0, False, AnyPropertyType,
2167                       &type, &format, &nitems, &after, &data);
2168
2169   if (gdk_error_code)
2170     {
2171       gdk_error_warnings = old_warnings;
2172
2173       return FALSE;
2174     }
2175
2176   if (type)
2177     {
2178       send = TRUE;
2179       XFree (data);
2180     }
2181   else
2182     {
2183       /* OK, we're all set, now let's find some windows to send this to */
2184       if (XQueryTree (gdk_display, xid, &ret_root, &ret_parent,
2185                       &ret_children, &ret_nchildren) != True ||
2186           gdk_error_code)
2187         {
2188           gdk_error_warnings = old_warnings;
2189
2190           return FALSE;
2191         }
2192
2193       for(i = 0; i < ret_nchildren; i++)
2194         if (gdk_event_send_client_message_to_all_recurse (xev, ret_children[i], level + 1))
2195           found = TRUE;
2196
2197       XFree (ret_children);
2198     }
2199
2200   if (send || (!found && (level == 1)))
2201     {
2202       xev->xclient.window = xid;
2203       gdk_send_xevent (xid, False, NoEventMask, xev);
2204     }
2205
2206   gdk_error_warnings = old_warnings;
2207
2208   return (send || found);
2209 }
2210
2211 void
2212 gdk_event_send_clientmessage_toall (GdkEvent *event)
2213 {
2214   XEvent sev;
2215   gint old_warnings = gdk_error_warnings;
2216
2217   g_return_if_fail(event != NULL);
2218   
2219   /* Set up our event to send, with the exception of its target window */
2220   sev.xclient.type = ClientMessage;
2221   sev.xclient.display = gdk_display;
2222   sev.xclient.format = event->client.data_format;
2223   memcpy(&sev.xclient.data, &event->client.data, sizeof(sev.xclient.data));
2224   sev.xclient.message_type = event->client.message_type;
2225
2226   gdk_event_send_client_message_to_all_recurse(&sev, gdk_root_window, 0);
2227
2228   gdk_error_warnings = old_warnings;
2229 }
2230
2231 /*
2232  *--------------------------------------------------------------
2233  * gdk_flush
2234  *
2235  *   Flushes the Xlib output buffer and then waits
2236  *   until all requests have been received and processed
2237  *   by the X server. The only real use for this function
2238  *   is in dealing with XShm.
2239  *
2240  * Arguments:
2241  *
2242  * Results:
2243  *
2244  * Side effects:
2245  *
2246  *--------------------------------------------------------------
2247  */
2248
2249 void
2250 gdk_flush (void)
2251 {
2252   XSync (gdk_display, False);
2253 }
2254
2255