]> Pileus Git - ~andy/gtk/blob - gdk/quartz/gdkevents-quartz.c
Fix GtkTooltip destroy the custom widget
[~andy/gtk] / gdk / quartz / gdkevents-quartz.c
1 /* gdkevents-quartz.c
2  *
3  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4  * Copyright (C) 1998-2002 Tor Lillqvist
5  * Copyright (C) 2005-2008 Imendio AB
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "config.h"
24 #include <sys/types.h>
25 #include <sys/sysctl.h>
26 #include <pthread.h>
27 #include <unistd.h>
28
29 #import <Cocoa/Cocoa.h>
30 #include <Carbon/Carbon.h>
31
32 #include "gdkscreen.h"
33 #include "gdkkeysyms.h"
34 #include "gdkprivate-quartz.h"
35
36 /* This is the window the mouse is currently over */
37 static GdkWindow   *current_mouse_window;
38
39 /* This is the window corresponding to the key window */
40 static GdkWindow   *current_keyboard_window;
41
42 /* This is the pointer grab window */
43 GdkWindow          *_gdk_quartz_pointer_grab_window;
44 static gboolean     pointer_grab_owner_events;
45 static GdkEventMask pointer_grab_event_mask;
46 static gboolean     pointer_grab_implicit;
47
48 /* This is the keyboard grab window */
49 GdkWindow *         _gdk_quartz_keyboard_grab_window;
50 static gboolean     keyboard_grab_owner_events;
51
52 /* This is the event mask and button state from the last event */
53 static GdkEventMask current_event_mask;
54 static int          current_button_state;
55
56 static void get_child_coordinates_from_ancestor (GdkWindow *ancestor_window,
57                                                  gint       ancestor_x,
58                                                  gint       ancestor_y,
59                                                  GdkWindow *child_window, 
60                                                  gint      *child_x, 
61                                                  gint      *child_y);
62 static void get_ancestor_coordinates_from_child (GdkWindow *child_window,
63                                                  gint       child_x,
64                                                  gint       child_y,
65                                                  GdkWindow *ancestor_window, 
66                                                  gint      *ancestor_x, 
67                                                  gint      *ancestor_y);
68 static void get_converted_window_coordinates    (GdkWindow *in_window,
69                                                  gint       in_x,
70                                                  gint       in_y,
71                                                  GdkWindow *out_window, 
72                                                  gint      *out_x, 
73                                                  gint      *out_y);
74 static void append_event                        (GdkEvent  *event);
75
76 static const gchar *
77 which_window_is_this (GdkWindow *window)
78 {
79   static gchar buf[256];
80   const gchar *name = NULL;
81   gpointer widget;
82
83   /* Get rid of compiler warning. */
84   if (0) which_window_is_this (window);
85
86   if (window == _gdk_root)
87     name = "root";
88   else if (window == NULL)
89     name = "null";
90
91   if (window)
92     {
93       gdk_window_get_user_data (window, &widget);
94       if (widget)
95         name = G_OBJECT_TYPE_NAME (widget);
96     }
97
98   if (!name)
99     name = "unknown";
100
101   snprintf (buf, 256, "<%s (%p)%s>", 
102             name, window, 
103             window == current_mouse_window ? ", is mouse" : "");
104
105   return buf;
106 }
107
108 NSEvent *
109 gdk_quartz_event_get_nsevent (GdkEvent *event)
110 {
111   /* FIXME: If the event here is unallocated, we crash. */
112   return ((GdkEventPrivate *) event)->windowing_data;
113 }
114
115 void 
116 _gdk_events_init (void)
117 {
118   _gdk_quartz_event_loop_init ();
119
120   current_mouse_window = g_object_ref (_gdk_root);
121   current_keyboard_window = g_object_ref (_gdk_root);
122 }
123
124 gboolean
125 gdk_events_pending (void)
126 {
127   return (_gdk_event_queue_find_first (_gdk_display) ||
128           (_gdk_quartz_event_loop_check_pending ()));
129 }
130
131 GdkEvent*
132 gdk_event_get_graphics_expose (GdkWindow *window)
133 {
134   /* FIXME: Implement */
135   return NULL;
136 }
137
138 static void
139 generate_grab_broken_event (GdkWindow *window,
140                             gboolean   keyboard,
141                             gboolean   implicit,
142                             GdkWindow *grab_window)
143 {
144   if (!GDK_WINDOW_DESTROYED (window))
145     {
146       GdkEvent *event = gdk_event_new (GDK_GRAB_BROKEN);
147
148       event->grab_broken.window = window;
149       event->grab_broken.send_event = 0;
150       event->grab_broken.keyboard = keyboard;
151       event->grab_broken.implicit = implicit;
152       event->grab_broken.grab_window = grab_window;
153       
154       append_event (event);
155     }
156 }
157
158 GdkGrabStatus
159 gdk_keyboard_grab (GdkWindow  *window,
160                    gint        owner_events,
161                    guint32     time)
162 {
163   g_return_val_if_fail (window != NULL, 0);
164   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
165
166   if (_gdk_quartz_keyboard_grab_window)
167     {
168       if (_gdk_quartz_keyboard_grab_window != window)
169         generate_grab_broken_event (_gdk_quartz_keyboard_grab_window,
170                                     TRUE, FALSE, window);
171       
172       g_object_unref (_gdk_quartz_keyboard_grab_window);
173     }
174
175   _gdk_quartz_keyboard_grab_window = g_object_ref (window);
176   keyboard_grab_owner_events = owner_events;
177
178   return GDK_GRAB_SUCCESS;
179 }
180
181 void
182 gdk_display_keyboard_ungrab (GdkDisplay *display,
183                              guint32     time)
184 {
185   if (_gdk_quartz_keyboard_grab_window)
186     g_object_unref (_gdk_quartz_keyboard_grab_window);
187   _gdk_quartz_keyboard_grab_window = NULL;
188 }
189
190 gboolean
191 gdk_keyboard_grab_info_libgtk_only (GdkDisplay *display,
192                                     GdkWindow **grab_window,
193                                     gboolean   *owner_events)
194 {
195   if (_gdk_quartz_keyboard_grab_window) 
196     {
197       if (grab_window)
198         *grab_window = _gdk_quartz_keyboard_grab_window;
199       if (owner_events)
200         *owner_events = keyboard_grab_owner_events;
201
202       return TRUE;
203     }
204
205   return FALSE;
206 }
207
208 static void
209 pointer_ungrab_internal (gboolean only_if_implicit)
210 {
211   if (!_gdk_quartz_pointer_grab_window)
212     return;
213
214   if (only_if_implicit && !pointer_grab_implicit)
215     return;
216
217   g_object_unref (_gdk_quartz_pointer_grab_window);
218   _gdk_quartz_pointer_grab_window = NULL;
219
220   pointer_grab_owner_events = FALSE;
221   pointer_grab_event_mask = 0;
222   pointer_grab_implicit = FALSE;
223
224   /* FIXME: Send crossing events */
225 }
226
227 gboolean
228 gdk_display_pointer_is_grabbed (GdkDisplay *display)
229 {
230   return (_gdk_quartz_pointer_grab_window != NULL && 
231           !pointer_grab_implicit);
232 }
233
234 gboolean
235 gdk_pointer_grab_info_libgtk_only (GdkDisplay *display,
236                                    GdkWindow **grab_window,
237                                    gboolean   *owner_events)
238 {
239   if (!_gdk_quartz_pointer_grab_window)
240     return FALSE;
241
242   if (grab_window)
243     *grab_window = _gdk_quartz_pointer_grab_window;
244
245   if (owner_events)
246     *owner_events = pointer_grab_owner_events;
247
248   return TRUE;
249 }
250
251 void
252 gdk_display_pointer_ungrab (GdkDisplay *display,
253                             guint32     time)
254 {
255   pointer_ungrab_internal (FALSE);
256 }
257
258 static GdkGrabStatus
259 pointer_grab_internal (GdkWindow    *window,
260                        gboolean      owner_events,
261                        GdkEventMask  event_mask,
262                        GdkWindow    *confine_to,
263                        GdkCursor    *cursor,
264                        gboolean      implicit)
265 {
266   /* FIXME: Send crossing events */
267   
268   _gdk_quartz_pointer_grab_window = g_object_ref (window);
269   pointer_grab_owner_events = owner_events;
270   pointer_grab_event_mask = event_mask;
271   pointer_grab_implicit = implicit;
272
273   return GDK_GRAB_SUCCESS;
274 }
275
276 GdkGrabStatus
277 gdk_pointer_grab (GdkWindow    *window,
278                   gboolean      owner_events,
279                   GdkEventMask  event_mask,
280                   GdkWindow    *confine_to,
281                   GdkCursor    *cursor,
282                   guint32       time)
283 {
284   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
285   g_return_val_if_fail (confine_to == NULL || GDK_IS_WINDOW (confine_to), 0);
286
287   if (_gdk_quartz_pointer_grab_window)
288     {
289       if (_gdk_quartz_pointer_grab_window != window)
290         generate_grab_broken_event (_gdk_quartz_pointer_grab_window,
291                                     FALSE, pointer_grab_implicit, window);
292
293       pointer_ungrab_internal (FALSE);
294     }
295
296   return pointer_grab_internal (window, owner_events, event_mask, 
297                                 confine_to, cursor, FALSE);
298 }
299
300 /* This is used to break any grabs in the case where we have to due to
301  * the grab emulation. Instead of enforcing the desktop wide grab, we
302  * break it when the app loses focus for example.
303  */
304 static void
305 break_all_grabs (void)
306 {
307   if (_gdk_quartz_keyboard_grab_window)
308     {
309       generate_grab_broken_event (_gdk_quartz_keyboard_grab_window,
310                                   TRUE, FALSE,
311                                   NULL);
312       g_object_unref (_gdk_quartz_keyboard_grab_window);
313       _gdk_quartz_keyboard_grab_window = NULL;
314     }
315
316   if (_gdk_quartz_pointer_grab_window)
317     {
318       generate_grab_broken_event (_gdk_quartz_pointer_grab_window,
319                                   FALSE, pointer_grab_implicit,
320                                   NULL);
321       pointer_ungrab_internal (FALSE);
322     }
323 }
324
325 static void
326 fixup_event (GdkEvent *event)
327 {
328   if (event->any.window)
329     g_object_ref (event->any.window);
330   if (((event->any.type == GDK_ENTER_NOTIFY) ||
331        (event->any.type == GDK_LEAVE_NOTIFY)) &&
332       (event->crossing.subwindow != NULL))
333     g_object_ref (event->crossing.subwindow);
334   event->any.send_event = FALSE;
335 }
336
337 static void
338 append_event (GdkEvent *event)
339 {
340   fixup_event (event);
341   _gdk_event_queue_append (_gdk_display, event);
342 }
343
344 static GdkFilterReturn
345 apply_filters (GdkWindow  *window,
346                NSEvent    *nsevent,
347                GList      *filters)
348 {
349   GdkFilterReturn result = GDK_FILTER_CONTINUE;
350   GdkEvent *event;
351   GList *node;
352   GList *tmp_list;
353
354   event = gdk_event_new (GDK_NOTHING);
355   if (window != NULL)
356     event->any.window = g_object_ref (window);
357   ((GdkEventPrivate *)event)->flags |= GDK_EVENT_PENDING;
358
359   /* I think GdkFilterFunc semantics require the passed-in event
360    * to already be in the queue. The filter func can generate
361    * more events and append them after it if it likes.
362    */
363   node = _gdk_event_queue_append (_gdk_display, event);
364   
365   tmp_list = filters;
366   while (tmp_list)
367     {
368       GdkEventFilter *filter = (GdkEventFilter *) tmp_list->data;
369       
370       tmp_list = tmp_list->next;
371       result = filter->function (nsevent, event, filter->data);
372       if (result != GDK_FILTER_CONTINUE)
373         break;
374     }
375
376   if (result == GDK_FILTER_CONTINUE || result == GDK_FILTER_REMOVE)
377     {
378       _gdk_event_queue_remove_link (_gdk_display, node);
379       g_list_free_1 (node);
380       gdk_event_free (event);
381     }
382   else /* GDK_FILTER_TRANSLATE */
383     {
384       ((GdkEventPrivate *)event)->flags &= ~GDK_EVENT_PENDING;
385       fixup_event (event);
386     }
387   return result;
388 }
389
390 /* Checks if the passed in window is interested in the event mask, and
391  * if so, it's returned. If not, the event can be propagated through
392  * its ancestors until one with the right event mask is found, up to
393  * the nearest toplevel.
394  */
395 static GdkWindow *
396 find_window_interested_in_event_mask (GdkWindow    *window, 
397                                       GdkEventMask  event_mask,
398                                       gboolean      propagate)
399 {
400   GdkWindowObject *private;
401
402   private = GDK_WINDOW_OBJECT (window);
403   while (private)
404     {
405       if (private->event_mask & event_mask)
406         return (GdkWindow *)private;
407
408       if (!propagate)
409         return NULL;
410
411       /* Don't traverse beyond toplevels. */
412       if (GDK_WINDOW_TYPE (private) != GDK_WINDOW_CHILD)
413         break;
414
415       private = private->parent;
416     }
417
418   return NULL;
419 }
420
421 static guint32
422 get_time_from_ns_event (NSEvent *event)
423 {
424   double time = [event timestamp];
425   
426   return time * 1000.0;
427 }
428
429 static int
430 get_mouse_button_from_ns_event (NSEvent *event)
431 {
432   int button;
433
434   button = [event buttonNumber];
435
436   switch (button)
437     {
438     case 0:
439       return 1;
440     case 1:
441       return 3;
442     case 2:
443       return 2;
444     default:
445       return button + 1;
446     }
447 }
448
449 static GdkModifierType
450 get_mouse_button_modifiers_from_ns_event (NSEvent *event)
451 {
452   int button;
453   GdkModifierType state = 0;
454
455   /* This maps buttons 1 to 5 to GDK_BUTTON[1-5]_MASK */
456   button = get_mouse_button_from_ns_event (event);
457   if (button >= 1 && button <= 5)
458     state = (1 << (button + 7));
459
460   return state;
461 }
462
463 static GdkModifierType
464 get_keyboard_modifiers_from_ns_event (NSEvent *nsevent)
465 {
466   GdkModifierType modifiers = 0;
467   int nsflags;
468
469   nsflags = [nsevent modifierFlags];
470   
471   if (nsflags & NSAlphaShiftKeyMask)
472     modifiers |= GDK_LOCK_MASK;
473   if (nsflags & NSShiftKeyMask)
474     modifiers |= GDK_SHIFT_MASK;
475   if (nsflags & NSControlKeyMask)
476     modifiers |= GDK_CONTROL_MASK;
477   if (nsflags & NSCommandKeyMask)
478     modifiers |= GDK_MOD1_MASK;
479
480   return modifiers;
481 }
482
483 /* Return an event mask from an NSEvent */
484 static GdkEventMask
485 get_event_mask_from_ns_event (NSEvent *nsevent)
486 {
487   switch ([nsevent type])
488     {
489     case NSLeftMouseDown:
490     case NSRightMouseDown:
491     case NSOtherMouseDown:
492       return GDK_BUTTON_PRESS_MASK;
493     case NSLeftMouseUp:
494     case NSRightMouseUp:
495     case NSOtherMouseUp:
496       return GDK_BUTTON_RELEASE_MASK;
497     case NSMouseMoved:
498       return GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
499     case NSScrollWheel:
500       /* Since applications that want button press events can get
501        * scroll events on X11 (since scroll wheel events are really
502        * button press events there), we need to use GDK_BUTTON_PRESS_MASK too.
503        */
504       return GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK;
505     case NSLeftMouseDragged:
506       return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
507               GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | 
508               GDK_BUTTON1_MASK);
509     case NSRightMouseDragged:
510       return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
511               GDK_BUTTON_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | 
512               GDK_BUTTON3_MASK);
513     case NSOtherMouseDragged:
514       {
515         GdkEventMask mask;
516
517         mask = (GDK_POINTER_MOTION_MASK |
518                 GDK_POINTER_MOTION_HINT_MASK |
519                 GDK_BUTTON_MOTION_MASK);
520
521         if (get_mouse_button_from_ns_event (nsevent) == 2)
522           mask |= (GDK_BUTTON2_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | 
523                    GDK_BUTTON2_MASK);
524
525         return mask;
526       }
527     case NSKeyDown:
528     case NSKeyUp:
529     case NSFlagsChanged:
530       {
531         switch (_gdk_quartz_keys_event_type (nsevent))
532           {
533           case GDK_KEY_PRESS:
534             return GDK_KEY_PRESS_MASK;
535           case GDK_KEY_RELEASE:
536             return GDK_KEY_RELEASE_MASK;
537           case GDK_NOTHING:
538             return 0;
539           default:
540             g_assert_not_reached ();
541           }
542       }
543     default:
544       g_assert_not_reached ();
545     }
546
547   return 0;
548 }
549
550 static GdkEvent *
551 create_focus_event (GdkWindow *window,
552                     gboolean   in)
553 {
554   GdkEvent *event;
555
556   event = gdk_event_new (GDK_FOCUS_CHANGE);
557   event->focus_change.window = window;
558   event->focus_change.in = in;
559
560   return event;
561 }
562
563 /* Note: Used to both set a new focus window and to unset the old one. */
564 void
565 _gdk_quartz_events_update_focus_window (GdkWindow *window,
566                                         gboolean   got_focus)
567 {
568   GdkEvent *event;
569
570   if (got_focus && window == current_keyboard_window)
571     return;
572
573   /* FIXME: Don't do this when grabbed? Or make GdkQuartzWindow
574    * disallow it in the first place instead?
575    */
576   
577   if (!got_focus && window == current_keyboard_window)
578     {
579       event = create_focus_event (current_keyboard_window, FALSE);
580       append_event (event);
581       g_object_unref (current_keyboard_window);
582       current_keyboard_window = NULL;
583     }
584
585   if (got_focus)
586     {
587       if (current_keyboard_window)
588         {
589           event = create_focus_event (current_keyboard_window, FALSE);
590           append_event (event);
591           g_object_unref (current_keyboard_window);
592           current_keyboard_window = NULL;
593         }
594       
595       event = create_focus_event (window, TRUE);
596       append_event (event);
597       current_keyboard_window = g_object_ref (window);
598     }
599 }
600
601 static void
602 convert_window_coordinates_to_root (GdkWindow *window,
603                                     gdouble    x,
604                                     gdouble    y,
605                                     gdouble   *x_root,
606                                     gdouble   *y_root)
607 {
608   gint ox, oy;
609
610   *x_root = x;
611   *y_root = y;
612   
613   if (gdk_window_get_origin (window, &ox, &oy))
614     {
615       *x_root += ox;
616       *y_root += oy;
617     }
618 }
619
620 /* FIXME: Refactor and share with scroll event. */
621 static GdkEvent *
622 create_crossing_event (GdkWindow      *window, 
623                        NSEvent        *nsevent, 
624                        GdkEventType    event_type,
625                        GdkCrossingMode mode, 
626                        GdkNotifyType   detail)
627 {
628   GdkEvent *event;
629   gint x_tmp, y_tmp;
630
631   event = gdk_event_new (event_type);
632
633   event->crossing.window = window;
634   event->crossing.subwindow = NULL; /* FIXME */
635   event->crossing.time = get_time_from_ns_event (nsevent);
636
637   /* Split out this block: */
638   {
639     NSWindow *nswindow;
640     GdkWindow *toplevel;
641     GdkWindowImplQuartz *impl;
642     NSPoint point;
643
644     nswindow = [nsevent window];
645     point = [nsevent locationInWindow];
646
647     toplevel = [(GdkQuartzView *)[nswindow contentView] gdkWindow];
648
649     impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (toplevel)->impl);
650
651     x_tmp = point.x;
652     y_tmp = impl->height - point.y;
653
654     get_converted_window_coordinates (toplevel,
655                                       x_tmp, y_tmp,
656                                       window,
657                                       &x_tmp, &y_tmp);
658     }
659
660   event->crossing.x = x_tmp;
661   event->crossing.y = y_tmp;
662
663   convert_window_coordinates_to_root (window, 
664                                       event->crossing.x, 
665                                       event->crossing.y, 
666                                       &event->crossing.x_root,
667                                       &event->crossing.y_root);
668
669   event->crossing.mode = mode;
670   event->crossing.detail = detail;
671   event->crossing.state = get_keyboard_modifiers_from_ns_event (nsevent);
672
673   /* FIXME: focus and button state */
674
675   return event;
676 }
677
678 static void
679 synthesize_enter_event (GdkWindow      *window,
680                         NSEvent        *nsevent,
681                         GdkCrossingMode mode,
682                         GdkNotifyType   detail)
683 {
684   GdkEvent *event;
685
686   if (_gdk_quartz_pointer_grab_window != NULL && 
687       !pointer_grab_owner_events && 
688       !(pointer_grab_event_mask & GDK_ENTER_NOTIFY_MASK))
689     return;
690
691   if (!(GDK_WINDOW_OBJECT (window)->event_mask & GDK_ENTER_NOTIFY_MASK))
692     return;
693
694   event = create_crossing_event (window, nsevent, GDK_ENTER_NOTIFY,
695                                  mode, detail);
696
697   append_event (event);
698 }
699   
700 static void
701 synthesize_enter_events (GdkWindow      *from,
702                          GdkWindow      *to,
703                          NSEvent        *nsevent,
704                          GdkCrossingMode mode,
705                          GdkNotifyType   detail)
706 {
707   GdkWindow *prev = gdk_window_get_parent (to);
708
709   if (prev != from)
710     synthesize_enter_events (from, prev, nsevent, mode, detail);
711   synthesize_enter_event (to, nsevent, mode, detail);
712 }
713
714 static void
715 synthesize_leave_event (GdkWindow      *window,
716                         NSEvent        *nsevent,
717                         GdkCrossingMode mode,
718                         GdkNotifyType   detail)
719 {
720   GdkEvent *event;
721
722   if (_gdk_quartz_pointer_grab_window != NULL && 
723       !pointer_grab_owner_events && 
724       !(pointer_grab_event_mask & GDK_LEAVE_NOTIFY_MASK))
725     return;
726
727   if (!(GDK_WINDOW_OBJECT (window)->event_mask & GDK_LEAVE_NOTIFY_MASK))
728     return;
729
730   event = create_crossing_event (window, nsevent, GDK_LEAVE_NOTIFY,
731                                  mode, detail);
732
733   append_event (event);
734 }
735                          
736 static void
737 synthesize_leave_events (GdkWindow      *from,
738                          GdkWindow      *to,
739                          NSEvent        *nsevent,
740                          GdkCrossingMode mode,
741                          GdkNotifyType   detail)
742 {
743   GdkWindow *next = gdk_window_get_parent (from);
744   
745   synthesize_leave_event (from, nsevent, mode, detail);
746   if (next != to)
747     synthesize_leave_events (next, to, nsevent, mode, detail);
748 }
749                          
750 static void
751 synthesize_crossing_events (GdkWindow      *window,
752                             GdkCrossingMode mode,
753                             NSEvent        *nsevent,
754                             gint            x,
755                             gint            y)
756 {
757   GdkWindow *intermediate, *tem, *common_ancestor;
758
759   if (window == current_mouse_window)
760     return;
761
762   if (_gdk_quartz_window_is_ancestor (current_mouse_window, window))
763     {
764       /* Pointer has moved to an inferior window. */
765       synthesize_leave_event (current_mouse_window, nsevent, mode, GDK_NOTIFY_INFERIOR);
766
767       /* If there are intermediate windows, generate ENTER_NOTIFY
768        * events for them
769        */
770       intermediate = gdk_window_get_parent (window);
771
772       if (intermediate != current_mouse_window)
773         {
774           synthesize_enter_events (current_mouse_window, intermediate, nsevent, mode, GDK_NOTIFY_VIRTUAL);
775         }
776
777       synthesize_enter_event (window, nsevent, mode, GDK_NOTIFY_ANCESTOR);
778     }
779   else if (_gdk_quartz_window_is_ancestor (window, current_mouse_window))
780     {
781       /* Pointer has moved to an ancestor window. */
782       synthesize_leave_event (current_mouse_window, nsevent, mode, GDK_NOTIFY_ANCESTOR);
783       
784       /* If there are intermediate windows, generate LEAVE_NOTIFY
785        * events for them
786        */
787       intermediate = gdk_window_get_parent (current_mouse_window);
788       if (intermediate != window)
789         {
790           synthesize_leave_events (intermediate, window, nsevent, mode, GDK_NOTIFY_VIRTUAL);
791         }
792
793       synthesize_enter_event (window, nsevent, mode, GDK_NOTIFY_INFERIOR);
794     }
795   else if (current_mouse_window)
796     {
797       /* Find least common ancestor of current_mouse_window and window */
798       tem = current_mouse_window;
799       do {
800         common_ancestor = gdk_window_get_parent (tem);
801         tem = common_ancestor;
802       } while (common_ancestor &&
803                !_gdk_quartz_window_is_ancestor (common_ancestor, window));
804       if (common_ancestor)
805         {
806           synthesize_leave_event (current_mouse_window, nsevent, mode, GDK_NOTIFY_NONLINEAR);
807           intermediate = gdk_window_get_parent (current_mouse_window);
808           if (intermediate != common_ancestor)
809             {
810               synthesize_leave_events (intermediate, common_ancestor,
811                                        nsevent, mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
812             }
813           intermediate = gdk_window_get_parent (window);
814           if (intermediate != common_ancestor)
815             {
816               synthesize_enter_events (common_ancestor, intermediate,
817                                        nsevent, mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
818             }
819           synthesize_enter_event (window, nsevent, mode, GDK_NOTIFY_NONLINEAR);
820         }
821     }
822   else
823     {
824       /* This means we have no current_mouse_window, which probably
825        * means that there is a bug somewhere, we should always have
826        * the root in we don't have another window. Does this ever
827        * happen?
828        */
829       g_warning ("Trying to create crossing event when current_mouse_window is NULL");
830     }
831
832   _gdk_quartz_events_update_mouse_window (window);
833
834   /* FIXME: This does't work when someone calls gdk_window_set_cursor
835    * during a grab. The right behavior is that the cursor doesn't
836    * change when a grab is in effect, but in that case it does.
837    */
838   if (window && !_gdk_quartz_pointer_grab_window)
839     _gdk_quartz_events_update_cursor (window);
840 }
841
842 void 
843 _gdk_quartz_events_send_map_events (GdkWindow *window)
844 {
845   GList *list;
846   GdkWindow *interested_window;
847   GdkWindowObject *private = (GdkWindowObject *)window;
848
849   interested_window = find_window_interested_in_event_mask (window, 
850                                                             GDK_STRUCTURE_MASK,
851                                                             TRUE);
852   
853   if (interested_window)
854     {
855       GdkEvent *event = gdk_event_new (GDK_MAP);
856       event->any.window = interested_window;
857       append_event (event);
858     }
859
860   for (list = private->children; list != NULL; list = list->next)
861     _gdk_quartz_events_send_map_events ((GdkWindow *)list->data);
862 }
863
864 /* Get current mouse window */
865 GdkWindow *
866 _gdk_quartz_events_get_mouse_window (gboolean consider_grabs)
867 {
868   if (!consider_grabs)
869     return current_mouse_window;
870
871   if (_gdk_quartz_pointer_grab_window && !pointer_grab_owner_events)
872     return _gdk_quartz_pointer_grab_window;
873   
874   return current_mouse_window;
875 }
876
877 /* Update mouse window */
878 void 
879 _gdk_quartz_events_update_mouse_window (GdkWindow *window)
880 {
881   if (window == current_mouse_window)
882     return;
883
884 #ifdef G_ENABLE_DEBUG
885   if (_gdk_debug_flags & GDK_DEBUG_EVENTS)
886     _gdk_quartz_window_debug_highlight (window, 0);
887 #endif /* G_ENABLE_DEBUG */  
888
889   if (window)
890     g_object_ref (window);
891   if (current_mouse_window)
892     g_object_unref (current_mouse_window);
893
894   current_mouse_window = window;
895 }
896
897 /* Update current cursor */
898 void
899 _gdk_quartz_events_update_cursor (GdkWindow *window)
900 {
901   GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
902   NSCursor *nscursor = nil;
903
904   while (private)
905     {
906       GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
907
908       nscursor = impl->nscursor;
909       if (nscursor)
910         break;
911
912       private = private->parent;
913     }
914
915   GDK_QUARTZ_ALLOC_POOL;
916
917   if (!nscursor)
918     nscursor = [NSCursor arrowCursor];
919
920   if ([NSCursor currentCursor] != nscursor)
921     [nscursor set];
922
923   GDK_QUARTZ_RELEASE_POOL;
924 }
925
926 /* Translates coordinates from an ancestor window + coords, to
927  * coordinates that are relative the child window.
928  */
929 static void
930 get_child_coordinates_from_ancestor (GdkWindow *ancestor_window,
931                                      gint       ancestor_x,
932                                      gint       ancestor_y,
933                                      GdkWindow *child_window, 
934                                      gint      *child_x, 
935                                      gint      *child_y)
936 {
937   GdkWindowObject *ancestor_private = GDK_WINDOW_OBJECT (ancestor_window);
938   GdkWindowObject *child_private = GDK_WINDOW_OBJECT (child_window);
939
940   while (child_private != ancestor_private)
941     {
942       ancestor_x -= child_private->x;
943       ancestor_y -= child_private->y;
944
945       child_private = child_private->parent;
946     }
947
948   *child_x = ancestor_x;
949   *child_y = ancestor_y;
950 }
951
952 /* Translates coordinates from a child window + coords, to
953  * coordinates that are relative the ancestor window.
954  */
955 static void
956 get_ancestor_coordinates_from_child (GdkWindow *child_window,
957                                      gint       child_x,
958                                      gint       child_y,
959                                      GdkWindow *ancestor_window, 
960                                      gint      *ancestor_x, 
961                                      gint      *ancestor_y)
962 {
963   GdkWindowObject *child_private = GDK_WINDOW_OBJECT (child_window);
964   GdkWindowObject *ancestor_private = GDK_WINDOW_OBJECT (ancestor_window);
965
966   while (child_private != ancestor_private)
967     {
968       child_x += child_private->x;
969       child_y += child_private->y;
970
971       child_private = child_private->parent;
972     }
973
974   *ancestor_x = child_x;
975   *ancestor_y = child_y;
976 }
977
978 /* Translates coordinates relative to one window (in_window) into
979  * coordinates relative to another window (out_window).
980  */
981 static void
982 get_converted_window_coordinates (GdkWindow *in_window,
983                                   gint       in_x,
984                                   gint       in_y,
985                                   GdkWindow *out_window, 
986                                   gint      *out_x, 
987                                   gint      *out_y)
988 {
989   GdkWindow *in_toplevel;
990   GdkWindow *out_toplevel;
991   int in_origin_x, in_origin_y;
992   int out_origin_x, out_origin_y;
993
994   if (in_window == out_window)
995     {
996       *out_x = in_x;
997       *out_y = in_y;
998       return;
999     }
1000
1001   /* First translate to "in" toplevel coordinates, then on to "out"
1002    * toplevel coordinates, and finally to "out" child (the passed in
1003    * window) coordinates.
1004    */
1005
1006   in_toplevel = gdk_window_get_toplevel (in_window);
1007   out_toplevel  = gdk_window_get_toplevel (out_window);
1008
1009   /* Translate in_x, in_y to "in" toplevel coordinates. */
1010   get_ancestor_coordinates_from_child (in_window, in_x, in_y,
1011                                        in_toplevel, &in_x, &in_y);
1012
1013   gdk_window_get_origin (in_toplevel, &in_origin_x, &in_origin_y);
1014   gdk_window_get_origin (out_toplevel, &out_origin_x, &out_origin_y);
1015
1016   /* Translate in_x, in_y to "out" toplevel coordinates. */
1017   in_x -= out_origin_x - in_origin_x;
1018   in_y -= out_origin_y - in_origin_y;
1019
1020   get_child_coordinates_from_ancestor (out_toplevel, 
1021                                        in_x, in_y,
1022                                        out_window,
1023                                        out_x, out_y);
1024 }
1025
1026 /* Given a mouse NSEvent (must be a mouse event for a GDK window),
1027  * finds the subwindow over which the pointer is located. Returns
1028  * coordinates relative to the found window. If no window is found,
1029  * returns the root window, and root window coordinates.
1030  */
1031 static GdkWindow *
1032 find_mouse_window_for_ns_event (NSEvent *nsevent,
1033                                 gint    *x_ret,
1034                                 gint    *y_ret)
1035 {
1036   GdkWindow *event_toplevel;
1037   GdkWindowImplQuartz *impl;
1038   GdkWindow *mouse_toplevel;
1039   GdkWindow *mouse_window;
1040   NSPoint point;
1041   gint x_tmp, y_tmp;
1042
1043   event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
1044   impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (event_toplevel)->impl);
1045   point = [nsevent locationInWindow];
1046
1047   x_tmp = point.x;
1048   y_tmp = impl->height - point.y;
1049
1050   mouse_toplevel = gdk_window_get_toplevel (current_mouse_window);
1051
1052   get_converted_window_coordinates (event_toplevel,
1053                                     x_tmp, y_tmp,
1054                                     mouse_toplevel,
1055                                     &x_tmp, &y_tmp);
1056
1057   mouse_window = _gdk_quartz_window_find_child (mouse_toplevel, x_tmp, y_tmp);
1058   if (mouse_window && mouse_window != mouse_toplevel)
1059     {
1060       get_child_coordinates_from_ancestor (mouse_toplevel,
1061                                            x_tmp, y_tmp,
1062                                            mouse_window,
1063                                            &x_tmp, &y_tmp);
1064     }
1065   else if (!mouse_window)
1066     {
1067       /* This happens for events on the window title buttons and the
1068        * desktop, treat those as being on the root window.
1069        */
1070       get_converted_window_coordinates (mouse_toplevel,
1071                                         x_tmp, y_tmp,
1072                                         _gdk_root,
1073                                         &x_tmp, &y_tmp);
1074       mouse_window = _gdk_root;
1075     }
1076
1077   *x_ret = x_tmp;
1078   *y_ret = y_tmp;
1079
1080   return mouse_window;
1081 }
1082
1083 /* Trigger crossing events if necessary. This is used when showing a new
1084  * window, since the tracking rect API doesn't work reliably when a window
1085  * shows up under the mouse cursor. It's done by finding the topmost window
1086  * under the mouse pointer and synthesizing crossing events into that
1087  * window.
1088  */
1089 void
1090 _gdk_quartz_events_trigger_crossing_events (gboolean defer_to_mainloop)
1091 {
1092   NSPoint point;
1093   gint x, y; 
1094   gint x_toplevel, y_toplevel;
1095   GdkWindow *mouse_window;
1096   GdkWindow *toplevel;
1097   GdkWindowImplQuartz *impl;
1098   guint flags = 0;
1099   NSTimeInterval timestamp = 0;
1100   NSEvent *current_event;
1101   NSEvent *nsevent;
1102
1103   if (defer_to_mainloop)
1104     {
1105       nsevent = [NSEvent otherEventWithType:NSApplicationDefined
1106                                    location:NSZeroPoint
1107                               modifierFlags:0
1108                                   timestamp:0
1109                                windowNumber:0
1110                                     context:nil
1111                                     subtype:GDK_QUARTZ_EVENT_SUBTYPE_FAKE_CROSSING
1112                                       data1:0
1113                                       data2:0];
1114       [NSApp postEvent:nsevent atStart:NO];
1115       return;
1116     }
1117
1118   point = [NSEvent mouseLocation];
1119   x = point.x;
1120   y = _gdk_quartz_window_get_inverted_screen_y (point.y);
1121
1122   mouse_window = _gdk_quartz_window_find_child (_gdk_root, x, y);
1123   if (!mouse_window || mouse_window == _gdk_root)
1124     return;
1125
1126   toplevel = gdk_window_get_toplevel (mouse_window);
1127
1128   /* We ignore crossing within the same toplevel since that is already
1129    * handled elsewhere.
1130    */
1131   if (toplevel == gdk_window_get_toplevel (current_mouse_window))
1132     return;
1133
1134   get_converted_window_coordinates (_gdk_root,
1135                                     x, y,
1136                                     toplevel,
1137                                     &x_toplevel, &y_toplevel);
1138
1139   get_converted_window_coordinates (_gdk_root,
1140                                     x, y,
1141                                     mouse_window,
1142                                     &x, &y);
1143
1144   /* Fix up the event to be less fake if possible. */
1145   current_event = [NSApp currentEvent];
1146   if (current_event)
1147     {
1148       flags = [current_event modifierFlags];
1149       timestamp = [current_event timestamp];
1150     }
1151
1152   if (timestamp == 0)
1153     timestamp = GetCurrentEventTime ();
1154
1155   impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (toplevel)->impl);
1156   nsevent = [NSEvent otherEventWithType:NSApplicationDefined
1157                                location:NSMakePoint (x_toplevel, impl->height - y_toplevel)
1158                           modifierFlags:flags
1159                               timestamp:timestamp
1160                            windowNumber:[impl->toplevel windowNumber]
1161                                 context:nil
1162                                 subtype:GDK_QUARTZ_EVENT_SUBTYPE_FAKE_CROSSING
1163                                   data1:0
1164                                   data2:0];
1165
1166 #ifdef G_ENABLE_DEBUG
1167   /*_gdk_quartz_window_debug_highlight (mouse_window, 0);*/
1168 #endif
1169
1170   synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, x, y);
1171 }
1172
1173 /* Synthesizes crossing events if necessary, based on the passed in
1174  * NSEvent. Uses NSMouseEntered and NSMouseExisted for toplevels and
1175  * the mouse moved/dragged events for child windows, to see if the
1176  * mouse window has changed.
1177  */
1178 static void
1179 synthesize_crossing_events_for_ns_event (NSEvent *nsevent)
1180 {
1181   NSEventType event_type;
1182   GdkWindow *mouse_window;
1183   gint x; 
1184   gint y;
1185
1186   event_type = [nsevent type];
1187
1188   switch (event_type)
1189     {
1190     case NSMouseMoved:
1191     case NSLeftMouseDragged:
1192     case NSRightMouseDragged:
1193     case NSOtherMouseDragged:
1194       /* We only handle moving the pointer to another GDK window.
1195        * Leaving to a non-GDK toplevel window (or window title bar or
1196        * the desktop) is covered by NSMouseExited events.
1197        */
1198       mouse_window = find_mouse_window_for_ns_event (nsevent, &x, &y);
1199       if (mouse_window != _gdk_root)
1200         synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, x, y);
1201
1202       break;
1203
1204     case NSMouseEntered:
1205       {
1206         GdkWindow *event_toplevel;
1207         GdkWindowImplQuartz *impl;
1208         NSPoint point;
1209
1210         /* This is the only case where we actually use the window from
1211          * the event since we need to know which toplevel we entered
1212          * so it can be tracked properly.
1213          */
1214         event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
1215         impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (event_toplevel)->impl);
1216
1217         point = [nsevent locationInWindow];
1218
1219         x = point.x;
1220         y = impl->height - point.y;
1221
1222         mouse_window = _gdk_quartz_window_find_child (event_toplevel, x, y);
1223
1224         /* Treat unknown windows (like the title bar buttons or
1225          * desktop) as the root window.
1226          */
1227         if (!mouse_window)
1228           mouse_window = _gdk_root;
1229
1230         if (mouse_window != event_toplevel)
1231           get_converted_window_coordinates (event_toplevel,
1232                                             x, y,
1233                                             mouse_window,
1234                                             &x, &y);
1235
1236         synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, x, y);
1237       }
1238       break;
1239
1240     case NSMouseExited:
1241       {
1242         GdkWindow *event_toplevel;
1243         GdkWindowImplQuartz *impl;
1244         NSPoint point;
1245
1246         /* We only use NSMouseExited when leaving to the root
1247          * window. The other cases are handled above by checking the
1248          * motion/button events, or getting a NSMouseEntered for
1249          * another GDK window. The reason we don't use NSMouseExited
1250          * for other windows is that quartz first delivers the entered
1251          * event and then the exited which is the opposite from what
1252          * we need.
1253          */
1254         event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
1255         impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (event_toplevel)->impl);
1256         point = [nsevent locationInWindow];
1257
1258         x = point.x;
1259         y = impl->height - point.y;
1260
1261         x += GDK_WINDOW_OBJECT (event_toplevel)->x;
1262         y += GDK_WINDOW_OBJECT (event_toplevel)->y;
1263
1264         /* If there is a window other than the root window at this
1265          * position, it means we didn't exit to the root window and we
1266          * ignore the event. (Note that we can get NULL here when swithing
1267          * spaces for example.)
1268          *
1269          * FIXME: This is not enough, it doesn't catch the case where
1270          * we leave a GDK window to a non-GDK window that has GDK
1271          * windows below it.
1272          */
1273         mouse_window = _gdk_quartz_window_find_child (_gdk_root, x, y);
1274
1275         if (!mouse_window ||
1276             gdk_window_get_toplevel (mouse_window) ==
1277             gdk_window_get_toplevel (current_mouse_window))
1278           {
1279             mouse_window = _gdk_root;
1280           }
1281
1282         if (mouse_window == _gdk_root)
1283           synthesize_crossing_events (_gdk_root, GDK_CROSSING_NORMAL, nsevent, x, y);
1284       }
1285       break;
1286
1287     default:
1288       break;
1289     }
1290 }
1291
1292 /* This function finds the correct window to send an event to, taking
1293  * into account grabs, event propagation, and event masks.
1294  */
1295 static GdkWindow *
1296 find_window_for_ns_event (NSEvent *nsevent, 
1297                           gint    *x, 
1298                           gint    *y)
1299 {
1300   NSEventType event_type;
1301
1302   event_type = [nsevent type];
1303
1304   switch (event_type)
1305     {
1306     case NSLeftMouseDown:
1307     case NSRightMouseDown:
1308     case NSOtherMouseDown:
1309     case NSLeftMouseUp:
1310     case NSRightMouseUp:
1311     case NSOtherMouseUp:
1312     case NSMouseMoved:
1313     case NSScrollWheel:
1314     case NSLeftMouseDragged:
1315     case NSRightMouseDragged:
1316     case NSOtherMouseDragged:
1317       {
1318         GdkWindow *mouse_window;
1319         GdkEventMask event_mask;
1320         GdkWindow *real_window;
1321
1322         /* From the docs for XGrabPointer:
1323          *
1324          * If owner_events is True and if a generated pointer event
1325          * would normally be reported to this client, it is reported
1326          * as usual. Otherwise, the event is reported with respect to
1327          * the grab_window and is reported only if selected by
1328          * event_mask. For either value of owner_events, unreported
1329          * events are discarded.
1330          *
1331          * This means we first try the owner, then the grab window,
1332          * then give up.
1333          */
1334         if (_gdk_quartz_pointer_grab_window)
1335           {
1336             if (pointer_grab_owner_events)
1337               {
1338                 mouse_window = find_mouse_window_for_ns_event (nsevent, x, y);
1339                 event_mask = get_event_mask_from_ns_event (nsevent);
1340                 real_window = find_window_interested_in_event_mask (mouse_window, event_mask, TRUE);
1341                 
1342                 if (mouse_window && real_window && mouse_window != real_window)
1343                   get_ancestor_coordinates_from_child (mouse_window,
1344                                                        *x, *y,
1345                                                        real_window,
1346                                                        x, y);
1347
1348                 if (real_window)
1349                   return real_window;
1350               }
1351
1352             /* Finally check the grab window. */
1353             if (pointer_grab_event_mask & get_event_mask_from_ns_event (nsevent))
1354               {
1355                 GdkWindow *event_toplevel;
1356                 GdkWindow *grab_toplevel;
1357                 NSPoint point;
1358                 int x_tmp, y_tmp;
1359
1360                 event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
1361                 grab_toplevel = gdk_window_get_toplevel (_gdk_quartz_pointer_grab_window);
1362                 point = [nsevent locationInWindow];
1363
1364                 x_tmp = point.x;
1365                 y_tmp = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (grab_toplevel)->impl)->height - point.y;
1366
1367                 /* Translate the coordinates so they are relative to
1368                  * the grab window instead of the event toplevel for
1369                  * the cases where they are not the same.
1370                  */
1371                 get_converted_window_coordinates (event_toplevel,
1372                                                   x_tmp, y_tmp,
1373                                                   _gdk_quartz_pointer_grab_window,
1374                                                   x, y);
1375
1376                 return _gdk_quartz_pointer_grab_window;
1377               }
1378
1379             return NULL;
1380           }
1381         else 
1382           {
1383             /* The non-grabbed case. */
1384             mouse_window = find_mouse_window_for_ns_event (nsevent, x, y);
1385             event_mask = get_event_mask_from_ns_event (nsevent);
1386             real_window = find_window_interested_in_event_mask (mouse_window, event_mask, TRUE);
1387             
1388             /* We have to translate the coordinates if the actual
1389              * window is different from the mouse window.
1390              */
1391             if (mouse_window && real_window && mouse_window != real_window)
1392               get_ancestor_coordinates_from_child (mouse_window,
1393                                                    *x, *y,
1394                                                    real_window,
1395                                                    x, y);
1396
1397             return real_window;
1398           }
1399       }
1400       break;
1401       
1402     case NSMouseEntered:
1403     case NSMouseExited:
1404       /* Already handled in synthesize_crossing_events_for_ns_event. */
1405       break;
1406
1407     case NSKeyDown:
1408     case NSKeyUp:
1409     case NSFlagsChanged:
1410       {
1411         GdkEventMask event_mask;
1412
1413         if (_gdk_quartz_keyboard_grab_window && !keyboard_grab_owner_events)
1414           return _gdk_quartz_keyboard_grab_window;
1415
1416         event_mask = get_event_mask_from_ns_event (nsevent);
1417         return find_window_interested_in_event_mask (current_keyboard_window, event_mask, TRUE);
1418       }
1419       break;
1420
1421     default:
1422       /* Ignore everything else. */
1423       break;
1424     }
1425
1426   return NULL;
1427 }
1428
1429 static GdkEvent *
1430 create_button_event (GdkWindow *window, 
1431                      NSEvent   *nsevent,
1432                      gint       x,
1433                      gint       y)
1434 {
1435   GdkEvent *event;
1436   GdkEventType type;
1437   gint state;
1438   gint button;
1439
1440   state = get_keyboard_modifiers_from_ns_event (nsevent);
1441
1442   switch ([nsevent type])
1443     {
1444     case NSLeftMouseDown:
1445     case NSRightMouseDown:
1446     case NSOtherMouseDown:
1447       type = GDK_BUTTON_PRESS;
1448       break;
1449     case NSLeftMouseUp:
1450     case NSRightMouseUp:
1451     case NSOtherMouseUp:
1452       type = GDK_BUTTON_RELEASE;
1453       state |= get_mouse_button_modifiers_from_ns_event (nsevent);
1454       break;
1455     default:
1456       g_assert_not_reached ();
1457     }
1458   
1459   button = get_mouse_button_from_ns_event (nsevent);
1460
1461   event = gdk_event_new (type);
1462   event->button.window = window;
1463   event->button.time = get_time_from_ns_event (nsevent);
1464   event->button.x = x;
1465   event->button.y = y;
1466   /* FIXME event->axes */
1467   event->button.state = state;
1468   event->button.button = button;
1469   event->button.device = _gdk_display->core_pointer;
1470   convert_window_coordinates_to_root (window, x, y, 
1471                                       &event->button.x_root,
1472                                       &event->button.y_root);
1473
1474   return event;
1475 }
1476
1477 static GdkEvent *
1478 create_motion_event (GdkWindow *window, 
1479                      NSEvent   *nsevent, 
1480                      gint       x, 
1481                      gint       y)
1482 {
1483   GdkEvent *event;
1484   GdkEventType type;
1485   GdkModifierType state = 0;
1486
1487   switch ([nsevent type])
1488     {
1489     case NSLeftMouseDragged:
1490     case NSRightMouseDragged:
1491     case NSOtherMouseDragged:
1492       state = get_mouse_button_modifiers_from_ns_event (nsevent);
1493       /* Fall through */
1494     case NSMouseMoved:
1495       type = GDK_MOTION_NOTIFY;
1496       break;
1497     default:
1498       g_assert_not_reached ();
1499     }
1500
1501   state |= get_keyboard_modifiers_from_ns_event (nsevent);
1502
1503   event = gdk_event_new (type);
1504   event->motion.window = window;
1505   event->motion.time = get_time_from_ns_event (nsevent);
1506   event->motion.x = x;
1507   event->motion.y = y;
1508   /* FIXME event->axes */
1509   event->motion.state = state;
1510   event->motion.is_hint = FALSE;
1511   event->motion.device = _gdk_display->core_pointer;
1512   convert_window_coordinates_to_root (window, x, y,
1513                                       &event->motion.x_root, &event->motion.y_root);
1514   
1515   return event;
1516 }
1517
1518 static GdkEvent *
1519 create_scroll_event (GdkWindow          *window, 
1520                      NSEvent            *nsevent, 
1521                      GdkScrollDirection  direction)
1522 {
1523   GdkEvent *event;
1524   NSPoint point;
1525   
1526   event = gdk_event_new (GDK_SCROLL);
1527   event->scroll.window = window;
1528   event->scroll.time = get_time_from_ns_event (nsevent);
1529
1530   point = [nsevent locationInWindow];
1531   event->scroll.x = point.x;
1532   event->scroll.y = point.y;
1533   event->scroll.state = get_keyboard_modifiers_from_ns_event (nsevent);
1534   convert_window_coordinates_to_root (window, event->scroll.x, event->scroll.y, 
1535                                       &event->scroll.x_root,
1536                                       &event->scroll.y_root);
1537
1538   event->scroll.direction = direction;
1539   event->scroll.device = _gdk_display->core_pointer;
1540   
1541   return event;
1542 }
1543
1544 static GdkEvent *
1545 create_key_event (GdkWindow    *window, 
1546                   NSEvent      *nsevent, 
1547                   GdkEventType  type)
1548 {
1549   GdkEvent *event;
1550   GdkEventPrivate *priv;
1551   gchar buf[7];
1552   gunichar c = 0;
1553
1554   event = gdk_event_new (type);
1555
1556   priv = (GdkEventPrivate *) event;
1557   priv->windowing_data = [nsevent retain];
1558
1559   event->key.window = window;
1560   event->key.time = get_time_from_ns_event (nsevent);
1561   event->key.state = get_keyboard_modifiers_from_ns_event (nsevent);
1562   event->key.hardware_keycode = [nsevent keyCode];
1563   event->key.group = ([nsevent modifierFlags] & NSAlternateKeyMask) ? 1 : 0;
1564
1565   event->key.keyval = GDK_VoidSymbol;
1566   
1567   gdk_keymap_translate_keyboard_state (NULL,
1568                                        event->key.hardware_keycode,
1569                                        event->key.state, 
1570                                        event->key.group,
1571                                        &event->key.keyval,
1572                                        NULL, NULL, NULL);
1573
1574   event->key.is_modifier = _gdk_quartz_keys_is_modifier (event->key.hardware_keycode);
1575
1576   /* If the key press is a modifier, the state should include the mask
1577    * for that modifier but only for releases, not presses. This
1578    * matches the X11 backend behavior.
1579    */
1580   if (event->key.is_modifier)
1581     {
1582       int mask = 0;
1583
1584       switch (event->key.keyval)
1585         {
1586         case GDK_Meta_R:
1587         case GDK_Meta_L:
1588           mask = GDK_MOD1_MASK;
1589           break;
1590         case GDK_Shift_R:
1591         case GDK_Shift_L:
1592           mask = GDK_SHIFT_MASK;
1593           break;
1594         case GDK_Caps_Lock:
1595           mask = GDK_LOCK_MASK;
1596           break;
1597         case GDK_Alt_R:
1598         case GDK_Alt_L:
1599           mask = GDK_MOD5_MASK;
1600           break;
1601         case GDK_Control_R:
1602         case GDK_Control_L:
1603           mask = GDK_CONTROL_MASK;
1604           break;
1605         default:
1606           mask = 0;
1607         }
1608
1609       if (type == GDK_KEY_PRESS)
1610         event->key.state &= ~mask;
1611       else if (type == GDK_KEY_RELEASE)
1612         event->key.state |= mask;
1613     }
1614
1615   event->key.state |= current_button_state;
1616
1617   event->key.string = NULL;
1618
1619   /* Fill in ->string since apps depend on it, taken from the x11 backend. */
1620   if (event->key.keyval != GDK_VoidSymbol)
1621     c = gdk_keyval_to_unicode (event->key.keyval);
1622
1623     if (c)
1624     {
1625       gsize bytes_written;
1626       gint len;
1627
1628       len = g_unichar_to_utf8 (c, buf);
1629       buf[len] = '\0';
1630       
1631       event->key.string = g_locale_from_utf8 (buf, len,
1632                                               NULL, &bytes_written,
1633                                               NULL);
1634       if (event->key.string)
1635         event->key.length = bytes_written;
1636     }
1637   else if (event->key.keyval == GDK_Escape)
1638     {
1639       event->key.length = 1;
1640       event->key.string = g_strdup ("\033");
1641     }
1642   else if (event->key.keyval == GDK_Return ||
1643           event->key.keyval == GDK_KP_Enter)
1644     {
1645       event->key.length = 1;
1646       event->key.string = g_strdup ("\r");
1647     }
1648
1649   if (!event->key.string)
1650     {
1651       event->key.length = 0;
1652       event->key.string = g_strdup ("");
1653     }
1654
1655   GDK_NOTE(EVENTS,
1656     g_message ("key %s:\t\twindow: %p  key: %12s  %d",
1657           type == GDK_KEY_PRESS ? "press" : "release",
1658           event->key.window,
1659           event->key.keyval ? gdk_keyval_name (event->key.keyval) : "(none)",
1660           event->key.keyval));
1661   return event;
1662 }
1663
1664 GdkEventMask 
1665 _gdk_quartz_events_get_current_event_mask (void)
1666 {
1667   return current_event_mask;
1668 }
1669
1670 static gboolean
1671 gdk_event_translate (NSEvent *nsevent)
1672 {
1673   NSWindow *nswindow;
1674   GdkWindow *window;
1675   GdkFilterReturn result;
1676   GdkEvent *event;
1677   int x, y;
1678
1679   /* There is no support for real desktop wide grabs, so we break
1680    * grabs when the application loses focus (gets deactivated).
1681    */
1682   if ([nsevent type] == NSAppKitDefined)
1683     {
1684       if ([nsevent subtype] == NSApplicationDeactivatedEventType)
1685         break_all_grabs ();
1686
1687       /* This could potentially be used to break grabs when clicking
1688        * on the title. The subtype 20 is undocumented so it's probably
1689        * not a good idea: else if (subtype == 20) break_all_grabs ();
1690        */
1691     }
1692
1693   /* Handle our generated "fake" crossing events. */
1694   if ([nsevent type] == NSApplicationDefined && 
1695       [nsevent subtype] == GDK_QUARTZ_EVENT_SUBTYPE_FAKE_CROSSING)
1696     {
1697       _gdk_quartz_events_trigger_crossing_events (FALSE);
1698       return TRUE;
1699     }
1700
1701   /* Keep track of button state, since we don't get that information
1702    * for key events. 
1703    */
1704   switch ([nsevent type])
1705     {
1706     case NSLeftMouseDown:
1707     case NSRightMouseDown:
1708     case NSOtherMouseDown:
1709       current_button_state |= get_mouse_button_modifiers_from_ns_event (nsevent);
1710       break;
1711     case NSLeftMouseUp:
1712     case NSRightMouseUp:
1713     case NSOtherMouseUp:
1714       current_button_state &= ~get_mouse_button_modifiers_from_ns_event (nsevent);
1715       break;
1716     default:
1717       break;
1718     }
1719
1720   nswindow = [nsevent window];
1721
1722   /* Apply any global filters. */
1723   if (_gdk_default_filters)
1724     {
1725       result = apply_filters (NULL, nsevent, _gdk_default_filters);
1726
1727       /* If result is GDK_FILTER_CONTINUE, we continue as if nothing
1728        * happened. If it is GDK_FILTER_REMOVE,
1729        * we return TRUE and won't send the message to Quartz.
1730        */
1731       if (result == GDK_FILTER_REMOVE)
1732         return TRUE;
1733     }
1734
1735   /* Ignore events for no window or ones not created by GDK. */
1736   if (!nswindow || ![[nswindow contentView] isKindOfClass:[GdkQuartzView class]])
1737     return FALSE;
1738
1739   /* Ignore events and break grabs while the window is being
1740    * dragged. This is a workaround for the window getting events for
1741    * the window title.
1742    */
1743   if ([(GdkQuartzWindow *)nswindow isInMove])
1744     {
1745       break_all_grabs ();
1746       return FALSE;
1747     }
1748
1749   /* Take care of NSMouseEntered/Exited events and mouse movements
1750    * events and emit the right GDK crossing events.
1751    */
1752   synthesize_crossing_events_for_ns_event (nsevent);
1753
1754   /* Find the right GDK window to send the event to, taking grabs and
1755    * event masks into consideration.
1756    */
1757   window = find_window_for_ns_event (nsevent, &x, &y);
1758   if (!window)
1759     return FALSE;
1760
1761   /* Apply any window filters. */
1762   result = apply_filters (window, nsevent, ((GdkWindowObject *) window)->filters);
1763   if (result == GDK_FILTER_REMOVE)
1764     return TRUE;
1765
1766   /* We need the appliction to be activated on clicks so that popups
1767    * like context menus get events routed properly. This is handled
1768    * automatically for left mouse button presses but not other
1769    * buttons, so we do it here.
1770    */
1771   if ([nsevent type] == NSRightMouseDown || [nsevent type] == NSOtherMouseDown)
1772     {
1773       if (![NSApp isActive])
1774         [NSApp activateIgnoringOtherApps:YES];
1775     }
1776
1777   current_event_mask = get_event_mask_from_ns_event (nsevent);
1778
1779   switch ([nsevent type])
1780     {
1781     case NSLeftMouseDown:
1782     case NSRightMouseDown:
1783     case NSOtherMouseDown:
1784       {
1785         GdkEventMask event_mask;
1786
1787         /* Emulate implicit grab, when the window has both PRESS and RELEASE
1788          * in its mask, like X.
1789          */
1790         event_mask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
1791         if (!_gdk_quartz_pointer_grab_window &&
1792             (GDK_WINDOW_OBJECT (window)->event_mask & event_mask) == event_mask)
1793           {
1794             pointer_grab_internal (window, FALSE,
1795                                    GDK_WINDOW_OBJECT (window)->event_mask,
1796                                    NULL, NULL, TRUE);
1797           }
1798       }
1799       
1800       event = create_button_event (window, nsevent, x, y);
1801       append_event (event);
1802       
1803       _gdk_event_button_generate (_gdk_display, event);
1804       break;
1805
1806     case NSLeftMouseUp:
1807     case NSRightMouseUp:
1808     case NSOtherMouseUp:
1809       event = create_button_event (window, nsevent, x, y);
1810       append_event (event);
1811       
1812       /* Ungrab implicit grab */
1813       if (_gdk_quartz_pointer_grab_window && pointer_grab_implicit)
1814         pointer_ungrab_internal (TRUE);
1815       break;
1816
1817     case NSLeftMouseDragged:
1818     case NSRightMouseDragged:
1819     case NSOtherMouseDragged:
1820     case NSMouseMoved:
1821       event = create_motion_event (window, nsevent, x, y);
1822       append_event (event);
1823       break;
1824
1825     case NSScrollWheel:
1826       {
1827         float dx = [nsevent deltaX];
1828         float dy = [nsevent deltaY];
1829         GdkScrollDirection direction;
1830
1831         /* The delta is how much the mouse wheel has moved. Since there's no such thing in GTK+
1832          * we accomodate by sending a different number of scroll wheel events.
1833          */
1834
1835         /* First do y events */
1836         if (dy < 0.0)
1837           {
1838             dy = -dy;
1839             direction = GDK_SCROLL_DOWN;
1840           }
1841         else
1842           direction = GDK_SCROLL_UP;
1843
1844         while (dy > 0.0)
1845           {
1846             event = create_scroll_event (window, nsevent, direction);
1847             append_event (event);
1848             dy--;
1849
1850             /* Ignore the delta for now, things get too slow when the events queue up. */
1851             break;
1852           }
1853
1854         /* Now do x events */
1855         if (dx < 0.0)
1856           {
1857             dx = -dx;
1858             direction = GDK_SCROLL_RIGHT;
1859           }
1860         else
1861           direction = GDK_SCROLL_LEFT;
1862
1863         while (dx > 0.0)
1864           {
1865             event = create_scroll_event (window, nsevent, direction);
1866             append_event (event);
1867             dx--;
1868             
1869             /* Ignore the delta for now, things get too slow when the events queue up. */
1870             break;
1871           }
1872
1873       }
1874       break;
1875
1876     case NSKeyDown:
1877     case NSKeyUp:
1878     case NSFlagsChanged:
1879       {
1880         GdkEventType type;
1881
1882         type = _gdk_quartz_keys_event_type (nsevent);
1883         if (type == GDK_NOTHING)
1884           return FALSE;
1885         
1886         event = create_key_event (window, nsevent, type);
1887         append_event (event);
1888         return TRUE;
1889       }
1890       break;
1891
1892     default:
1893       /* Ignore everything elsee. */
1894       break;
1895     }
1896
1897   return FALSE;
1898 }
1899
1900 void
1901 _gdk_events_queue (GdkDisplay *display)
1902 {  
1903   NSEvent *event;
1904
1905   event = _gdk_quartz_event_loop_get_pending ();
1906   if (event)
1907     {
1908       if (!gdk_event_translate (event))
1909         {
1910           GDK_THREADS_LEAVE ();
1911           [NSApp sendEvent:event];
1912           GDK_THREADS_ENTER ();
1913         }
1914
1915       _gdk_quartz_event_loop_release_event (event);
1916     }
1917 }
1918
1919 void
1920 gdk_flush (void)
1921 {
1922   /* Not supported. */
1923 }
1924
1925 void 
1926 gdk_display_add_client_message_filter (GdkDisplay   *display,
1927                                        GdkAtom       message_type,
1928                                        GdkFilterFunc func,
1929                                        gpointer      data)
1930 {
1931   /* Not supported. */
1932 }
1933
1934 void 
1935 gdk_add_client_message_filter (GdkAtom       message_type,
1936                                GdkFilterFunc func,
1937                                gpointer      data)
1938 {
1939   /* Not supported. */
1940 }
1941
1942 void
1943 gdk_display_sync (GdkDisplay *display)
1944 {
1945   /* Not supported. */
1946 }
1947
1948 void
1949 gdk_display_flush (GdkDisplay *display)
1950 {
1951   /* Not supported. */
1952 }
1953
1954 gboolean
1955 gdk_event_send_client_message_for_display (GdkDisplay      *display,
1956                                            GdkEvent        *event,
1957                                            GdkNativeWindow  winid)
1958 {
1959   /* Not supported. */
1960   return FALSE;
1961 }
1962
1963 void
1964 gdk_screen_broadcast_client_message (GdkScreen *screen,
1965                                      GdkEvent  *event)
1966 {
1967   /* Not supported. */
1968 }
1969
1970 gboolean
1971 gdk_screen_get_setting (GdkScreen   *screen,
1972                         const gchar *name,
1973                         GValue      *value)
1974 {
1975   if (strcmp (name, "gtk-double-click-time") == 0)
1976     {
1977       NSUserDefaults *defaults;
1978       float t;
1979
1980       GDK_QUARTZ_ALLOC_POOL;
1981
1982       defaults = [NSUserDefaults standardUserDefaults];
1983             
1984       t = [defaults floatForKey:@"com.apple.mouse.doubleClickThreshold"];
1985       if (t == 0.0)
1986         {
1987           /* No user setting, use the default in OS X. */
1988           t = 0.5;
1989         }
1990
1991       GDK_QUARTZ_RELEASE_POOL;
1992
1993       g_value_set_int (value, t * 1000);
1994
1995       return TRUE;
1996     }
1997   else if (strcmp (name, "gtk-font-name") == 0)
1998     {
1999       NSString *name;
2000       char *str;
2001
2002       GDK_QUARTZ_ALLOC_POOL;
2003
2004       name = [[NSFont systemFontOfSize:0] familyName];
2005
2006       /* Let's try to use the "views" font size (12pt) by default. This is
2007        * used for lists/text/other "content" which is the largest parts of
2008        * apps, using the "regular control" size (13pt) looks a bit out of
2009        * place. We might have to tweak this.
2010        */
2011
2012       /* The size has to be hardcoded as there doesn't seem to be a way to
2013        * get the views font size programmatically.
2014        */
2015       str = g_strdup_printf ("%s 12", [name UTF8String]);
2016       g_value_set_string (value, str);
2017       g_free (str);
2018
2019       GDK_QUARTZ_RELEASE_POOL;
2020
2021       return TRUE;
2022     }
2023   
2024   /* FIXME: Add more settings */
2025
2026   return FALSE;
2027 }
2028
2029 void
2030 _gdk_windowing_event_data_copy (const GdkEvent *src,
2031                                 GdkEvent       *dst)
2032 {
2033   GdkEventPrivate *priv_src = (GdkEventPrivate *) src;
2034   GdkEventPrivate *priv_dst = (GdkEventPrivate *) dst;
2035
2036   if (priv_src->windowing_data)
2037     {
2038       priv_dst->windowing_data = priv_src->windowing_data;
2039       [(NSEvent *)priv_dst->windowing_data retain];
2040     }
2041 }
2042
2043 void
2044 _gdk_windowing_event_data_free (GdkEvent *event)
2045 {
2046   GdkEventPrivate *priv = (GdkEventPrivate *) event;
2047
2048   if (priv->windowing_data)
2049     {
2050       [(NSEvent *)priv->windowing_data release];
2051       priv->windowing_data = NULL;
2052     }
2053 }