]> Pileus Git - ~andy/gtk/blob - gdk/quartz/gdkevents-quartz.c
Fix typo, we need both press and release in the mask to trigger implicit
[~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-2006 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 #include <Carbon/Carbon.h>
30
31 #include "gdkscreen.h"
32 #include "gdkkeysyms.h"
33
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 static void append_event (GdkEvent *event);
53
54 void 
55 _gdk_events_init (void)
56 {
57   _gdk_quartz_event_loop_init ();
58
59   current_mouse_window = g_object_ref (_gdk_root);
60   current_keyboard_window = g_object_ref (_gdk_root);
61 }
62
63 gboolean
64 gdk_events_pending (void)
65 {
66   return (_gdk_event_queue_find_first (_gdk_display) ||
67           (_gdk_quartz_event_loop_get_current () != NULL));
68 }
69
70 GdkEvent*
71 gdk_event_get_graphics_expose (GdkWindow *window)
72 {
73   /* FIXME: Implement */
74   return NULL;
75 }
76
77 static void
78 generate_grab_broken_event (GdkWindow *window,
79                             gboolean   keyboard,
80                             gboolean   implicit,
81                             GdkWindow *grab_window)
82 {
83   if (!GDK_WINDOW_DESTROYED (window))
84     {
85       GdkEvent *event = gdk_event_new (GDK_GRAB_BROKEN);
86
87       event->grab_broken.window = window;
88       event->grab_broken.send_event = 0;
89       event->grab_broken.keyboard = keyboard;
90       event->grab_broken.implicit = implicit;
91       event->grab_broken.grab_window = grab_window;
92       
93       append_event (event);
94     }
95 }
96
97 GdkGrabStatus
98 gdk_keyboard_grab (GdkWindow  *window,
99                    gint        owner_events,
100                    guint32     time)
101 {
102   g_return_val_if_fail (window != NULL, 0);
103   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
104
105   if (_gdk_quartz_keyboard_grab_window)
106     {
107       if (_gdk_quartz_keyboard_grab_window != window)
108         generate_grab_broken_event (_gdk_quartz_keyboard_grab_window,
109                                     TRUE, FALSE, window);
110       
111       g_object_unref (_gdk_quartz_keyboard_grab_window);
112     }
113
114   _gdk_quartz_keyboard_grab_window = g_object_ref (window);
115   keyboard_grab_owner_events = owner_events;
116
117   return GDK_GRAB_SUCCESS;
118 }
119
120 void
121 gdk_display_keyboard_ungrab (GdkDisplay *display,
122                              guint32     time)
123 {
124   if (_gdk_quartz_keyboard_grab_window)
125     g_object_unref (_gdk_quartz_keyboard_grab_window);
126   _gdk_quartz_keyboard_grab_window = NULL;
127 }
128
129 gboolean
130 gdk_keyboard_grab_info_libgtk_only (GdkDisplay *display,
131                                     GdkWindow **grab_window,
132                                     gboolean   *owner_events)
133 {
134   if (_gdk_quartz_keyboard_grab_window) 
135     {
136       if (grab_window)
137         *grab_window = _gdk_quartz_keyboard_grab_window;
138       if (owner_events)
139         *owner_events = keyboard_grab_owner_events;
140
141       return TRUE;
142     }
143
144   return FALSE;
145 }
146
147 static void
148 pointer_ungrab_internal (gboolean only_if_implicit)
149 {
150   if (!_gdk_quartz_pointer_grab_window)
151     return;
152
153   if (only_if_implicit && !pointer_grab_implicit)
154     return;
155
156   g_object_unref (_gdk_quartz_pointer_grab_window);
157   _gdk_quartz_pointer_grab_window = NULL;
158
159   pointer_grab_owner_events = FALSE;
160   pointer_grab_event_mask = 0;
161   pointer_grab_implicit = FALSE;
162
163   /* FIXME: Send crossing events */
164 }
165
166 gboolean
167 gdk_display_pointer_is_grabbed (GdkDisplay *display)
168 {
169   return _gdk_quartz_pointer_grab_window != NULL;
170 }
171
172 gboolean
173 gdk_pointer_grab_info_libgtk_only (GdkDisplay *display,
174                                    GdkWindow **grab_window,
175                                    gboolean   *owner_events)
176 {
177   if (!_gdk_quartz_pointer_grab_window)
178     return FALSE;
179
180   if (grab_window)
181     *grab_window = _gdk_quartz_pointer_grab_window;
182
183   if (owner_events)
184     *owner_events = pointer_grab_owner_events;
185
186   return FALSE;
187 }
188
189 void
190 gdk_display_pointer_ungrab (GdkDisplay *display,
191                             guint32     time)
192 {
193   pointer_ungrab_internal (FALSE);
194 }
195
196 static GdkGrabStatus
197 pointer_grab_internal (GdkWindow    *window,
198                        gboolean      owner_events,
199                        GdkEventMask  event_mask,
200                        GdkWindow    *confine_to,
201                        GdkCursor    *cursor,
202                        gboolean      implicit)
203 {
204   /* FIXME: Send crossing events */
205   
206   _gdk_quartz_pointer_grab_window = g_object_ref (window);
207   pointer_grab_owner_events = owner_events;
208   pointer_grab_event_mask = event_mask;
209   pointer_grab_implicit = implicit;
210
211   return GDK_GRAB_SUCCESS;
212 }
213
214 GdkGrabStatus
215 gdk_pointer_grab (GdkWindow    *window,
216                   gboolean      owner_events,
217                   GdkEventMask  event_mask,
218                   GdkWindow    *confine_to,
219                   GdkCursor    *cursor,
220                   guint32       time)
221 {
222   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
223   g_return_val_if_fail (confine_to == NULL || GDK_IS_WINDOW (confine_to), 0);
224
225   if (_gdk_quartz_pointer_grab_window)
226     {
227       if (_gdk_quartz_pointer_grab_window == window && !pointer_grab_implicit)
228         return GDK_GRAB_ALREADY_GRABBED;
229       else
230         {
231           if (_gdk_quartz_pointer_grab_window != window)
232             generate_grab_broken_event (_gdk_quartz_pointer_grab_window,
233                                         FALSE, pointer_grab_implicit, window);
234           pointer_ungrab_internal (TRUE);
235         }
236     }
237
238   return pointer_grab_internal (window, owner_events, event_mask, 
239                                 confine_to, cursor, FALSE);
240 }
241
242 static void
243 fixup_event (GdkEvent *event)
244 {
245   if (event->any.window)
246     g_object_ref (event->any.window);
247   if (((event->any.type == GDK_ENTER_NOTIFY) ||
248        (event->any.type == GDK_LEAVE_NOTIFY)) &&
249       (event->crossing.subwindow != NULL))
250     g_object_ref (event->crossing.subwindow);
251   event->any.send_event = FALSE;
252 }
253
254 static void
255 append_event (GdkEvent *event)
256 {
257   fixup_event (event);
258   _gdk_event_queue_append (_gdk_display, event);
259 }
260
261 static GdkFilterReturn
262 apply_filters (GdkWindow  *window,
263                NSEvent    *nsevent,
264                GList      *filters)
265 {
266   GdkFilterReturn result = GDK_FILTER_CONTINUE;
267   GdkEvent *event;
268   GList *node;
269   GList *tmp_list;
270
271   event = gdk_event_new (GDK_NOTHING);
272   if (window != NULL)
273     event->any.window = g_object_ref (window);
274   ((GdkEventPrivate *)event)->flags |= GDK_EVENT_PENDING;
275
276   /* I think GdkFilterFunc semantics require the passed-in event
277    * to already be in the queue. The filter func can generate
278    * more events and append them after it if it likes.
279    */
280   node = _gdk_event_queue_append (_gdk_display, event);
281   
282   tmp_list = filters;
283   while (tmp_list)
284     {
285       GdkEventFilter *filter = (GdkEventFilter *) tmp_list->data;
286       
287       tmp_list = tmp_list->next;
288       result = filter->function (nsevent, event, filter->data);
289       if (result != GDK_FILTER_CONTINUE)
290         break;
291     }
292
293   if (result == GDK_FILTER_CONTINUE || result == GDK_FILTER_REMOVE)
294     {
295       _gdk_event_queue_remove_link (_gdk_display, node);
296       g_list_free_1 (node);
297       gdk_event_free (event);
298     }
299   else /* GDK_FILTER_TRANSLATE */
300     {
301       ((GdkEventPrivate *)event)->flags &= ~GDK_EVENT_PENDING;
302       fixup_event (event);
303     }
304   return result;
305 }
306
307 /* Checks if the passed in window is interested in the event mask, and
308  * if so, it's returned. If not, the event can be propagated through
309  * its ancestors until one with the right event mask is found, up to
310  * the nearest toplevel.
311  */
312 static GdkWindow *
313 find_window_interested_in_event_mask (GdkWindow    *window, 
314                                       GdkEventMask  event_mask,
315                                       gboolean      propagate)
316 {
317   GdkWindowObject *private;
318
319   private = GDK_WINDOW_OBJECT (window);
320   while (private)
321     {
322       if (private->event_mask & event_mask)
323         return (GdkWindow *)private;
324
325       if (!propagate)
326         return NULL;
327
328       /* Don't traverse beyond toplevels. */
329       if (GDK_WINDOW_TYPE (private) != GDK_WINDOW_CHILD)
330         break;
331
332       private = private->parent;
333     }
334
335   return NULL;
336 }
337
338 static guint32
339 get_event_time (NSEvent *event)
340 {
341   double time = [event timestamp];
342   
343   return time * 1000.0;
344 }
345
346 static int
347 convert_mouse_button_number (int button)
348 {
349   switch (button)
350     {
351     case 0:
352       return 1;
353     case 1:
354       return 3;
355     case 2:
356       return 2;
357     default:
358       return button + 1;
359     }
360 }
361
362 /* Return an event mask from an NSEvent */
363 static GdkEventMask
364 get_event_mask_from_ns_event (NSEvent *nsevent)
365 {
366   switch ([nsevent type])
367     {
368     case NSLeftMouseDown:
369     case NSRightMouseDown:
370     case NSOtherMouseDown:
371       return GDK_BUTTON_PRESS_MASK;
372     case NSLeftMouseUp:
373     case NSRightMouseUp:
374     case NSOtherMouseUp:
375       return GDK_BUTTON_RELEASE_MASK;
376     case NSMouseMoved:
377       return GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
378     case NSScrollWheel:
379       /* Since applications that want button press events can get
380        * scroll events on X11 (since scroll wheel events are really
381        * button press events there), we need to use GDK_BUTTON_PRESS_MASK too.
382        */
383       return GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK;
384     case NSLeftMouseDragged:
385       return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
386               GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | 
387               GDK_BUTTON1_MASK);
388     case NSRightMouseDragged:
389       return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
390               GDK_BUTTON_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | 
391               GDK_BUTTON3_MASK);
392     case NSOtherMouseDragged:
393       {
394         GdkEventMask mask;
395
396         mask = (GDK_POINTER_MOTION_MASK |
397                 GDK_POINTER_MOTION_HINT_MASK |
398                 GDK_BUTTON_MOTION_MASK);
399
400         if (convert_mouse_button_number ([nsevent buttonNumber]) == 2)
401           mask |= (GDK_BUTTON2_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | 
402                    GDK_BUTTON2_MASK);
403
404         return mask;
405       }
406     case NSKeyDown:
407     case NSKeyUp:
408     case NSFlagsChanged:
409       {
410         switch (_gdk_quartz_keys_event_type (nsevent))
411           {
412           case GDK_KEY_PRESS:
413             return GDK_KEY_PRESS_MASK;
414           case GDK_KEY_RELEASE:
415             return GDK_KEY_RELEASE_MASK;
416           case GDK_NOTHING:
417             return 0;
418           default:
419             g_assert_not_reached ();
420           }
421       }
422     default:
423       g_assert_not_reached ();
424     }
425
426   return 0;
427 }
428
429 static GdkEvent *
430 create_focus_event (GdkWindow *window,
431                     gboolean   in)
432 {
433   GdkEvent *event;
434
435   event = gdk_event_new (GDK_FOCUS_CHANGE);
436   event->focus_change.window = window;
437   event->focus_change.in = in;
438
439   return event;
440 }
441
442 /* Note: Used to both set a new focus window and to unset the old one. */
443 void
444 _gdk_quartz_events_update_focus_window (GdkWindow *window,
445                                         gboolean   got_focus)
446 {
447   GdkEvent *event;
448
449   if (got_focus && window == current_keyboard_window)
450     return;
451
452   /* FIXME: Don't do this when grabbed? Or make GdkQuartzWindow
453    * disallow it in the first place instead?
454    */
455   
456   if (!got_focus && window == current_keyboard_window)
457     {
458           event = create_focus_event (current_keyboard_window, FALSE);
459           append_event (event);
460           g_object_unref (current_keyboard_window);
461           current_keyboard_window = NULL;
462     }
463
464   if (got_focus)
465     {
466       if (current_keyboard_window)
467         {
468           event = create_focus_event (current_keyboard_window, FALSE);
469           append_event (event);
470           g_object_unref (current_keyboard_window);
471           current_keyboard_window = NULL;
472         }
473       
474       event = create_focus_event (window, TRUE);
475       append_event (event);
476       current_keyboard_window = g_object_ref (window);
477     }
478 }
479
480 static gboolean
481 gdk_window_is_ancestor (GdkWindow *ancestor,
482                         GdkWindow *window)
483 {
484   if (ancestor == NULL || window == NULL)
485     return FALSE;
486
487   return (gdk_window_get_parent (window) == ancestor ||
488           gdk_window_is_ancestor (ancestor, gdk_window_get_parent (window)));
489 }
490
491 static GdkModifierType
492 get_keyboard_modifiers_from_nsevent (NSEvent *nsevent)
493 {
494   GdkModifierType modifiers = 0;
495   int nsflags;
496
497   nsflags = [nsevent modifierFlags];
498   
499   if (nsflags & NSAlphaShiftKeyMask)
500     modifiers |= GDK_LOCK_MASK;
501   if (nsflags & NSShiftKeyMask)
502     modifiers |= GDK_SHIFT_MASK;
503   if (nsflags & NSControlKeyMask)
504     modifiers |= GDK_CONTROL_MASK;
505   if (nsflags & NSCommandKeyMask)
506     modifiers |= GDK_MOD1_MASK;
507
508   /* FIXME: Support GDK_BUTTON_MASK */
509
510   return modifiers;
511 }
512
513 static void
514 convert_window_coordinates_to_root (GdkWindow *window,
515                                     gdouble    x,
516                                     gdouble    y,
517                                     gdouble   *x_root,
518                                     gdouble   *y_root)
519 {
520   gint ox, oy;
521
522   *x_root = x;
523   *y_root = y;
524   
525   if (gdk_window_get_origin (window, &ox, &oy))
526     {
527       *x_root += ox;
528       *y_root += oy;
529     }
530 }
531
532 static GdkEvent *
533 create_crossing_event (GdkWindow      *window, 
534                        NSEvent        *nsevent, 
535                        GdkEventType    event_type,
536                        GdkCrossingMode mode, 
537                        GdkNotifyType   detail)
538 {
539   GdkEvent *event;
540   NSPoint point;
541
542   event = gdk_event_new (event_type);
543   
544   event->crossing.window = window;
545   event->crossing.subwindow = NULL; /* FIXME */
546   event->crossing.time = get_event_time (nsevent);
547
548   point = [nsevent locationInWindow];
549   event->crossing.x = point.x;
550   event->crossing.y = point.y;
551   convert_window_coordinates_to_root (window, event->crossing.x, event->crossing.y, 
552                                       &event->crossing.x_root,
553                                       &event->crossing.y_root);
554
555   event->crossing.mode = mode;
556   event->crossing.detail = detail;
557   /* FIXME: focus */
558   /* FIXME: state, (button state too) */
559
560   return event;
561 }
562
563 static void
564 synthesize_enter_event (GdkWindow      *window,
565                         NSEvent        *nsevent,
566                         GdkCrossingMode mode,
567                         GdkNotifyType   detail)
568 {
569   GdkEvent *event;
570
571   if (_gdk_quartz_pointer_grab_window != NULL && 
572       !pointer_grab_owner_events && 
573       !(pointer_grab_event_mask & GDK_ENTER_NOTIFY_MASK))
574     return;
575
576   if (!(GDK_WINDOW_OBJECT (window)->event_mask & GDK_ENTER_NOTIFY_MASK))
577     return;
578
579   event = create_crossing_event (window, nsevent, GDK_ENTER_NOTIFY,
580                                  mode, detail);
581
582   append_event (event);
583 }
584   
585 static void
586 synthesize_enter_events (GdkWindow      *from,
587                          GdkWindow      *to,
588                          NSEvent        *nsevent,
589                          GdkCrossingMode mode,
590                          GdkNotifyType   detail)
591 {
592   GdkWindow *prev = gdk_window_get_parent (to);
593
594   if (prev != from)
595     synthesize_enter_events (from, prev, nsevent, mode, detail);
596   synthesize_enter_event (to, nsevent, mode, detail);
597 }
598
599 static void
600 synthesize_leave_event (GdkWindow      *window,
601                         NSEvent        *nsevent,
602                         GdkCrossingMode mode,
603                         GdkNotifyType   detail)
604 {
605   GdkEvent *event;
606
607   if (_gdk_quartz_pointer_grab_window != NULL && 
608       !pointer_grab_owner_events && 
609       !(pointer_grab_event_mask & GDK_LEAVE_NOTIFY_MASK))
610     return;
611
612   if (!(GDK_WINDOW_OBJECT (window)->event_mask & GDK_LEAVE_NOTIFY_MASK))
613     return;
614
615   event = create_crossing_event (window, nsevent, GDK_LEAVE_NOTIFY,
616                                  mode, detail);
617
618   append_event (event);
619 }
620                          
621 static void
622 synthesize_leave_events (GdkWindow      *from,
623                          GdkWindow      *to,
624                          NSEvent        *nsevent,
625                          GdkCrossingMode mode,
626                          GdkNotifyType   detail)
627 {
628   GdkWindow *next = gdk_window_get_parent (from);
629   
630   synthesize_leave_event (from, nsevent, mode, detail);
631   if (next != to)
632     synthesize_leave_events (next, to, nsevent, mode, detail);
633 }
634                          
635 static void
636 synthesize_crossing_events (GdkWindow      *window,
637                             GdkCrossingMode mode,
638                             NSEvent        *nsevent,
639                             gint            x,
640                             gint            y)
641 {
642   GdkWindow *intermediate, *tem, *common_ancestor;
643
644   if (gdk_window_is_ancestor (current_mouse_window, window))
645     {
646       /* Pointer has moved to an inferior window. */
647       synthesize_leave_event (current_mouse_window, nsevent, mode, GDK_NOTIFY_INFERIOR);
648
649       /* If there are intermediate windows, generate ENTER_NOTIFY
650        * events for them
651        */
652       intermediate = gdk_window_get_parent (window);
653
654       if (intermediate != current_mouse_window)
655         {
656           synthesize_enter_events (current_mouse_window, intermediate, nsevent, mode, GDK_NOTIFY_VIRTUAL);
657         }
658
659       synthesize_enter_event (window, nsevent, mode, GDK_NOTIFY_ANCESTOR);
660     }
661   else if (gdk_window_is_ancestor (window, current_mouse_window))
662     {
663       /* Pointer has moved to an ancestor window. */
664       synthesize_leave_event (current_mouse_window, nsevent, mode, GDK_NOTIFY_ANCESTOR);
665       
666       /* If there are intermediate windows, generate LEAVE_NOTIFY
667        * events for them
668        */
669       intermediate = gdk_window_get_parent (current_mouse_window);
670       if (intermediate != window)
671         {
672           synthesize_leave_events (intermediate, window, nsevent, mode, GDK_NOTIFY_VIRTUAL);
673         }
674
675       synthesize_enter_event (window, nsevent, mode, GDK_NOTIFY_INFERIOR);
676     }
677   else if (current_mouse_window)
678     {
679       /* Find least common ancestor of current_mouse_window and window */
680       tem = current_mouse_window;
681       do {
682         common_ancestor = gdk_window_get_parent (tem);
683         tem = common_ancestor;
684       } while (common_ancestor &&
685                !gdk_window_is_ancestor (common_ancestor, window));
686       if (common_ancestor)
687         {
688           synthesize_leave_event (current_mouse_window, nsevent, mode, GDK_NOTIFY_NONLINEAR);
689           intermediate = gdk_window_get_parent (current_mouse_window);
690           if (intermediate != common_ancestor)
691             {
692               synthesize_leave_events (intermediate, common_ancestor,
693                                        nsevent, mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
694             }
695           intermediate = gdk_window_get_parent (window);
696           if (intermediate != common_ancestor)
697             {
698               synthesize_enter_events (common_ancestor, intermediate,
699                                        nsevent, mode, GDK_NOTIFY_NONLINEAR_VIRTUAL);
700             }
701           synthesize_enter_event (window, nsevent, mode, GDK_NOTIFY_NONLINEAR);
702         }
703     }
704   else
705     {
706       /* This means we have not current_mouse_window. FIXME: Should
707        * we make sure to always set the root window instead of NULL?
708        */
709
710       /* FIXME: Figure out why this is being called with window being
711        * NULL. The check works around a crash for now.
712        */ 
713       if (window)
714         synthesize_enter_event (window, nsevent, mode, GDK_NOTIFY_UNKNOWN);
715     }
716   
717   _gdk_quartz_events_update_mouse_window (window);
718 }
719
720 void 
721 _gdk_quartz_events_send_map_events (GdkWindow *window)
722 {
723   GList *list;
724   GdkWindow *interested_window;
725   GdkWindowObject *private = (GdkWindowObject *)window;
726
727   interested_window = find_window_interested_in_event_mask (window, 
728                                                             GDK_STRUCTURE_MASK,
729                                                             TRUE);
730   
731   if (interested_window)
732     {
733       GdkEvent *event = gdk_event_new (GDK_MAP);
734       event->any.window = interested_window;
735       append_event (event);
736     }
737
738   for (list = private->children; list != NULL; list = list->next)
739     _gdk_quartz_events_send_map_events ((GdkWindow *)list->data);
740 }
741
742 /* Get current mouse window */
743 GdkWindow *
744 _gdk_quartz_events_get_mouse_window (void)
745 {
746   if (_gdk_quartz_pointer_grab_window && !pointer_grab_owner_events)
747     return _gdk_quartz_pointer_grab_window;
748   
749   return current_mouse_window;
750 }
751
752 /* Update mouse window */
753 void 
754 _gdk_quartz_events_update_mouse_window (GdkWindow *window)
755 {
756   if (window)
757     g_object_ref (window);
758   if (current_mouse_window)
759     g_object_unref (current_mouse_window);
760
761   current_mouse_window = window;
762 }
763
764 /* Update current cursor */
765 void
766 _gdk_quartz_events_update_cursor (GdkWindow *window)
767 {
768   GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
769   NSCursor *nscursor = nil;
770
771   while (private) {
772     GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
773
774     nscursor = impl->nscursor;
775     if (nscursor)
776       break;
777
778     private = private->parent;
779   }
780
781   if (!nscursor)
782     nscursor = [NSCursor arrowCursor];
783
784   if ([NSCursor currentCursor] != nscursor)
785     [nscursor set];
786 }
787
788 /* This function finds the correct window to send an event to,
789  * taking into account grabs (FIXME: not done yet), event propagation,
790  * and event masks.
791  */
792 static GdkWindow *
793 find_window_for_event (NSEvent *nsevent, gint *x, gint *y)
794 {
795   NSWindow *nswindow = [nsevent window];
796   NSEventType event_type = [nsevent type];
797
798   if (!nswindow)
799     return NULL;
800  
801   /* Window where not created by GDK so the event should be handled by Quartz */
802   if (![[nswindow contentView] isKindOfClass:[GdkQuartzView class]]) 
803     return NULL;
804   
805   if (event_type == NSMouseMoved ||
806       event_type == NSLeftMouseDragged ||
807       event_type == NSRightMouseDragged ||
808       event_type == NSOtherMouseDragged)
809     {
810       GdkWindow *toplevel = [(GdkQuartzView *)[nswindow contentView] gdkWindow];
811       NSPoint point = [nsevent locationInWindow];
812       GdkWindow *mouse_window;
813
814       mouse_window = _gdk_quartz_window_find_child_by_point (toplevel, point.x, point.y, x, y);
815
816       if (!mouse_window)
817         mouse_window = _gdk_root;
818
819       if (_gdk_quartz_pointer_grab_window)
820         {
821           if (mouse_window != current_mouse_window)
822             synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, *x, *y);
823         }
824       else
825         {
826           if (current_mouse_window != mouse_window)
827             {
828               synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, *x, *y);
829               
830               _gdk_quartz_events_update_cursor (mouse_window);
831             }
832         }
833     }
834
835   switch (event_type)
836     {
837     case NSLeftMouseDown:
838     case NSRightMouseDown:
839     case NSOtherMouseDown:
840     case NSLeftMouseUp:
841     case NSRightMouseUp:
842     case NSOtherMouseUp:
843     case NSMouseMoved:
844     case NSScrollWheel:
845     case NSLeftMouseDragged:
846     case NSRightMouseDragged:
847     case NSOtherMouseDragged:
848       {
849         GdkWindow *toplevel = [(GdkQuartzView *)[nswindow contentView] gdkWindow];
850         NSPoint point = [nsevent locationInWindow];
851         GdkWindow *mouse_window;
852         GdkEventMask event_mask;
853         GdkWindow *real_window;
854
855         if (_gdk_quartz_pointer_grab_window && !pointer_grab_owner_events)
856           {
857             if (pointer_grab_event_mask & get_event_mask_from_ns_event (nsevent))
858               {
859                 int tempx, tempy;
860                 GdkWindowObject *w;
861                 GdkWindowObject *grab_toplevel;
862
863                 w = GDK_WINDOW_OBJECT (_gdk_quartz_pointer_grab_window);
864                 grab_toplevel = GDK_WINDOW_OBJECT (gdk_window_get_toplevel (_gdk_quartz_pointer_grab_window));
865
866                 tempx = point.x;
867                 tempy = GDK_WINDOW_IMPL_QUARTZ (grab_toplevel->impl)->height -
868                   point.y;
869
870                 while (w != grab_toplevel)
871                   {
872                     tempx -= w->x;
873                     tempy -= w->y;
874
875                     w = w->parent;
876                   }
877
878                 *x = tempx;
879                 *y = tempy;
880
881                 return _gdk_quartz_pointer_grab_window;
882               }
883             else
884               {
885                 return NULL;
886               }
887           }
888
889         if (!nswindow)
890           {
891             mouse_window = _gdk_root;
892           }
893         else
894           {
895             mouse_window = _gdk_quartz_window_find_child_by_point (toplevel, point.x, point.y, x, y);
896           }
897
898         event_mask = get_event_mask_from_ns_event (nsevent);
899         real_window = find_window_interested_in_event_mask (mouse_window, event_mask, TRUE);
900         
901         return real_window;
902       }
903       break;
904       
905     case NSMouseEntered:
906       {
907         NSPoint point;
908         GdkWindow *toplevel;
909         GdkWindow *mouse_window;
910
911         point = [nsevent locationInWindow];
912         toplevel = [(GdkQuartzView *)[nswindow contentView] gdkWindow];
913         
914         mouse_window = _gdk_quartz_window_find_child_by_point (toplevel, point.x, point.y, x, y);
915         
916         synthesize_crossing_events (mouse_window, GDK_CROSSING_NORMAL, nsevent, *x, *y);
917       }
918       break;
919
920     case NSMouseExited:
921       synthesize_crossing_events (_gdk_root, GDK_CROSSING_NORMAL, nsevent, *x, *y);
922       break;
923
924     case NSKeyDown:
925     case NSKeyUp:
926     case NSFlagsChanged:
927       {
928         GdkEventMask event_mask;
929
930         if (_gdk_quartz_keyboard_grab_window && !keyboard_grab_owner_events)
931           return _gdk_quartz_keyboard_grab_window;
932
933         event_mask = get_event_mask_from_ns_event (nsevent);
934         return find_window_interested_in_event_mask (current_keyboard_window, event_mask, TRUE);
935       }
936       break;
937
938     case NSAppKitDefined:
939     case NSSystemDefined:
940       /* We ignore these events */
941       break;
942     default:
943       NSLog(@"Unhandled event %@", nsevent);
944     }
945
946   return NULL;
947 }
948
949 static GdkEvent *
950 create_button_event (GdkWindow *window, NSEvent *nsevent,
951                      gint x, gint y)
952 {
953   GdkEvent *event;
954   GdkEventType type;
955   guint button;
956
957   switch ([nsevent type])
958     {
959     case NSLeftMouseDown:
960     case NSRightMouseDown:
961     case NSOtherMouseDown:
962       type = GDK_BUTTON_PRESS;
963       break;
964     case NSLeftMouseUp:
965     case NSRightMouseUp:
966     case NSOtherMouseUp:
967       type = GDK_BUTTON_RELEASE;
968       break;
969     default:
970       g_assert_not_reached ();
971     }
972   
973   button = convert_mouse_button_number ([nsevent buttonNumber]);
974
975   event = gdk_event_new (type);
976   event->button.window = window;
977   event->button.time = get_event_time (nsevent);
978   event->button.x = x;
979   event->button.y = y;
980   /* FIXME event->axes */
981   event->button.state = get_keyboard_modifiers_from_nsevent (nsevent);
982   event->button.button = button;
983   event->button.device = _gdk_display->core_pointer;
984   convert_window_coordinates_to_root (window, x, y, 
985                                       &event->button.x_root,
986                                       &event->button.y_root);
987
988   return event;
989 }
990
991 static GdkEvent *
992 create_motion_event (GdkWindow *window, NSEvent *nsevent, gint x, gint y)
993 {
994   GdkEvent *event;
995   GdkEventType type;
996   GdkModifierType state = 0;
997   int button = 0;
998
999   switch ([nsevent type])
1000     {
1001     case NSLeftMouseDragged:
1002     case NSRightMouseDragged:
1003     case NSOtherMouseDragged:
1004       button = convert_mouse_button_number ([nsevent buttonNumber]);
1005       /* Fall through */
1006     case NSMouseMoved:
1007       type = GDK_MOTION_NOTIFY;
1008       break;
1009     default:
1010       g_assert_not_reached ();
1011     }
1012
1013   /* This maps buttons 1 to 5 to GDK_BUTTON[1-5]_MASK */
1014   if (button >= 1 && button <= 5)
1015     state = (1 << (button + 7));
1016   
1017   state |= get_keyboard_modifiers_from_nsevent (nsevent);
1018
1019   event = gdk_event_new (type);
1020   event->motion.window = window;
1021   event->motion.time = get_event_time (nsevent);
1022   event->motion.x = x;
1023   event->motion.y = y;
1024   /* FIXME event->axes */
1025   event->motion.state = state;
1026   event->motion.is_hint = FALSE;
1027   event->motion.device = _gdk_display->core_pointer;
1028   convert_window_coordinates_to_root (window, x, y,
1029                                       &event->motion.x_root, &event->motion.y_root);
1030   
1031   return event;
1032 }
1033
1034 static GdkEvent *
1035 create_scroll_event (GdkWindow *window, NSEvent *nsevent, GdkScrollDirection direction)
1036 {
1037   GdkEvent *event;
1038   NSPoint point;
1039   
1040   event = gdk_event_new (GDK_SCROLL);
1041   event->scroll.window = window;
1042   event->scroll.time = get_event_time (nsevent);
1043
1044   point = [nsevent locationInWindow];
1045   event->scroll.x = point.x;
1046   event->scroll.y = point.y;
1047   convert_window_coordinates_to_root (window, event->scroll.x, event->scroll.y, 
1048                                       &event->scroll.x_root,
1049                                       &event->scroll.y_root);
1050
1051   event->scroll.direction = direction;
1052   event->scroll.device = _gdk_display->core_pointer;
1053   
1054   return event;
1055 }
1056
1057 static GdkEvent *
1058 create_key_event (GdkWindow *window, NSEvent *nsevent, GdkEventType type)
1059 {
1060   GdkEvent *event;
1061   gchar buf[7];
1062   gunichar c = 0;
1063
1064   event = gdk_event_new (type);
1065   event->key.window = window;
1066   event->key.time = get_event_time (nsevent);
1067   event->key.state = get_keyboard_modifiers_from_nsevent (nsevent);
1068   event->key.hardware_keycode = [nsevent keyCode];
1069   event->key.group = ([nsevent modifierFlags] & NSAlternateKeyMask) ? 1 : 0;
1070
1071   event->key.keyval = GDK_VoidSymbol;
1072   
1073   gdk_keymap_translate_keyboard_state (NULL,
1074                                        event->key.hardware_keycode,
1075                                        event->key.state, 
1076                                        event->key.group,
1077                                        &event->key.keyval,
1078                                        NULL, NULL, NULL);
1079
1080   event->key.is_modifier = _gdk_quartz_keys_is_modifier (event->key.hardware_keycode);
1081
1082   event->key.string = NULL;
1083
1084   /* Fill in ->string since apps depend on it, taken from the x11 backend. */
1085   if (event->key.keyval != GDK_VoidSymbol)
1086     c = gdk_keyval_to_unicode (event->key.keyval);
1087
1088     if (c)
1089     {
1090       gsize bytes_written;
1091       gint len;
1092
1093       len = g_unichar_to_utf8 (c, buf);
1094       buf[len] = '\0';
1095       
1096       event->key.string = g_locale_from_utf8 (buf, len,
1097                                               NULL, &bytes_written,
1098                                               NULL);
1099       if (event->key.string)
1100         event->key.length = bytes_written;
1101     }
1102   else if (event->key.keyval == GDK_Escape)
1103     {
1104       event->key.length = 1;
1105       event->key.string = g_strdup ("\033");
1106     }
1107   else if (event->key.keyval == GDK_Return ||
1108           event->key.keyval == GDK_KP_Enter)
1109     {
1110       event->key.length = 1;
1111       event->key.string = g_strdup ("\r");
1112     }
1113
1114   if (!event->key.string)
1115     {
1116       event->key.length = 0;
1117       event->key.string = g_strdup ("");
1118     }
1119
1120   GDK_NOTE(EVENTS,
1121     g_message ("key %s:\t\twindow: %p  key: %12s  %d",
1122           type == GDK_KEY_PRESS ? "press" : "release",
1123           event->key.window,
1124           event->key.keyval ? gdk_keyval_name (event->key.keyval) : "(none)",
1125           event->key.keyval));
1126   return event;
1127 }
1128
1129 static GdkEventMask current_mask = 0;
1130 GdkEventMask 
1131 _gdk_quartz_events_get_current_event_mask (void)
1132 {
1133   return current_mask;
1134 }
1135
1136 static gboolean
1137 gdk_event_translate (NSEvent *nsevent)
1138 {
1139   GdkWindow *window;
1140   GdkFilterReturn result;
1141   GdkEvent *event;
1142   int x, y;
1143
1144   if (_gdk_default_filters)
1145     {
1146       /* Apply global filters */
1147
1148       GdkFilterReturn result = apply_filters (NULL, nsevent, _gdk_default_filters);
1149       
1150       /* If result is GDK_FILTER_CONTINUE, we continue as if nothing
1151        * happened. If it is GDK_FILTER_REMOVE,
1152        * we return TRUE and won't send the message to Quartz.
1153        */
1154       if (result == GDK_FILTER_REMOVE)
1155         return TRUE;
1156     }
1157
1158   /* Catch the case where the entire app loses focus, and break any grabs. */
1159   if ([nsevent type] == NSAppKitDefined)
1160     {
1161       if ([nsevent subtype] == NSApplicationDeactivatedEventType)
1162         {
1163           if (_gdk_quartz_keyboard_grab_window)
1164             {
1165               generate_grab_broken_event (_gdk_quartz_keyboard_grab_window,
1166                                           TRUE, FALSE,
1167                                           NULL);
1168               g_object_unref (_gdk_quartz_keyboard_grab_window);
1169               _gdk_quartz_keyboard_grab_window = NULL;
1170             }
1171
1172           if (_gdk_quartz_pointer_grab_window)
1173             {
1174               generate_grab_broken_event (_gdk_quartz_pointer_grab_window,
1175                                           FALSE, pointer_grab_implicit,
1176                                           NULL);
1177               pointer_ungrab_internal (FALSE);
1178             }
1179         }
1180     }
1181
1182   window = find_window_for_event (nsevent, &x, &y);
1183
1184   /* FIXME: During owner_event grabs, we don't find a window when there is a
1185    * click on a no-window widget, which makes popups etc still stay up. Need
1186    * to figure out why that is.
1187    */
1188   
1189   if (!window)
1190     return FALSE;
1191
1192   result = apply_filters (window, nsevent, ((GdkWindowObject *) window)->filters);
1193
1194   if (result == GDK_FILTER_REMOVE)
1195     return TRUE;
1196
1197   current_mask = get_event_mask_from_ns_event (nsevent);
1198
1199   switch ([nsevent type])
1200     {
1201     case NSLeftMouseDown:
1202     case NSRightMouseDown:
1203     case NSOtherMouseDown:
1204       {
1205         GdkEventMask event_mask;
1206
1207         /* Emulate implicit grab, when the window has both PRESS and RELEASE
1208          * in its mask, like X (and make it owner_events since that's what
1209          * implicit grabs are like).
1210          */
1211         event_mask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
1212         if (!_gdk_quartz_pointer_grab_window &&
1213             (GDK_WINDOW_OBJECT (window)->event_mask & event_mask) == event_mask)
1214           {
1215             pointer_grab_internal (window, TRUE,
1216                                    GDK_WINDOW_OBJECT (window)->event_mask,
1217                                    NULL, NULL, TRUE);
1218           }
1219       }
1220       
1221       event = create_button_event (window, nsevent, x, y);
1222       append_event (event);
1223       
1224       _gdk_event_button_generate (_gdk_display, event);
1225       break;
1226
1227     case NSLeftMouseUp:
1228     case NSRightMouseUp:
1229     case NSOtherMouseUp:
1230       event = create_button_event (window, nsevent, x, y);
1231       append_event (event);
1232       
1233       /* Ungrab implicit grab */
1234       if (_gdk_quartz_pointer_grab_window && pointer_grab_implicit)
1235         pointer_ungrab_internal (TRUE);
1236       break;
1237
1238     case NSLeftMouseDragged:
1239     case NSRightMouseDragged:
1240     case NSOtherMouseDragged:
1241     case NSMouseMoved:
1242       event = create_motion_event (window, nsevent, x, y);
1243       append_event (event);
1244       break;
1245
1246     case NSScrollWheel:
1247       {
1248         float dx = [nsevent deltaX];
1249         float dy = [nsevent deltaY];
1250         GdkScrollDirection direction;
1251
1252         /* The delta is how much the mouse wheel has moved. Since there's no such thing in GTK+
1253          * we accomodate by sending a different number of scroll wheel events.
1254          */
1255
1256         /* First do y events */
1257         if (dy < 0.0)
1258           {
1259             dy = -dy;
1260             direction = GDK_SCROLL_DOWN;
1261           }
1262         else
1263           direction = GDK_SCROLL_UP;
1264
1265         while (dy > 0.0)
1266           {
1267             event = create_scroll_event (window, nsevent, direction);
1268             append_event (event);
1269             dy--;
1270           }
1271
1272         /* Now do x events */
1273         if (dx < 0.0)
1274           {
1275             dx = -dx;
1276             direction = GDK_SCROLL_RIGHT;
1277           }
1278         else
1279           direction = GDK_SCROLL_LEFT;
1280
1281         while (dx > 0.0)
1282           {
1283             event = create_scroll_event (window, nsevent, direction);
1284             append_event (event);
1285             dx--;
1286           }
1287
1288         break;
1289       }
1290     case NSKeyDown:
1291     case NSKeyUp:
1292     case NSFlagsChanged:
1293       {
1294         GdkEventType type;
1295
1296         type = _gdk_quartz_keys_event_type (nsevent);
1297         if (type == GDK_NOTHING)
1298           return FALSE;
1299         
1300         event = create_key_event (window, nsevent, type);
1301         append_event (event);
1302         return TRUE;
1303       }
1304       break;
1305     default:
1306       NSLog(@"Untranslated: %@", nsevent);
1307     }
1308
1309   return FALSE;
1310 }
1311
1312 void
1313 _gdk_events_queue (GdkDisplay *display)
1314 {  
1315   NSEvent *current_event = _gdk_quartz_event_loop_get_current ();
1316
1317   if (current_event)
1318     {
1319       if (!gdk_event_translate (current_event)) 
1320         [NSApp sendEvent:current_event];
1321                 
1322       _gdk_quartz_event_loop_release_current ();
1323     }
1324 }
1325
1326 void
1327 gdk_flush (void)
1328 {
1329   /* Not supported. */
1330 }
1331
1332 void 
1333 gdk_display_add_client_message_filter (GdkDisplay   *display,
1334                                        GdkAtom       message_type,
1335                                        GdkFilterFunc func,
1336                                        gpointer      data)
1337 {
1338   /* Not supported. */
1339 }
1340
1341 void 
1342 gdk_add_client_message_filter (GdkAtom       message_type,
1343                                GdkFilterFunc func,
1344                                gpointer      data)
1345 {
1346   /* Not supported. */
1347 }
1348
1349 void
1350 gdk_display_sync (GdkDisplay *display)
1351 {
1352   /* Not supported. */
1353 }
1354
1355 void
1356 gdk_display_flush (GdkDisplay *display)
1357 {
1358   /* Not supported. */
1359 }
1360
1361 gboolean
1362 gdk_event_send_client_message_for_display (GdkDisplay      *display,
1363                                            GdkEvent        *event,
1364                                            GdkNativeWindow  winid)
1365 {
1366   /* Not supported. */
1367   return FALSE;
1368 }
1369
1370 void
1371 gdk_screen_broadcast_client_message (GdkScreen *screen,
1372                                      GdkEvent  *event)
1373 {
1374   /* Not supported. */
1375 }
1376
1377 gboolean
1378 gdk_screen_get_setting (GdkScreen   *screen,
1379                         const gchar *name,
1380                         GValue      *value)
1381 {
1382   if (strcmp (name, "gtk-double-click-time") == 0)
1383     {
1384       NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
1385       float t;
1386
1387       GDK_QUARTZ_ALLOC_POOL;
1388             
1389       t = [defaults floatForKey:@"com.apple.mouse.doubleClickThreshold"];
1390       if (t == 0.0)
1391         {
1392           /* No user setting, use the default in OS X. */
1393           t = 0.5;
1394         }
1395
1396       GDK_QUARTZ_RELEASE_POOL;
1397
1398       g_value_set_int (value, t * 1000);
1399       return TRUE;
1400     }
1401   
1402   /* FIXME: Add more settings */
1403
1404   return FALSE;
1405 }
1406