]> Pileus Git - ~andy/gtk/blob - gdk/quartz/gdkevents-quartz.c
Merge branch 'master' into client-side-windows
[~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 corresponding to the key window */
37 static GdkWindow   *current_keyboard_window;
38
39 /* This is the event mask and button state from the last event */
40 static GdkEventMask current_event_mask;
41 static int          current_button_state;
42
43 static void append_event                        (GdkEvent  *event);
44
45 NSEvent *
46 gdk_quartz_event_get_nsevent (GdkEvent *event)
47 {
48   /* FIXME: If the event here is unallocated, we crash. */
49   return ((GdkEventPrivate *) event)->windowing_data;
50 }
51
52 void
53 _gdk_events_init (void)
54 {
55   _gdk_quartz_event_loop_init ();
56
57   current_keyboard_window = g_object_ref (_gdk_root);
58 }
59
60 gboolean
61 gdk_events_pending (void)
62 {
63   return (_gdk_event_queue_find_first (_gdk_display) ||
64           (_gdk_quartz_event_loop_check_pending ()));
65 }
66
67 GdkEvent*
68 gdk_event_get_graphics_expose (GdkWindow *window)
69 {
70   /* FIXME: Implement */
71   return NULL;
72 }
73
74 GdkGrabStatus
75 gdk_keyboard_grab (GdkWindow  *window,
76                    gint        owner_events,
77                    guint32     time)
78 {
79   GdkDisplay *display;
80   GdkWindow  *toplevel;
81
82   g_return_val_if_fail (window != NULL, 0);
83   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
84
85   display = gdk_drawable_get_display (window);
86   toplevel = gdk_window_get_toplevel (window);
87
88   _gdk_display_set_has_keyboard_grab (display,
89                                       window,
90                                       toplevel,
91                                       owner_events,
92                                       0,
93                                       time);
94
95   return GDK_GRAB_SUCCESS;
96 }
97
98 void
99 gdk_display_keyboard_ungrab (GdkDisplay *display,
100                              guint32     time)
101 {
102   _gdk_display_unset_has_keyboard_grab (display, FALSE);
103 }
104
105 void
106 gdk_display_pointer_ungrab (GdkDisplay *display,
107                             guint32     time)
108 {
109   GdkPointerGrabInfo *grab;
110
111   grab = _gdk_display_get_last_pointer_grab (display);
112   if (grab)
113     grab->serial_end = 0;
114
115   _gdk_display_pointer_grab_update (display, 0);
116 }
117
118 GdkGrabStatus
119 gdk_pointer_grab (GdkWindow    *window,
120                   gboolean      owner_events,
121                   GdkEventMask  event_mask,
122                   GdkWindow    *confine_to,
123                   GdkCursor    *cursor,
124                   guint32       time)
125 {
126   GdkWindow *native;
127
128   g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
129   g_return_val_if_fail (confine_to == NULL || GDK_IS_WINDOW (confine_to), 0);
130
131   native = gdk_window_get_toplevel (window);
132
133   /* TODO: What do we do for offscreens and  their children? We need to proxy the grab somehow */
134   if (!GDK_IS_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (native)->impl))
135     return GDK_GRAB_SUCCESS;
136
137   if (!_gdk_window_has_impl (window) &&
138       !gdk_window_is_viewable (window))
139     return GDK_GRAB_NOT_VIEWABLE;
140
141   _gdk_display_add_pointer_grab (_gdk_display,
142                                  window,
143                                  native,
144                                  owner_events,
145                                  event_mask,
146                                  0,
147                                  time,
148                                  FALSE);
149
150   return GDK_GRAB_SUCCESS;
151 }
152
153 static void
154 break_all_grabs (guint32 time)
155 {
156   GdkPointerGrabInfo *grab;
157
158   if (_gdk_display->keyboard_grab.window)
159     _gdk_display_unset_has_keyboard_grab (_gdk_display, FALSE);
160
161   grab = _gdk_display_get_last_pointer_grab (_gdk_display);
162   if (grab)
163     {
164       grab->serial_end = 0;
165       grab->implicit_ungrab = TRUE;
166     }
167
168   _gdk_display_pointer_grab_update (_gdk_display, 0);
169 }
170
171 static void
172 fixup_event (GdkEvent *event)
173 {
174   if (event->any.window)
175     g_object_ref (event->any.window);
176   if (((event->any.type == GDK_ENTER_NOTIFY) ||
177        (event->any.type == GDK_LEAVE_NOTIFY)) &&
178       (event->crossing.subwindow != NULL))
179     g_object_ref (event->crossing.subwindow);
180   event->any.send_event = FALSE;
181 }
182
183 static void
184 append_event (GdkEvent *event)
185 {
186   fixup_event (event);
187   _gdk_event_queue_append (_gdk_display, event);
188 }
189
190 static gint
191 gdk_event_apply_filters (NSEvent *nsevent,
192                          GdkEvent *event,
193                          GList *filters)
194 {
195   GList *tmp_list;
196   GdkFilterReturn result;
197   
198   tmp_list = filters;
199
200   while (tmp_list)
201     {
202       GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
203       
204       tmp_list = tmp_list->next;
205       result = filter->function (nsevent, event, filter->data);
206       if (result !=  GDK_FILTER_CONTINUE)
207         return result;
208     }
209
210   return GDK_FILTER_CONTINUE;
211 }
212
213 static guint32
214 get_time_from_ns_event (NSEvent *event)
215 {
216   double time = [event timestamp];
217   
218   return time * 1000.0;
219 }
220
221 static int
222 get_mouse_button_from_ns_event (NSEvent *event)
223 {
224   int button;
225
226   button = [event buttonNumber];
227
228   switch (button)
229     {
230     case 0:
231       return 1;
232     case 1:
233       return 3;
234     case 2:
235       return 2;
236     default:
237       return button + 1;
238     }
239 }
240
241 static GdkModifierType
242 get_mouse_button_modifiers_from_ns_event (NSEvent *event)
243 {
244   int button;
245   GdkModifierType state = 0;
246
247   /* This maps buttons 1 to 5 to GDK_BUTTON[1-5]_MASK */
248   button = get_mouse_button_from_ns_event (event);
249   if (button >= 1 && button <= 5)
250     state = (1 << (button + 7));
251
252   return state;
253 }
254
255 static GdkModifierType
256 get_keyboard_modifiers_from_ns_event (NSEvent *nsevent)
257 {
258   GdkModifierType modifiers = 0;
259   int nsflags;
260
261   nsflags = [nsevent modifierFlags];
262   
263   if (nsflags & NSAlphaShiftKeyMask)
264     modifiers |= GDK_LOCK_MASK;
265   if (nsflags & NSShiftKeyMask)
266     modifiers |= GDK_SHIFT_MASK;
267   if (nsflags & NSControlKeyMask)
268     modifiers |= GDK_CONTROL_MASK;
269   if (nsflags & NSCommandKeyMask)
270     modifiers |= GDK_MOD1_MASK;
271
272   return modifiers;
273 }
274
275 /* Return an event mask from an NSEvent */
276 static GdkEventMask
277 get_event_mask_from_ns_event (NSEvent *nsevent)
278 {
279   switch ([nsevent type])
280     {
281     case NSLeftMouseDown:
282     case NSRightMouseDown:
283     case NSOtherMouseDown:
284       return GDK_BUTTON_PRESS_MASK;
285     case NSLeftMouseUp:
286     case NSRightMouseUp:
287     case NSOtherMouseUp:
288       return GDK_BUTTON_RELEASE_MASK;
289     case NSMouseMoved:
290       return GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
291     case NSScrollWheel:
292       /* Since applications that want button press events can get
293        * scroll events on X11 (since scroll wheel events are really
294        * button press events there), we need to use GDK_BUTTON_PRESS_MASK too.
295        */
296       return GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK;
297     case NSLeftMouseDragged:
298       return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
299               GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | 
300               GDK_BUTTON1_MASK);
301     case NSRightMouseDragged:
302       return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
303               GDK_BUTTON_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | 
304               GDK_BUTTON3_MASK);
305     case NSOtherMouseDragged:
306       {
307         GdkEventMask mask;
308
309         mask = (GDK_POINTER_MOTION_MASK |
310                 GDK_POINTER_MOTION_HINT_MASK |
311                 GDK_BUTTON_MOTION_MASK);
312
313         if (get_mouse_button_from_ns_event (nsevent) == 2)
314           mask |= (GDK_BUTTON2_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | 
315                    GDK_BUTTON2_MASK);
316
317         return mask;
318       }
319     case NSKeyDown:
320     case NSKeyUp:
321     case NSFlagsChanged:
322       {
323         switch (_gdk_quartz_keys_event_type (nsevent))
324           {
325           case GDK_KEY_PRESS:
326             return GDK_KEY_PRESS_MASK;
327           case GDK_KEY_RELEASE:
328             return GDK_KEY_RELEASE_MASK;
329           case GDK_NOTHING:
330             return 0;
331           default:
332             g_assert_not_reached ();
333           }
334       }
335       break;
336
337     case NSMouseEntered:
338       return GDK_ENTER_NOTIFY_MASK;
339
340     case NSMouseExited:
341       return GDK_LEAVE_NOTIFY_MASK;
342
343     default:
344       g_assert_not_reached ();
345     }
346
347   return 0;
348 }
349
350 static GdkEvent *
351 create_focus_event (GdkWindow *window,
352                     gboolean   in)
353 {
354   GdkEvent *event;
355
356   event = gdk_event_new (GDK_FOCUS_CHANGE);
357   event->focus_change.window = window;
358   event->focus_change.in = in;
359
360   return event;
361 }
362
363 /* Note: Used to both set a new focus window and to unset the old one. */
364 void
365 _gdk_quartz_events_update_focus_window (GdkWindow *window,
366                                         gboolean   got_focus)
367 {
368   GdkEvent *event;
369
370   if (got_focus && window == current_keyboard_window)
371     return;
372
373   /* FIXME: Don't do this when grabbed? Or make GdkQuartzWindow
374    * disallow it in the first place instead?
375    */
376   
377   if (!got_focus && window == current_keyboard_window)
378     {
379       event = create_focus_event (current_keyboard_window, FALSE);
380       append_event (event);
381       g_object_unref (current_keyboard_window);
382       current_keyboard_window = NULL;
383     }
384
385   if (got_focus)
386     {
387       if (current_keyboard_window)
388         {
389           event = create_focus_event (current_keyboard_window, FALSE);
390           append_event (event);
391           g_object_unref (current_keyboard_window);
392           current_keyboard_window = NULL;
393         }
394       
395       event = create_focus_event (window, TRUE);
396       append_event (event);
397       current_keyboard_window = g_object_ref (window);
398     }
399 }
400
401 void
402 _gdk_quartz_events_send_map_event (GdkWindow *window)
403 {
404   GdkWindowObject *private = (GdkWindowObject *)window;
405   GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
406
407   if (!impl->toplevel)
408     return;
409
410   if (private->event_mask & GDK_STRUCTURE_MASK)
411     {
412       GdkEvent event;
413
414       event.any.type = GDK_MAP;
415       event.any.window = window;
416   
417       gdk_event_put (&event);
418     }
419 }
420
421 static GdkWindow *
422 find_toplevel_under_pointer (GdkDisplay *display,
423                              NSPoint     screen_point,
424                              gint       *x,
425                              gint       *y)
426 {
427   GdkWindow *toplevel;
428
429   toplevel = display->pointer_info.toplevel_under_pointer;
430   if (toplevel)
431     {
432       GdkWindowObject *private;
433       NSWindow *nswindow;
434       NSPoint point;
435
436       private = (GdkWindowObject *)toplevel;
437       nswindow = ((GdkWindowImplQuartz *)private->impl)->toplevel;
438
439       point = [nswindow convertScreenToBase:screen_point];
440
441       *x = point.x;
442       *y = private->height - point.y;
443     }
444
445   return toplevel;
446 }
447
448 /* This function finds the correct window to send an event to, taking
449  * into account grabs, event propagation, and event masks.
450  */
451 static GdkWindow *
452 find_window_for_ns_event (NSEvent *nsevent, 
453                           gint    *x, 
454                           gint    *y,
455                           gint    *x_root,
456                           gint    *y_root)
457 {
458   GdkQuartzView *view;
459   GdkWindow *toplevel;
460   GdkWindowObject *private;
461   NSPoint point;
462   NSPoint screen_point;
463   NSEventType event_type;
464
465   view = (GdkQuartzView *)[[nsevent window] contentView];
466
467   toplevel = [view gdkWindow];
468   private = GDK_WINDOW_OBJECT (toplevel);
469
470   point = [nsevent locationInWindow];
471   screen_point = [[nsevent window] convertBaseToScreen:point];
472
473   *x = point.x;
474   *y = private->height - point.y;
475
476   *x_root = screen_point.x;
477   *y_root = _gdk_quartz_window_get_inverted_screen_y (screen_point.y);
478
479   event_type = [nsevent type];
480
481   switch (event_type)
482     {
483     case NSLeftMouseDown:
484     case NSRightMouseDown:
485     case NSOtherMouseDown:
486     case NSLeftMouseUp:
487     case NSRightMouseUp:
488     case NSOtherMouseUp:
489     case NSMouseMoved:
490     case NSScrollWheel:
491     case NSLeftMouseDragged:
492     case NSRightMouseDragged:
493     case NSOtherMouseDragged:
494       {
495         GdkDisplay *display;
496         GdkPointerGrabInfo *grab;
497
498         display = gdk_drawable_get_display (toplevel);
499
500         /* From the docs for XGrabPointer:
501          *
502          * If owner_events is True and if a generated pointer event
503          * would normally be reported to this client, it is reported
504          * as usual. Otherwise, the event is reported with respect to
505          * the grab_window and is reported only if selected by
506          * event_mask. For either value of owner_events, unreported
507          * events are discarded.
508          */
509         grab = _gdk_display_get_last_pointer_grab (display);
510         if (grab)
511           {
512             if ((grab->event_mask & get_event_mask_from_ns_event (nsevent)) == 0)
513               return NULL;
514
515             if (grab->owner_events)
516               {
517                 /* For owner events, we need to use the toplevel under the
518                  * pointer, not the window from the NSEvent, since that is
519                  * reported with respect to the key window, which could be
520                  * wrong.
521                  */
522                 GdkWindow *toplevel_under_pointer;
523                 gint x_tmp, y_tmp;
524
525                 toplevel_under_pointer = find_toplevel_under_pointer (display,
526                                                                       screen_point,
527                                                                       &x_tmp, &y_tmp);
528                 if (toplevel_under_pointer)
529                   {
530                     toplevel = toplevel_under_pointer;
531                     *x = x_tmp;
532                     *y = y_tmp;
533                   }
534
535                 return toplevel;
536               }
537             else
538               {
539                 /* Finally check the grab window. */
540                 GdkWindow *grab_toplevel;
541                 GdkWindowObject *grab_private;
542                 NSWindow *grab_nswindow;
543
544                 grab_toplevel = gdk_window_get_toplevel (grab->window);
545                 grab_private = (GdkWindowObject *)grab_toplevel;
546
547                 grab_nswindow = ((GdkWindowImplQuartz *)grab_private->impl)->toplevel;
548                 point = [grab_nswindow convertScreenToBase:screen_point];
549
550                 /* Note: x_root and y_root are already right. */
551                 *x = point.x;
552                 *y = grab_private->height - point.y;
553
554                 return grab_toplevel;
555               }
556
557             return NULL;
558           }
559         else 
560           {
561             /* The non-grabbed case. */
562             GdkWindow *toplevel_under_pointer;
563             gint x_tmp, y_tmp;
564
565             /* Ignore all events but mouse moved that might be on the title
566              * bar (above the content view). The reason is that otherwise
567              * gdk gets confused about getting e.g. button presses with no
568              * window (the title bar is not known to it).
569              */
570             if (event_type != NSMouseMoved)
571               if (*y < 0)
572                 return NULL;
573
574             /* FIXME: Also need to leave resize events to cocoa somehow? */
575
576             /* As for owner events, we need to use the toplevel under the
577              * pointer, not the window from the NSEvent.
578              */
579             toplevel_under_pointer = find_toplevel_under_pointer (display,
580                                                                   screen_point,
581                                                                   &x_tmp, &y_tmp);
582             if (toplevel_under_pointer)
583               {
584                 toplevel = toplevel_under_pointer;
585                 *x = x_tmp;
586                 *y = y_tmp;
587               }
588
589             return toplevel;
590           }
591       }
592       break;
593       
594     case NSMouseEntered:
595     case NSMouseExited:
596       /* Only handle our own entered/exited events, not the ones for the
597        * titlebar buttons.
598        */
599       if ([view trackingRect] == [nsevent trackingNumber])
600         return toplevel;
601       else
602         return NULL;
603
604     case NSKeyDown:
605     case NSKeyUp:
606     case NSFlagsChanged:
607       if (_gdk_display->keyboard_grab.window && !_gdk_display->keyboard_grab.owner_events)
608         return gdk_window_get_toplevel (_gdk_display->keyboard_grab.window);
609
610       return toplevel;
611
612     default:
613       /* Ignore everything else. */
614       break;
615     }
616
617   return NULL;
618 }
619
620 static void
621 fill_crossing_event (GdkWindow       *toplevel,
622                      GdkEvent        *event,
623                      NSEvent         *nsevent,
624                      gint             x,
625                      gint             y,
626                      gint             x_root,
627                      gint             y_root,
628                      GdkEventType     event_type,
629                      GdkCrossingMode  mode,
630                      GdkNotifyType    detail)
631 {
632   event->any.type = event_type;
633   event->crossing.window = toplevel;
634   event->crossing.subwindow = NULL;
635   event->crossing.time = get_time_from_ns_event (nsevent);
636   event->crossing.x = x;
637   event->crossing.y = y;
638   event->crossing.x_root = x_root;
639   event->crossing.y_root = y_root;
640   event->crossing.mode = mode;
641   event->crossing.detail = detail;
642   event->crossing.state = get_keyboard_modifiers_from_ns_event (nsevent);
643
644   /* FIXME: Focus and button state? */
645 }
646
647 static void
648 fill_button_event (GdkWindow *window,
649                    GdkEvent  *event,
650                    NSEvent   *nsevent,
651                    gint       x,
652                    gint       y,
653                    gint       x_root,
654                    gint       y_root)
655 {
656   GdkEventType type;
657   gint state;
658   gint button;
659
660   state = get_keyboard_modifiers_from_ns_event (nsevent);
661
662   switch ([nsevent type])
663     {
664     case NSLeftMouseDown:
665     case NSRightMouseDown:
666     case NSOtherMouseDown:
667       type = GDK_BUTTON_PRESS;
668       break;
669     case NSLeftMouseUp:
670     case NSRightMouseUp:
671     case NSOtherMouseUp:
672       type = GDK_BUTTON_RELEASE;
673       state |= get_mouse_button_modifiers_from_ns_event (nsevent);
674       break;
675     default:
676       g_assert_not_reached ();
677     }
678   
679   button = get_mouse_button_from_ns_event (nsevent);
680
681   event->any.type = type;
682   event->button.window = window;
683   event->button.time = get_time_from_ns_event (nsevent);
684   event->button.x = x;
685   event->button.y = y;
686   event->button.x_root = x_root;
687   event->button.y_root = y_root;
688   /* FIXME event->axes */
689   event->button.state = state;
690   event->button.button = button;
691   event->button.device = _gdk_display->core_pointer;
692 }
693
694 static void
695 fill_motion_event (GdkWindow *window,
696                    GdkEvent  *event,
697                    NSEvent   *nsevent,
698                    gint       x,
699                    gint       y,
700                    gint       x_root,
701                    gint       y_root)
702 {
703   GdkModifierType state;
704
705   state = get_keyboard_modifiers_from_ns_event (nsevent);
706
707   switch ([nsevent type])
708     {
709     case NSLeftMouseDragged:
710     case NSRightMouseDragged:
711     case NSOtherMouseDragged:
712       state |= get_mouse_button_modifiers_from_ns_event (nsevent);
713       break;
714
715     case NSMouseMoved:
716       break;
717     }
718
719   event->any.type = GDK_MOTION_NOTIFY;
720   event->motion.window = window;
721   event->motion.time = get_time_from_ns_event (nsevent);
722   event->motion.x = x;
723   event->motion.y = y;
724   event->motion.x_root = x_root;
725   event->motion.y_root = y_root;
726   /* FIXME event->axes */
727   event->motion.state = state;
728   event->motion.is_hint = FALSE;
729   event->motion.device = _gdk_display->core_pointer;
730 }
731
732 static void
733 fill_scroll_event (GdkWindow          *window,
734                    GdkEvent           *event,
735                    NSEvent            *nsevent,
736                    gint                x,
737                    gint                y,
738                    gint                x_root,
739                    gint                y_root,
740                    GdkScrollDirection  direction)
741 {
742   GdkWindowObject *private;
743   NSPoint point;
744
745   private = GDK_WINDOW_OBJECT (window);
746
747   point = [nsevent locationInWindow];
748
749   event->any.type = GDK_SCROLL;
750   event->scroll.window = window;
751   event->scroll.time = get_time_from_ns_event (nsevent);
752   event->scroll.x = x;
753   event->scroll.y = y;
754   event->scroll.x_root = x_root;
755   event->scroll.y_root = y_root;
756   event->scroll.state = get_keyboard_modifiers_from_ns_event (nsevent);
757   event->scroll.direction = direction;
758   event->scroll.device = _gdk_display->core_pointer;
759 }
760
761 static void
762 fill_key_event (GdkWindow    *window,
763                 GdkEvent     *event,
764                 NSEvent      *nsevent,
765                 GdkEventType  type)
766 {
767   GdkEventPrivate *priv;
768   gchar buf[7];
769   gunichar c = 0;
770
771   priv = (GdkEventPrivate *) event;
772   priv->windowing_data = [nsevent retain];
773
774   event->any.type = type;
775   event->key.window = window;
776   event->key.time = get_time_from_ns_event (nsevent);
777   event->key.state = get_keyboard_modifiers_from_ns_event (nsevent);
778   event->key.hardware_keycode = [nsevent keyCode];
779   event->key.group = ([nsevent modifierFlags] & NSAlternateKeyMask) ? 1 : 0;
780   event->key.keyval = GDK_VoidSymbol;
781   
782   gdk_keymap_translate_keyboard_state (NULL,
783                                        event->key.hardware_keycode,
784                                        event->key.state, 
785                                        event->key.group,
786                                        &event->key.keyval,
787                                        NULL, NULL, NULL);
788
789   event->key.is_modifier = _gdk_quartz_keys_is_modifier (event->key.hardware_keycode);
790
791   /* If the key press is a modifier, the state should include the mask
792    * for that modifier but only for releases, not presses. This
793    * matches the X11 backend behavior.
794    */
795   if (event->key.is_modifier)
796     {
797       int mask = 0;
798
799       switch (event->key.keyval)
800         {
801         case GDK_Meta_R:
802         case GDK_Meta_L:
803           mask = GDK_MOD1_MASK;
804           break;
805         case GDK_Shift_R:
806         case GDK_Shift_L:
807           mask = GDK_SHIFT_MASK;
808           break;
809         case GDK_Caps_Lock:
810           mask = GDK_LOCK_MASK;
811           break;
812         case GDK_Alt_R:
813         case GDK_Alt_L:
814           mask = GDK_MOD5_MASK;
815           break;
816         case GDK_Control_R:
817         case GDK_Control_L:
818           mask = GDK_CONTROL_MASK;
819           break;
820         default:
821           mask = 0;
822         }
823
824       if (type == GDK_KEY_PRESS)
825         event->key.state &= ~mask;
826       else if (type == GDK_KEY_RELEASE)
827         event->key.state |= mask;
828     }
829
830   event->key.state |= current_button_state;
831
832   event->key.string = NULL;
833
834   /* Fill in ->string since apps depend on it, taken from the x11 backend. */
835   if (event->key.keyval != GDK_VoidSymbol)
836     c = gdk_keyval_to_unicode (event->key.keyval);
837
838   if (c)
839     {
840       gsize bytes_written;
841       gint len;
842
843       len = g_unichar_to_utf8 (c, buf);
844       buf[len] = '\0';
845       
846       event->key.string = g_locale_from_utf8 (buf, len,
847                                               NULL, &bytes_written,
848                                               NULL);
849       if (event->key.string)
850         event->key.length = bytes_written;
851     }
852   else if (event->key.keyval == GDK_Escape)
853     {
854       event->key.length = 1;
855       event->key.string = g_strdup ("\033");
856     }
857   else if (event->key.keyval == GDK_Return ||
858           event->key.keyval == GDK_KP_Enter)
859     {
860       event->key.length = 1;
861       event->key.string = g_strdup ("\r");
862     }
863
864   if (!event->key.string)
865     {
866       event->key.length = 0;
867       event->key.string = g_strdup ("");
868     }
869
870   GDK_NOTE(EVENTS,
871     g_message ("key %s:\t\twindow: %p  key: %12s  %d",
872           type == GDK_KEY_PRESS ? "press" : "release",
873           event->key.window,
874           event->key.keyval ? gdk_keyval_name (event->key.keyval) : "(none)",
875           event->key.keyval));
876 }
877
878 static gboolean
879 synthesize_crossing_event (GdkWindow *window,
880                            GdkEvent  *event,
881                            NSEvent   *nsevent,
882                            gint       x,
883                            gint       y,
884                            gint       x_root,
885                            gint       y_root)
886 {
887   GdkWindowObject *private;
888
889   private = GDK_WINDOW_OBJECT (window);
890
891   switch ([nsevent type])
892     {
893     case NSMouseEntered:
894       /* Enter events are considered always to be from the root window as we
895        * can't know for sure from what window we enter.
896        */
897       if (!(private->event_mask & GDK_ENTER_NOTIFY_MASK))
898         return FALSE;
899
900       fill_crossing_event (window, event, nsevent,
901                            x, y,
902                            x_root, y_root,
903                            GDK_ENTER_NOTIFY,
904                            GDK_CROSSING_NORMAL,
905                            GDK_NOTIFY_ANCESTOR);
906       return TRUE;
907
908     case NSMouseExited:
909       /* Exited always is to the root window as far as we are concerned,
910        * since there is no way to reliably get information about what new
911        * window is entered when exiting one.
912        */
913       if (!(private->event_mask & GDK_LEAVE_NOTIFY_MASK))
914         return FALSE;
915
916       fill_crossing_event (window, event, nsevent,
917                            x, y,
918                            x_root, y_root,
919                            GDK_LEAVE_NOTIFY,
920                            GDK_CROSSING_NORMAL,
921                            GDK_NOTIFY_ANCESTOR);
922       return TRUE;
923
924     default:
925       break;
926     }
927
928   return FALSE;
929 }
930
931 GdkEventMask 
932 _gdk_quartz_events_get_current_event_mask (void)
933 {
934   return current_event_mask;
935 }
936
937 static gboolean
938 gdk_event_translate (GdkEvent *event,
939                      NSEvent  *nsevent)
940 {
941   NSEventType event_type;
942   NSWindow *nswindow;
943   GdkWindow *window;
944   int x, y;
945   int x_root, y_root;
946   gboolean return_val;
947
948   /* There is no support for real desktop wide grabs, so we break
949    * grabs when the application loses focus (gets deactivated).
950    */
951   event_type = [nsevent type];
952   if (event_type == NSAppKitDefined)
953     {
954       if ([nsevent subtype] == NSApplicationDeactivatedEventType)
955         break_all_grabs (get_time_from_ns_event (nsevent));
956
957       /* This could potentially be used to break grabs when clicking
958        * on the title. The subtype 20 is undocumented so it's probably
959        * not a good idea: else if (subtype == 20) break_all_grabs ();
960        */
961
962       /* Leave all AppKit events to AppKit. */
963       return FALSE;
964     }
965
966   /* Keep track of button state, since we don't get that information
967    * for key events. 
968    */
969   switch (event_type)
970     {
971     case NSLeftMouseDown:
972     case NSRightMouseDown:
973     case NSOtherMouseDown:
974       current_button_state |= get_mouse_button_modifiers_from_ns_event (nsevent);
975       break;
976     case NSLeftMouseUp:
977     case NSRightMouseUp:
978     case NSOtherMouseUp:
979       current_button_state &= ~get_mouse_button_modifiers_from_ns_event (nsevent);
980       break;
981     default:
982       break;
983     }
984
985   if (_gdk_default_filters)
986     {
987       /* Apply global filters */
988       GdkFilterReturn result;
989
990       result = gdk_event_apply_filters (nsevent, event, _gdk_default_filters);
991       if (result != GDK_FILTER_CONTINUE)
992         {
993           return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
994           goto done;
995         }
996     }
997
998   nswindow = [nsevent window];
999
1000   /* Ignore events for no window or ones not created by GDK. */
1001   if (!nswindow || ![[nswindow contentView] isKindOfClass:[GdkQuartzView class]])
1002     return FALSE;
1003
1004   /* Ignore events and break grabs while the window is being
1005    * dragged. This is a workaround for the window getting events for
1006    * the window title.
1007    */
1008   if ([(GdkQuartzWindow *)nswindow isInMove])
1009     {
1010       break_all_grabs (get_time_from_ns_event (nsevent));
1011       return FALSE;
1012     }
1013
1014   /* Find the right GDK window to send the event to, taking grabs and
1015    * event masks into consideration.
1016    */
1017   window = find_window_for_ns_event (nsevent, &x, &y, &x_root, &y_root);
1018   if (!window)
1019     return FALSE;
1020
1021   /* Apply any window filters. */
1022   if (GDK_IS_WINDOW (window))
1023     {
1024       GdkWindowObject *filter_private = (GdkWindowObject *) window;
1025       GdkFilterReturn result;
1026
1027       if (filter_private->filters)
1028         {
1029           g_object_ref (window);
1030
1031           result = gdk_event_apply_filters (nsevent, event, filter_private->filters);
1032
1033           g_object_unref (window);
1034
1035           if (result != GDK_FILTER_CONTINUE)
1036             {
1037               return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
1038               goto done;
1039             }
1040         }
1041     }
1042
1043   /* If the app is not active leave the event to AppKit so the window gets
1044    * focused correctly and don't do click-through (so we behave like most
1045    * native apps). If the app is active, we focus the window and then handle
1046    * the event, also to match native apps.
1047    */
1048   if ((event_type == NSRightMouseDown ||
1049        event_type == NSOtherMouseDown ||
1050        event_type == NSLeftMouseDown))
1051     {
1052       GdkWindowObject *private = (GdkWindowObject *)window;
1053       GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
1054
1055       if (![NSApp isActive])
1056         {
1057           [NSApp activateIgnoringOtherApps:YES];
1058           return FALSE;
1059         }
1060       else if (![impl->toplevel isKeyWindow])
1061         {
1062           GdkPointerGrabInfo *grab;
1063
1064           grab = _gdk_display_get_last_pointer_grab (_gdk_display);
1065           if (!grab)
1066             [impl->toplevel makeKeyWindow];
1067         }
1068     }
1069
1070   current_event_mask = get_event_mask_from_ns_event (nsevent);
1071
1072   return_val = TRUE;
1073
1074   switch (event_type)
1075     {
1076     case NSLeftMouseDown:
1077     case NSRightMouseDown:
1078     case NSOtherMouseDown:
1079     case NSLeftMouseUp:
1080     case NSRightMouseUp:
1081     case NSOtherMouseUp:
1082       fill_button_event (window, event, nsevent, x, y, x_root, y_root);
1083       break;
1084
1085     case NSLeftMouseDragged:
1086     case NSRightMouseDragged:
1087     case NSOtherMouseDragged:
1088     case NSMouseMoved:
1089       fill_motion_event (window, event, nsevent, x, y, x_root, y_root);
1090       break;
1091
1092     case NSScrollWheel:
1093       {
1094         float dx = [nsevent deltaX];
1095         float dy = [nsevent deltaY];
1096         GdkScrollDirection direction;
1097
1098         if (dy != 0)
1099           {
1100             if (dy < 0.0)
1101               direction = GDK_SCROLL_DOWN;
1102             else
1103               direction = GDK_SCROLL_UP;
1104
1105             fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
1106           }
1107
1108         if (dx != 0)
1109           {
1110             if (dx < 0.0)
1111               direction = GDK_SCROLL_RIGHT;
1112             else
1113               direction = GDK_SCROLL_LEFT;
1114
1115             fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
1116           }
1117       }
1118       break;
1119
1120     case NSMouseEntered:
1121     case NSMouseExited:
1122       return_val = synthesize_crossing_event (window, event, nsevent, x, y, x_root, y_root);
1123       break;
1124
1125     case NSKeyDown:
1126     case NSKeyUp:
1127     case NSFlagsChanged:
1128       {
1129         GdkEventType type;
1130
1131         type = _gdk_quartz_keys_event_type (nsevent);
1132         if (type == GDK_NOTHING)
1133           return_val = FALSE;
1134         else
1135           fill_key_event (window, event, nsevent, type);
1136       }
1137       break;
1138
1139     default:
1140       /* Ignore everything elsee. */
1141       return_val = FALSE;
1142       break;
1143     }
1144
1145  done:
1146   if (return_val)
1147     {
1148       if (event->any.window)
1149         g_object_ref (event->any.window);
1150       if (((event->any.type == GDK_ENTER_NOTIFY) ||
1151            (event->any.type == GDK_LEAVE_NOTIFY)) &&
1152           (event->crossing.subwindow != NULL))
1153         g_object_ref (event->crossing.subwindow);
1154     }
1155   else
1156     {
1157       /* Mark this event as having no resources to be freed */
1158       event->any.window = NULL;
1159       event->any.type = GDK_NOTHING;
1160     }
1161
1162   return return_val;
1163 }
1164
1165 void
1166 _gdk_events_queue (GdkDisplay *display)
1167 {  
1168   NSEvent *nsevent;
1169
1170   nsevent = _gdk_quartz_event_loop_get_pending ();
1171   if (nsevent)
1172     {
1173       GdkEvent *event;
1174       GList *node;
1175
1176       event = gdk_event_new (GDK_NOTHING);
1177
1178       event->any.window = NULL;
1179       event->any.send_event = FALSE;
1180
1181       ((GdkEventPrivate *)event)->flags |= GDK_EVENT_PENDING;
1182
1183       node = _gdk_event_queue_append (display, event);
1184
1185       if (gdk_event_translate (event, nsevent))
1186         {
1187           ((GdkEventPrivate *)event)->flags &= ~GDK_EVENT_PENDING;
1188           _gdk_windowing_got_event (display, node, event, 0);
1189         }
1190       else
1191         {
1192           _gdk_event_queue_remove_link (display, node);
1193           g_list_free_1 (node);
1194           gdk_event_free (event);
1195
1196           GDK_THREADS_LEAVE ();
1197           [NSApp sendEvent:nsevent];
1198           GDK_THREADS_ENTER ();
1199         }
1200
1201       _gdk_quartz_event_loop_release_event (nsevent);
1202     }
1203 }
1204
1205 void
1206 gdk_flush (void)
1207 {
1208   /* Not supported. */
1209 }
1210
1211 void
1212 gdk_display_add_client_message_filter (GdkDisplay   *display,
1213                                        GdkAtom       message_type,
1214                                        GdkFilterFunc func,
1215                                        gpointer      data)
1216 {
1217   /* Not supported. */
1218 }
1219
1220 void
1221 gdk_add_client_message_filter (GdkAtom       message_type,
1222                                GdkFilterFunc func,
1223                                gpointer      data)
1224 {
1225   /* Not supported. */
1226 }
1227
1228 void
1229 gdk_display_sync (GdkDisplay *display)
1230 {
1231   /* Not supported. */
1232 }
1233
1234 void
1235 gdk_display_flush (GdkDisplay *display)
1236 {
1237   /* Not supported. */
1238 }
1239
1240 gboolean
1241 gdk_event_send_client_message_for_display (GdkDisplay      *display,
1242                                            GdkEvent        *event,
1243                                            GdkNativeWindow  winid)
1244 {
1245   /* Not supported. */
1246   return FALSE;
1247 }
1248
1249 void
1250 gdk_screen_broadcast_client_message (GdkScreen *screen,
1251                                      GdkEvent  *event)
1252 {
1253   /* Not supported. */
1254 }
1255
1256 gboolean
1257 gdk_screen_get_setting (GdkScreen   *screen,
1258                         const gchar *name,
1259                         GValue      *value)
1260 {
1261   if (strcmp (name, "gtk-double-click-time") == 0)
1262     {
1263       NSUserDefaults *defaults;
1264       float t;
1265
1266       GDK_QUARTZ_ALLOC_POOL;
1267
1268       defaults = [NSUserDefaults standardUserDefaults];
1269             
1270       t = [defaults floatForKey:@"com.apple.mouse.doubleClickThreshold"];
1271       if (t == 0.0)
1272         {
1273           /* No user setting, use the default in OS X. */
1274           t = 0.5;
1275         }
1276
1277       GDK_QUARTZ_RELEASE_POOL;
1278
1279       g_value_set_int (value, t * 1000);
1280
1281       return TRUE;
1282     }
1283   else if (strcmp (name, "gtk-font-name") == 0)
1284     {
1285       NSString *name;
1286       char *str;
1287
1288       GDK_QUARTZ_ALLOC_POOL;
1289
1290       name = [[NSFont systemFontOfSize:0] familyName];
1291
1292       /* Let's try to use the "views" font size (12pt) by default. This is
1293        * used for lists/text/other "content" which is the largest parts of
1294        * apps, using the "regular control" size (13pt) looks a bit out of
1295        * place. We might have to tweak this.
1296        */
1297
1298       /* The size has to be hardcoded as there doesn't seem to be a way to
1299        * get the views font size programmatically.
1300        */
1301       str = g_strdup_printf ("%s 12", [name UTF8String]);
1302       g_value_set_string (value, str);
1303       g_free (str);
1304
1305       GDK_QUARTZ_RELEASE_POOL;
1306
1307       return TRUE;
1308     }
1309   
1310   /* FIXME: Add more settings */
1311
1312   return FALSE;
1313 }
1314
1315 void
1316 _gdk_windowing_event_data_copy (const GdkEvent *src,
1317                                 GdkEvent       *dst)
1318 {
1319   GdkEventPrivate *priv_src = (GdkEventPrivate *) src;
1320   GdkEventPrivate *priv_dst = (GdkEventPrivate *) dst;
1321
1322   if (priv_src->windowing_data)
1323     {
1324       priv_dst->windowing_data = priv_src->windowing_data;
1325       [(NSEvent *)priv_dst->windowing_data retain];
1326     }
1327 }
1328
1329 void
1330 _gdk_windowing_event_data_free (GdkEvent *event)
1331 {
1332   GdkEventPrivate *priv = (GdkEventPrivate *) event;
1333
1334   if (priv->windowing_data)
1335     {
1336       [(NSEvent *)priv->windowing_data release];
1337       priv->windowing_data = NULL;
1338     }
1339 }