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