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