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
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.
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.
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.
24 #include <sys/types.h>
25 #include <sys/sysctl.h>
29 #import <Cocoa/Cocoa.h>
30 #include <Carbon/Carbon.h>
32 #include "gdkscreen.h"
33 #include "gdkkeysyms.h"
34 #include "gdkprivate-quartz.h"
36 /* This is the window the mouse is currently over */
37 static GdkWindow *current_mouse_window;
39 /* This is the window corresponding to the key window */
40 static GdkWindow *current_keyboard_window;
42 /* This is the pointer grab window */
43 GdkWindow *_gdk_quartz_pointer_grab_window;
44 static gboolean pointer_grab_owner_events;
45 static GdkEventMask pointer_grab_event_mask;
47 /* This is the keyboard grab window */
48 GdkWindow * _gdk_quartz_keyboard_grab_window;
49 static gboolean keyboard_grab_owner_events;
51 /* This is the event mask and button state from the last event */
52 static GdkEventMask current_event_mask;
53 static int current_button_state;
55 static void get_child_coordinates_from_ancestor (GdkWindow *ancestor_window,
58 GdkWindow *child_window,
61 static void get_ancestor_coordinates_from_child (GdkWindow *child_window,
64 GdkWindow *ancestor_window,
67 static void get_converted_window_coordinates (GdkWindow *in_window,
70 GdkWindow *out_window,
73 static void append_event (GdkEvent *event);
76 which_window_is_this (GdkWindow *window)
78 static gchar buf[256];
79 const gchar *name = NULL;
82 /* Get rid of compiler warning. */
83 if (0) which_window_is_this (window);
85 if (window == _gdk_root)
87 else if (window == NULL)
92 gdk_window_get_user_data (window, &widget);
94 name = G_OBJECT_TYPE_NAME (widget);
100 snprintf (buf, 256, "<%s (%p)%s>",
102 window == current_mouse_window ? ", is mouse" : "");
108 gdk_quartz_event_get_nsevent (GdkEvent *event)
110 /* FIXME: If the event here is unallocated, we crash. */
111 return ((GdkEventPrivate *) event)->windowing_data;
115 _gdk_events_init (void)
117 _gdk_quartz_event_loop_init ();
119 current_mouse_window = g_object_ref (_gdk_root);
120 current_keyboard_window = g_object_ref (_gdk_root);
124 gdk_events_pending (void)
126 return (_gdk_event_queue_find_first (_gdk_display) ||
127 (_gdk_quartz_event_loop_check_pending ()));
131 gdk_event_get_graphics_expose (GdkWindow *window)
133 /* FIXME: Implement */
138 generate_grab_broken_event (GdkWindow *window,
140 GdkWindow *grab_window)
142 if (!GDK_WINDOW_DESTROYED (window))
144 GdkEvent *event = gdk_event_new (GDK_GRAB_BROKEN);
146 event->grab_broken.window = window;
147 event->grab_broken.send_event = 0;
148 event->grab_broken.keyboard = keyboard;
149 event->grab_broken.implicit = FALSE;
150 event->grab_broken.grab_window = grab_window;
152 append_event (event);
157 gdk_keyboard_grab (GdkWindow *window,
161 g_return_val_if_fail (window != NULL, 0);
162 g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
164 if (_gdk_quartz_keyboard_grab_window)
166 if (_gdk_quartz_keyboard_grab_window != window)
167 generate_grab_broken_event (_gdk_quartz_keyboard_grab_window,
170 g_object_unref (_gdk_quartz_keyboard_grab_window);
173 _gdk_quartz_keyboard_grab_window = g_object_ref (window);
174 keyboard_grab_owner_events = owner_events;
176 return GDK_GRAB_SUCCESS;
180 gdk_display_keyboard_ungrab (GdkDisplay *display,
183 if (_gdk_quartz_keyboard_grab_window)
184 g_object_unref (_gdk_quartz_keyboard_grab_window);
185 _gdk_quartz_keyboard_grab_window = NULL;
189 pointer_ungrab_internal (void)
191 if (!_gdk_quartz_pointer_grab_window)
194 g_object_unref (_gdk_quartz_pointer_grab_window);
195 _gdk_quartz_pointer_grab_window = NULL;
197 pointer_grab_owner_events = FALSE;
198 pointer_grab_event_mask = 0;
200 /* FIXME: Send crossing events */
204 gdk_display_pointer_ungrab (GdkDisplay *display,
207 pointer_ungrab_internal ();
211 pointer_grab_internal (GdkWindow *window,
212 gboolean owner_events,
213 GdkEventMask event_mask,
214 GdkWindow *confine_to,
217 /* FIXME: Send crossing events */
219 _gdk_quartz_pointer_grab_window = g_object_ref (window);
220 pointer_grab_owner_events = owner_events;
221 pointer_grab_event_mask = event_mask;
223 return GDK_GRAB_SUCCESS;
227 gdk_pointer_grab (GdkWindow *window,
228 gboolean owner_events,
229 GdkEventMask event_mask,
230 GdkWindow *confine_to,
236 g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
237 g_return_val_if_fail (confine_to == NULL || GDK_IS_WINDOW (confine_to), 0);
239 toplevel = gdk_window_get_toplevel (window);
241 /* TODO: What do we do for offscreens and their children? We need to proxy the grab somehow */
242 if (!GDK_IS_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (toplevel)->impl))
243 return GDK_GRAB_SUCCESS;
245 if (_gdk_quartz_pointer_grab_window)
247 if (_gdk_quartz_pointer_grab_window != window)
248 generate_grab_broken_event (_gdk_quartz_pointer_grab_window,
252 pointer_ungrab_internal ();
255 return pointer_grab_internal (window, owner_events, event_mask,
259 /* This is used to break any grabs in the case where we have to due to
260 * the grab emulation. Instead of enforcing the desktop wide grab, we
261 * break it when the app loses focus for example.
264 break_all_grabs (void)
266 if (_gdk_quartz_keyboard_grab_window)
268 generate_grab_broken_event (_gdk_quartz_keyboard_grab_window,
271 g_object_unref (_gdk_quartz_keyboard_grab_window);
272 _gdk_quartz_keyboard_grab_window = NULL;
275 if (_gdk_quartz_pointer_grab_window)
277 generate_grab_broken_event (_gdk_quartz_pointer_grab_window,
280 pointer_ungrab_internal ();
285 fixup_event (GdkEvent *event)
287 if (event->any.window)
288 g_object_ref (event->any.window);
289 if (((event->any.type == GDK_ENTER_NOTIFY) ||
290 (event->any.type == GDK_LEAVE_NOTIFY)) &&
291 (event->crossing.subwindow != NULL))
292 g_object_ref (event->crossing.subwindow);
293 event->any.send_event = FALSE;
297 append_event (GdkEvent *event)
300 _gdk_event_queue_append (_gdk_display, event);
304 gdk_event_apply_filters (NSEvent *nsevent,
309 GdkFilterReturn result;
315 GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
317 tmp_list = tmp_list->next;
318 result = filter->function (nsevent, event, filter->data);
319 if (result != GDK_FILTER_CONTINUE)
323 return GDK_FILTER_CONTINUE;
326 /* Checks if the passed in window is interested in the event mask, and
327 * if so, it's returned. If not, the event can be propagated through
328 * its ancestors until one with the right event mask is found, up to
329 * the nearest toplevel.
332 find_window_interested_in_event_mask (GdkWindow *window,
333 GdkEventMask event_mask,
336 GdkWindowObject *private;
338 private = GDK_WINDOW_OBJECT (window);
341 if (private->event_mask & event_mask)
342 return (GdkWindow *)private;
347 /* Don't traverse beyond toplevels. */
348 if (GDK_WINDOW_TYPE (private) != GDK_WINDOW_CHILD)
351 private = private->parent;
358 get_time_from_ns_event (NSEvent *event)
360 double time = [event timestamp];
362 return time * 1000.0;
366 get_mouse_button_from_ns_event (NSEvent *event)
370 button = [event buttonNumber];
385 static GdkModifierType
386 get_mouse_button_modifiers_from_ns_event (NSEvent *event)
389 GdkModifierType state = 0;
391 /* This maps buttons 1 to 5 to GDK_BUTTON[1-5]_MASK */
392 button = get_mouse_button_from_ns_event (event);
393 if (button >= 1 && button <= 5)
394 state = (1 << (button + 7));
399 static GdkModifierType
400 get_keyboard_modifiers_from_ns_event (NSEvent *nsevent)
402 GdkModifierType modifiers = 0;
405 nsflags = [nsevent modifierFlags];
407 if (nsflags & NSAlphaShiftKeyMask)
408 modifiers |= GDK_LOCK_MASK;
409 if (nsflags & NSShiftKeyMask)
410 modifiers |= GDK_SHIFT_MASK;
411 if (nsflags & NSControlKeyMask)
412 modifiers |= GDK_CONTROL_MASK;
413 if (nsflags & NSCommandKeyMask)
414 modifiers |= GDK_MOD1_MASK;
419 /* Return an event mask from an NSEvent */
421 get_event_mask_from_ns_event (NSEvent *nsevent)
423 switch ([nsevent type])
425 case NSLeftMouseDown:
426 case NSRightMouseDown:
427 case NSOtherMouseDown:
428 return GDK_BUTTON_PRESS_MASK;
432 return GDK_BUTTON_RELEASE_MASK;
434 return GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
436 /* Since applications that want button press events can get
437 * scroll events on X11 (since scroll wheel events are really
438 * button press events there), we need to use GDK_BUTTON_PRESS_MASK too.
440 return GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK;
441 case NSLeftMouseDragged:
442 return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
443 GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK |
445 case NSRightMouseDragged:
446 return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
447 GDK_BUTTON_MOTION_MASK | GDK_BUTTON3_MOTION_MASK |
449 case NSOtherMouseDragged:
453 mask = (GDK_POINTER_MOTION_MASK |
454 GDK_POINTER_MOTION_HINT_MASK |
455 GDK_BUTTON_MOTION_MASK);
457 if (get_mouse_button_from_ns_event (nsevent) == 2)
458 mask |= (GDK_BUTTON2_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
467 switch (_gdk_quartz_keys_event_type (nsevent))
470 return GDK_KEY_PRESS_MASK;
471 case GDK_KEY_RELEASE:
472 return GDK_KEY_RELEASE_MASK;
476 g_assert_not_reached ();
482 return GDK_ENTER_NOTIFY_MASK;
485 return GDK_LEAVE_NOTIFY_MASK;
488 g_assert_not_reached ();
495 create_focus_event (GdkWindow *window,
500 event = gdk_event_new (GDK_FOCUS_CHANGE);
501 event->focus_change.window = window;
502 event->focus_change.in = in;
507 /* Note: Used to both set a new focus window and to unset the old one. */
509 _gdk_quartz_events_update_focus_window (GdkWindow *window,
514 if (got_focus && window == current_keyboard_window)
517 /* FIXME: Don't do this when grabbed? Or make GdkQuartzWindow
518 * disallow it in the first place instead?
521 if (!got_focus && window == current_keyboard_window)
523 event = create_focus_event (current_keyboard_window, FALSE);
524 append_event (event);
525 g_object_unref (current_keyboard_window);
526 current_keyboard_window = NULL;
531 if (current_keyboard_window)
533 event = create_focus_event (current_keyboard_window, FALSE);
534 append_event (event);
535 g_object_unref (current_keyboard_window);
536 current_keyboard_window = NULL;
539 event = create_focus_event (window, TRUE);
540 append_event (event);
541 current_keyboard_window = g_object_ref (window);
546 _gdk_quartz_events_send_map_event (GdkWindow *window)
548 GdkWindowObject *private = (GdkWindowObject *)window;
549 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
554 if (private->event_mask & GDK_STRUCTURE_MASK)
558 event.any.type = GDK_MAP;
559 event.any.window = window;
561 gdk_event_put (&event);
565 /* Get current mouse window */
567 _gdk_quartz_events_get_mouse_window (gboolean consider_grabs)
570 return current_mouse_window;
572 if (_gdk_quartz_pointer_grab_window && !pointer_grab_owner_events)
573 return _gdk_quartz_pointer_grab_window;
575 return current_mouse_window;
578 /* Update mouse window */
580 _gdk_quartz_events_update_mouse_window (GdkWindow *window)
582 if (window == current_mouse_window)
585 #ifdef G_ENABLE_DEBUG
586 if (_gdk_debug_flags & GDK_DEBUG_EVENTS)
587 _gdk_quartz_window_debug_highlight (window, 0);
588 #endif /* G_ENABLE_DEBUG */
591 g_object_ref (window);
592 if (current_mouse_window)
593 g_object_unref (current_mouse_window);
595 current_mouse_window = window;
598 /* Update current cursor */
600 _gdk_quartz_events_update_cursor (GdkWindow *window)
602 GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
603 NSCursor *nscursor = nil;
607 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
609 nscursor = impl->nscursor;
613 private = private->parent;
616 GDK_QUARTZ_ALLOC_POOL;
619 nscursor = [NSCursor arrowCursor];
621 if ([NSCursor currentCursor] != nscursor)
624 GDK_QUARTZ_RELEASE_POOL;
627 /* Translates coordinates from an ancestor window + coords, to
628 * coordinates that are relative the child window.
631 get_child_coordinates_from_ancestor (GdkWindow *ancestor_window,
634 GdkWindow *child_window,
638 GdkWindowObject *ancestor_private = GDK_WINDOW_OBJECT (ancestor_window);
639 GdkWindowObject *child_private = GDK_WINDOW_OBJECT (child_window);
641 while (child_private != ancestor_private)
643 ancestor_x -= child_private->x;
644 ancestor_y -= child_private->y;
646 child_private = child_private->parent;
649 *child_x = ancestor_x;
650 *child_y = ancestor_y;
653 /* Translates coordinates from a child window + coords, to
654 * coordinates that are relative the ancestor window.
657 get_ancestor_coordinates_from_child (GdkWindow *child_window,
660 GdkWindow *ancestor_window,
664 GdkWindowObject *child_private = GDK_WINDOW_OBJECT (child_window);
665 GdkWindowObject *ancestor_private = GDK_WINDOW_OBJECT (ancestor_window);
667 while (child_private != ancestor_private)
669 child_x += child_private->x;
670 child_y += child_private->y;
672 child_private = child_private->parent;
675 *ancestor_x = child_x;
676 *ancestor_y = child_y;
679 /* Translates coordinates relative to one window (in_window) into
680 * coordinates relative to another window (out_window).
683 get_converted_window_coordinates (GdkWindow *in_window,
686 GdkWindow *out_window,
690 GdkWindow *in_toplevel;
691 GdkWindow *out_toplevel;
692 int in_origin_x, in_origin_y;
693 int out_origin_x, out_origin_y;
695 if (in_window == out_window)
702 /* First translate to "in" toplevel coordinates, then on to "out"
703 * toplevel coordinates, and finally to "out" child (the passed in
704 * window) coordinates.
707 in_toplevel = gdk_window_get_toplevel (in_window);
708 out_toplevel = gdk_window_get_toplevel (out_window);
710 /* Translate in_x, in_y to "in" toplevel coordinates. */
711 get_ancestor_coordinates_from_child (in_window, in_x, in_y,
712 in_toplevel, &in_x, &in_y);
714 gdk_window_get_origin (in_toplevel, &in_origin_x, &in_origin_y);
715 gdk_window_get_origin (out_toplevel, &out_origin_x, &out_origin_y);
717 /* Translate in_x, in_y to "out" toplevel coordinates. */
718 in_x -= out_origin_x - in_origin_x;
719 in_y -= out_origin_y - in_origin_y;
721 get_child_coordinates_from_ancestor (out_toplevel,
727 /* Given a mouse NSEvent (must be a mouse event for a GDK window),
728 * finds the subwindow over which the pointer is located. Returns
729 * coordinates relative to the found window. If no window is found,
730 * returns the root window, and root window coordinates.
733 find_mouse_window_for_ns_event (NSEvent *nsevent,
737 GdkWindow *event_toplevel;
738 GdkWindowImplQuartz *impl;
739 GdkWindowObject *private;
740 GdkWindow *mouse_toplevel;
741 GdkWindow *mouse_window;
745 event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
746 impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (event_toplevel)->impl);
747 private = GDK_WINDOW_OBJECT (event_toplevel);
748 point = [nsevent locationInWindow];
751 y_tmp = private->height - point.y;
753 mouse_toplevel = gdk_window_get_toplevel (current_mouse_window);
755 get_converted_window_coordinates (event_toplevel,
760 mouse_window = _gdk_quartz_window_find_child (mouse_toplevel, x_tmp, y_tmp);
761 if (mouse_window && mouse_window != mouse_toplevel)
763 get_child_coordinates_from_ancestor (mouse_toplevel,
768 else if (!mouse_window)
770 /* This happens for events on the window title buttons and the
771 * desktop, treat those as being on the root window.
773 get_converted_window_coordinates (mouse_toplevel,
777 mouse_window = _gdk_root;
786 /* Trigger crossing events if necessary. This is used when showing a new
787 * window, since the tracking rect API doesn't work reliably when a window
788 * shows up under the mouse cursor. It's done by finding the topmost window
789 * under the mouse pointer and synthesizing crossing events into that
793 _gdk_quartz_events_trigger_crossing_events (gboolean defer_to_mainloop)
797 gint x_toplevel, y_toplevel;
798 GdkWindow *mouse_window;
800 GdkWindowImplQuartz *impl;
801 GdkWindowObject *private;
803 NSTimeInterval timestamp = 0;
804 NSEvent *current_event;
807 if (defer_to_mainloop)
809 nsevent = [NSEvent otherEventWithType:NSApplicationDefined
815 subtype:GDK_QUARTZ_EVENT_SUBTYPE_FAKE_CROSSING
818 [NSApp postEvent:nsevent atStart:NO];
822 point = [NSEvent mouseLocation];
824 y = _gdk_quartz_window_get_inverted_screen_y (point.y);
826 mouse_window = _gdk_quartz_window_find_child (_gdk_root, x, y);
827 if (!mouse_window || mouse_window == _gdk_root)
830 toplevel = gdk_window_get_toplevel (mouse_window);
832 /* We ignore crossing within the same toplevel since that is already
835 if (toplevel == gdk_window_get_toplevel (current_mouse_window))
838 get_converted_window_coordinates (_gdk_root,
841 &x_toplevel, &y_toplevel);
843 get_converted_window_coordinates (_gdk_root,
848 /* Fix up the event to be less fake if possible. */
849 current_event = [NSApp currentEvent];
852 flags = [current_event modifierFlags];
853 timestamp = [current_event timestamp];
857 timestamp = GetCurrentEventTime ();
859 impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (toplevel)->impl);
860 private = GDK_WINDOW_OBJECT (toplevel);
861 nsevent = [NSEvent otherEventWithType:NSApplicationDefined
862 location:NSMakePoint (x_toplevel, private->height - y_toplevel)
865 windowNumber:[impl->toplevel windowNumber]
867 subtype:GDK_QUARTZ_EVENT_SUBTYPE_FAKE_CROSSING
871 #ifdef G_ENABLE_DEBUG
872 /*_gdk_quartz_window_debug_highlight (mouse_window, 0);*/
875 /* FIXME: create an event, fill it, put on the queue... */
878 /* This function finds the correct window to send an event to, taking
879 * into account grabs, event propagation, and event masks.
882 find_window_for_ns_event (NSEvent *nsevent,
889 GdkWindowObject *private;
890 GdkWindowImplQuartz *impl;
893 NSEventType event_type;
895 toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
896 private = GDK_WINDOW_OBJECT (toplevel);
897 impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
899 point = [nsevent locationInWindow];
900 base = [[nsevent window] convertBaseToScreen:point];
903 *y = private->height - point.y;
906 *y_root = _gdk_quartz_window_get_inverted_screen_y (base.y);
908 event_type = [nsevent type];
912 case NSLeftMouseDown:
913 case NSRightMouseDown:
914 case NSOtherMouseDown:
920 case NSLeftMouseDragged:
921 case NSRightMouseDragged:
922 case NSOtherMouseDragged:
924 GdkWindow *mouse_window;
925 GdkEventMask event_mask;
926 GdkWindow *real_window;
928 /* From the docs for XGrabPointer:
930 * If owner_events is True and if a generated pointer event
931 * would normally be reported to this client, it is reported
932 * as usual. Otherwise, the event is reported with respect to
933 * the grab_window and is reported only if selected by
934 * event_mask. For either value of owner_events, unreported
935 * events are discarded.
937 * This means we first try the owner, then the grab window,
940 if (0 && _gdk_quartz_pointer_grab_window) /* FIXME: Implement grabs? */
942 if (pointer_grab_owner_events)
944 mouse_window = find_mouse_window_for_ns_event (nsevent, x, y);
945 event_mask = get_event_mask_from_ns_event (nsevent);
946 real_window = find_window_interested_in_event_mask (mouse_window, event_mask, TRUE);
948 if (mouse_window && real_window && mouse_window != real_window)
949 get_ancestor_coordinates_from_child (mouse_window,
958 /* Finally check the grab window. */
959 if (pointer_grab_event_mask & get_event_mask_from_ns_event (nsevent))
961 GdkWindow *event_toplevel;
962 GdkWindow *grab_toplevel;
966 event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
967 grab_toplevel = gdk_window_get_toplevel (_gdk_quartz_pointer_grab_window);
968 point = [nsevent locationInWindow];
971 y_tmp = GDK_WINDOW_OBJECT (grab_toplevel)->height - point.y;
973 /* Translate the coordinates so they are relative to
974 * the grab window instead of the event toplevel for
975 * the cases where they are not the same.
977 get_converted_window_coordinates (event_toplevel,
979 _gdk_quartz_pointer_grab_window,
982 return _gdk_quartz_pointer_grab_window;
989 /* The non-grabbed case. */
991 /* Leave events above the window (e.g. possibly on the titlebar)
997 /* FIXME: Also need to leave resize events to cocoa somehow? */
1004 case NSMouseEntered:
1010 case NSFlagsChanged:
1012 if (_gdk_quartz_keyboard_grab_window && !keyboard_grab_owner_events)
1013 return _gdk_quartz_keyboard_grab_window;
1020 /* Ignore everything else. */
1028 fill_crossing_event (GdkWindow *toplevel,
1035 GdkEventType event_type,
1036 GdkCrossingMode mode,
1037 GdkNotifyType detail)
1039 GdkWindowObject *private;
1042 private = GDK_WINDOW_OBJECT (toplevel);
1044 point = [nsevent locationInWindow];
1046 event->any.type = event_type;
1047 event->crossing.window = toplevel;
1048 event->crossing.subwindow = NULL;
1049 event->crossing.time = get_time_from_ns_event (nsevent);
1050 event->crossing.x = x;
1051 event->crossing.y = y;
1052 event->crossing.x_root = x_root;
1053 event->crossing.y_root = y_root;
1054 event->crossing.mode = mode;
1055 event->crossing.detail = detail;
1056 event->crossing.state = get_keyboard_modifiers_from_ns_event (nsevent);
1058 /* FIXME: Focus and button state? */
1062 fill_button_event (GdkWindow *window,
1074 state = get_keyboard_modifiers_from_ns_event (nsevent);
1076 switch ([nsevent type])
1078 case NSLeftMouseDown:
1079 case NSRightMouseDown:
1080 case NSOtherMouseDown:
1081 type = GDK_BUTTON_PRESS;
1084 case NSRightMouseUp:
1085 case NSOtherMouseUp:
1086 type = GDK_BUTTON_RELEASE;
1087 state |= get_mouse_button_modifiers_from_ns_event (nsevent);
1090 g_assert_not_reached ();
1093 button = get_mouse_button_from_ns_event (nsevent);
1095 event->any.type = type;
1096 event->button.window = window;
1097 event->button.time = get_time_from_ns_event (nsevent);
1098 event->button.x = x;
1099 event->button.y = y;
1100 event->button.x_root = x_root;
1101 event->button.y_root = y_root;
1102 /* FIXME event->axes */
1103 event->button.state = state;
1104 event->button.button = button;
1105 event->button.device = _gdk_display->core_pointer;
1109 fill_motion_event (GdkWindow *window,
1117 GdkModifierType state;
1119 state = get_keyboard_modifiers_from_ns_event (nsevent);
1121 switch ([nsevent type])
1123 case NSLeftMouseDragged:
1124 case NSRightMouseDragged:
1125 case NSOtherMouseDragged:
1126 state |= get_mouse_button_modifiers_from_ns_event (nsevent);
1133 event->any.type = GDK_MOTION_NOTIFY;
1134 event->motion.window = window;
1135 event->motion.time = get_time_from_ns_event (nsevent);
1136 event->motion.x = x;
1137 event->motion.y = y;
1138 event->motion.x_root = x_root;
1139 event->motion.y_root = y_root;
1140 /* FIXME event->axes */
1141 event->motion.state = state;
1142 event->motion.is_hint = FALSE;
1143 event->motion.device = _gdk_display->core_pointer;
1147 fill_scroll_event (GdkWindow *window,
1154 GdkScrollDirection direction)
1156 GdkWindowObject *private;
1159 private = GDK_WINDOW_OBJECT (window);
1161 point = [nsevent locationInWindow];
1163 event->any.type = GDK_SCROLL;
1164 event->scroll.window = window;
1165 event->scroll.time = get_time_from_ns_event (nsevent);
1166 event->scroll.x = x;
1167 event->scroll.y = y;
1168 event->scroll.x_root = x_root;
1169 event->scroll.y_root = y_root;
1170 event->scroll.state = get_keyboard_modifiers_from_ns_event (nsevent);
1171 event->scroll.direction = direction;
1172 event->scroll.device = _gdk_display->core_pointer;
1176 fill_key_event (GdkWindow *window,
1181 GdkEventPrivate *priv;
1185 priv = (GdkEventPrivate *) event;
1186 priv->windowing_data = [nsevent retain];
1188 event->any.type = type;
1189 event->key.window = window;
1190 event->key.time = get_time_from_ns_event (nsevent);
1191 event->key.state = get_keyboard_modifiers_from_ns_event (nsevent);
1192 event->key.hardware_keycode = [nsevent keyCode];
1193 event->key.group = ([nsevent modifierFlags] & NSAlternateKeyMask) ? 1 : 0;
1194 event->key.keyval = GDK_VoidSymbol;
1196 gdk_keymap_translate_keyboard_state (NULL,
1197 event->key.hardware_keycode,
1203 event->key.is_modifier = _gdk_quartz_keys_is_modifier (event->key.hardware_keycode);
1205 /* If the key press is a modifier, the state should include the mask
1206 * for that modifier but only for releases, not presses. This
1207 * matches the X11 backend behavior.
1209 if (event->key.is_modifier)
1213 switch (event->key.keyval)
1217 mask = GDK_MOD1_MASK;
1221 mask = GDK_SHIFT_MASK;
1224 mask = GDK_LOCK_MASK;
1228 mask = GDK_MOD5_MASK;
1232 mask = GDK_CONTROL_MASK;
1238 if (type == GDK_KEY_PRESS)
1239 event->key.state &= ~mask;
1240 else if (type == GDK_KEY_RELEASE)
1241 event->key.state |= mask;
1244 event->key.state |= current_button_state;
1246 event->key.string = NULL;
1248 /* Fill in ->string since apps depend on it, taken from the x11 backend. */
1249 if (event->key.keyval != GDK_VoidSymbol)
1250 c = gdk_keyval_to_unicode (event->key.keyval);
1254 gsize bytes_written;
1257 len = g_unichar_to_utf8 (c, buf);
1260 event->key.string = g_locale_from_utf8 (buf, len,
1261 NULL, &bytes_written,
1263 if (event->key.string)
1264 event->key.length = bytes_written;
1266 else if (event->key.keyval == GDK_Escape)
1268 event->key.length = 1;
1269 event->key.string = g_strdup ("\033");
1271 else if (event->key.keyval == GDK_Return ||
1272 event->key.keyval == GDK_KP_Enter)
1274 event->key.length = 1;
1275 event->key.string = g_strdup ("\r");
1278 if (!event->key.string)
1280 event->key.length = 0;
1281 event->key.string = g_strdup ("");
1285 g_message ("key %s:\t\twindow: %p key: %12s %d",
1286 type == GDK_KEY_PRESS ? "press" : "release",
1288 event->key.keyval ? gdk_keyval_name (event->key.keyval) : "(none)",
1289 event->key.keyval));
1293 synthesize_crossing_event (GdkWindow *window,
1301 GdkWindowObject *private;
1303 private = GDK_WINDOW_OBJECT (window);
1305 /* FIXME: had this before csw:
1306 _gdk_quartz_events_update_mouse_window (window);
1307 if (window && !_gdk_quartz_pointer_grab_window)
1308 _gdk_quartz_events_update_cursor (window);
1311 switch ([nsevent type])
1313 case NSMouseEntered:
1315 /* Enter events are considered always to be from the root window as
1316 * we can't know for sure from what window we enter.
1318 if (!(private->event_mask & GDK_ENTER_NOTIFY_MASK))
1321 fill_crossing_event (window, event, nsevent,
1325 GDK_CROSSING_NORMAL,
1326 GDK_NOTIFY_ANCESTOR);
1332 /* Exited always is to the root window as far as we are concerned,
1333 * since there is no way to reliably get information about what new
1334 * window is entered when exiting one.
1336 if (!(private->event_mask & GDK_LEAVE_NOTIFY_MASK))
1339 /*if (!mouse_window ||
1340 gdk_window_get_toplevel (mouse_window) ==
1341 gdk_window_get_toplevel (current_mouse_window))
1343 mouse_window = _gdk_root;
1347 fill_crossing_event (window, event, nsevent,
1351 GDK_CROSSING_NORMAL,
1352 GDK_NOTIFY_ANCESTOR);
1364 _gdk_quartz_events_get_current_event_mask (void)
1366 return current_event_mask;
1369 #define GDK_ANY_BUTTON_MASK (GDK_BUTTON1_MASK | \
1370 GDK_BUTTON2_MASK | \
1371 GDK_BUTTON3_MASK | \
1372 GDK_BUTTON4_MASK | \
1376 button_event_check_implicit_grab (GdkWindow *window,
1380 GdkDisplay *display = gdk_drawable_get_display (window);
1382 /* track implicit grabs for button presses */
1383 switch (event->type)
1385 case GDK_BUTTON_PRESS:
1386 if (!display->pointer_grab.window)
1388 _gdk_display_set_has_pointer_grab (display,
1392 gdk_window_get_events (window),
1399 case GDK_BUTTON_RELEASE:
1400 if (display->pointer_grab.window &&
1401 display->pointer_grab.implicit &&
1402 (current_button_state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
1404 _gdk_display_unset_has_pointer_grab (display, TRUE, TRUE,
1405 event->button.time);
1409 g_assert_not_reached ();
1414 gdk_event_translate (GdkEvent *event,
1417 NSEventType event_type;
1422 gboolean return_val;
1424 /* There is no support for real desktop wide grabs, so we break
1425 * grabs when the application loses focus (gets deactivated).
1427 event_type = [nsevent type];
1428 if (event_type == NSAppKitDefined)
1430 if ([nsevent subtype] == NSApplicationDeactivatedEventType)
1433 /* This could potentially be used to break grabs when clicking
1434 * on the title. The subtype 20 is undocumented so it's probably
1435 * not a good idea: else if (subtype == 20) break_all_grabs ();
1438 /* Leave all AppKit events to AppKit. */
1442 /* Handle our generated "fake" crossing events. */
1443 if (event_type == NSApplicationDefined &&
1444 [nsevent subtype] == GDK_QUARTZ_EVENT_SUBTYPE_FAKE_CROSSING)
1446 /* FIXME: This needs to actually fill in the event we have... */
1447 _gdk_quartz_events_trigger_crossing_events (FALSE);
1448 return FALSE; /* ...and return TRUE instead. */
1451 /* Keep track of button state, since we don't get that information
1456 case NSLeftMouseDown:
1457 case NSRightMouseDown:
1458 case NSOtherMouseDown:
1459 current_button_state |= get_mouse_button_modifiers_from_ns_event (nsevent);
1462 case NSRightMouseUp:
1463 case NSOtherMouseUp:
1464 current_button_state &= ~get_mouse_button_modifiers_from_ns_event (nsevent);
1470 if (_gdk_default_filters)
1472 /* Apply global filters */
1473 GdkFilterReturn result;
1475 result = gdk_event_apply_filters (nsevent, event, _gdk_default_filters);
1476 if (result != GDK_FILTER_CONTINUE)
1478 return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
1483 nswindow = [nsevent window];
1485 /* Ignore events for no window or ones not created by GDK. */
1486 if (!nswindow || ![[nswindow contentView] isKindOfClass:[GdkQuartzView class]])
1489 /* Ignore events and break grabs while the window is being
1490 * dragged. This is a workaround for the window getting events for
1493 if ([(GdkQuartzWindow *)nswindow isInMove])
1499 /* Find the right GDK window to send the event to, taking grabs and
1500 * event masks into consideration.
1502 window = find_window_for_ns_event (nsevent, &x, &y, &x_root, &y_root);
1506 /* Apply any window filters. */
1507 if (GDK_IS_WINDOW (window))
1509 GdkWindowObject *filter_private = (GdkWindowObject *) window;
1510 GdkFilterReturn result;
1512 if (filter_private->filters)
1514 g_object_ref (window);
1516 result = gdk_event_apply_filters (nsevent, event, filter_private->filters);
1518 g_object_unref (window);
1520 if (result != GDK_FILTER_CONTINUE)
1522 return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
1528 /* We only activate the application on click if it's not already active,
1529 * or if it's active but the window isn't focused. This matches most use
1530 * cases of native apps (no click-through).
1532 if ((event_type == NSRightMouseDown ||
1533 event_type == NSOtherMouseDown ||
1534 event_type == NSLeftMouseDown))
1536 GdkWindowObject *private = (GdkWindowObject *)window;
1537 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
1539 if (![NSApp isActive])
1541 else if (![impl->toplevel isMainWindow])
1545 current_event_mask = get_event_mask_from_ns_event (nsevent);
1551 case NSLeftMouseDown:
1552 case NSRightMouseDown:
1553 case NSOtherMouseDown:
1554 fill_button_event (window, event, nsevent, x, y, x_root, y_root);
1555 button_event_check_implicit_grab (window, event, nsevent);
1559 case NSRightMouseUp:
1560 case NSOtherMouseUp:
1561 fill_button_event (window, event, nsevent, x, y, x_root, y_root);
1562 button_event_check_implicit_grab (window, event, nsevent);
1565 case NSLeftMouseDragged:
1566 case NSRightMouseDragged:
1567 case NSOtherMouseDragged:
1569 fill_motion_event (window, event, nsevent, x, y, x_root, y_root);
1574 float dx = [nsevent deltaX];
1575 float dy = [nsevent deltaY];
1576 GdkScrollDirection direction;
1581 direction = GDK_SCROLL_DOWN;
1583 direction = GDK_SCROLL_UP;
1585 fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
1591 direction = GDK_SCROLL_RIGHT;
1593 direction = GDK_SCROLL_LEFT;
1595 fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
1600 case NSMouseEntered:
1602 return_val = synthesize_crossing_event (window, event, nsevent, x, y, x_root, y_root);
1607 case NSFlagsChanged:
1611 type = _gdk_quartz_keys_event_type (nsevent);
1612 if (type == GDK_NOTHING)
1615 fill_key_event (window, event, nsevent, type);
1620 /* Ignore everything elsee. */
1628 if (event->any.window)
1629 g_object_ref (event->any.window);
1630 if (((event->any.type == GDK_ENTER_NOTIFY) ||
1631 (event->any.type == GDK_LEAVE_NOTIFY)) &&
1632 (event->crossing.subwindow != NULL))
1633 g_object_ref (event->crossing.subwindow);
1637 /* Mark this event as having no resources to be freed */
1638 event->any.window = NULL;
1639 event->any.type = GDK_NOTHING;
1646 _gdk_events_queue (GdkDisplay *display)
1650 nsevent = _gdk_quartz_event_loop_get_pending ();
1656 event = gdk_event_new (GDK_NOTHING);
1658 event->any.window = NULL;
1659 event->any.send_event = FALSE;
1661 ((GdkEventPrivate *)event)->flags |= GDK_EVENT_PENDING;
1663 node = _gdk_event_queue_append (display, event);
1665 if (gdk_event_translate (event, nsevent))
1667 ((GdkEventPrivate *)event)->flags &= ~GDK_EVENT_PENDING;
1668 _gdk_windowing_got_event (display, node, event, 0);
1672 _gdk_event_queue_remove_link (display, node);
1673 g_list_free_1 (node);
1674 gdk_event_free (event);
1676 GDK_THREADS_LEAVE ();
1677 [NSApp sendEvent:nsevent];
1678 GDK_THREADS_ENTER ();
1681 _gdk_quartz_event_loop_release_event (nsevent);
1688 /* Not supported. */
1692 gdk_display_add_client_message_filter (GdkDisplay *display,
1693 GdkAtom message_type,
1697 /* Not supported. */
1701 gdk_add_client_message_filter (GdkAtom message_type,
1705 /* Not supported. */
1709 gdk_display_sync (GdkDisplay *display)
1711 /* Not supported. */
1715 gdk_display_flush (GdkDisplay *display)
1717 /* Not supported. */
1721 gdk_event_send_client_message_for_display (GdkDisplay *display,
1723 GdkNativeWindow winid)
1725 /* Not supported. */
1730 gdk_screen_broadcast_client_message (GdkScreen *screen,
1733 /* Not supported. */
1737 gdk_screen_get_setting (GdkScreen *screen,
1741 if (strcmp (name, "gtk-double-click-time") == 0)
1743 NSUserDefaults *defaults;
1746 GDK_QUARTZ_ALLOC_POOL;
1748 defaults = [NSUserDefaults standardUserDefaults];
1750 t = [defaults floatForKey:@"com.apple.mouse.doubleClickThreshold"];
1753 /* No user setting, use the default in OS X. */
1757 GDK_QUARTZ_RELEASE_POOL;
1759 g_value_set_int (value, t * 1000);
1763 else if (strcmp (name, "gtk-font-name") == 0)
1768 GDK_QUARTZ_ALLOC_POOL;
1770 name = [[NSFont systemFontOfSize:0] familyName];
1772 /* Let's try to use the "views" font size (12pt) by default. This is
1773 * used for lists/text/other "content" which is the largest parts of
1774 * apps, using the "regular control" size (13pt) looks a bit out of
1775 * place. We might have to tweak this.
1778 /* The size has to be hardcoded as there doesn't seem to be a way to
1779 * get the views font size programmatically.
1781 str = g_strdup_printf ("%s 12", [name UTF8String]);
1782 g_value_set_string (value, str);
1785 GDK_QUARTZ_RELEASE_POOL;
1790 /* FIXME: Add more settings */
1796 _gdk_windowing_event_data_copy (const GdkEvent *src,
1799 GdkEventPrivate *priv_src = (GdkEventPrivate *) src;
1800 GdkEventPrivate *priv_dst = (GdkEventPrivate *) dst;
1802 if (priv_src->windowing_data)
1804 priv_dst->windowing_data = priv_src->windowing_data;
1805 [(NSEvent *)priv_dst->windowing_data retain];
1810 _gdk_windowing_event_data_free (GdkEvent *event)
1812 GdkEventPrivate *priv = (GdkEventPrivate *) event;
1814 if (priv->windowing_data)
1816 [(NSEvent *)priv->windowing_data release];
1817 priv->windowing_data = NULL;