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 event mask and button state from the last event */
43 static GdkEventMask current_event_mask;
44 static int current_button_state;
46 static void get_child_coordinates_from_ancestor (GdkWindow *ancestor_window,
49 GdkWindow *child_window,
52 static void get_ancestor_coordinates_from_child (GdkWindow *child_window,
55 GdkWindow *ancestor_window,
58 static void get_converted_window_coordinates (GdkWindow *in_window,
61 GdkWindow *out_window,
64 static void append_event (GdkEvent *event);
67 which_window_is_this (GdkWindow *window)
69 static gchar buf[256];
70 const gchar *name = NULL;
73 /* Get rid of compiler warning. */
74 if (0) which_window_is_this (window);
76 if (window == _gdk_root)
78 else if (window == NULL)
83 gdk_window_get_user_data (window, &widget);
85 name = G_OBJECT_TYPE_NAME (widget);
91 snprintf (buf, 256, "<%s (%p)%s>",
93 window == current_mouse_window ? ", is mouse" : "");
99 gdk_quartz_event_get_nsevent (GdkEvent *event)
101 /* FIXME: If the event here is unallocated, we crash. */
102 return ((GdkEventPrivate *) event)->windowing_data;
106 _gdk_events_init (void)
108 _gdk_quartz_event_loop_init ();
110 current_mouse_window = g_object_ref (_gdk_root);
111 current_keyboard_window = g_object_ref (_gdk_root);
115 gdk_events_pending (void)
117 return (_gdk_event_queue_find_first (_gdk_display) ||
118 (_gdk_quartz_event_loop_check_pending ()));
122 gdk_event_get_graphics_expose (GdkWindow *window)
124 /* FIXME: Implement */
129 gdk_keyboard_grab (GdkWindow *window,
136 g_return_val_if_fail (window != NULL, 0);
137 g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
139 display = gdk_drawable_get_display (window);
140 toplevel = gdk_window_get_toplevel (window);
142 _gdk_display_set_has_keyboard_grab (display,
149 return GDK_GRAB_SUCCESS;
153 gdk_display_keyboard_ungrab (GdkDisplay *display,
156 _gdk_display_unset_has_keyboard_grab (display, FALSE);
160 gdk_display_pointer_ungrab (GdkDisplay *display,
163 _gdk_display_unset_has_pointer_grab (display, FALSE, FALSE, time);
167 gdk_pointer_grab (GdkWindow *window,
168 gboolean owner_events,
169 GdkEventMask event_mask,
170 GdkWindow *confine_to,
176 g_return_val_if_fail (GDK_IS_WINDOW (window), 0);
177 g_return_val_if_fail (confine_to == NULL || GDK_IS_WINDOW (confine_to), 0);
179 toplevel = gdk_window_get_toplevel (window);
181 _gdk_display_set_has_pointer_grab (_gdk_display,
190 return GDK_GRAB_SUCCESS;
194 break_all_grabs (guint32 time)
196 if (_gdk_display->keyboard_grab.window)
197 _gdk_display_unset_has_keyboard_grab (_gdk_display, FALSE);
199 if (_gdk_display->pointer_grab.window)
200 _gdk_display_unset_has_pointer_grab (_gdk_display,
207 fixup_event (GdkEvent *event)
209 if (event->any.window)
210 g_object_ref (event->any.window);
211 if (((event->any.type == GDK_ENTER_NOTIFY) ||
212 (event->any.type == GDK_LEAVE_NOTIFY)) &&
213 (event->crossing.subwindow != NULL))
214 g_object_ref (event->crossing.subwindow);
215 event->any.send_event = FALSE;
219 append_event (GdkEvent *event)
222 _gdk_event_queue_append (_gdk_display, event);
226 gdk_event_apply_filters (NSEvent *nsevent,
231 GdkFilterReturn result;
237 GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
239 tmp_list = tmp_list->next;
240 result = filter->function (nsevent, event, filter->data);
241 if (result != GDK_FILTER_CONTINUE)
245 return GDK_FILTER_CONTINUE;
249 get_time_from_ns_event (NSEvent *event)
251 double time = [event timestamp];
253 return time * 1000.0;
257 get_mouse_button_from_ns_event (NSEvent *event)
261 button = [event buttonNumber];
276 static GdkModifierType
277 get_mouse_button_modifiers_from_ns_event (NSEvent *event)
280 GdkModifierType state = 0;
282 /* This maps buttons 1 to 5 to GDK_BUTTON[1-5]_MASK */
283 button = get_mouse_button_from_ns_event (event);
284 if (button >= 1 && button <= 5)
285 state = (1 << (button + 7));
290 static GdkModifierType
291 get_keyboard_modifiers_from_ns_event (NSEvent *nsevent)
293 GdkModifierType modifiers = 0;
296 nsflags = [nsevent modifierFlags];
298 if (nsflags & NSAlphaShiftKeyMask)
299 modifiers |= GDK_LOCK_MASK;
300 if (nsflags & NSShiftKeyMask)
301 modifiers |= GDK_SHIFT_MASK;
302 if (nsflags & NSControlKeyMask)
303 modifiers |= GDK_CONTROL_MASK;
304 if (nsflags & NSCommandKeyMask)
305 modifiers |= GDK_MOD1_MASK;
310 /* Return an event mask from an NSEvent */
312 get_event_mask_from_ns_event (NSEvent *nsevent)
314 switch ([nsevent type])
316 case NSLeftMouseDown:
317 case NSRightMouseDown:
318 case NSOtherMouseDown:
319 return GDK_BUTTON_PRESS_MASK;
323 return GDK_BUTTON_RELEASE_MASK;
325 return GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
327 /* Since applications that want button press events can get
328 * scroll events on X11 (since scroll wheel events are really
329 * button press events there), we need to use GDK_BUTTON_PRESS_MASK too.
331 return GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK;
332 case NSLeftMouseDragged:
333 return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
334 GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK |
336 case NSRightMouseDragged:
337 return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
338 GDK_BUTTON_MOTION_MASK | GDK_BUTTON3_MOTION_MASK |
340 case NSOtherMouseDragged:
344 mask = (GDK_POINTER_MOTION_MASK |
345 GDK_POINTER_MOTION_HINT_MASK |
346 GDK_BUTTON_MOTION_MASK);
348 if (get_mouse_button_from_ns_event (nsevent) == 2)
349 mask |= (GDK_BUTTON2_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
358 switch (_gdk_quartz_keys_event_type (nsevent))
361 return GDK_KEY_PRESS_MASK;
362 case GDK_KEY_RELEASE:
363 return GDK_KEY_RELEASE_MASK;
367 g_assert_not_reached ();
373 return GDK_ENTER_NOTIFY_MASK;
376 return GDK_LEAVE_NOTIFY_MASK;
379 g_assert_not_reached ();
386 create_focus_event (GdkWindow *window,
391 event = gdk_event_new (GDK_FOCUS_CHANGE);
392 event->focus_change.window = window;
393 event->focus_change.in = in;
398 /* Note: Used to both set a new focus window and to unset the old one. */
400 _gdk_quartz_events_update_focus_window (GdkWindow *window,
405 if (got_focus && window == current_keyboard_window)
408 /* FIXME: Don't do this when grabbed? Or make GdkQuartzWindow
409 * disallow it in the first place instead?
412 if (!got_focus && window == current_keyboard_window)
414 event = create_focus_event (current_keyboard_window, FALSE);
415 append_event (event);
416 g_object_unref (current_keyboard_window);
417 current_keyboard_window = NULL;
422 if (current_keyboard_window)
424 event = create_focus_event (current_keyboard_window, FALSE);
425 append_event (event);
426 g_object_unref (current_keyboard_window);
427 current_keyboard_window = NULL;
430 event = create_focus_event (window, TRUE);
431 append_event (event);
432 current_keyboard_window = g_object_ref (window);
437 _gdk_quartz_events_send_map_event (GdkWindow *window)
439 GdkWindowObject *private = (GdkWindowObject *)window;
440 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
445 if (private->event_mask & GDK_STRUCTURE_MASK)
449 event.any.type = GDK_MAP;
450 event.any.window = window;
452 gdk_event_put (&event);
456 /* Get current mouse window */
458 _gdk_quartz_events_get_mouse_window (gboolean consider_grabs)
461 return current_mouse_window;
463 if (_gdk_display->pointer_grab.window && !_gdk_display->pointer_grab.owner_events)
464 return _gdk_display->pointer_grab.window;
466 return current_mouse_window;
469 /* Update mouse window */
471 _gdk_quartz_events_update_mouse_window (GdkWindow *window)
473 if (window == current_mouse_window)
476 #ifdef G_ENABLE_DEBUG
477 if (_gdk_debug_flags & GDK_DEBUG_EVENTS)
478 _gdk_quartz_window_debug_highlight (window, 0);
479 #endif /* G_ENABLE_DEBUG */
482 g_object_ref (window);
483 if (current_mouse_window)
484 g_object_unref (current_mouse_window);
486 current_mouse_window = window;
489 /* Update current cursor */
491 _gdk_quartz_events_update_cursor (GdkWindow *window)
493 GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
494 NSCursor *nscursor = nil;
498 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
500 nscursor = impl->nscursor;
504 private = private->parent;
507 GDK_QUARTZ_ALLOC_POOL;
510 nscursor = [NSCursor arrowCursor];
512 if ([NSCursor currentCursor] != nscursor)
515 GDK_QUARTZ_RELEASE_POOL;
518 /* Translates coordinates from an ancestor window + coords, to
519 * coordinates that are relative the child window.
522 get_child_coordinates_from_ancestor (GdkWindow *ancestor_window,
525 GdkWindow *child_window,
529 GdkWindowObject *ancestor_private = GDK_WINDOW_OBJECT (ancestor_window);
530 GdkWindowObject *child_private = GDK_WINDOW_OBJECT (child_window);
532 while (child_private != ancestor_private)
534 ancestor_x -= child_private->x;
535 ancestor_y -= child_private->y;
537 child_private = child_private->parent;
540 *child_x = ancestor_x;
541 *child_y = ancestor_y;
544 /* Translates coordinates from a child window + coords, to
545 * coordinates that are relative the ancestor window.
548 get_ancestor_coordinates_from_child (GdkWindow *child_window,
551 GdkWindow *ancestor_window,
555 GdkWindowObject *child_private = GDK_WINDOW_OBJECT (child_window);
556 GdkWindowObject *ancestor_private = GDK_WINDOW_OBJECT (ancestor_window);
558 while (child_private != ancestor_private)
560 child_x += child_private->x;
561 child_y += child_private->y;
563 child_private = child_private->parent;
566 *ancestor_x = child_x;
567 *ancestor_y = child_y;
570 /* Translates coordinates relative to one window (in_window) into
571 * coordinates relative to another window (out_window).
574 get_converted_window_coordinates (GdkWindow *in_window,
577 GdkWindow *out_window,
581 GdkWindow *in_toplevel;
582 GdkWindow *out_toplevel;
583 int in_origin_x, in_origin_y;
584 int out_origin_x, out_origin_y;
586 if (in_window == out_window)
593 /* First translate to "in" toplevel coordinates, then on to "out"
594 * toplevel coordinates, and finally to "out" child (the passed in
595 * window) coordinates.
598 in_toplevel = gdk_window_get_toplevel (in_window);
599 out_toplevel = gdk_window_get_toplevel (out_window);
601 /* Translate in_x, in_y to "in" toplevel coordinates. */
602 get_ancestor_coordinates_from_child (in_window, in_x, in_y,
603 in_toplevel, &in_x, &in_y);
605 gdk_window_get_origin (in_toplevel, &in_origin_x, &in_origin_y);
606 gdk_window_get_origin (out_toplevel, &out_origin_x, &out_origin_y);
608 /* Translate in_x, in_y to "out" toplevel coordinates. */
609 in_x -= out_origin_x - in_origin_x;
610 in_y -= out_origin_y - in_origin_y;
612 get_child_coordinates_from_ancestor (out_toplevel,
618 /* Trigger crossing events if necessary. This is used when showing a new
619 * window, since the tracking rect API doesn't work reliably when a window
620 * shows up under the mouse cursor. It's done by finding the topmost window
621 * under the mouse pointer and synthesizing crossing events into that
625 _gdk_quartz_events_trigger_crossing_events (gboolean defer_to_mainloop)
629 gint x_toplevel, y_toplevel;
630 GdkWindow *mouse_window;
632 GdkWindowImplQuartz *impl;
633 GdkWindowObject *private;
635 NSTimeInterval timestamp = 0;
636 NSEvent *current_event;
639 if (defer_to_mainloop)
641 nsevent = [NSEvent otherEventWithType:NSApplicationDefined
647 subtype:GDK_QUARTZ_EVENT_SUBTYPE_FAKE_CROSSING
650 [NSApp postEvent:nsevent atStart:NO];
654 point = [NSEvent mouseLocation];
656 y = _gdk_quartz_window_get_inverted_screen_y (point.y);
658 mouse_window = _gdk_quartz_window_find_child (_gdk_root, x, y);
659 if (!mouse_window || mouse_window == _gdk_root)
662 toplevel = gdk_window_get_toplevel (mouse_window);
664 /* We ignore crossing within the same toplevel since that is already
667 if (toplevel == gdk_window_get_toplevel (current_mouse_window))
670 get_converted_window_coordinates (_gdk_root,
673 &x_toplevel, &y_toplevel);
675 get_converted_window_coordinates (_gdk_root,
680 /* Fix up the event to be less fake if possible. */
681 current_event = [NSApp currentEvent];
684 flags = [current_event modifierFlags];
685 timestamp = [current_event timestamp];
689 timestamp = GetCurrentEventTime ();
691 impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (toplevel)->impl);
692 private = GDK_WINDOW_OBJECT (toplevel);
693 nsevent = [NSEvent otherEventWithType:NSApplicationDefined
694 location:NSMakePoint (x_toplevel, private->height - y_toplevel)
697 windowNumber:[impl->toplevel windowNumber]
699 subtype:GDK_QUARTZ_EVENT_SUBTYPE_FAKE_CROSSING
703 #ifdef G_ENABLE_DEBUG
704 /*_gdk_quartz_window_debug_highlight (mouse_window, 0);*/
707 /* FIXME: create an event, fill it, put on the queue... */
710 /* This function finds the correct window to send an event to, taking
711 * into account grabs, event propagation, and event masks.
714 find_window_for_ns_event (NSEvent *nsevent,
721 GdkWindowObject *private;
722 GdkWindowImplQuartz *impl;
724 NSPoint screen_point;
725 NSEventType event_type;
727 toplevel = [(GdkQuartzView *)[[nsevent window] contentView] gdkWindow];
728 private = GDK_WINDOW_OBJECT (toplevel);
729 impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
731 point = [nsevent locationInWindow];
732 screen_point = [[nsevent window] convertBaseToScreen:point];
735 *y = private->height - point.y;
737 *x_root = screen_point.x;
738 *y_root = _gdk_quartz_window_get_inverted_screen_y (screen_point.y);
740 event_type = [nsevent type];
744 case NSLeftMouseDown:
745 case NSRightMouseDown:
746 case NSOtherMouseDown:
752 case NSLeftMouseDragged:
753 case NSRightMouseDragged:
754 case NSOtherMouseDragged:
758 display = gdk_drawable_get_display (toplevel);
760 /* From the docs for XGrabPointer:
762 * If owner_events is True and if a generated pointer event
763 * would normally be reported to this client, it is reported
764 * as usual. Otherwise, the event is reported with respect to
765 * the grab_window and is reported only if selected by
766 * event_mask. For either value of owner_events, unreported
767 * events are discarded.
769 * This means we first try the owner, then the grab window,
772 if (display->pointer_grab.window)
774 if (display->pointer_grab.owner_events)
777 /* Finally check the grab window. */
778 if (display->pointer_grab.event_mask & get_event_mask_from_ns_event (nsevent))
780 GdkWindow *grab_toplevel;
781 GdkWindowObject *grab_private;
783 NSWindow *grab_nswindow;
785 grab_toplevel = gdk_window_get_toplevel (display->pointer_grab.window);
786 grab_private = (GdkWindowObject *)grab_toplevel;
788 point = [[nsevent window] convertBaseToScreen:[nsevent locationInWindow]];
790 grab_nswindow = ((GdkWindowImplQuartz *)private->impl)->toplevel;
791 point = [grab_nswindow convertScreenToBase:point];
794 *y = grab_private->height - point.y;
796 /* Note: x_root and y_root are already right. */
798 return grab_toplevel;
805 /* The non-grabbed case. */
807 /* Leave events above the window (e.g. possibly on the titlebar)
813 /* FIXME: Also need to leave resize events to cocoa somehow? */
828 if (_gdk_display->keyboard_grab.window && !_gdk_display->keyboard_grab.owner_events)
829 return gdk_window_get_toplevel (_gdk_display->keyboard_grab.window);
836 /* Ignore everything else. */
844 fill_crossing_event (GdkWindow *toplevel,
851 GdkEventType event_type,
852 GdkCrossingMode mode,
853 GdkNotifyType detail)
855 GdkWindowObject *private;
858 private = GDK_WINDOW_OBJECT (toplevel);
860 point = [nsevent locationInWindow];
862 event->any.type = event_type;
863 event->crossing.window = toplevel;
864 event->crossing.subwindow = NULL;
865 event->crossing.time = get_time_from_ns_event (nsevent);
866 event->crossing.x = x;
867 event->crossing.y = y;
868 event->crossing.x_root = x_root;
869 event->crossing.y_root = y_root;
870 event->crossing.mode = mode;
871 event->crossing.detail = detail;
872 event->crossing.state = get_keyboard_modifiers_from_ns_event (nsevent);
874 /* FIXME: Focus and button state? */
878 fill_button_event (GdkWindow *window,
890 state = get_keyboard_modifiers_from_ns_event (nsevent);
892 switch ([nsevent type])
894 case NSLeftMouseDown:
895 case NSRightMouseDown:
896 case NSOtherMouseDown:
897 type = GDK_BUTTON_PRESS;
902 type = GDK_BUTTON_RELEASE;
903 state |= get_mouse_button_modifiers_from_ns_event (nsevent);
906 g_assert_not_reached ();
909 button = get_mouse_button_from_ns_event (nsevent);
911 event->any.type = type;
912 event->button.window = window;
913 event->button.time = get_time_from_ns_event (nsevent);
916 event->button.x_root = x_root;
917 event->button.y_root = y_root;
918 /* FIXME event->axes */
919 event->button.state = state;
920 event->button.button = button;
921 event->button.device = _gdk_display->core_pointer;
925 fill_motion_event (GdkWindow *window,
933 GdkModifierType state;
935 state = get_keyboard_modifiers_from_ns_event (nsevent);
937 switch ([nsevent type])
939 case NSLeftMouseDragged:
940 case NSRightMouseDragged:
941 case NSOtherMouseDragged:
942 state |= get_mouse_button_modifiers_from_ns_event (nsevent);
949 event->any.type = GDK_MOTION_NOTIFY;
950 event->motion.window = window;
951 event->motion.time = get_time_from_ns_event (nsevent);
954 event->motion.x_root = x_root;
955 event->motion.y_root = y_root;
956 /* FIXME event->axes */
957 event->motion.state = state;
958 event->motion.is_hint = FALSE;
959 event->motion.device = _gdk_display->core_pointer;
963 fill_scroll_event (GdkWindow *window,
970 GdkScrollDirection direction)
972 GdkWindowObject *private;
975 private = GDK_WINDOW_OBJECT (window);
977 point = [nsevent locationInWindow];
979 event->any.type = GDK_SCROLL;
980 event->scroll.window = window;
981 event->scroll.time = get_time_from_ns_event (nsevent);
984 event->scroll.x_root = x_root;
985 event->scroll.y_root = y_root;
986 event->scroll.state = get_keyboard_modifiers_from_ns_event (nsevent);
987 event->scroll.direction = direction;
988 event->scroll.device = _gdk_display->core_pointer;
992 fill_key_event (GdkWindow *window,
997 GdkEventPrivate *priv;
1001 priv = (GdkEventPrivate *) event;
1002 priv->windowing_data = [nsevent retain];
1004 event->any.type = type;
1005 event->key.window = window;
1006 event->key.time = get_time_from_ns_event (nsevent);
1007 event->key.state = get_keyboard_modifiers_from_ns_event (nsevent);
1008 event->key.hardware_keycode = [nsevent keyCode];
1009 event->key.group = ([nsevent modifierFlags] & NSAlternateKeyMask) ? 1 : 0;
1010 event->key.keyval = GDK_VoidSymbol;
1012 gdk_keymap_translate_keyboard_state (NULL,
1013 event->key.hardware_keycode,
1019 event->key.is_modifier = _gdk_quartz_keys_is_modifier (event->key.hardware_keycode);
1021 /* If the key press is a modifier, the state should include the mask
1022 * for that modifier but only for releases, not presses. This
1023 * matches the X11 backend behavior.
1025 if (event->key.is_modifier)
1029 switch (event->key.keyval)
1033 mask = GDK_MOD1_MASK;
1037 mask = GDK_SHIFT_MASK;
1040 mask = GDK_LOCK_MASK;
1044 mask = GDK_MOD5_MASK;
1048 mask = GDK_CONTROL_MASK;
1054 if (type == GDK_KEY_PRESS)
1055 event->key.state &= ~mask;
1056 else if (type == GDK_KEY_RELEASE)
1057 event->key.state |= mask;
1060 event->key.state |= current_button_state;
1062 event->key.string = NULL;
1064 /* Fill in ->string since apps depend on it, taken from the x11 backend. */
1065 if (event->key.keyval != GDK_VoidSymbol)
1066 c = gdk_keyval_to_unicode (event->key.keyval);
1070 gsize bytes_written;
1073 len = g_unichar_to_utf8 (c, buf);
1076 event->key.string = g_locale_from_utf8 (buf, len,
1077 NULL, &bytes_written,
1079 if (event->key.string)
1080 event->key.length = bytes_written;
1082 else if (event->key.keyval == GDK_Escape)
1084 event->key.length = 1;
1085 event->key.string = g_strdup ("\033");
1087 else if (event->key.keyval == GDK_Return ||
1088 event->key.keyval == GDK_KP_Enter)
1090 event->key.length = 1;
1091 event->key.string = g_strdup ("\r");
1094 if (!event->key.string)
1096 event->key.length = 0;
1097 event->key.string = g_strdup ("");
1101 g_message ("key %s:\t\twindow: %p key: %12s %d",
1102 type == GDK_KEY_PRESS ? "press" : "release",
1104 event->key.keyval ? gdk_keyval_name (event->key.keyval) : "(none)",
1105 event->key.keyval));
1109 synthesize_crossing_event (GdkWindow *window,
1117 GdkWindowObject *private;
1119 private = GDK_WINDOW_OBJECT (window);
1121 /* FIXME: had this before csw:
1122 _gdk_quartz_events_update_mouse_window (window);
1123 if (window && !_gdk_quartz_pointer_grab_window)
1124 _gdk_quartz_events_update_cursor (window);
1127 switch ([nsevent type])
1129 case NSMouseEntered:
1131 /* Enter events are considered always to be from the root window as
1132 * we can't know for sure from what window we enter.
1134 if (!(private->event_mask & GDK_ENTER_NOTIFY_MASK))
1137 fill_crossing_event (window, event, nsevent,
1141 GDK_CROSSING_NORMAL,
1142 GDK_NOTIFY_ANCESTOR);
1148 /* Exited always is to the root window as far as we are concerned,
1149 * since there is no way to reliably get information about what new
1150 * window is entered when exiting one.
1152 if (!(private->event_mask & GDK_LEAVE_NOTIFY_MASK))
1155 /*if (!mouse_window ||
1156 gdk_window_get_toplevel (mouse_window) ==
1157 gdk_window_get_toplevel (current_mouse_window))
1159 mouse_window = _gdk_root;
1163 fill_crossing_event (window, event, nsevent,
1167 GDK_CROSSING_NORMAL,
1168 GDK_NOTIFY_ANCESTOR);
1180 _gdk_quartz_events_get_current_event_mask (void)
1182 return current_event_mask;
1185 #define GDK_ANY_BUTTON_MASK (GDK_BUTTON1_MASK | \
1186 GDK_BUTTON2_MASK | \
1187 GDK_BUTTON3_MASK | \
1188 GDK_BUTTON4_MASK | \
1192 button_event_check_implicit_grab (GdkWindow *window,
1196 GdkDisplay *display = gdk_drawable_get_display (window);
1198 /* track implicit grabs for button presses */
1199 switch (event->type)
1201 case GDK_BUTTON_PRESS:
1202 if (!display->pointer_grab.window)
1204 _gdk_display_set_has_pointer_grab (display,
1208 gdk_window_get_events (window),
1215 case GDK_BUTTON_RELEASE:
1216 if (display->pointer_grab.window &&
1217 display->pointer_grab.implicit &&
1218 (current_button_state & GDK_ANY_BUTTON_MASK & ~(GDK_BUTTON1_MASK << (event->button.button - 1))) == 0)
1220 _gdk_display_unset_has_pointer_grab (display, TRUE, TRUE,
1221 event->button.time);
1225 g_assert_not_reached ();
1230 gdk_event_translate (GdkEvent *event,
1233 NSEventType event_type;
1238 gboolean return_val;
1240 /* Ignore events altogether when we're not active, otherwise we get
1241 * tooltips etc for inactive apps.
1243 if (![NSApp isActive])
1246 /* There is no support for real desktop wide grabs, so we break
1247 * grabs when the application loses focus (gets deactivated).
1249 event_type = [nsevent type];
1250 if (event_type == NSAppKitDefined)
1252 if ([nsevent subtype] == NSApplicationDeactivatedEventType)
1253 break_all_grabs (get_time_from_ns_event (nsevent));
1255 /* This could potentially be used to break grabs when clicking
1256 * on the title. The subtype 20 is undocumented so it's probably
1257 * not a good idea: else if (subtype == 20) break_all_grabs ();
1260 /* Leave all AppKit events to AppKit. */
1264 /* Handle our generated "fake" crossing events. */
1265 if (event_type == NSApplicationDefined &&
1266 [nsevent subtype] == GDK_QUARTZ_EVENT_SUBTYPE_FAKE_CROSSING)
1268 /* FIXME: This needs to actually fill in the event we have... */
1269 _gdk_quartz_events_trigger_crossing_events (FALSE);
1270 return FALSE; /* ...and return TRUE instead. */
1273 /* Keep track of button state, since we don't get that information
1278 case NSLeftMouseDown:
1279 case NSRightMouseDown:
1280 case NSOtherMouseDown:
1281 current_button_state |= get_mouse_button_modifiers_from_ns_event (nsevent);
1284 case NSRightMouseUp:
1285 case NSOtherMouseUp:
1286 current_button_state &= ~get_mouse_button_modifiers_from_ns_event (nsevent);
1292 if (_gdk_default_filters)
1294 /* Apply global filters */
1295 GdkFilterReturn result;
1297 result = gdk_event_apply_filters (nsevent, event, _gdk_default_filters);
1298 if (result != GDK_FILTER_CONTINUE)
1300 return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
1305 nswindow = [nsevent window];
1307 /* Ignore events for no window or ones not created by GDK. */
1308 if (!nswindow || ![[nswindow contentView] isKindOfClass:[GdkQuartzView class]])
1311 /* Ignore events and break grabs while the window is being
1312 * dragged. This is a workaround for the window getting events for
1315 if ([(GdkQuartzWindow *)nswindow isInMove])
1317 break_all_grabs (get_time_from_ns_event (nsevent));
1321 /* Find the right GDK window to send the event to, taking grabs and
1322 * event masks into consideration.
1324 window = find_window_for_ns_event (nsevent, &x, &y, &x_root, &y_root);
1328 /* Apply any window filters. */
1329 if (GDK_IS_WINDOW (window))
1331 GdkWindowObject *filter_private = (GdkWindowObject *) window;
1332 GdkFilterReturn result;
1334 if (filter_private->filters)
1336 g_object_ref (window);
1338 result = gdk_event_apply_filters (nsevent, event, filter_private->filters);
1340 g_object_unref (window);
1342 if (result != GDK_FILTER_CONTINUE)
1344 return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
1350 /* We only activate the application on click if it's not already active,
1351 * or if it's active but the window isn't focused. This matches most use
1352 * cases of native apps (no click-through).
1354 if ((event_type == NSRightMouseDown ||
1355 event_type == NSOtherMouseDown ||
1356 event_type == NSLeftMouseDown))
1358 GdkWindowObject *private = (GdkWindowObject *)window;
1359 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
1361 if (![NSApp isActive])
1363 else if (![impl->toplevel isKeyWindow])
1367 current_event_mask = get_event_mask_from_ns_event (nsevent);
1373 case NSLeftMouseDown:
1374 case NSRightMouseDown:
1375 case NSOtherMouseDown:
1376 fill_button_event (window, event, nsevent, x, y, x_root, y_root);
1377 button_event_check_implicit_grab (window, event, nsevent);
1381 case NSRightMouseUp:
1382 case NSOtherMouseUp:
1383 fill_button_event (window, event, nsevent, x, y, x_root, y_root);
1384 button_event_check_implicit_grab (window, event, nsevent);
1387 case NSLeftMouseDragged:
1388 case NSRightMouseDragged:
1389 case NSOtherMouseDragged:
1391 fill_motion_event (window, event, nsevent, x, y, x_root, y_root);
1396 float dx = [nsevent deltaX];
1397 float dy = [nsevent deltaY];
1398 GdkScrollDirection direction;
1403 direction = GDK_SCROLL_DOWN;
1405 direction = GDK_SCROLL_UP;
1407 fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
1413 direction = GDK_SCROLL_RIGHT;
1415 direction = GDK_SCROLL_LEFT;
1417 fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
1422 case NSMouseEntered:
1424 return_val = synthesize_crossing_event (window, event, nsevent, x, y, x_root, y_root);
1429 case NSFlagsChanged:
1433 type = _gdk_quartz_keys_event_type (nsevent);
1434 if (type == GDK_NOTHING)
1437 fill_key_event (window, event, nsevent, type);
1442 /* Ignore everything elsee. */
1450 if (event->any.window)
1451 g_object_ref (event->any.window);
1452 if (((event->any.type == GDK_ENTER_NOTIFY) ||
1453 (event->any.type == GDK_LEAVE_NOTIFY)) &&
1454 (event->crossing.subwindow != NULL))
1455 g_object_ref (event->crossing.subwindow);
1459 /* Mark this event as having no resources to be freed */
1460 event->any.window = NULL;
1461 event->any.type = GDK_NOTHING;
1468 _gdk_events_queue (GdkDisplay *display)
1472 nsevent = _gdk_quartz_event_loop_get_pending ();
1478 event = gdk_event_new (GDK_NOTHING);
1480 event->any.window = NULL;
1481 event->any.send_event = FALSE;
1483 ((GdkEventPrivate *)event)->flags |= GDK_EVENT_PENDING;
1485 node = _gdk_event_queue_append (display, event);
1487 if (gdk_event_translate (event, nsevent))
1489 ((GdkEventPrivate *)event)->flags &= ~GDK_EVENT_PENDING;
1490 _gdk_windowing_got_event (display, node, event, 0);
1494 _gdk_event_queue_remove_link (display, node);
1495 g_list_free_1 (node);
1496 gdk_event_free (event);
1498 GDK_THREADS_LEAVE ();
1499 [NSApp sendEvent:nsevent];
1500 GDK_THREADS_ENTER ();
1503 _gdk_quartz_event_loop_release_event (nsevent);
1510 /* Not supported. */
1514 gdk_display_add_client_message_filter (GdkDisplay *display,
1515 GdkAtom message_type,
1519 /* Not supported. */
1523 gdk_add_client_message_filter (GdkAtom message_type,
1527 /* Not supported. */
1531 gdk_display_sync (GdkDisplay *display)
1533 /* Not supported. */
1537 gdk_display_flush (GdkDisplay *display)
1539 /* Not supported. */
1543 gdk_event_send_client_message_for_display (GdkDisplay *display,
1545 GdkNativeWindow winid)
1547 /* Not supported. */
1552 gdk_screen_broadcast_client_message (GdkScreen *screen,
1555 /* Not supported. */
1559 gdk_screen_get_setting (GdkScreen *screen,
1563 if (strcmp (name, "gtk-double-click-time") == 0)
1565 NSUserDefaults *defaults;
1568 GDK_QUARTZ_ALLOC_POOL;
1570 defaults = [NSUserDefaults standardUserDefaults];
1572 t = [defaults floatForKey:@"com.apple.mouse.doubleClickThreshold"];
1575 /* No user setting, use the default in OS X. */
1579 GDK_QUARTZ_RELEASE_POOL;
1581 g_value_set_int (value, t * 1000);
1585 else if (strcmp (name, "gtk-font-name") == 0)
1590 GDK_QUARTZ_ALLOC_POOL;
1592 name = [[NSFont systemFontOfSize:0] familyName];
1594 /* Let's try to use the "views" font size (12pt) by default. This is
1595 * used for lists/text/other "content" which is the largest parts of
1596 * apps, using the "regular control" size (13pt) looks a bit out of
1597 * place. We might have to tweak this.
1600 /* The size has to be hardcoded as there doesn't seem to be a way to
1601 * get the views font size programmatically.
1603 str = g_strdup_printf ("%s 12", [name UTF8String]);
1604 g_value_set_string (value, str);
1607 GDK_QUARTZ_RELEASE_POOL;
1612 /* FIXME: Add more settings */
1618 _gdk_windowing_event_data_copy (const GdkEvent *src,
1621 GdkEventPrivate *priv_src = (GdkEventPrivate *) src;
1622 GdkEventPrivate *priv_dst = (GdkEventPrivate *) dst;
1624 if (priv_src->windowing_data)
1626 priv_dst->windowing_data = priv_src->windowing_data;
1627 [(NSEvent *)priv_dst->windowing_data retain];
1632 _gdk_windowing_event_data_free (GdkEvent *event)
1634 GdkEventPrivate *priv = (GdkEventPrivate *) event;
1636 if (priv->windowing_data)
1638 [(NSEvent *)priv->windowing_data release];
1639 priv->windowing_data = NULL;