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 <gdk/gdkdisplayprivate.h>
34 #include "gdkscreen.h"
35 #include "gdkkeysyms.h"
36 #include "gdkquartz.h"
37 #include "gdkquartzdisplay.h"
38 #include "gdkprivate-quartz.h"
39 #include "gdkquartzdevicemanager-core.h"
42 #define GRIP_HEIGHT 15
43 #define GDK_LION_RESIZE 5
45 #define WINDOW_IS_TOPLEVEL(window) \
46 (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD && \
47 GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN && \
48 GDK_WINDOW_TYPE (window) != GDK_WINDOW_OFFSCREEN)
50 /* This is the window corresponding to the key window */
51 static GdkWindow *current_keyboard_window;
53 /* This is the event mask and button state from the last event */
54 static GdkModifierType current_keyboard_modifiers;
55 static GdkModifierType current_button_state;
57 static void append_event (GdkEvent *event,
61 _gdk_quartz_events_init (void)
63 _gdk_quartz_event_loop_init ();
65 current_keyboard_window = g_object_ref (_gdk_root);
69 _gdk_quartz_display_has_pending (GdkDisplay *display)
71 return (_gdk_event_queue_find_first (display) ||
72 (_gdk_quartz_event_loop_check_pending ()));
76 break_all_grabs (guint32 time)
79 GdkDeviceManager *device_manager;
81 device_manager = gdk_display_get_device_manager (_gdk_display);
82 list = gdk_device_manager_list_devices (device_manager,
83 GDK_DEVICE_TYPE_MASTER);
84 for (l = list; l; l = l->next)
86 GdkDeviceGrabInfo *grab;
88 grab = _gdk_display_get_last_device_grab (_gdk_display, l->data);
92 grab->implicit_ungrab = TRUE;
95 _gdk_display_device_grab_update (_gdk_display, l->data, NULL, 0);
102 fixup_event (GdkEvent *event)
104 if (event->any.window)
105 g_object_ref (event->any.window);
106 if (((event->any.type == GDK_ENTER_NOTIFY) ||
107 (event->any.type == GDK_LEAVE_NOTIFY)) &&
108 (event->crossing.subwindow != NULL))
109 g_object_ref (event->crossing.subwindow);
110 event->any.send_event = FALSE;
114 append_event (GdkEvent *event,
120 node = _gdk_event_queue_append (_gdk_display, event);
123 _gdk_windowing_got_event (_gdk_display, node, event, 0);
127 gdk_event_apply_filters (NSEvent *nsevent,
132 GdkFilterReturn result;
138 GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
141 if ((filter->flags & GDK_EVENT_FILTER_REMOVED) != 0)
143 tmp_list = tmp_list->next;
148 result = filter->function (nsevent, event, filter->data);
150 /* get the next node after running the function since the
151 function may add or remove a next node */
153 tmp_list = tmp_list->next;
156 if (filter->ref_count == 0)
158 *filters = g_list_remove_link (*filters, node);
159 g_list_free_1 (node);
163 if (result != GDK_FILTER_CONTINUE)
167 return GDK_FILTER_CONTINUE;
171 get_time_from_ns_event (NSEvent *event)
173 double time = [event timestamp];
175 return time * 1000.0;
179 get_mouse_button_from_ns_event (NSEvent *event)
183 button = [event buttonNumber];
198 static GdkModifierType
199 get_mouse_button_modifiers_from_ns_event (NSEvent *event)
202 GdkModifierType state = 0;
204 /* This maps buttons 1 to 5 to GDK_BUTTON[1-5]_MASK */
205 button = get_mouse_button_from_ns_event (event);
206 if (button >= 1 && button <= 5)
207 state = (1 << (button + 7));
212 static GdkModifierType
213 get_keyboard_modifiers_from_ns_event (NSEvent *nsevent)
215 GdkModifierType modifiers = 0;
218 nsflags = [nsevent modifierFlags];
220 if (nsflags & NSAlphaShiftKeyMask)
221 modifiers |= GDK_LOCK_MASK;
222 if (nsflags & NSShiftKeyMask)
223 modifiers |= GDK_SHIFT_MASK;
224 if (nsflags & NSControlKeyMask)
225 modifiers |= GDK_CONTROL_MASK;
226 if (nsflags & NSCommandKeyMask)
227 modifiers |= GDK_MOD1_MASK;
232 /* Return an event mask from an NSEvent */
234 get_event_mask_from_ns_event (NSEvent *nsevent)
236 switch ([nsevent type])
238 case NSLeftMouseDown:
239 case NSRightMouseDown:
240 case NSOtherMouseDown:
241 return GDK_BUTTON_PRESS_MASK;
245 return GDK_BUTTON_RELEASE_MASK;
247 return GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
249 /* Since applications that want button press events can get
250 * scroll events on X11 (since scroll wheel events are really
251 * button press events there), we need to use GDK_BUTTON_PRESS_MASK too.
253 return GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK;
254 case NSLeftMouseDragged:
255 return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
256 GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK |
258 case NSRightMouseDragged:
259 return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
260 GDK_BUTTON_MOTION_MASK | GDK_BUTTON3_MOTION_MASK |
262 case NSOtherMouseDragged:
266 mask = (GDK_POINTER_MOTION_MASK |
267 GDK_POINTER_MOTION_HINT_MASK |
268 GDK_BUTTON_MOTION_MASK);
270 if (get_mouse_button_from_ns_event (nsevent) == 2)
271 mask |= (GDK_BUTTON2_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
280 switch (_gdk_quartz_keys_event_type (nsevent))
283 return GDK_KEY_PRESS_MASK;
284 case GDK_KEY_RELEASE:
285 return GDK_KEY_RELEASE_MASK;
289 g_assert_not_reached ();
295 return GDK_ENTER_NOTIFY_MASK;
298 return GDK_LEAVE_NOTIFY_MASK;
301 g_assert_not_reached ();
308 create_focus_event (GdkWindow *window,
312 GdkQuartzDeviceManagerCore *device_manager;
314 event = gdk_event_new (GDK_FOCUS_CHANGE);
315 event->focus_change.window = window;
316 event->focus_change.in = in;
318 device_manager = GDK_QUARTZ_DEVICE_MANAGER_CORE (_gdk_display->device_manager);
319 gdk_event_set_device (event, device_manager->core_keyboard);
326 generate_motion_event (GdkWindow *window)
329 NSPoint screen_point;
333 gint x, y, x_root, y_root;
336 event = gdk_event_new (GDK_MOTION_NOTIFY);
337 event->any.window = NULL;
338 event->any.send_event = TRUE;
340 nswindow = ((GdkWindowImplQuartz *)window->impl)->toplevel;
341 view = (GdkQuartzView *)[nswindow contentView];
343 display = gdk_window_get_display (window);
345 screen_point = [NSEvent mouseLocation];
347 _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &x_root, &y_root);
349 point = [nswindow convertScreenToBase:screen_point];
352 y = window->height - point.y;
354 event->any.type = GDK_MOTION_NOTIFY;
355 event->motion.window = window;
356 event->motion.time = GDK_CURRENT_TIME;
359 event->motion.x_root = x_root;
360 event->motion.y_root = y_root;
361 /* FIXME event->axes */
362 event->motion.state = 0;
363 event->motion.is_hint = FALSE;
364 event->motion.device = _gdk_display->core_pointer;
366 append_event (event, TRUE);
369 /* Note: Used to both set a new focus window and to unset the old one. */
371 _gdk_quartz_events_update_focus_window (GdkWindow *window,
376 if (got_focus && window == current_keyboard_window)
379 /* FIXME: Don't do this when grabbed? Or make GdkQuartzNSWindow
380 * disallow it in the first place instead?
383 if (!got_focus && window == current_keyboard_window)
385 event = create_focus_event (current_keyboard_window, FALSE);
386 append_event (event, FALSE);
387 g_object_unref (current_keyboard_window);
388 current_keyboard_window = NULL;
393 if (current_keyboard_window)
395 event = create_focus_event (current_keyboard_window, FALSE);
396 append_event (event, FALSE);
397 g_object_unref (current_keyboard_window);
398 current_keyboard_window = NULL;
401 event = create_focus_event (window, TRUE);
402 append_event (event, FALSE);
403 current_keyboard_window = g_object_ref (window);
405 /* We just became the active window. Unlike X11, Mac OS X does
406 * not send us motion events while the window does not have focus
407 * ("is not key"). We send a dummy motion notify event now, so that
408 * everything in the window is set to correct state.
410 generate_motion_event (window);
415 _gdk_quartz_events_send_enter_notify_event (GdkWindow *window)
418 NSPoint screen_point;
421 gint x, y, x_root, y_root;
423 event = gdk_event_new (GDK_ENTER_NOTIFY);
424 event->any.window = NULL;
425 event->any.send_event = FALSE;
427 nswindow = ((GdkWindowImplQuartz *)window->impl)->toplevel;
429 screen_point = [NSEvent mouseLocation];
431 _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &x_root, &y_root);
433 point = [nswindow convertScreenToBase:screen_point];
436 y = window->height - point.y;
438 event->crossing.window = window;
439 event->crossing.subwindow = NULL;
440 event->crossing.time = GDK_CURRENT_TIME;
441 event->crossing.x = x;
442 event->crossing.y = y;
443 event->crossing.x_root = x_root;
444 event->crossing.y_root = y_root;
445 event->crossing.mode = GDK_CROSSING_NORMAL;
446 event->crossing.detail = GDK_NOTIFY_ANCESTOR;
447 event->crossing.state = 0;
449 gdk_event_set_device (event, _gdk_display->core_pointer);
451 append_event (event, TRUE);
455 _gdk_quartz_events_send_map_event (GdkWindow *window)
457 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
462 if (window->event_mask & GDK_STRUCTURE_MASK)
466 event.any.type = GDK_MAP;
467 event.any.window = window;
469 gdk_event_put (&event);
474 find_toplevel_under_pointer (GdkDisplay *display,
475 NSPoint screen_point,
480 GdkPointerWindowInfo *info;
482 info = _gdk_display_get_pointer_info (display, display->core_pointer);
483 toplevel = info->toplevel_under_pointer;
484 if (toplevel && WINDOW_IS_TOPLEVEL (toplevel))
489 nswindow = ((GdkWindowImplQuartz *)toplevel->impl)->toplevel;
491 point = [nswindow convertScreenToBase:screen_point];
494 *y = toplevel->height - point.y;
501 find_toplevel_for_keyboard_event (NSEvent *nsevent)
507 GdkDeviceManager *device_manager;
509 view = (GdkQuartzView *)[[nsevent window] contentView];
510 window = [view gdkWindow];
512 display = gdk_window_get_display (window);
514 device_manager = gdk_display_get_device_manager (display);
515 list = gdk_device_manager_list_devices (device_manager,
516 GDK_DEVICE_TYPE_MASTER);
517 for (l = list; l; l = l->next)
519 GdkDeviceGrabInfo *grab;
520 GdkDevice *device = l->data;
522 if (gdk_device_get_source(device) != GDK_SOURCE_KEYBOARD)
525 grab = _gdk_display_get_last_device_grab (display, device);
526 if (grab && grab->window && !grab->owner_events)
528 window = gdk_window_get_effective_toplevel (grab->window);
539 find_toplevel_for_mouse_event (NSEvent *nsevent,
544 NSPoint screen_point;
545 NSEventType event_type;
549 GdkDeviceGrabInfo *grab;
551 view = (GdkQuartzView *)[[nsevent window] contentView];
552 toplevel = [view gdkWindow];
554 display = gdk_window_get_display (toplevel);
556 event_type = [nsevent type];
557 point = [nsevent locationInWindow];
558 screen_point = [[nsevent window] convertBaseToScreen:point];
560 /* From the docs for XGrabPointer:
562 * If owner_events is True and if a generated pointer event
563 * would normally be reported to this client, it is reported
564 * as usual. Otherwise, the event is reported with respect to
565 * the grab_window and is reported only if selected by
566 * event_mask. For either value of owner_events, unreported
567 * events are discarded.
569 grab = _gdk_display_get_last_device_grab (display,
570 display->core_pointer);
571 if (WINDOW_IS_TOPLEVEL (toplevel) && grab)
573 /* Implicit grabs do not go through XGrabPointer and thus the
574 * event mask should not be checked.
577 && (grab->event_mask & get_event_mask_from_ns_event (nsevent)) == 0)
580 if (grab->owner_events)
582 /* For owner events, we need to use the toplevel under the
583 * pointer, not the window from the NSEvent, since that is
584 * reported with respect to the key window, which could be
587 GdkWindow *toplevel_under_pointer;
590 toplevel_under_pointer = find_toplevel_under_pointer (display,
593 if (toplevel_under_pointer)
595 toplevel = toplevel_under_pointer;
604 /* Finally check the grab window. */
605 GdkWindow *grab_toplevel;
606 NSWindow *grab_nswindow;
608 grab_toplevel = gdk_window_get_effective_toplevel (grab->window);
610 grab_nswindow = ((GdkWindowImplQuartz *)grab_toplevel->impl)->toplevel;
611 point = [grab_nswindow convertScreenToBase:screen_point];
613 /* Note: x_root and y_root are already right. */
615 *y = grab_toplevel->height - point.y;
617 return grab_toplevel;
624 /* The non-grabbed case. */
625 GdkWindow *toplevel_under_pointer;
628 /* Ignore all events but mouse moved that might be on the title
629 * bar (above the content view). The reason is that otherwise
630 * gdk gets confused about getting e.g. button presses with no
631 * window (the title bar is not known to it).
633 if (event_type != NSMouseMoved)
637 /* As for owner events, we need to use the toplevel under the
638 * pointer, not the window from the NSEvent.
640 toplevel_under_pointer = find_toplevel_under_pointer (display,
643 if (toplevel_under_pointer
644 && WINDOW_IS_TOPLEVEL (toplevel_under_pointer))
646 GdkWindowImplQuartz *toplevel_impl;
648 toplevel = toplevel_under_pointer;
650 toplevel_impl = (GdkWindowImplQuartz *)toplevel->impl;
662 /* This function finds the correct window to send an event to, taking
663 * into account grabs, event propagation, and event masks.
666 find_window_for_ns_event (NSEvent *nsevent,
674 NSPoint screen_point;
675 NSEventType event_type;
678 view = (GdkQuartzView *)[[nsevent window] contentView];
679 toplevel = [view gdkWindow];
681 point = [nsevent locationInWindow];
682 screen_point = [[nsevent window] convertBaseToScreen:point];
685 *y = toplevel->height - point.y;
687 _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, x_root, y_root);
689 event_type = [nsevent type];
693 case NSLeftMouseDown:
694 case NSRightMouseDown:
695 case NSOtherMouseDown:
701 case NSLeftMouseDragged:
702 case NSRightMouseDragged:
703 case NSOtherMouseDragged:
704 return find_toplevel_for_mouse_event (nsevent, x, y);
708 /* Only handle our own entered/exited events, not the ones for the
711 if ([view trackingRect] == [nsevent trackingNumber])
719 return find_toplevel_for_keyboard_event (nsevent);
722 /* Ignore everything else. */
730 fill_crossing_event (GdkWindow *toplevel,
737 GdkEventType event_type,
738 GdkCrossingMode mode,
739 GdkNotifyType detail)
741 event->any.type = event_type;
742 event->crossing.window = toplevel;
743 event->crossing.subwindow = NULL;
744 event->crossing.time = get_time_from_ns_event (nsevent);
745 event->crossing.x = x;
746 event->crossing.y = y;
747 event->crossing.x_root = x_root;
748 event->crossing.y_root = y_root;
749 event->crossing.mode = mode;
750 event->crossing.detail = detail;
751 event->crossing.state = get_keyboard_modifiers_from_ns_event (nsevent);
753 gdk_event_set_device (event, _gdk_display->core_pointer);
755 /* FIXME: Focus and button state? */
759 fill_button_event (GdkWindow *window,
771 state = get_keyboard_modifiers_from_ns_event (nsevent);
773 switch ([nsevent type])
775 case NSLeftMouseDown:
776 case NSRightMouseDown:
777 case NSOtherMouseDown:
778 type = GDK_BUTTON_PRESS;
783 type = GDK_BUTTON_RELEASE;
784 state |= get_mouse_button_modifiers_from_ns_event (nsevent);
787 g_assert_not_reached ();
790 button = get_mouse_button_from_ns_event (nsevent);
792 event->any.type = type;
793 event->button.window = window;
794 event->button.time = get_time_from_ns_event (nsevent);
797 event->button.x_root = x_root;
798 event->button.y_root = y_root;
799 /* FIXME event->axes */
800 event->button.state = state;
801 event->button.button = button;
802 event->button.device = _gdk_display->core_pointer;
806 fill_motion_event (GdkWindow *window,
814 GdkModifierType state;
816 state = get_keyboard_modifiers_from_ns_event (nsevent);
818 switch ([nsevent type])
820 case NSLeftMouseDragged:
821 case NSRightMouseDragged:
822 case NSOtherMouseDragged:
823 state |= get_mouse_button_modifiers_from_ns_event (nsevent);
830 event->any.type = GDK_MOTION_NOTIFY;
831 event->motion.window = window;
832 event->motion.time = get_time_from_ns_event (nsevent);
835 event->motion.x_root = x_root;
836 event->motion.y_root = y_root;
837 /* FIXME event->axes */
838 event->motion.state = state;
839 event->motion.is_hint = FALSE;
840 event->motion.device = _gdk_display->core_pointer;
844 fill_scroll_event (GdkWindow *window,
851 GdkScrollDirection direction)
855 point = [nsevent locationInWindow];
857 event->any.type = GDK_SCROLL;
858 event->scroll.window = window;
859 event->scroll.time = get_time_from_ns_event (nsevent);
862 event->scroll.x_root = x_root;
863 event->scroll.y_root = y_root;
864 event->scroll.state = get_keyboard_modifiers_from_ns_event (nsevent);
865 event->scroll.direction = direction;
866 event->scroll.device = _gdk_display->core_pointer;
870 fill_key_event (GdkWindow *window,
875 GdkEventPrivate *priv;
876 GdkQuartzDeviceManagerCore *device_manager;
880 priv = (GdkEventPrivate *) event;
881 priv->windowing_data = [nsevent retain];
883 event->any.type = type;
884 event->key.window = window;
885 event->key.time = get_time_from_ns_event (nsevent);
886 event->key.state = get_keyboard_modifiers_from_ns_event (nsevent);
887 event->key.hardware_keycode = [nsevent keyCode];
888 event->key.group = ([nsevent modifierFlags] & NSAlternateKeyMask) ? 1 : 0;
889 event->key.keyval = GDK_KEY_VoidSymbol;
891 device_manager = GDK_QUARTZ_DEVICE_MANAGER_CORE (_gdk_display->device_manager);
892 gdk_event_set_device (event, device_manager->core_keyboard);
894 gdk_keymap_translate_keyboard_state (gdk_keymap_get_for_display (_gdk_display),
895 event->key.hardware_keycode,
901 event->key.is_modifier = _gdk_quartz_keys_is_modifier (event->key.hardware_keycode);
903 /* If the key press is a modifier, the state should include the mask
904 * for that modifier but only for releases, not presses. This
905 * matches the X11 backend behavior.
907 if (event->key.is_modifier)
911 switch (event->key.keyval)
915 mask = GDK_MOD1_MASK;
917 case GDK_KEY_Shift_R:
918 case GDK_KEY_Shift_L:
919 mask = GDK_SHIFT_MASK;
921 case GDK_KEY_Caps_Lock:
922 mask = GDK_LOCK_MASK;
926 mask = GDK_MOD5_MASK;
928 case GDK_KEY_Control_R:
929 case GDK_KEY_Control_L:
930 mask = GDK_CONTROL_MASK;
936 if (type == GDK_KEY_PRESS)
937 event->key.state &= ~mask;
938 else if (type == GDK_KEY_RELEASE)
939 event->key.state |= mask;
942 event->key.state |= current_button_state;
944 event->key.string = NULL;
946 /* Fill in ->string since apps depend on it, taken from the x11 backend. */
947 if (event->key.keyval != GDK_KEY_VoidSymbol)
948 c = gdk_keyval_to_unicode (event->key.keyval);
955 len = g_unichar_to_utf8 (c, buf);
958 event->key.string = g_locale_from_utf8 (buf, len,
959 NULL, &bytes_written,
961 if (event->key.string)
962 event->key.length = bytes_written;
964 else if (event->key.keyval == GDK_KEY_Escape)
966 event->key.length = 1;
967 event->key.string = g_strdup ("\033");
969 else if (event->key.keyval == GDK_KEY_Return ||
970 event->key.keyval == GDK_KEY_KP_Enter)
972 event->key.length = 1;
973 event->key.string = g_strdup ("\r");
976 if (!event->key.string)
978 event->key.length = 0;
979 event->key.string = g_strdup ("");
983 g_message ("key %s:\t\twindow: %p key: %12s %d",
984 type == GDK_KEY_PRESS ? "press" : "release",
986 event->key.keyval ? gdk_keyval_name (event->key.keyval) : "(none)",
991 synthesize_crossing_event (GdkWindow *window,
999 switch ([nsevent type])
1001 case NSMouseEntered:
1002 /* Enter events are considered always to be from the root window as we
1003 * can't know for sure from what window we enter.
1005 if (!(window->event_mask & GDK_ENTER_NOTIFY_MASK))
1008 fill_crossing_event (window, event, nsevent,
1012 GDK_CROSSING_NORMAL,
1013 GDK_NOTIFY_ANCESTOR);
1017 /* Exited always is to the root window as far as we are concerned,
1018 * since there is no way to reliably get information about what new
1019 * window is entered when exiting one.
1021 if (!(window->event_mask & GDK_LEAVE_NOTIFY_MASK))
1024 fill_crossing_event (window, event, nsevent,
1028 GDK_CROSSING_NORMAL,
1029 GDK_NOTIFY_ANCESTOR);
1040 _gdk_quartz_events_get_current_keyboard_modifiers (void)
1042 return current_keyboard_modifiers;
1046 _gdk_quartz_events_get_current_mouse_modifiers (void)
1048 return current_button_state;
1051 /* Detect window resizing */
1054 test_resize (NSEvent *event, GdkWindow *toplevel, gint x, gint y)
1056 GdkWindowImplQuartz *toplevel_impl;
1058 /* Resizing only begins if an NSLeftMouseButton event is received in
1059 * the resizing area. Handle anything else.
1061 if ([event type] != NSLeftMouseDown)
1064 toplevel_impl = (GdkWindowImplQuartz *)toplevel->impl;
1065 if ([toplevel_impl->toplevel showsResizeIndicator])
1069 /* If the resize indicator is visible and the event
1070 * is in the lower right 15x15 corner, we leave these
1071 * events to Cocoa as to be handled as resize events.
1072 * Applications may have widgets in this area. These
1073 * will most likely be larger than 15x15 and for
1074 * scroll bars there are also other means to move
1075 * the scroll bar. Since the resize indicator is
1076 * the only way of resizing windows on Mac OS, it
1077 * is too important to not make functional.
1079 frame = [toplevel_impl->view bounds];
1080 if (x > frame.size.width - GRIP_WIDTH
1081 && x < frame.size.width
1082 && y > frame.size.height - GRIP_HEIGHT
1083 && y < frame.size.height)
1088 /* If we're on Lion and within 5 pixels of an edge,
1089 * then assume that the user wants to resize, and
1090 * return NULL to let Quartz get on with it. We check
1091 * the selector isRestorable to see if we're on 10.7.
1092 * This extra check is in case the user starts
1093 * dragging before GDK recognizes the grab.
1096 lion = gdk_quartz_osx_version() >= GDK_OSX_LION;
1097 if (lion && (x < GDK_LION_RESIZE ||
1098 x > toplevel->width - GDK_LION_RESIZE ||
1099 y > toplevel->height - GDK_LION_RESIZE))
1107 gdk_event_translate (GdkEvent *event,
1110 NSEventType event_type;
1115 gboolean return_val;
1117 /* There is no support for real desktop wide grabs, so we break
1118 * grabs when the application loses focus (gets deactivated).
1120 event_type = [nsevent type];
1121 if (event_type == NSAppKitDefined)
1123 if ([nsevent subtype] == NSApplicationDeactivatedEventType)
1124 break_all_grabs (get_time_from_ns_event (nsevent));
1126 /* This could potentially be used to break grabs when clicking
1127 * on the title. The subtype 20 is undocumented so it's probably
1128 * not a good idea: else if (subtype == 20) break_all_grabs ();
1131 /* Leave all AppKit events to AppKit. */
1135 /* Keep track of button state, since we don't get that information
1140 case NSLeftMouseDown:
1141 case NSRightMouseDown:
1142 case NSOtherMouseDown:
1143 current_button_state |= get_mouse_button_modifiers_from_ns_event (nsevent);
1146 case NSRightMouseUp:
1147 case NSOtherMouseUp:
1148 current_button_state &= ~get_mouse_button_modifiers_from_ns_event (nsevent);
1154 if (_gdk_default_filters)
1156 /* Apply global filters */
1157 GdkFilterReturn result;
1159 result = gdk_event_apply_filters (nsevent, event, &_gdk_default_filters);
1160 if (result != GDK_FILTER_CONTINUE)
1162 return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
1167 nswindow = [nsevent window];
1169 /* Ignore events for no window or ones not created by GDK. */
1170 if (!nswindow || ![[nswindow contentView] isKindOfClass:[GdkQuartzView class]])
1173 /* Ignore events and break grabs while the window is being
1174 * dragged. This is a workaround for the window getting events for
1177 if ([(GdkQuartzNSWindow *)nswindow isInMove])
1179 break_all_grabs (get_time_from_ns_event (nsevent));
1183 /* Find the right GDK window to send the event to, taking grabs and
1184 * event masks into consideration.
1186 window = find_window_for_ns_event (nsevent, &x, &y, &x_root, &y_root);
1189 /* Quartz handles resizing on its own, so we want to stay out of the way. */
1190 if (test_resize(nsevent, window, x, y))
1193 /* Apply any window filters. */
1194 if (GDK_IS_WINDOW (window))
1196 GdkFilterReturn result;
1198 if (window->filters)
1200 g_object_ref (window);
1202 result = gdk_event_apply_filters (nsevent, event, &window->filters);
1204 g_object_unref (window);
1206 if (result != GDK_FILTER_CONTINUE)
1208 return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
1214 /* If the app is not active leave the event to AppKit so the window gets
1215 * focused correctly and don't do click-through (so we behave like most
1216 * native apps). If the app is active, we focus the window and then handle
1217 * the event, also to match native apps.
1219 if ((event_type == NSRightMouseDown ||
1220 event_type == NSOtherMouseDown ||
1221 event_type == NSLeftMouseDown))
1223 GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
1225 if (![NSApp isActive])
1227 [NSApp activateIgnoringOtherApps:YES];
1230 else if (![impl->toplevel isKeyWindow])
1232 GdkDeviceGrabInfo *grab;
1234 grab = _gdk_display_get_last_device_grab (_gdk_display,
1235 _gdk_display->core_pointer);
1237 [impl->toplevel makeKeyWindow];
1241 current_keyboard_modifiers = get_keyboard_modifiers_from_ns_event (nsevent);
1247 case NSLeftMouseDown:
1248 case NSRightMouseDown:
1249 case NSOtherMouseDown:
1251 case NSRightMouseUp:
1252 case NSOtherMouseUp:
1253 fill_button_event (window, event, nsevent, x, y, x_root, y_root);
1256 case NSLeftMouseDragged:
1257 case NSRightMouseDragged:
1258 case NSOtherMouseDragged:
1260 fill_motion_event (window, event, nsevent, x, y, x_root, y_root);
1265 float dx = [nsevent deltaX];
1266 float dy = [nsevent deltaY];
1267 GdkScrollDirection direction;
1272 direction = GDK_SCROLL_DOWN;
1274 direction = GDK_SCROLL_UP;
1276 fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
1282 direction = GDK_SCROLL_RIGHT;
1284 direction = GDK_SCROLL_LEFT;
1286 fill_scroll_event (window, event, nsevent, x, y, x_root, y_root, direction);
1291 case NSMouseEntered:
1293 return_val = synthesize_crossing_event (window, event, nsevent, x, y, x_root, y_root);
1298 case NSFlagsChanged:
1302 type = _gdk_quartz_keys_event_type (nsevent);
1303 if (type == GDK_NOTHING)
1306 fill_key_event (window, event, nsevent, type);
1311 /* Ignore everything elsee. */
1319 if (event->any.window)
1320 g_object_ref (event->any.window);
1321 if (((event->any.type == GDK_ENTER_NOTIFY) ||
1322 (event->any.type == GDK_LEAVE_NOTIFY)) &&
1323 (event->crossing.subwindow != NULL))
1324 g_object_ref (event->crossing.subwindow);
1328 /* Mark this event as having no resources to be freed */
1329 event->any.window = NULL;
1330 event->any.type = GDK_NOTHING;
1337 _gdk_quartz_display_queue_events (GdkDisplay *display)
1341 nsevent = _gdk_quartz_event_loop_get_pending ();
1347 event = gdk_event_new (GDK_NOTHING);
1349 event->any.window = NULL;
1350 event->any.send_event = FALSE;
1352 ((GdkEventPrivate *)event)->flags |= GDK_EVENT_PENDING;
1354 node = _gdk_event_queue_append (display, event);
1356 if (gdk_event_translate (event, nsevent))
1358 ((GdkEventPrivate *)event)->flags &= ~GDK_EVENT_PENDING;
1359 _gdk_windowing_got_event (display, node, event, 0);
1363 _gdk_event_queue_remove_link (display, node);
1364 g_list_free_1 (node);
1365 gdk_event_free (event);
1367 GDK_THREADS_LEAVE ();
1368 [NSApp sendEvent:nsevent];
1369 GDK_THREADS_ENTER ();
1372 _gdk_quartz_event_loop_release_event (nsevent);
1377 _gdk_quartz_screen_broadcast_client_message (GdkScreen *screen,
1380 /* Not supported. */
1384 _gdk_quartz_screen_get_setting (GdkScreen *screen,
1388 if (strcmp (name, "gtk-double-click-time") == 0)
1390 NSUserDefaults *defaults;
1393 GDK_QUARTZ_ALLOC_POOL;
1395 defaults = [NSUserDefaults standardUserDefaults];
1397 t = [defaults floatForKey:@"com.apple.mouse.doubleClickThreshold"];
1400 /* No user setting, use the default in OS X. */
1404 GDK_QUARTZ_RELEASE_POOL;
1406 g_value_set_int (value, t * 1000);
1410 else if (strcmp (name, "gtk-font-name") == 0)
1415 GDK_QUARTZ_ALLOC_POOL;
1417 name = [[NSFont systemFontOfSize:0] familyName];
1419 /* Let's try to use the "views" font size (12pt) by default. This is
1420 * used for lists/text/other "content" which is the largest parts of
1421 * apps, using the "regular control" size (13pt) looks a bit out of
1422 * place. We might have to tweak this.
1425 /* The size has to be hardcoded as there doesn't seem to be a way to
1426 * get the views font size programmatically.
1428 str = g_strdup_printf ("%s 12", [name UTF8String]);
1429 g_value_set_string (value, str);
1432 GDK_QUARTZ_RELEASE_POOL;
1437 /* FIXME: Add more settings */
1443 _gdk_quartz_display_event_data_copy (GdkDisplay *display,
1444 const GdkEvent *src,
1447 GdkEventPrivate *priv_src = (GdkEventPrivate *) src;
1448 GdkEventPrivate *priv_dst = (GdkEventPrivate *) dst;
1450 if (priv_src->windowing_data)
1452 priv_dst->windowing_data = priv_src->windowing_data;
1453 [(NSEvent *)priv_dst->windowing_data retain];
1458 _gdk_quartz_display_event_data_free (GdkDisplay *display,
1461 GdkEventPrivate *priv = (GdkEventPrivate *) event;
1463 if (priv->windowing_data)
1465 [(NSEvent *)priv->windowing_data release];
1466 priv->windowing_data = NULL;