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