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;
46 static gboolean pointer_grab_implicit;
48 /* This is the keyboard grab window */
49 GdkWindow * _gdk_quartz_keyboard_grab_window;
50 static gboolean keyboard_grab_owner_events;
52 /* This is the event mask and button state from the last event */
53 static GdkEventMask current_event_mask;
54 static int current_button_state;
56 static void get_child_coordinates_from_ancestor (GdkWindow *ancestor_window,
59 GdkWindow *child_window,
62 static void get_ancestor_coordinates_from_child (GdkWindow *child_window,
65 GdkWindow *ancestor_window,
68 static void get_converted_window_coordinates (GdkWindow *in_window,
71 GdkWindow *out_window,
74 static void append_event (GdkEvent *event);
77 which_window_is_this (GdkWindow *window)
79 static gchar buf[256];
80 const gchar *name = NULL;
83 /* Get rid of compiler warning. */
84 if (0) which_window_is_this (window);
86 if (window == _gdk_root)
88 else if (window == NULL)
93 gdk_window_get_user_data (window, &widget);
95 name = G_OBJECT_TYPE_NAME (widget);
101 snprintf (buf, 256, "<%s (%p)%s>",
103 window == current_mouse_window ? ", is mouse" : "");
109 gdk_quartz_event_get_nsevent (GdkEvent *event)
111 /* FIXME: If the event here is unallocated, we crash. */
112 return ((GdkEventPrivate *) event)->windowing_data;
116 _gdk_events_init (void)
118 _gdk_quartz_event_loop_init ();
120 current_mouse_window = g_object_ref (_gdk_root);
121 current_keyboard_window = g_object_ref (_gdk_root);
125 gdk_events_pending (void)
127 return (_gdk_event_queue_find_first (_gdk_display) ||
128 (_gdk_quartz_event_loop_check_pending ()));
132 gdk_event_get_graphics_expose (GdkWindow *window)
134 /* FIXME: Implement */
139 generate_grab_broken_event (GdkWindow *window,
142 GdkWindow *grab_window)
144 if (!GDK_WINDOW_DESTROYED (window))
146 GdkEvent *event = gdk_event_new (GDK_GRAB_BROKEN);
148 event->grab_broken.window = window;
149 event->grab_broken.send_event = 0;
150 event->grab_broken.keyboard = keyboard;
151 event->grab_broken.implicit = implicit;
152 event->grab_broken.grab_window = grab_window;
154 append_event (event);
159 gdk_keyboard_grab (GdkWindow *window,
163 g_return_val_if_fail (window != NULL, 0);
164 g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
166 if (_gdk_quartz_keyboard_grab_window)
168 if (_gdk_quartz_keyboard_grab_window != window)
169 generate_grab_broken_event (_gdk_quartz_keyboard_grab_window,
170 TRUE, FALSE, window);
172 g_object_unref (_gdk_quartz_keyboard_grab_window);
175 _gdk_quartz_keyboard_grab_window = g_object_ref (window);
176 keyboard_grab_owner_events = owner_events;
178 return GDK_GRAB_SUCCESS;
182 gdk_display_keyboard_ungrab (GdkDisplay *display,
185 if (_gdk_quartz_keyboard_grab_window)
186 g_object_unref (_gdk_quartz_keyboard_grab_window);
187 _gdk_quartz_keyboard_grab_window = NULL;
191 pointer_ungrab_internal (gboolean only_if_implicit)
193 if (!_gdk_quartz_pointer_grab_window)
196 if (only_if_implicit && !pointer_grab_implicit)
199 g_object_unref (_gdk_quartz_pointer_grab_window);
200 _gdk_quartz_pointer_grab_window = NULL;
202 pointer_grab_owner_events = FALSE;
203 pointer_grab_event_mask = 0;
204 pointer_grab_implicit = FALSE;
206 /* FIXME: Send crossing events */
210 gdk_display_pointer_ungrab (GdkDisplay *display,
213 pointer_ungrab_internal (FALSE);
217 pointer_grab_internal (GdkWindow *window,
218 gboolean owner_events,
219 GdkEventMask event_mask,
220 GdkWindow *confine_to,
224 /* FIXME: Send crossing events */
226 _gdk_quartz_pointer_grab_window = g_object_ref (window);
227 pointer_grab_owner_events = owner_events;
228 pointer_grab_event_mask = event_mask;
229 pointer_grab_implicit = implicit;
231 return GDK_GRAB_SUCCESS;
235 gdk_pointer_grab (GdkWindow *window,
236 gboolean owner_events,
237 GdkEventMask event_mask,
238 GdkWindow *confine_to,
242 g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
243 g_return_val_if_fail (confine_to == NULL || GDK_IS_WINDOW (confine_to), 0);
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,
249 FALSE, pointer_grab_implicit, window);
251 pointer_ungrab_internal (FALSE);
254 return pointer_grab_internal (window, owner_events, event_mask,
255 confine_to, cursor, FALSE);
258 /* This is used to break any grabs in the case where we have to due to
259 * the grab emulation. Instead of enforcing the desktop wide grab, we
260 * break it when the app loses focus for example.
263 break_all_grabs (void)
265 if (_gdk_quartz_keyboard_grab_window)
267 generate_grab_broken_event (_gdk_quartz_keyboard_grab_window,
270 g_object_unref (_gdk_quartz_keyboard_grab_window);
271 _gdk_quartz_keyboard_grab_window = NULL;
274 if (_gdk_quartz_pointer_grab_window)
276 generate_grab_broken_event (_gdk_quartz_pointer_grab_window,
277 FALSE, pointer_grab_implicit,
279 pointer_ungrab_internal (FALSE);
284 fixup_event (GdkEvent *event)
286 if (event->any.window)
287 g_object_ref (event->any.window);
288 if (((event->any.type == GDK_ENTER_NOTIFY) ||
289 (event->any.type == GDK_LEAVE_NOTIFY)) &&
290 (event->crossing.subwindow != NULL))
291 g_object_ref (event->crossing.subwindow);
292 event->any.send_event = FALSE;
296 append_event (GdkEvent *event)
299 _gdk_event_queue_append (_gdk_display, event);
303 gdk_event_apply_filters (NSEvent *nsevent,
308 GdkFilterReturn result;
314 GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
316 tmp_list = tmp_list->next;
317 result = filter->function (nsevent, event, filter->data);
318 if (result != GDK_FILTER_CONTINUE)
322 return GDK_FILTER_CONTINUE;
325 /* Checks if the passed in window is interested in the event mask, and
326 * if so, it's returned. If not, the event can be propagated through
327 * its ancestors until one with the right event mask is found, up to
328 * the nearest toplevel.
331 find_window_interested_in_event_mask (GdkWindow *window,
332 GdkEventMask event_mask,
335 GdkWindowObject *private;
337 private = GDK_WINDOW_OBJECT (window);
340 if (private->event_mask & event_mask)
341 return (GdkWindow *)private;
346 /* Don't traverse beyond toplevels. */
347 if (GDK_WINDOW_TYPE (private) != GDK_WINDOW_CHILD)
350 private = private->parent;
357 get_time_from_ns_event (NSEvent *event)
359 double time = [event timestamp];
361 return time * 1000.0;
365 get_mouse_button_from_ns_event (NSEvent *event)
369 button = [event buttonNumber];
384 static GdkModifierType
385 get_mouse_button_modifiers_from_ns_event (NSEvent *event)
388 GdkModifierType state = 0;
390 /* This maps buttons 1 to 5 to GDK_BUTTON[1-5]_MASK */
391 button = get_mouse_button_from_ns_event (event);
392 if (button >= 1 && button <= 5)
393 state = (1 << (button + 7));
398 static GdkModifierType
399 get_keyboard_modifiers_from_ns_event (NSEvent *nsevent)
401 GdkModifierType modifiers = 0;
404 nsflags = [nsevent modifierFlags];
406 if (nsflags & NSAlphaShiftKeyMask)
407 modifiers |= GDK_LOCK_MASK;
408 if (nsflags & NSShiftKeyMask)
409 modifiers |= GDK_SHIFT_MASK;
410 if (nsflags & NSControlKeyMask)
411 modifiers |= GDK_CONTROL_MASK;
412 if (nsflags & NSCommandKeyMask)
413 modifiers |= GDK_MOD1_MASK;
418 /* Return an event mask from an NSEvent */
420 get_event_mask_from_ns_event (NSEvent *nsevent)
422 switch ([nsevent type])
424 case NSLeftMouseDown:
425 case NSRightMouseDown:
426 case NSOtherMouseDown:
427 return GDK_BUTTON_PRESS_MASK;
431 return GDK_BUTTON_RELEASE_MASK;
433 return GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
435 /* Since applications that want button press events can get
436 * scroll events on X11 (since scroll wheel events are really
437 * button press events there), we need to use GDK_BUTTON_PRESS_MASK too.
439 return GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK;
440 case NSLeftMouseDragged:
441 return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
442 GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK |
444 case NSRightMouseDragged:
445 return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
446 GDK_BUTTON_MOTION_MASK | GDK_BUTTON3_MOTION_MASK |
448 case NSOtherMouseDragged:
452 mask = (GDK_POINTER_MOTION_MASK |
453 GDK_POINTER_MOTION_HINT_MASK |
454 GDK_BUTTON_MOTION_MASK);
456 if (get_mouse_button_from_ns_event (nsevent) == 2)
457 mask |= (GDK_BUTTON2_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
466 switch (_gdk_quartz_keys_event_type (nsevent))
469 return GDK_KEY_PRESS_MASK;
470 case GDK_KEY_RELEASE:
471 return GDK_KEY_RELEASE_MASK;
475 g_assert_not_reached ();
481 return GDK_ENTER_NOTIFY_MASK;
484 return GDK_LEAVE_NOTIFY_MASK;
487 g_assert_not_reached ();
494 create_focus_event (GdkWindow *window,
499 event = gdk_event_new (GDK_FOCUS_CHANGE);
500 event->focus_change.window = window;
501 event->focus_change.in = in;
506 /* Note: Used to both set a new focus window and to unset the old one. */
508 _gdk_quartz_events_update_focus_window (GdkWindow *window,
513 if (got_focus && window == current_keyboard_window)
516 /* FIXME: Don't do this when grabbed? Or make GdkQuartzWindow
517 * disallow it in the first place instead?
520 if (!got_focus && window == current_keyboard_window)
522 event = create_focus_event (current_keyboard_window, FALSE);
523 append_event (event);
524 g_object_unref (current_keyboard_window);
525 current_keyboard_window = NULL;
530 if (current_keyboard_window)
532 event = create_focus_event (current_keyboard_window, FALSE);
533 append_event (event);
534 g_object_unref (current_keyboard_window);
535 current_keyboard_window = NULL;
538 event = create_focus_event (window, TRUE);
539 append_event (event);
540 current_keyboard_window = g_object_ref (window);
545 convert_window_coordinates_to_root (GdkWindow *window,
556 if (gdk_window_get_origin (window, &ox, &oy))
564 _gdk_quartz_events_send_map_event (GdkWindow *window)
566 GdkWindowObject *private = (GdkWindowObject *)window;
567 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
572 if (private->event_mask & GDK_STRUCTURE_MASK)
576 event.any.type = GDK_MAP;
577 event.any.window = window;
579 gdk_event_put (&event);
583 /* Get current mouse window */
585 _gdk_quartz_events_get_mouse_window (gboolean consider_grabs)
588 return current_mouse_window;
590 if (_gdk_quartz_pointer_grab_window && !pointer_grab_owner_events)
591 return _gdk_quartz_pointer_grab_window;
593 return current_mouse_window;
596 /* Update mouse window */
598 _gdk_quartz_events_update_mouse_window (GdkWindow *window)
600 if (window == current_mouse_window)
603 #ifdef G_ENABLE_DEBUG
604 if (_gdk_debug_flags & GDK_DEBUG_EVENTS)
605 _gdk_quartz_window_debug_highlight (window, 0);
606 #endif /* G_ENABLE_DEBUG */
609 g_object_ref (window);
610 if (current_mouse_window)
611 g_object_unref (current_mouse_window);
613 current_mouse_window = window;
616 /* Update current cursor */
618 _gdk_quartz_events_update_cursor (GdkWindow *window)
620 GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
621 NSCursor *nscursor = nil;
625 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
627 nscursor = impl->nscursor;
631 private = private->parent;
634 GDK_QUARTZ_ALLOC_POOL;
637 nscursor = [NSCursor arrowCursor];
639 if ([NSCursor currentCursor] != nscursor)
642 GDK_QUARTZ_RELEASE_POOL;
645 /* Translates coordinates from an ancestor window + coords, to
646 * coordinates that are relative the child window.
649 get_child_coordinates_from_ancestor (GdkWindow *ancestor_window,
652 GdkWindow *child_window,
656 GdkWindowObject *ancestor_private = GDK_WINDOW_OBJECT (ancestor_window);
657 GdkWindowObject *child_private = GDK_WINDOW_OBJECT (child_window);
659 while (child_private != ancestor_private)
661 ancestor_x -= child_private->x;
662 ancestor_y -= child_private->y;
664 child_private = child_private->parent;
667 *child_x = ancestor_x;
668 *child_y = ancestor_y;
671 /* Translates coordinates from a child window + coords, to
672 * coordinates that are relative the ancestor window.
675 get_ancestor_coordinates_from_child (GdkWindow *child_window,
678 GdkWindow *ancestor_window,
682 GdkWindowObject *child_private = GDK_WINDOW_OBJECT (child_window);
683 GdkWindowObject *ancestor_private = GDK_WINDOW_OBJECT (ancestor_window);
685 while (child_private != ancestor_private)
687 child_x += child_private->x;
688 child_y += child_private->y;
690 child_private = child_private->parent;
693 *ancestor_x = child_x;
694 *ancestor_y = child_y;
697 /* Translates coordinates relative to one window (in_window) into
698 * coordinates relative to another window (out_window).
701 get_converted_window_coordinates (GdkWindow *in_window,
704 GdkWindow *out_window,
708 GdkWindow *in_toplevel;
709 GdkWindow *out_toplevel;
710 int in_origin_x, in_origin_y;
711 int out_origin_x, out_origin_y;
713 if (in_window == out_window)
720 /* First translate to "in" toplevel coordinates, then on to "out"
721 * toplevel coordinates, and finally to "out" child (the passed in
722 * window) coordinates.
725 in_toplevel = gdk_window_get_toplevel (in_window);
726 out_toplevel = gdk_window_get_toplevel (out_window);
728 /* Translate in_x, in_y to "in" toplevel coordinates. */
729 get_ancestor_coordinates_from_child (in_window, in_x, in_y,
730 in_toplevel, &in_x, &in_y);
732 gdk_window_get_origin (in_toplevel, &in_origin_x, &in_origin_y);
733 gdk_window_get_origin (out_toplevel, &out_origin_x, &out_origin_y);
735 /* Translate in_x, in_y to "out" toplevel coordinates. */
736 in_x -= out_origin_x - in_origin_x;
737 in_y -= out_origin_y - in_origin_y;
739 get_child_coordinates_from_ancestor (out_toplevel,
745 /* Given a mouse NSEvent (must be a mouse event for a GDK window),
746 * finds the subwindow over which the pointer is located. Returns
747 * coordinates relative to the found window. If no window is found,
748 * returns the root window, and root window coordinates.
751 find_mouse_window_for_ns_event (NSEvent *nsevent,
755 GdkWindow *event_toplevel;
756 GdkWindowImplQuartz *impl;
757 GdkWindowObject *private;
758 GdkWindow *mouse_toplevel;
759 GdkWindow *mouse_window;
763 event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
764 impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (event_toplevel)->impl);
765 private = GDK_WINDOW_OBJECT (event_toplevel);
766 point = [nsevent locationInWindow];
769 y_tmp = private->height - point.y;
771 mouse_toplevel = gdk_window_get_toplevel (current_mouse_window);
773 get_converted_window_coordinates (event_toplevel,
778 mouse_window = _gdk_quartz_window_find_child (mouse_toplevel, x_tmp, y_tmp);
779 if (mouse_window && mouse_window != mouse_toplevel)
781 get_child_coordinates_from_ancestor (mouse_toplevel,
786 else if (!mouse_window)
788 /* This happens for events on the window title buttons and the
789 * desktop, treat those as being on the root window.
791 get_converted_window_coordinates (mouse_toplevel,
795 mouse_window = _gdk_root;
804 /* Trigger crossing events if necessary. This is used when showing a new
805 * window, since the tracking rect API doesn't work reliably when a window
806 * shows up under the mouse cursor. It's done by finding the topmost window
807 * under the mouse pointer and synthesizing crossing events into that
811 _gdk_quartz_events_trigger_crossing_events (gboolean defer_to_mainloop)
815 gint x_toplevel, y_toplevel;
816 GdkWindow *mouse_window;
818 GdkWindowImplQuartz *impl;
819 GdkWindowObject *private;
821 NSTimeInterval timestamp = 0;
822 NSEvent *current_event;
825 if (defer_to_mainloop)
827 nsevent = [NSEvent otherEventWithType:NSApplicationDefined
833 subtype:GDK_QUARTZ_EVENT_SUBTYPE_FAKE_CROSSING
836 [NSApp postEvent:nsevent atStart:NO];
840 point = [NSEvent mouseLocation];
842 y = _gdk_quartz_window_get_inverted_screen_y (point.y);
844 mouse_window = _gdk_quartz_window_find_child (_gdk_root, x, y);
845 if (!mouse_window || mouse_window == _gdk_root)
848 toplevel = gdk_window_get_toplevel (mouse_window);
850 /* We ignore crossing within the same toplevel since that is already
853 if (toplevel == gdk_window_get_toplevel (current_mouse_window))
856 get_converted_window_coordinates (_gdk_root,
859 &x_toplevel, &y_toplevel);
861 get_converted_window_coordinates (_gdk_root,
866 /* Fix up the event to be less fake if possible. */
867 current_event = [NSApp currentEvent];
870 flags = [current_event modifierFlags];
871 timestamp = [current_event timestamp];
875 timestamp = GetCurrentEventTime ();
877 impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (toplevel)->impl);
878 private = GDK_WINDOW_OBJECT (toplevel);
879 nsevent = [NSEvent otherEventWithType:NSApplicationDefined
880 location:NSMakePoint (x_toplevel, private->height - y_toplevel)
883 windowNumber:[impl->toplevel windowNumber]
885 subtype:GDK_QUARTZ_EVENT_SUBTYPE_FAKE_CROSSING
889 #ifdef G_ENABLE_DEBUG
890 /*_gdk_quartz_window_debug_highlight (mouse_window, 0);*/
893 /* FIXME: create an event, fill it, put on the queue... */
896 /* This function finds the correct window to send an event to, taking
897 * into account grabs, event propagation, and event masks.
900 find_window_for_ns_event (NSEvent *nsevent,
905 GdkWindowObject *private;
906 GdkWindowImplQuartz *impl;
908 NSEventType event_type;
910 toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
911 private = GDK_WINDOW_OBJECT (toplevel);
912 impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
914 point = [nsevent locationInWindow];
917 *y = private->height - point.y;
919 event_type = [nsevent type];
923 case NSLeftMouseDown:
924 case NSRightMouseDown:
925 case NSOtherMouseDown:
931 case NSLeftMouseDragged:
932 case NSRightMouseDragged:
933 case NSOtherMouseDragged:
935 GdkWindow *mouse_window;
936 GdkEventMask event_mask;
937 GdkWindow *real_window;
939 /* From the docs for XGrabPointer:
941 * If owner_events is True and if a generated pointer event
942 * would normally be reported to this client, it is reported
943 * as usual. Otherwise, the event is reported with respect to
944 * the grab_window and is reported only if selected by
945 * event_mask. For either value of owner_events, unreported
946 * events are discarded.
948 * This means we first try the owner, then the grab window,
951 if (0 && _gdk_quartz_pointer_grab_window) /* FIXME: Implement grabs? */
953 if (pointer_grab_owner_events)
955 mouse_window = find_mouse_window_for_ns_event (nsevent, x, y);
956 event_mask = get_event_mask_from_ns_event (nsevent);
957 real_window = find_window_interested_in_event_mask (mouse_window, event_mask, TRUE);
959 if (mouse_window && real_window && mouse_window != real_window)
960 get_ancestor_coordinates_from_child (mouse_window,
969 /* Finally check the grab window. */
970 if (pointer_grab_event_mask & get_event_mask_from_ns_event (nsevent))
972 GdkWindow *event_toplevel;
973 GdkWindow *grab_toplevel;
977 event_toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
978 grab_toplevel = gdk_window_get_toplevel (_gdk_quartz_pointer_grab_window);
979 point = [nsevent locationInWindow];
982 y_tmp = GDK_WINDOW_OBJECT (grab_toplevel)->height - point.y;
984 /* Translate the coordinates so they are relative to
985 * the grab window instead of the event toplevel for
986 * the cases where they are not the same.
988 get_converted_window_coordinates (event_toplevel,
990 _gdk_quartz_pointer_grab_window,
993 return _gdk_quartz_pointer_grab_window;
1000 /* The non-grabbed case. */
1002 /* Leave events above the window (e.g. possibly on the titlebar)
1008 /* FIXME: Also need to leave resize events to cocoa somehow? */
1015 case NSMouseEntered:
1021 case NSFlagsChanged:
1023 if (_gdk_quartz_keyboard_grab_window && !keyboard_grab_owner_events)
1024 return _gdk_quartz_keyboard_grab_window;
1031 /* Ignore everything else. */
1039 fill_crossing_event (GdkWindow *toplevel,
1044 GdkEventType event_type,
1045 GdkCrossingMode mode,
1046 GdkNotifyType detail)
1048 GdkWindowObject *private;
1051 private = GDK_WINDOW_OBJECT (toplevel);
1053 point = [nsevent locationInWindow];
1055 event->any.type = event_type;
1056 event->crossing.window = toplevel;
1057 event->crossing.subwindow = NULL;
1058 event->crossing.time = get_time_from_ns_event (nsevent);
1059 event->crossing.x = x;
1060 event->crossing.y = y;
1061 event->crossing.mode = mode;
1062 event->crossing.detail = detail;
1063 event->crossing.state = get_keyboard_modifiers_from_ns_event (nsevent);
1065 convert_window_coordinates_to_root (toplevel,
1068 &event->crossing.x_root,
1069 &event->crossing.y_root);
1071 /* FIXME: Focus and button state? */
1075 fill_button_event (GdkWindow *window,
1085 state = get_keyboard_modifiers_from_ns_event (nsevent);
1087 switch ([nsevent type])
1089 case NSLeftMouseDown:
1090 case NSRightMouseDown:
1091 case NSOtherMouseDown:
1092 type = GDK_BUTTON_PRESS;
1095 case NSRightMouseUp:
1096 case NSOtherMouseUp:
1097 type = GDK_BUTTON_RELEASE;
1098 state |= get_mouse_button_modifiers_from_ns_event (nsevent);
1101 g_assert_not_reached ();
1104 button = get_mouse_button_from_ns_event (nsevent);
1106 event->any.type = type;
1107 event->button.window = window;
1108 event->button.time = get_time_from_ns_event (nsevent);
1109 event->button.x = x;
1110 event->button.y = y;
1111 /* FIXME event->axes */
1112 event->button.state = state;
1113 event->button.button = button;
1114 event->button.device = _gdk_display->core_pointer;
1116 convert_window_coordinates_to_root (window,
1119 &event->button.x_root,
1120 &event->button.y_root);
1124 fill_motion_event (GdkWindow *window,
1130 GdkModifierType state;
1132 state = get_keyboard_modifiers_from_ns_event (nsevent);
1134 switch ([nsevent type])
1136 case NSLeftMouseDragged:
1137 case NSRightMouseDragged:
1138 case NSOtherMouseDragged:
1139 state |= get_mouse_button_modifiers_from_ns_event (nsevent);
1146 event->any.type = GDK_MOTION_NOTIFY;
1147 event->motion.window = window;
1148 event->motion.time = get_time_from_ns_event (nsevent);
1149 event->motion.x = x;
1150 event->motion.y = y;
1151 /* FIXME event->axes */
1152 event->motion.state = state;
1153 event->motion.is_hint = FALSE;
1154 event->motion.device = _gdk_display->core_pointer;
1156 convert_window_coordinates_to_root (window,
1159 &event->motion.x_root,
1160 &event->motion.y_root);
1164 fill_scroll_event (GdkWindow *window,
1169 GdkScrollDirection direction)
1171 GdkWindowObject *private;
1174 private = GDK_WINDOW_OBJECT (window);
1176 point = [nsevent locationInWindow];
1178 event->any.type = GDK_SCROLL;
1179 event->scroll.window = window;
1180 event->scroll.time = get_time_from_ns_event (nsevent);
1181 event->scroll.x = x;
1182 event->scroll.y = y;
1183 event->scroll.state = get_keyboard_modifiers_from_ns_event (nsevent);
1184 event->scroll.direction = direction;
1185 event->scroll.device = _gdk_display->core_pointer;
1187 convert_window_coordinates_to_root (window,
1190 &event->scroll.x_root,
1191 &event->scroll.y_root);
1195 fill_key_event (GdkWindow *window,
1200 GdkEventPrivate *priv;
1204 priv = (GdkEventPrivate *) event;
1205 priv->windowing_data = [nsevent retain];
1207 event->any.type = type;
1208 event->key.window = window;
1209 event->key.time = get_time_from_ns_event (nsevent);
1210 event->key.state = get_keyboard_modifiers_from_ns_event (nsevent);
1211 event->key.hardware_keycode = [nsevent keyCode];
1212 event->key.group = ([nsevent modifierFlags] & NSAlternateKeyMask) ? 1 : 0;
1213 event->key.keyval = GDK_VoidSymbol;
1215 gdk_keymap_translate_keyboard_state (NULL,
1216 event->key.hardware_keycode,
1222 event->key.is_modifier = _gdk_quartz_keys_is_modifier (event->key.hardware_keycode);
1224 /* If the key press is a modifier, the state should include the mask
1225 * for that modifier but only for releases, not presses. This
1226 * matches the X11 backend behavior.
1228 if (event->key.is_modifier)
1232 switch (event->key.keyval)
1236 mask = GDK_MOD1_MASK;
1240 mask = GDK_SHIFT_MASK;
1243 mask = GDK_LOCK_MASK;
1247 mask = GDK_MOD5_MASK;
1251 mask = GDK_CONTROL_MASK;
1257 if (type == GDK_KEY_PRESS)
1258 event->key.state &= ~mask;
1259 else if (type == GDK_KEY_RELEASE)
1260 event->key.state |= mask;
1263 event->key.state |= current_button_state;
1265 event->key.string = NULL;
1267 /* Fill in ->string since apps depend on it, taken from the x11 backend. */
1268 if (event->key.keyval != GDK_VoidSymbol)
1269 c = gdk_keyval_to_unicode (event->key.keyval);
1273 gsize bytes_written;
1276 len = g_unichar_to_utf8 (c, buf);
1279 event->key.string = g_locale_from_utf8 (buf, len,
1280 NULL, &bytes_written,
1282 if (event->key.string)
1283 event->key.length = bytes_written;
1285 else if (event->key.keyval == GDK_Escape)
1287 event->key.length = 1;
1288 event->key.string = g_strdup ("\033");
1290 else if (event->key.keyval == GDK_Return ||
1291 event->key.keyval == GDK_KP_Enter)
1293 event->key.length = 1;
1294 event->key.string = g_strdup ("\r");
1297 if (!event->key.string)
1299 event->key.length = 0;
1300 event->key.string = g_strdup ("");
1304 g_message ("key %s:\t\twindow: %p key: %12s %d",
1305 type == GDK_KEY_PRESS ? "press" : "release",
1307 event->key.keyval ? gdk_keyval_name (event->key.keyval) : "(none)",
1308 event->key.keyval));
1312 synthesize_crossing_event (GdkWindow *window,
1318 GdkWindowObject *private;
1320 private = GDK_WINDOW_OBJECT (window);
1322 /* FIXME: had this before csw:
1323 _gdk_quartz_events_update_mouse_window (window);
1324 if (window && !_gdk_quartz_pointer_grab_window)
1325 _gdk_quartz_events_update_cursor (window);
1328 switch ([nsevent type])
1330 case NSMouseEntered:
1332 /* Enter events are considered always to be from the root window as
1333 * we can't know for sure from what window we enter.
1335 if (!(private->event_mask & GDK_ENTER_NOTIFY_MASK))
1338 fill_crossing_event (window, event, nsevent,
1341 GDK_CROSSING_NORMAL,
1342 GDK_NOTIFY_ANCESTOR);
1348 /* Exited always is to the root window as far as we are concerned,
1349 * since there is no way to reliably get information about what new
1350 * window is entered when exiting one.
1352 if (!(private->event_mask & GDK_LEAVE_NOTIFY_MASK))
1355 /*if (!mouse_window ||
1356 gdk_window_get_toplevel (mouse_window) ==
1357 gdk_window_get_toplevel (current_mouse_window))
1359 mouse_window = _gdk_root;
1363 fill_crossing_event (window, event, nsevent,
1366 GDK_CROSSING_NORMAL,
1367 GDK_NOTIFY_ANCESTOR);
1379 _gdk_quartz_events_get_current_event_mask (void)
1381 return current_event_mask;
1385 gdk_event_translate (GdkEvent *event,
1391 gboolean return_val;
1393 /* There is no support for real desktop wide grabs, so we break
1394 * grabs when the application loses focus (gets deactivated).
1396 if ([nsevent type] == NSAppKitDefined)
1398 if ([nsevent subtype] == NSApplicationDeactivatedEventType)
1401 /* This could potentially be used to break grabs when clicking
1402 * on the title. The subtype 20 is undocumented so it's probably
1403 * not a good idea: else if (subtype == 20) break_all_grabs ();
1407 /* Handle our generated "fake" crossing events. */
1408 if ([nsevent type] == NSApplicationDefined &&
1409 [nsevent subtype] == GDK_QUARTZ_EVENT_SUBTYPE_FAKE_CROSSING)
1411 /* FIXME: This needs to actually fill in the event we have... */
1412 _gdk_quartz_events_trigger_crossing_events (FALSE);
1413 return FALSE; /* ...and return TRUE instead. */
1416 /* Keep track of button state, since we don't get that information
1419 switch ([nsevent type])
1421 case NSLeftMouseDown:
1422 case NSRightMouseDown:
1423 case NSOtherMouseDown:
1424 current_button_state |= get_mouse_button_modifiers_from_ns_event (nsevent);
1427 case NSRightMouseUp:
1428 case NSOtherMouseUp:
1429 current_button_state &= ~get_mouse_button_modifiers_from_ns_event (nsevent);
1435 if (_gdk_default_filters)
1437 /* Apply global filters */
1438 GdkFilterReturn result;
1440 result = gdk_event_apply_filters (nsevent, event, _gdk_default_filters);
1441 if (result != GDK_FILTER_CONTINUE)
1443 return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
1448 nswindow = [nsevent window];
1450 /* Ignore events for no window or ones not created by GDK. */
1451 if (!nswindow || ![[nswindow contentView] isKindOfClass:[GdkQuartzView class]])
1454 /* Ignore events and break grabs while the window is being
1455 * dragged. This is a workaround for the window getting events for
1458 if ([(GdkQuartzWindow *)nswindow isInMove])
1464 /* Find the right GDK window to send the event to, taking grabs and
1465 * event masks into consideration.
1467 window = find_window_for_ns_event (nsevent, &x, &y);
1471 /* Apply any window filters. */
1472 if (GDK_IS_WINDOW (window))
1474 GdkWindowObject *filter_private = (GdkWindowObject *) window;
1475 GdkFilterReturn result;
1477 if (filter_private->filters)
1479 g_object_ref (window);
1481 result = gdk_event_apply_filters (nsevent, event, filter_private->filters);
1483 g_object_unref (window);
1485 if (result != GDK_FILTER_CONTINUE)
1487 return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
1493 /* We only activate the application on click if it's not already active,
1494 * this matches most use cases of native apps (no click-through).
1496 if (([nsevent type] == NSRightMouseDown ||
1497 [nsevent type] == NSOtherMouseDown ||
1498 [nsevent type] == NSLeftMouseDown) && ![NSApp isActive])
1500 [NSApp activateIgnoringOtherApps:YES];
1505 current_event_mask = get_event_mask_from_ns_event (nsevent);
1509 switch ([nsevent type])
1511 case NSLeftMouseDown:
1512 case NSRightMouseDown:
1513 case NSOtherMouseDown:
1515 GdkEventMask event_mask;
1517 /* Emulate implicit grab, when the window has both PRESS and RELEASE
1518 * in its mask, like X.
1520 event_mask = (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
1521 if (0 && !_gdk_quartz_pointer_grab_window && /* FIXME: add back for grabs? */
1522 (GDK_WINDOW_OBJECT (window)->event_mask & event_mask) == event_mask)
1524 pointer_grab_internal (window, FALSE,
1525 GDK_WINDOW_OBJECT (window)->event_mask,
1530 fill_button_event (window, event, nsevent, x, y);
1534 case NSRightMouseUp:
1535 case NSOtherMouseUp:
1536 fill_button_event (window, event, nsevent, x, y);
1538 /* Ungrab implicit grab */
1539 if (0 && _gdk_quartz_pointer_grab_window && pointer_grab_implicit) /* FIXME: add back? */
1540 pointer_ungrab_internal (TRUE);
1543 case NSLeftMouseDragged:
1544 case NSRightMouseDragged:
1545 case NSOtherMouseDragged:
1547 fill_motion_event (window, event, nsevent, x, y);
1552 float dx = [nsevent deltaX];
1553 float dy = [nsevent deltaY];
1554 GdkScrollDirection direction;
1559 direction = GDK_SCROLL_DOWN;
1561 direction = GDK_SCROLL_UP;
1563 fill_scroll_event (window, event, nsevent, x, y, direction);
1569 direction = GDK_SCROLL_RIGHT;
1571 direction = GDK_SCROLL_LEFT;
1573 fill_scroll_event (window, event, nsevent, x, y, direction);
1578 case NSMouseEntered:
1580 return_val = synthesize_crossing_event (window, event, nsevent, x, y);
1585 case NSFlagsChanged:
1589 type = _gdk_quartz_keys_event_type (nsevent);
1590 if (type == GDK_NOTHING)
1593 fill_key_event (window, event, nsevent, type);
1598 /* Ignore everything elsee. */
1606 if (event->any.window)
1607 g_object_ref (event->any.window);
1608 if (((event->any.type == GDK_ENTER_NOTIFY) ||
1609 (event->any.type == GDK_LEAVE_NOTIFY)) &&
1610 (event->crossing.subwindow != NULL))
1611 g_object_ref (event->crossing.subwindow);
1615 /* Mark this event as having no resources to be freed */
1616 event->any.window = NULL;
1617 event->any.type = GDK_NOTHING;
1624 _gdk_events_queue (GdkDisplay *display)
1628 nsevent = _gdk_quartz_event_loop_get_pending ();
1634 event = gdk_event_new (GDK_NOTHING);
1636 event->any.window = NULL;
1637 event->any.send_event = FALSE;
1639 ((GdkEventPrivate *)event)->flags |= GDK_EVENT_PENDING;
1641 node = _gdk_event_queue_append (display, event);
1643 if (gdk_event_translate (event, nsevent))
1645 ((GdkEventPrivate *)event)->flags &= ~GDK_EVENT_PENDING;
1646 _gdk_windowing_got_event (display, node, event, 0);
1650 _gdk_event_queue_remove_link (display, node);
1651 g_list_free_1 (node);
1652 gdk_event_free (event);
1654 GDK_THREADS_LEAVE ();
1655 [NSApp sendEvent:nsevent];
1656 GDK_THREADS_ENTER ();
1659 _gdk_quartz_event_loop_release_event (nsevent);
1666 /* Not supported. */
1670 gdk_display_add_client_message_filter (GdkDisplay *display,
1671 GdkAtom message_type,
1675 /* Not supported. */
1679 gdk_add_client_message_filter (GdkAtom message_type,
1683 /* Not supported. */
1687 gdk_display_sync (GdkDisplay *display)
1689 /* Not supported. */
1693 gdk_display_flush (GdkDisplay *display)
1695 /* Not supported. */
1699 gdk_event_send_client_message_for_display (GdkDisplay *display,
1701 GdkNativeWindow winid)
1703 /* Not supported. */
1708 gdk_screen_broadcast_client_message (GdkScreen *screen,
1711 /* Not supported. */
1715 gdk_screen_get_setting (GdkScreen *screen,
1719 if (strcmp (name, "gtk-double-click-time") == 0)
1721 NSUserDefaults *defaults;
1724 GDK_QUARTZ_ALLOC_POOL;
1726 defaults = [NSUserDefaults standardUserDefaults];
1728 t = [defaults floatForKey:@"com.apple.mouse.doubleClickThreshold"];
1731 /* No user setting, use the default in OS X. */
1735 GDK_QUARTZ_RELEASE_POOL;
1737 g_value_set_int (value, t * 1000);
1741 else if (strcmp (name, "gtk-font-name") == 0)
1746 GDK_QUARTZ_ALLOC_POOL;
1748 name = [[NSFont systemFontOfSize:0] familyName];
1750 /* Let's try to use the "views" font size (12pt) by default. This is
1751 * used for lists/text/other "content" which is the largest parts of
1752 * apps, using the "regular control" size (13pt) looks a bit out of
1753 * place. We might have to tweak this.
1756 /* The size has to be hardcoded as there doesn't seem to be a way to
1757 * get the views font size programmatically.
1759 str = g_strdup_printf ("%s 12", [name UTF8String]);
1760 g_value_set_string (value, str);
1763 GDK_QUARTZ_RELEASE_POOL;
1768 /* FIXME: Add more settings */
1774 _gdk_windowing_event_data_copy (const GdkEvent *src,
1777 GdkEventPrivate *priv_src = (GdkEventPrivate *) src;
1778 GdkEventPrivate *priv_dst = (GdkEventPrivate *) dst;
1780 if (priv_src->windowing_data)
1782 priv_dst->windowing_data = priv_src->windowing_data;
1783 [(NSEvent *)priv_dst->windowing_data retain];
1788 _gdk_windowing_event_data_free (GdkEvent *event)
1790 GdkEventPrivate *priv = (GdkEventPrivate *) event;
1792 if (priv->windowing_data)
1794 [(NSEvent *)priv->windowing_data release];
1795 priv->windowing_data = NULL;