]> Pileus Git - ~andy/gtk/blob - gdk/gdkevents.c
Throughout: assorted docs
[~andy/gtk] / gdk / gdkevents.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include "gdk.h"
28 #include "gdkinternals.h"
29
30 typedef struct _GdkIOClosure GdkIOClosure;
31 typedef struct _GdkEventPrivate GdkEventPrivate;
32
33 typedef enum
34 {
35   /* Following flag is set for events on the event queue during
36    * translation and cleared afterwards.
37    */
38   GDK_EVENT_PENDING = 1 << 0
39 } GdkEventFlags;
40
41 struct _GdkIOClosure
42 {
43   GdkInputFunction function;
44   GdkInputCondition condition;
45   GdkDestroyNotify notify;
46   gpointer data;
47 };
48
49 struct _GdkEventPrivate
50 {
51   GdkEvent event;
52   guint    flags;
53 };
54
55 /* Private variable declarations
56  */
57
58 static guint32 button_click_time[2] = { 0, 0}; /* The last 2 button click times. Used
59                                                 * to determine if the latest button click
60                                                 * is part of a double or triple click.
61                                                 */
62 static GdkWindow *button_window[2] = { NULL, NULL}; /* The last 2 windows to receive button presses.
63                                                      *  Also used to determine if the latest button
64                                                      *  click is part of a double or triple click.
65                                              */
66 static guint button_number[2] = { -1, -1 }; /* The last 2 buttons to be pressed.
67                                              */
68 GdkEventFunc   _gdk_event_func = NULL;    /* Callback for events */
69 gpointer       _gdk_event_data = NULL;
70 GDestroyNotify _gdk_event_notify = NULL;
71
72 static guint double_click_time = 250;
73 #define TRIPLE_CLICK_TIME      (2*double_click_time)
74 #define DOUBLE_CLICK_DIST      5
75 #define TRIPLE_CLICK_DIST      5
76
77 /*********************************************
78  * Functions for maintaining the event queue *
79  *********************************************/
80
81 /*************************************************************
82  * _gdk_event_queue_find_first:
83  *     Find the first event on the queue that is not still
84  *     being filled in.
85  *   arguments:
86  *     
87  *   results:
88  *     Pointer to the list node for that event, or NULL
89  *************************************************************/
90
91 GList*
92 _gdk_event_queue_find_first (void)
93 {
94   GList *tmp_list = _gdk_queued_events;
95
96   while (tmp_list)
97     {
98       GdkEventPrivate *event = tmp_list->data;
99       if (!(event->flags & GDK_EVENT_PENDING))
100         return tmp_list;
101
102       tmp_list = g_list_next (tmp_list);
103     }
104
105   return NULL;
106 }
107
108 /*************************************************************
109  * _gdk_event_queue_remove_link:
110  *     Remove a specified list node from the event queue.
111  *   arguments:
112  *     node: Node to remove.
113  *   results:
114  *************************************************************/
115
116 void
117 _gdk_event_queue_remove_link (GList *node)
118 {
119   if (node->prev)
120     node->prev->next = node->next;
121   else
122     _gdk_queued_events = node->next;
123   
124   if (node->next)
125     node->next->prev = node->prev;
126   else
127     _gdk_queued_tail = node->prev;
128 }
129
130 /*************************************************************
131  * _gdk_event_queue_append:
132  *     Append an event onto the tail of the event queue.
133  *   arguments:
134  *     event: Event to append.
135  *   results:
136  *************************************************************/
137
138 void
139 _gdk_event_queue_append (GdkEvent *event)
140 {
141   _gdk_queued_tail = g_list_append (_gdk_queued_tail, event);
142   
143   if (!_gdk_queued_events)
144     _gdk_queued_events = _gdk_queued_tail;
145   else
146     _gdk_queued_tail = _gdk_queued_tail->next;
147 }
148
149 /*************************************************************
150  * gdk_event_handler_set:
151  *     
152  *   arguments:
153  *     func: Callback function to be called for each event.
154  *     data: Data supplied to the function
155  *     notify: function called when function is no longer needed
156  * 
157  *   results:
158  *************************************************************/
159
160 void 
161 gdk_event_handler_set (GdkEventFunc   func,
162                        gpointer       data,
163                        GDestroyNotify notify)
164 {
165   if (_gdk_event_notify)
166     (*_gdk_event_notify) (_gdk_event_data);
167
168   _gdk_event_func = func;
169   _gdk_event_data = data;
170   _gdk_event_notify = notify;
171 }
172
173 /*
174  *--------------------------------------------------------------
175  * gdk_event_get
176  *
177  *   Gets the next event.
178  *
179  * Arguments:
180  *
181  * Results:
182  *   If an event is waiting that we care about, returns 
183  *   a pointer to that event, to be freed with gdk_event_free.
184  *   Otherwise, returns NULL.
185  *
186  * Side effects:
187  *
188  *--------------------------------------------------------------
189  */
190
191 GdkEvent*
192 gdk_event_get (void)
193 {
194   _gdk_events_queue ();
195
196   return _gdk_event_unqueue ();
197 }
198
199 /*
200  *--------------------------------------------------------------
201  * gdk_event_peek
202  *
203  *   Gets the next event.
204  *
205  * Arguments:
206  *
207  * Results:
208  *   If an event is waiting that we care about, returns 
209  *   a copy of that event, but does not remove it from
210  *   the queue. The pointer is to be freed with gdk_event_free.
211  *   Otherwise, returns NULL.
212  *
213  * Side effects:
214  *
215  *--------------------------------------------------------------
216  */
217
218 GdkEvent*
219 gdk_event_peek (void)
220 {
221   GList *tmp_list;
222
223   tmp_list = _gdk_event_queue_find_first ();
224   
225   if (tmp_list)
226     return gdk_event_copy (tmp_list->data);
227   else
228     return NULL;
229 }
230
231 void
232 gdk_event_put (GdkEvent *event)
233 {
234   GdkEvent *new_event;
235   
236   g_return_if_fail (event != NULL);
237   
238   new_event = gdk_event_copy (event);
239
240   _gdk_event_queue_append (new_event);
241 }
242
243 /*
244  *--------------------------------------------------------------
245  * gdk_event_copy
246  *
247  *   Copy a event structure into new storage.
248  *
249  * Arguments:
250  *   "event" is the event struct to copy.
251  *
252  * Results:
253  *   A new event structure.  Free it with gdk_event_free.
254  *
255  * Side effects:
256  *   The reference count of the window in the event is increased.
257  *
258  *--------------------------------------------------------------
259  */
260
261 static GMemChunk *event_chunk = NULL;
262
263 GdkEvent*
264 _gdk_event_new (void)
265 {
266   GdkEventPrivate *new_event;
267   
268   if (event_chunk == NULL)
269     event_chunk = g_mem_chunk_new ("events",
270                                    sizeof (GdkEventPrivate),
271                                    4096,
272                                    G_ALLOC_AND_FREE);
273   
274   new_event = g_chunk_new (GdkEventPrivate, event_chunk);
275   new_event->flags = 0;
276   
277   return (GdkEvent*) new_event;
278 }
279
280 GdkEvent*
281 gdk_event_copy (GdkEvent *event)
282 {
283   GdkEvent *new_event;
284   
285   g_return_val_if_fail (event != NULL, NULL);
286   
287   new_event = _gdk_event_new ();
288   
289   *new_event = *event;
290   if (new_event->any.window)
291     gdk_window_ref (new_event->any.window);
292   
293   switch (event->any.type)
294     {
295     case GDK_KEY_PRESS:
296     case GDK_KEY_RELEASE:
297       new_event->key.string = g_strdup (event->key.string);
298       break;
299       
300     case GDK_ENTER_NOTIFY:
301     case GDK_LEAVE_NOTIFY:
302       if (event->crossing.subwindow != NULL)
303         gdk_window_ref (event->crossing.subwindow);
304       break;
305       
306     case GDK_DRAG_ENTER:
307     case GDK_DRAG_LEAVE:
308     case GDK_DRAG_MOTION:
309     case GDK_DRAG_STATUS:
310     case GDK_DROP_START:
311     case GDK_DROP_FINISHED:
312       gdk_drag_context_ref (event->dnd.context);
313       break;
314       
315     case GDK_EXPOSE:
316       if (event->expose.region)
317         new_event->expose.region = gdk_region_copy (event->expose.region);
318       break;
319       
320     case GDK_SETTING:
321       new_event->setting.name = g_strdup (new_event->setting.name);
322       break;
323       
324     default:
325       break;
326     }
327   
328   return new_event;
329 }
330
331 /*
332  *--------------------------------------------------------------
333  * gdk_event_free
334  *
335  *   Free a event structure obtained from gdk_event_copy.  Do not use
336  *   with other event structures.
337  *
338  * Arguments:
339  *   "event" is the event struct to free.
340  *
341  * Results:
342  *
343  * Side effects:
344  *   The reference count of the window in the event is decreased and
345  *   might be freed, too.
346  *
347  *-------------------------------------------------------------- */
348
349 void
350 gdk_event_free (GdkEvent *event)
351 {
352   g_return_if_fail (event != NULL);
353
354   g_assert (event_chunk != NULL); /* paranoid */
355   
356   if (event->any.window)
357     gdk_window_unref (event->any.window);
358   
359   switch (event->any.type)
360     {
361     case GDK_KEY_PRESS:
362     case GDK_KEY_RELEASE:
363       g_free (event->key.string);
364       break;
365       
366     case GDK_ENTER_NOTIFY:
367     case GDK_LEAVE_NOTIFY:
368       if (event->crossing.subwindow != NULL)
369         gdk_window_unref (event->crossing.subwindow);
370       break;
371       
372     case GDK_DRAG_ENTER:
373     case GDK_DRAG_LEAVE:
374     case GDK_DRAG_MOTION:
375     case GDK_DRAG_STATUS:
376     case GDK_DROP_START:
377     case GDK_DROP_FINISHED:
378       gdk_drag_context_unref (event->dnd.context);
379       break;
380
381     case GDK_BUTTON_PRESS:
382     case GDK_BUTTON_RELEASE:
383       if (event->button.axes)
384         g_free (event->button.axes);
385       break;
386       
387     case GDK_EXPOSE:
388       if (event->expose.region)
389         gdk_region_destroy (event->expose.region);
390       break;
391       
392     case GDK_MOTION_NOTIFY:
393       if (event->motion.axes)
394         g_free (event->motion.axes);
395       break;
396       
397     case GDK_SETTING:
398       g_free (event->setting.name);
399       break;
400       
401     default:
402       break;
403     }
404   
405   g_mem_chunk_free (event_chunk, event);
406 }
407
408 /**
409  * gdk_event_get_time:
410  * @event: a #GdkEvent
411  * 
412  * Returns the time stamp from @event, if there is one; otherwise
413  * returns #GDK_CURRENT_TIME. If @event is %NULL, returns #GDK_CURRENT_TIME.
414  * 
415  * Return value: time stamp field from @event
416  **/
417 guint32
418 gdk_event_get_time (GdkEvent *event)
419 {
420   if (event)
421     switch (event->type)
422       {
423       case GDK_MOTION_NOTIFY:
424         return event->motion.time;
425       case GDK_BUTTON_PRESS:
426       case GDK_2BUTTON_PRESS:
427       case GDK_3BUTTON_PRESS:
428       case GDK_BUTTON_RELEASE:
429         return event->button.time;
430       case GDK_SCROLL:
431         return event->scroll.time;
432       case GDK_KEY_PRESS:
433       case GDK_KEY_RELEASE:
434         return event->key.time;
435       case GDK_ENTER_NOTIFY:
436       case GDK_LEAVE_NOTIFY:
437         return event->crossing.time;
438       case GDK_PROPERTY_NOTIFY:
439         return event->property.time;
440       case GDK_SELECTION_CLEAR:
441       case GDK_SELECTION_REQUEST:
442       case GDK_SELECTION_NOTIFY:
443         return event->selection.time;
444       case GDK_PROXIMITY_IN:
445       case GDK_PROXIMITY_OUT:
446         return event->proximity.time;
447       case GDK_DRAG_ENTER:
448       case GDK_DRAG_LEAVE:
449       case GDK_DRAG_MOTION:
450       case GDK_DRAG_STATUS:
451       case GDK_DROP_START:
452       case GDK_DROP_FINISHED:
453         return event->dnd.time;
454       case GDK_CLIENT_EVENT:
455       case GDK_VISIBILITY_NOTIFY:
456       case GDK_NO_EXPOSE:
457       case GDK_CONFIGURE:
458       case GDK_FOCUS_CHANGE:
459       case GDK_NOTHING:
460       case GDK_DELETE:
461       case GDK_DESTROY:
462       case GDK_EXPOSE:
463       case GDK_MAP:
464       case GDK_UNMAP:
465       case GDK_WINDOW_STATE:
466       case GDK_SETTING:
467         /* return current time */
468         break;
469       }
470   
471   return GDK_CURRENT_TIME;
472 }
473
474 /**
475  * gdk_event_get_state:
476  * @event: a #GdkEvent or NULL
477  * @state: return location for state
478  * 
479  * If the event contains a "state" field, puts that field in @state. Otherwise
480  * stores an empty state (0). Returns %TRUE if there was a state field
481  * in the event. @event may be %NULL, in which case it's treated
482  * as if the event had no state field.
483  * 
484  * Return value: %TRUE if there was a state field in the event 
485  **/
486 gboolean
487 gdk_event_get_state (GdkEvent        *event,
488                      GdkModifierType *state)
489 {
490   g_return_val_if_fail (state != NULL, FALSE);
491   
492   if (event)
493     switch (event->type)
494       {
495       case GDK_MOTION_NOTIFY:
496         *state = event->motion.state;
497         return TRUE;
498       case GDK_BUTTON_PRESS:
499       case GDK_2BUTTON_PRESS:
500       case GDK_3BUTTON_PRESS:
501       case GDK_BUTTON_RELEASE:
502         *state =  event->button.state;
503         return TRUE;
504       case GDK_SCROLL:
505         *state =  event->scroll.state;
506         return TRUE;
507       case GDK_KEY_PRESS:
508       case GDK_KEY_RELEASE:
509         *state =  event->key.state;
510         return TRUE;
511       case GDK_ENTER_NOTIFY:
512       case GDK_LEAVE_NOTIFY:
513         *state =  event->crossing.state;
514         return TRUE;
515       case GDK_PROPERTY_NOTIFY:
516         *state =  event->property.state;
517         return TRUE;
518       case GDK_VISIBILITY_NOTIFY:
519         *state =  event->visibility.state;
520         return TRUE;
521       case GDK_CLIENT_EVENT:
522       case GDK_NO_EXPOSE:
523       case GDK_CONFIGURE:
524       case GDK_FOCUS_CHANGE:
525       case GDK_SELECTION_CLEAR:
526       case GDK_SELECTION_REQUEST:
527       case GDK_SELECTION_NOTIFY:
528       case GDK_PROXIMITY_IN:
529       case GDK_PROXIMITY_OUT:
530       case GDK_DRAG_ENTER:
531       case GDK_DRAG_LEAVE:
532       case GDK_DRAG_MOTION:
533       case GDK_DRAG_STATUS:
534       case GDK_DROP_START:
535       case GDK_DROP_FINISHED:
536       case GDK_NOTHING:
537       case GDK_DELETE:
538       case GDK_DESTROY:
539       case GDK_EXPOSE:
540       case GDK_MAP:
541       case GDK_UNMAP:
542       case GDK_WINDOW_STATE:
543       case GDK_SETTING:
544         /* no state field */
545         break;
546       }
547
548   *state = 0;
549   return FALSE;
550 }
551
552 /**
553  * gdk_event_get_coords:
554  * @event: a #GdkEvent
555  * @x_root: location to put event window x coordinate
556  * @y_root: location to put event window y coordinate
557  * 
558  * Extract the event window relative x/y coordinates from an event.
559  * 
560  * Return value: %TRUE if the event delivered event window coordinates
561  **/
562 gboolean
563 gdk_event_get_coords (GdkEvent *event,
564                       gdouble  *x_win,
565                       gdouble  *y_win)
566 {
567   gdouble x = 0, y = 0;
568   gboolean fetched = TRUE;
569   
570   g_return_val_if_fail (event != NULL, FALSE);
571
572   switch (event->type)
573     {
574     case GDK_CONFIGURE:
575       x = event->configure.x;
576       y = event->configure.y;
577       break;
578     case GDK_ENTER_NOTIFY:
579     case GDK_LEAVE_NOTIFY:
580       x = event->crossing.x;
581       y = event->crossing.y;
582       break;
583     case GDK_SCROLL:
584       x = event->scroll.x;
585       y = event->scroll.y;
586       break;
587     case GDK_BUTTON_PRESS:
588     case GDK_2BUTTON_PRESS:
589     case GDK_3BUTTON_PRESS:
590     case GDK_BUTTON_RELEASE:
591       x = event->button.x;
592       y = event->button.y;
593       break;
594     case GDK_MOTION_NOTIFY:
595       x = event->motion.x;
596       y = event->motion.y;
597       break;
598     default:
599       fetched = FALSE;
600       break;
601     }
602
603   if (x_win)
604     *x_win = x;
605   if (y_win)
606     *y_win = x;
607
608   return fetched;
609 }
610
611 /**
612  * gdk_event_get_root_coords:
613  * @event: a #GdkEvent
614  * @x_root: location to put root window x coordinate
615  * @y_root: location to put root window y coordinate
616  * 
617  * Extract the root window relative x/y coordinates from an event.
618  * 
619  * Return value: %TRUE if the event delivered root window coordinates
620  **/
621 gboolean
622 gdk_event_get_root_coords (GdkEvent *event,
623                            gdouble  *x_root,
624                            gdouble  *y_root)
625 {
626   gdouble x = 0, y = 0;
627   gboolean fetched = TRUE;
628   
629   g_return_val_if_fail (event != NULL, FALSE);
630
631   switch (event->type)
632     {
633     case GDK_MOTION_NOTIFY:
634       x = event->motion.x_root;
635       y = event->motion.y_root;
636       break;
637     case GDK_BUTTON_PRESS:
638     case GDK_2BUTTON_PRESS:
639     case GDK_3BUTTON_PRESS:
640     case GDK_BUTTON_RELEASE:
641       x = event->button.x_root;
642       y = event->button.y_root;
643       break;
644     case GDK_ENTER_NOTIFY:
645     case GDK_LEAVE_NOTIFY:
646       x = event->crossing.x_root;
647       y = event->crossing.y_root;
648       break;
649     case GDK_DRAG_ENTER:
650     case GDK_DRAG_LEAVE:
651     case GDK_DRAG_MOTION:
652     case GDK_DRAG_STATUS:
653     case GDK_DROP_START:
654     case GDK_DROP_FINISHED:
655       x = event->dnd.x_root;
656       y = event->dnd.y_root;
657       break;
658     default:
659       fetched = FALSE;
660       break;
661     }
662
663   if (x_root)
664     *x_root = x;
665   if (y_root)
666     *y_root = x;
667
668   return fetched;
669 }
670
671 /**
672  * gdk_event_get_axis:
673  * @event: a #GdkEvent
674  * @axis_use: the axis use to look for
675  * @value: location to store the value found
676  * 
677  * Extract the axis value for a particular axis use from
678  * an event structure.
679  * 
680  * Return value: %TRUE if the specified axis was found, otherwise %FALSE
681  **/
682 gboolean
683 gdk_event_get_axis (GdkEvent   *event,
684                     GdkAxisUse  axis_use,
685                     gdouble    *value)
686 {
687   gdouble *axes;
688   GdkDevice *device;
689   
690   g_return_val_if_fail (event != NULL, FALSE);
691   
692   if (axis_use == GDK_AXIS_X || axis_use == GDK_AXIS_Y)
693     {
694       gdouble x, y;
695       
696       switch (event->type)
697         {
698         case GDK_MOTION_NOTIFY:
699           x = event->motion.x;
700           y = event->motion.y;
701           break;
702         case GDK_SCROLL:
703           x = event->scroll.x;
704           y = event->scroll.y;
705           break;
706         case GDK_BUTTON_PRESS:
707         case GDK_BUTTON_RELEASE:
708           x = event->button.x;
709           y = event->button.y;
710           break;
711         case GDK_ENTER_NOTIFY:
712         case GDK_LEAVE_NOTIFY:
713           x = event->crossing.x;
714           y = event->crossing.y;
715           break;
716           
717         default:
718           return FALSE;
719         }
720
721       if (axis_use == GDK_AXIS_X && value)
722         *value = x;
723       if (axis_use == GDK_AXIS_Y && value)
724         *value = y;
725
726       return TRUE;
727     }
728   else if (event->type == GDK_BUTTON_PRESS ||
729            event->type == GDK_BUTTON_RELEASE)
730     {
731       device = event->button.device;
732       axes = event->button.axes;
733     }
734   else if (event->type == GDK_MOTION_NOTIFY)
735     {
736       device = event->motion.device;
737       axes = event->motion.axes;
738     }
739   else
740     return FALSE;
741
742   return gdk_device_get_axis (device, axes, axis_use, value);
743 }
744
745 /*
746  *--------------------------------------------------------------
747  * gdk_set_show_events
748  *
749  *   Turns on/off the showing of events.
750  *
751  * Arguments:
752  *   "show_events" is a boolean describing whether or
753  *   not to show the events gdk receives.
754  *
755  * Results:
756  *
757  * Side effects:
758  *   When "show_events" is TRUE, calls to "gdk_event_get"
759  *   will output debugging informatin regarding the event
760  *   received to stdout.
761  *
762  *--------------------------------------------------------------
763  */
764
765 void
766 gdk_set_show_events (gboolean show_events)
767 {
768   if (show_events)
769     _gdk_debug_flags |= GDK_DEBUG_EVENTS;
770   else
771     _gdk_debug_flags &= ~GDK_DEBUG_EVENTS;
772 }
773
774 gboolean
775 gdk_get_show_events (void)
776 {
777   return (_gdk_debug_flags & GDK_DEBUG_EVENTS) != 0;
778 }
779
780 static void
781 gdk_io_destroy (gpointer data)
782 {
783   GdkIOClosure *closure = data;
784
785   if (closure->notify)
786     closure->notify (closure->data);
787
788   g_free (closure);
789 }
790
791 /* What do we do with G_IO_NVAL?
792  */
793 #define READ_CONDITION (G_IO_IN | G_IO_HUP | G_IO_ERR)
794 #define WRITE_CONDITION (G_IO_OUT | G_IO_ERR)
795 #define EXCEPTION_CONDITION (G_IO_PRI)
796
797 static gboolean  
798 gdk_io_invoke (GIOChannel   *source,
799                GIOCondition  condition,
800                gpointer      data)
801 {
802   GdkIOClosure *closure = data;
803   GdkInputCondition gdk_cond = 0;
804
805   if (condition & READ_CONDITION)
806     gdk_cond |= GDK_INPUT_READ;
807   if (condition & WRITE_CONDITION)
808     gdk_cond |= GDK_INPUT_WRITE;
809   if (condition & EXCEPTION_CONDITION)
810     gdk_cond |= GDK_INPUT_EXCEPTION;
811
812   if (closure->condition & gdk_cond)
813     closure->function (closure->data, g_io_channel_unix_get_fd (source), gdk_cond);
814
815   return TRUE;
816 }
817
818 gint
819 gdk_input_add_full (gint              source,
820                     GdkInputCondition condition,
821                     GdkInputFunction  function,
822                     gpointer          data,
823                     GdkDestroyNotify  destroy)
824 {
825   guint result;
826   GdkIOClosure *closure = g_new (GdkIOClosure, 1);
827   GIOChannel *channel;
828   GIOCondition cond = 0;
829
830   closure->function = function;
831   closure->condition = condition;
832   closure->notify = destroy;
833   closure->data = data;
834
835   if (condition & GDK_INPUT_READ)
836     cond |= READ_CONDITION;
837   if (condition & GDK_INPUT_WRITE)
838     cond |= WRITE_CONDITION;
839   if (condition & GDK_INPUT_EXCEPTION)
840     cond |= EXCEPTION_CONDITION;
841
842   channel = g_io_channel_unix_new (source);
843   result = g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, cond, 
844                                 gdk_io_invoke,
845                                 closure, gdk_io_destroy);
846   g_io_channel_unref (channel);
847
848   return result;
849 }
850
851 gint
852 gdk_input_add (gint              source,
853                GdkInputCondition condition,
854                GdkInputFunction  function,
855                gpointer          data)
856 {
857   return gdk_input_add_full (source, condition, function, data, NULL);
858 }
859
860 void
861 gdk_input_remove (gint tag)
862 {
863   g_source_remove (tag);
864 }
865
866 GdkEvent*
867 _gdk_event_unqueue (void)
868 {
869   GdkEvent *event = NULL;
870   GList *tmp_list;
871
872   tmp_list = _gdk_event_queue_find_first ();
873
874   if (tmp_list)
875     {
876       event = tmp_list->data;
877       _gdk_event_queue_remove_link (tmp_list);
878       g_list_free_1 (tmp_list);
879     }
880
881   return event;
882 }
883
884 void
885 gdk_synthesize_click (GdkEvent *event,
886                       gint      nclicks)
887 {
888   GdkEvent temp_event;
889   
890   g_return_if_fail (event != NULL);
891   
892   temp_event = *event;
893   temp_event.type = (nclicks == 2) ? GDK_2BUTTON_PRESS : GDK_3BUTTON_PRESS;
894   
895   gdk_event_put (&temp_event);
896 }
897
898 void
899 _gdk_event_button_generate (GdkEvent *event)
900 {
901   if ((event->button.time < (button_click_time[1] + TRIPLE_CLICK_TIME)) &&
902       (event->button.window == button_window[1]) &&
903       (event->button.button == button_number[1]))
904     {
905       gdk_synthesize_click (event, 3);
906       
907       button_click_time[1] = 0;
908       button_click_time[0] = 0;
909       button_window[1] = NULL;
910       button_window[0] = 0;
911       button_number[1] = -1;
912       button_number[0] = -1;
913     }
914   else if ((event->button.time < (button_click_time[0] + double_click_time)) &&
915            (event->button.window == button_window[0]) &&
916            (event->button.button == button_number[0]))
917     {
918       gdk_synthesize_click (event, 2);
919       
920       button_click_time[1] = button_click_time[0];
921       button_click_time[0] = event->button.time;
922       button_window[1] = button_window[0];
923       button_window[0] = event->button.window;
924       button_number[1] = button_number[0];
925       button_number[0] = event->button.button;
926     }
927   else
928     {
929       button_click_time[1] = 0;
930       button_click_time[0] = event->button.time;
931       button_window[1] = NULL;
932       button_window[0] = event->button.window;
933       button_number[1] = -1;
934       button_number[0] = event->button.button;
935     }
936 }
937
938
939 void
940 gdk_synthesize_window_state (GdkWindow     *window,
941                              GdkWindowState unset_flags,
942                              GdkWindowState set_flags)
943 {
944   GdkEventWindowState temp_event;
945   GdkWindowState old;
946   
947   g_return_if_fail (window != NULL);
948   
949   temp_event.window = window;
950   temp_event.type = GDK_WINDOW_STATE;
951   temp_event.send_event = FALSE;
952   
953   old = ((GdkWindowObject*) temp_event.window)->state;
954   
955   temp_event.changed_mask = (unset_flags | set_flags) ^ old;
956   temp_event.new_window_state = old;
957   temp_event.new_window_state |= set_flags;
958   temp_event.new_window_state &= ~unset_flags;
959
960   if (temp_event.new_window_state == old)
961     return; /* No actual work to do, nothing changed. */
962
963   /* Actually update the field in GdkWindow, this is sort of an odd
964    * place to do it, but seems like the safest since it ensures we expose no
965    * inconsistent state to the user.
966    */
967   
968   ((GdkWindowObject*) window)->state = temp_event.new_window_state;
969
970   /* We only really send the event to toplevels, since
971    * all the window states don't apply to non-toplevels.
972    * Non-toplevels do use the GDK_WINDOW_STATE_WITHDRAWN flag
973    * internally so we needed to update window->state.
974    */
975   switch (((GdkWindowObject*) window)->window_type)
976     {
977     case GDK_WINDOW_TOPLEVEL:
978     case GDK_WINDOW_DIALOG:
979     case GDK_WINDOW_TEMP: /* ? */
980       gdk_event_put ((GdkEvent*) &temp_event);
981       break;
982       
983     case GDK_WINDOW_FOREIGN:
984     case GDK_WINDOW_ROOT:
985     case GDK_WINDOW_CHILD:
986       break;
987     }
988 }
989
990 /**
991  * gdk_set_double_click_time:
992  * @msec: double click time in milliseconds (thousandths of a second)
993  *
994  * Sets the double click time (two clicks within this time interval
995  * count as a double click and result in a #GDK_2BUTTON_PRESS event).
996  * Applications should NOT set this, it is a global user-configured setting.
997  *
998  **/
999 void
1000 gdk_set_double_click_time (guint msec)
1001 {
1002   double_click_time = msec;
1003 }
1004
1005 GType
1006 gdk_event_get_type (void)
1007 {
1008   static GType our_type = 0;
1009   
1010   if (our_type == 0)
1011     our_type = g_boxed_type_register_static ("GdkEvent",
1012                                              (GBoxedCopyFunc)gdk_event_copy,
1013                                              (GBoxedFreeFunc)gdk_event_free);
1014   return our_type;
1015 }
1016
1017 GdkDevice *
1018 gdk_device_get_core_pointer (void)
1019 {
1020   return _gdk_core_pointer;
1021 }