]> Pileus Git - ~andy/gtk/blob - gdk/gdkevents.c
Document expose event->region change and that gtk_widget_event doesn't
[~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 #define DOUBLE_CLICK_TIME      250
34 #define TRIPLE_CLICK_TIME      500
35 #define DOUBLE_CLICK_DIST      5
36 #define TRIPLE_CLICK_DIST      5
37
38 typedef enum
39 {
40   /* Following flag is set for events on the event queue during
41    * translation and cleared afterwards.
42    */
43   GDK_EVENT_PENDING = 1 << 0
44 } GdkEventFlags;
45
46 struct _GdkIOClosure
47 {
48   GdkInputFunction function;
49   GdkInputCondition condition;
50   GdkDestroyNotify notify;
51   gpointer data;
52 };
53
54 struct _GdkEventPrivate
55 {
56   GdkEvent event;
57   guint    flags;
58 };
59
60 /* 
61  * Private function declarations
62  */
63
64 GdkFilterReturn gdk_wm_protocols_filter (GdkXEvent *xev,
65                                          GdkEvent  *event,
66                                          gpointer   data);
67
68 /* Private variable declarations
69  */
70
71 static guint32 button_click_time[2] = { 0, 0}; /* The last 2 button click times. Used
72                                                 * to determine if the latest button click
73                                                 * is part of a double or triple click.
74                                                 */
75 static GdkWindow *button_window[2] = { NULL, NULL}; /* The last 2 windows to receive button presses.
76                                                      *  Also used to determine if the latest button
77                                                      *  click is part of a double or triple click.
78                                              */
79 static guint button_number[2] = { -1, -1 }; /* The last 2 buttons to be pressed.
80                                              */
81 GdkEventFunc   gdk_event_func = NULL;    /* Callback for events */
82 gpointer       gdk_event_data = NULL;
83 GDestroyNotify gdk_event_notify = NULL;
84
85 GPollFD event_poll_fd;
86
87 /*********************************************
88  * Functions for maintaining the event queue *
89  *********************************************/
90
91 /*************************************************************
92  * gdk_event_queue_find_first:
93  *     Find the first event on the queue that is not still
94  *     being filled in.
95  *   arguments:
96  *     
97  *   results:
98  *     Pointer to the list node for that event, or NULL
99  *************************************************************/
100
101 GList*
102 gdk_event_queue_find_first (void)
103 {
104   GList *tmp_list = gdk_queued_events;
105
106   while (tmp_list)
107     {
108       GdkEventPrivate *event = tmp_list->data;
109       if (!(event->flags & GDK_EVENT_PENDING))
110         return tmp_list;
111
112       tmp_list = g_list_next (tmp_list);
113     }
114
115   return NULL;
116 }
117
118 /*************************************************************
119  * gdk_event_queue_remove_link:
120  *     Remove a specified list node from the event queue.
121  *   arguments:
122  *     node: Node to remove.
123  *   results:
124  *************************************************************/
125
126 void
127 gdk_event_queue_remove_link (GList *node)
128 {
129   if (node->prev)
130     node->prev->next = node->next;
131   else
132     gdk_queued_events = node->next;
133   
134   if (node->next)
135     node->next->prev = node->prev;
136   else
137     gdk_queued_tail = node->prev;
138 }
139
140 /*************************************************************
141  * gdk_event_queue_append:
142  *     Append an event onto the tail of the event queue.
143  *   arguments:
144  *     event: Event to append.
145  *   results:
146  *************************************************************/
147
148 void
149 gdk_event_queue_append (GdkEvent *event)
150 {
151   gdk_queued_tail = g_list_append (gdk_queued_tail, event);
152   
153   if (!gdk_queued_events)
154     gdk_queued_events = gdk_queued_tail;
155   else
156     gdk_queued_tail = gdk_queued_tail->next;
157 }
158
159 /*************************************************************
160  * gdk_event_handler_set:
161  *     
162  *   arguments:
163  *     func: Callback function to be called for each event.
164  *     data: Data supplied to the function
165  *     notify: function called when function is no longer needed
166  * 
167  *   results:
168  *************************************************************/
169
170 void 
171 gdk_event_handler_set (GdkEventFunc   func,
172                        gpointer       data,
173                        GDestroyNotify notify)
174 {
175   if (gdk_event_notify)
176     (*gdk_event_notify) (gdk_event_data);
177
178   gdk_event_func = func;
179   gdk_event_data = data;
180   gdk_event_notify = notify;
181 }
182
183 /*
184  *--------------------------------------------------------------
185  * gdk_event_get
186  *
187  *   Gets the next event.
188  *
189  * Arguments:
190  *
191  * Results:
192  *   If an event is waiting that we care about, returns 
193  *   a pointer to that event, to be freed with gdk_event_free.
194  *   Otherwise, returns NULL.
195  *
196  * Side effects:
197  *
198  *--------------------------------------------------------------
199  */
200
201 GdkEvent*
202 gdk_event_get (void)
203 {
204   gdk_events_queue ();
205
206   return gdk_event_unqueue ();
207 }
208
209 /*
210  *--------------------------------------------------------------
211  * gdk_event_peek
212  *
213  *   Gets the next event.
214  *
215  * Arguments:
216  *
217  * Results:
218  *   If an event is waiting that we care about, returns 
219  *   a copy of that event, but does not remove it from
220  *   the queue. The pointer is to be freed with gdk_event_free.
221  *   Otherwise, returns NULL.
222  *
223  * Side effects:
224  *
225  *--------------------------------------------------------------
226  */
227
228 GdkEvent*
229 gdk_event_peek (void)
230 {
231   GList *tmp_list;
232
233   tmp_list = gdk_event_queue_find_first ();
234   
235   if (tmp_list)
236     return gdk_event_copy (tmp_list->data);
237   else
238     return NULL;
239 }
240
241 void
242 gdk_event_put (GdkEvent *event)
243 {
244   GdkEvent *new_event;
245   
246   g_return_if_fail (event != NULL);
247   
248   new_event = gdk_event_copy (event);
249
250   gdk_event_queue_append (new_event);
251 }
252
253 /*
254  *--------------------------------------------------------------
255  * gdk_event_copy
256  *
257  *   Copy a event structure into new storage.
258  *
259  * Arguments:
260  *   "event" is the event struct to copy.
261  *
262  * Results:
263  *   A new event structure.  Free it with gdk_event_free.
264  *
265  * Side effects:
266  *   The reference count of the window in the event is increased.
267  *
268  *--------------------------------------------------------------
269  */
270
271 static GMemChunk *event_chunk = NULL;
272
273 GdkEvent*
274 gdk_event_new (void)
275 {
276   GdkEventPrivate *new_event;
277   
278   if (event_chunk == NULL)
279     event_chunk = g_mem_chunk_new ("events",
280                                    sizeof (GdkEventPrivate),
281                                    4096,
282                                    G_ALLOC_AND_FREE);
283   
284   new_event = g_chunk_new (GdkEventPrivate, event_chunk);
285   new_event->flags = 0;
286   
287   return (GdkEvent*) new_event;
288 }
289
290 GdkEvent*
291 gdk_event_copy (GdkEvent *event)
292 {
293   GdkEvent *new_event;
294   
295   g_return_val_if_fail (event != NULL, NULL);
296   
297   new_event = gdk_event_new ();
298   
299   *new_event = *event;
300   if (new_event->any.window)
301     gdk_window_ref (new_event->any.window);
302   
303   switch (event->any.type)
304     {
305     case GDK_KEY_PRESS:
306     case GDK_KEY_RELEASE:
307       new_event->key.string = g_strdup (event->key.string);
308       break;
309       
310     case GDK_ENTER_NOTIFY:
311     case GDK_LEAVE_NOTIFY:
312       if (event->crossing.subwindow != NULL)
313         gdk_window_ref (event->crossing.subwindow);
314       break;
315       
316     case GDK_DRAG_ENTER:
317     case GDK_DRAG_LEAVE:
318     case GDK_DRAG_MOTION:
319     case GDK_DRAG_STATUS:
320     case GDK_DROP_START:
321     case GDK_DROP_FINISHED:
322       gdk_drag_context_ref (event->dnd.context);
323       break;
324       
325     case GDK_EXPOSE:
326       if (event->expose.region)
327         new_event->expose.region = gdk_region_copy (event->expose.region);
328       break;
329    default:
330       break;
331     }
332   
333   return new_event;
334 }
335
336 /*
337  *--------------------------------------------------------------
338  * gdk_event_free
339  *
340  *   Free a event structure obtained from gdk_event_copy.  Do not use
341  *   with other event structures.
342  *
343  * Arguments:
344  *   "event" is the event struct to free.
345  *
346  * Results:
347  *
348  * Side effects:
349  *   The reference count of the window in the event is decreased and
350  *   might be freed, too.
351  *
352  *-------------------------------------------------------------- */
353
354 void
355 gdk_event_free (GdkEvent *event)
356 {
357   g_return_if_fail (event != NULL);
358
359   g_assert (event_chunk != NULL); /* paranoid */
360   
361   if (event->any.window)
362     gdk_window_unref (event->any.window);
363   
364   switch (event->any.type)
365     {
366     case GDK_KEY_PRESS:
367     case GDK_KEY_RELEASE:
368       g_free (event->key.string);
369       break;
370       
371     case GDK_ENTER_NOTIFY:
372     case GDK_LEAVE_NOTIFY:
373       if (event->crossing.subwindow != NULL)
374         gdk_window_unref (event->crossing.subwindow);
375       break;
376       
377     case GDK_DRAG_ENTER:
378     case GDK_DRAG_LEAVE:
379     case GDK_DRAG_MOTION:
380     case GDK_DRAG_STATUS:
381     case GDK_DROP_START:
382     case GDK_DROP_FINISHED:
383       gdk_drag_context_unref (event->dnd.context);
384       break;
385
386     case GDK_BUTTON_PRESS:
387     case GDK_BUTTON_RELEASE:
388       if (event->button.axes)
389         g_free (event->button.axes);
390       break;
391       
392     case GDK_EXPOSE:
393       if (event->expose.region)
394         gdk_region_destroy (event->expose.region);
395       break;
396       
397     case GDK_MOTION_NOTIFY:
398       if (event->motion.axes)
399         g_free (event->motion.axes);
400       break;
401       
402     default:
403       break;
404     }
405   
406   g_mem_chunk_free (event_chunk, event);
407 }
408
409 /**
410  * gdk_event_get_time:
411  * @event: a #GdkEvent
412  * 
413  * Returns the time stamp from @event, if there is one; otherwise
414  * returns #GDK_CURRENT_TIME. If @event is %NULL, returns #GDK_CURRENT_TIME.
415  * 
416  * Return value: time stamp field from @event
417  **/
418 guint32
419 gdk_event_get_time (GdkEvent *event)
420 {
421   if (event)
422     switch (event->type)
423       {
424       case GDK_MOTION_NOTIFY:
425         return event->motion.time;
426       case GDK_BUTTON_PRESS:
427       case GDK_2BUTTON_PRESS:
428       case GDK_3BUTTON_PRESS:
429       case GDK_BUTTON_RELEASE:
430         return event->button.time;
431       case GDK_SCROLL:
432         return event->scroll.time;
433       case GDK_KEY_PRESS:
434       case GDK_KEY_RELEASE:
435         return event->key.time;
436       case GDK_ENTER_NOTIFY:
437       case GDK_LEAVE_NOTIFY:
438         return event->crossing.time;
439       case GDK_PROPERTY_NOTIFY:
440         return event->property.time;
441       case GDK_SELECTION_CLEAR:
442       case GDK_SELECTION_REQUEST:
443       case GDK_SELECTION_NOTIFY:
444         return event->selection.time;
445       case GDK_PROXIMITY_IN:
446       case GDK_PROXIMITY_OUT:
447         return event->proximity.time;
448       case GDK_DRAG_ENTER:
449       case GDK_DRAG_LEAVE:
450       case GDK_DRAG_MOTION:
451       case GDK_DRAG_STATUS:
452       case GDK_DROP_START:
453       case GDK_DROP_FINISHED:
454         return event->dnd.time;
455       case GDK_CLIENT_EVENT:
456       case GDK_VISIBILITY_NOTIFY:
457       case GDK_NO_EXPOSE:
458       case GDK_CONFIGURE:
459       case GDK_FOCUS_CHANGE:
460       case GDK_NOTHING:
461       case GDK_DELETE:
462       case GDK_DESTROY:
463       case GDK_EXPOSE:
464       case GDK_MAP:
465       case GDK_UNMAP:
466       case GDK_WINDOW_STATE:
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         /* no state field */
544         break;
545       }
546
547   *state = 0;
548   return FALSE;
549 }
550
551 /**
552  * gdk_event_get_axis:
553  * @event: a #GdkEvent
554  * @axis_use: the axis use to look for
555  * @value: location to store the value found
556  * 
557  * Extract the axis value for a particular axis use from
558  * an event structure.
559  * 
560  * Return value: %TRUE if the specified axis was found, otherwise %FALSE
561  **/
562 gboolean
563 gdk_event_get_axis (GdkEvent   *event,
564                     GdkAxisUse  axis_use,
565                     gdouble    *value)
566 {
567   gdouble *axes;
568   GdkDevice *device;
569   
570   g_return_val_if_fail (event != NULL, FALSE);
571   
572   if (axis_use == GDK_AXIS_X || axis_use == GDK_AXIS_Y)
573     {
574       gdouble x, y;
575       
576       switch (event->type)
577         {
578         case GDK_MOTION_NOTIFY:
579           x = event->motion.x;
580           y = event->motion.y;
581           break;
582         case GDK_SCROLL:
583           x = event->scroll.x;
584           y = event->scroll.y;
585           break;
586         case GDK_BUTTON_PRESS:
587         case GDK_BUTTON_RELEASE:
588           x = event->button.x;
589           y = event->button.y;
590           break;
591         case GDK_ENTER_NOTIFY:
592         case GDK_LEAVE_NOTIFY:
593           x = event->crossing.x;
594           y = event->crossing.y;
595           break;
596           
597         default:
598           return FALSE;
599         }
600
601       if (axis_use == GDK_AXIS_X && value)
602         *value = x;
603       if (axis_use == GDK_AXIS_Y && value)
604         *value = y;
605
606       return TRUE;
607     }
608   else if (event->type == GDK_BUTTON_PRESS ||
609            event->type == GDK_BUTTON_RELEASE)
610     {
611       device = event->button.device;
612       axes = event->button.axes;
613     }
614   else if (event->type == GDK_MOTION_NOTIFY)
615     {
616       device = event->motion.device;
617       axes = event->motion.axes;
618     }
619   else
620     return FALSE;
621
622   return gdk_device_get_axis (device, axes, axis_use, value);
623 }
624
625 /*
626  *--------------------------------------------------------------
627  * gdk_set_show_events
628  *
629  *   Turns on/off the showing of events.
630  *
631  * Arguments:
632  *   "show_events" is a boolean describing whether or
633  *   not to show the events gdk receives.
634  *
635  * Results:
636  *
637  * Side effects:
638  *   When "show_events" is TRUE, calls to "gdk_event_get"
639  *   will output debugging informatin regarding the event
640  *   received to stdout.
641  *
642  *--------------------------------------------------------------
643  */
644
645 void
646 gdk_set_show_events (gboolean show_events)
647 {
648   if (show_events)
649     gdk_debug_flags |= GDK_DEBUG_EVENTS;
650   else
651     gdk_debug_flags &= ~GDK_DEBUG_EVENTS;
652 }
653
654 gboolean
655 gdk_get_show_events (void)
656 {
657   return (gdk_debug_flags & GDK_DEBUG_EVENTS) != 0;
658 }
659
660 static void
661 gdk_io_destroy (gpointer data)
662 {
663   GdkIOClosure *closure = data;
664
665   if (closure->notify)
666     closure->notify (closure->data);
667
668   g_free (closure);
669 }
670
671 /* What do we do with G_IO_NVAL?
672  */
673 #define READ_CONDITION (G_IO_IN | G_IO_HUP | G_IO_ERR)
674 #define WRITE_CONDITION (G_IO_OUT | G_IO_ERR)
675 #define EXCEPTION_CONDITION (G_IO_PRI)
676
677 static gboolean  
678 gdk_io_invoke (GIOChannel   *source,
679                GIOCondition  condition,
680                gpointer      data)
681 {
682   GdkIOClosure *closure = data;
683   GdkInputCondition gdk_cond = 0;
684
685   if (condition & READ_CONDITION)
686     gdk_cond |= GDK_INPUT_READ;
687   if (condition & WRITE_CONDITION)
688     gdk_cond |= GDK_INPUT_WRITE;
689   if (condition & EXCEPTION_CONDITION)
690     gdk_cond |= GDK_INPUT_EXCEPTION;
691
692   if (closure->condition & gdk_cond)
693     closure->function (closure->data, g_io_channel_unix_get_fd (source), gdk_cond);
694
695   return TRUE;
696 }
697
698 gint
699 gdk_input_add_full (gint              source,
700                     GdkInputCondition condition,
701                     GdkInputFunction  function,
702                     gpointer          data,
703                     GdkDestroyNotify  destroy)
704 {
705   guint result;
706   GdkIOClosure *closure = g_new (GdkIOClosure, 1);
707   GIOChannel *channel;
708   GIOCondition cond = 0;
709
710   closure->function = function;
711   closure->condition = condition;
712   closure->notify = destroy;
713   closure->data = data;
714
715   if (condition & GDK_INPUT_READ)
716     cond |= READ_CONDITION;
717   if (condition & GDK_INPUT_WRITE)
718     cond |= WRITE_CONDITION;
719   if (condition & GDK_INPUT_EXCEPTION)
720     cond |= EXCEPTION_CONDITION;
721
722   channel = g_io_channel_unix_new (source);
723   result = g_io_add_watch_full (channel, G_PRIORITY_DEFAULT, cond, 
724                                 gdk_io_invoke,
725                                 closure, gdk_io_destroy);
726   g_io_channel_unref (channel);
727
728   return result;
729 }
730
731 gint
732 gdk_input_add (gint              source,
733                GdkInputCondition condition,
734                GdkInputFunction  function,
735                gpointer          data)
736 {
737   return gdk_input_add_full (source, condition, function, data, NULL);
738 }
739
740 void
741 gdk_input_remove (gint tag)
742 {
743   g_source_remove (tag);
744 }
745
746 GdkEvent*
747 gdk_event_unqueue (void)
748 {
749   GdkEvent *event = NULL;
750   GList *tmp_list;
751
752   tmp_list = gdk_event_queue_find_first ();
753
754   if (tmp_list)
755     {
756       event = tmp_list->data;
757       gdk_event_queue_remove_link (tmp_list);
758       g_list_free_1 (tmp_list);
759     }
760
761   return event;
762 }
763
764 void
765 gdk_synthesize_click (GdkEvent *event,
766                       gint      nclicks)
767 {
768   GdkEvent temp_event;
769   
770   g_return_if_fail (event != NULL);
771   
772   temp_event = *event;
773   temp_event.type = (nclicks == 2) ? GDK_2BUTTON_PRESS : GDK_3BUTTON_PRESS;
774   
775   gdk_event_put (&temp_event);
776 }
777
778 void
779 gdk_event_button_generate (GdkEvent *event)
780 {
781   if ((event->button.time < (button_click_time[1] + TRIPLE_CLICK_TIME)) &&
782       (event->button.window == button_window[1]) &&
783       (event->button.button == button_number[1]))
784     {
785       gdk_synthesize_click (event, 3);
786       
787       button_click_time[1] = 0;
788       button_click_time[0] = 0;
789       button_window[1] = NULL;
790       button_window[0] = 0;
791       button_number[1] = -1;
792       button_number[0] = -1;
793     }
794   else if ((event->button.time < (button_click_time[0] + DOUBLE_CLICK_TIME)) &&
795            (event->button.window == button_window[0]) &&
796            (event->button.button == button_number[0]))
797     {
798       gdk_synthesize_click (event, 2);
799       
800       button_click_time[1] = button_click_time[0];
801       button_click_time[0] = event->button.time;
802       button_window[1] = button_window[0];
803       button_window[0] = event->button.window;
804       button_number[1] = button_number[0];
805       button_number[0] = event->button.button;
806     }
807   else
808     {
809       button_click_time[1] = 0;
810       button_click_time[0] = event->button.time;
811       button_window[1] = NULL;
812       button_window[0] = event->button.window;
813       button_number[1] = -1;
814       button_number[0] = event->button.button;
815     }
816 }
817
818
819 void
820 gdk_synthesize_window_state (GdkWindow     *window,
821                              GdkWindowState unset_flags,
822                              GdkWindowState set_flags)
823 {
824   GdkEventWindowState temp_event;
825   GdkWindowState old;
826   
827   g_return_if_fail (window != NULL);
828   
829   temp_event.window = window;
830   temp_event.type = GDK_WINDOW_STATE;
831   temp_event.send_event = FALSE;
832   
833   old = ((GdkWindowObject*) temp_event.window)->state;
834   
835   temp_event.changed_mask = (unset_flags | set_flags) ^ old;
836   temp_event.new_window_state = old;
837   temp_event.new_window_state |= set_flags;
838   temp_event.new_window_state &= ~unset_flags;
839
840   if (temp_event.new_window_state == old)
841     return; /* No actual work to do, nothing changed. */
842
843   /* Actually update the field in GdkWindow, this is sort of an odd
844    * place to do it, but seems like the safest since it ensures we expose no
845    * inconsistent state to the user.
846    */
847   
848   ((GdkWindowObject*) window)->state = temp_event.new_window_state;
849
850   /* We only really send the event to toplevels, since
851    * all the window states don't apply to non-toplevels.
852    * Non-toplevels do use the GDK_WINDOW_STATE_WITHDRAWN flag
853    * internally so we needed to update window->state.
854    */
855   switch (((GdkWindowObject*) window)->window_type)
856     {
857     case GDK_WINDOW_TOPLEVEL:
858     case GDK_WINDOW_DIALOG:
859     case GDK_WINDOW_TEMP: /* ? */
860       gdk_event_put ((GdkEvent*) &temp_event);
861       break;
862       
863     case GDK_WINDOW_FOREIGN:
864     case GDK_WINDOW_ROOT:
865     case GDK_WINDOW_CHILD:
866       break;
867     }
868 }
869