source: trunk/third/gtk/gdk/gdkevents.c @ 15781

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