gdkevents-quartz.c 54.3 KB
Newer Older
Anders Carlsson's avatar
Anders Carlsson committed
1 2 3 4
/* gdkevents-quartz.c
 *
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 * Copyright (C) 1998-2002 Tor Lillqvist
5
 * Copyright (C) 2005-2008 Imendio AB
Anders Carlsson's avatar
Anders Carlsson committed
6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
18
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Anders Carlsson's avatar
Anders Carlsson committed
19 20
 */

21
#include "config.h"
Anders Carlsson's avatar
Anders Carlsson committed
22 23
#include <sys/types.h>
#include <sys/sysctl.h>
24 25
#include <pthread.h>
#include <unistd.h>
Anders Carlsson's avatar
Anders Carlsson committed
26

27
#import <Cocoa/Cocoa.h>
28 29
#include <Carbon/Carbon.h>

30 31
#include <gdk/gdkdisplayprivate.h>

Anders Carlsson's avatar
Anders Carlsson committed
32
#include "gdkscreen.h"
33
#include "gdkkeysyms.h"
34
#include "gdkquartz.h"
35
#include "gdkquartzdisplay.h"
Anders Carlsson's avatar
Anders Carlsson committed
36
#include "gdkprivate-quartz.h"
37
#include "gdkquartzdevicemanager-core.h"
38
#include "gdkquartzkeys.h"
Anders Carlsson's avatar
Anders Carlsson committed
39

40 41
#define GRIP_WIDTH 15
#define GRIP_HEIGHT 15
42
#define GDK_LION_RESIZE 5
43

44 45 46 47 48
#define WINDOW_IS_TOPLEVEL(window)		     \
  (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD &&   \
   GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN && \
   GDK_WINDOW_TYPE (window) != GDK_WINDOW_OFFSCREEN)

Anders Carlsson's avatar
Anders Carlsson committed
49
/* This is the window corresponding to the key window */
50
static GdkWindow   *current_keyboard_window;
Anders Carlsson's avatar
Anders Carlsson committed
51

52

53 54
static void append_event                        (GdkEvent  *event,
                                                 gboolean   windowing);
55

56 57 58 59 60 61
static GdkWindow *find_toplevel_under_pointer   (GdkDisplay *display,
                                                 NSPoint     screen_point,
                                                 gint       *x,
                                                 gint       *y);


62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
static void
gdk_quartz_ns_notification_callback (CFNotificationCenterRef  center,
                                     void                    *observer,
                                     CFStringRef              name,
                                     const void              *object,
                                     CFDictionaryRef          userInfo)
{
  GdkEvent new_event;

  new_event.type = GDK_SETTING;
  new_event.setting.window = gdk_screen_get_root_window (_gdk_screen);
  new_event.setting.send_event = FALSE;
  new_event.setting.action = GDK_SETTING_ACTION_CHANGED;
  new_event.setting.name = NULL;

  /* Translate name */
  if (CFStringCompare (name,
                       CFSTR("AppleNoRedisplayAppearancePreferenceChanged"),
                       0) == kCFCompareEqualTo)
    new_event.setting.name = "gtk-primary-button-warps-slider";

  if (!new_event.setting.name)
    return;

  gdk_event_put (&new_event);
}

static void
gdk_quartz_events_init_notifications (void)
{
  static gboolean notifications_initialized = FALSE;

  if (notifications_initialized)
    return;
  notifications_initialized = TRUE;

  /* Initialize any handlers for notifications we want to push to GTK
   * through GdkEventSettings.
   */

  /* This is an undocumented *distributed* notification to listen for changes
   * in scrollbar jump behavior. It is used by LibreOffice and WebKit as well.
   */
  CFNotificationCenterAddObserver (CFNotificationCenterGetDistributedCenter (),
                                   NULL,
                                   &gdk_quartz_ns_notification_callback,
                                   CFSTR ("AppleNoRedisplayAppearancePreferenceChanged"),
                                   NULL,
                                   CFNotificationSuspensionBehaviorDeliverImmediately);
}

Richard Hult's avatar
Richard Hult committed
113
void
114
_gdk_quartz_events_init (void)
Anders Carlsson's avatar
Anders Carlsson committed
115
{
116
  _gdk_quartz_event_loop_init ();
117
  gdk_quartz_events_init_notifications ();
Anders Carlsson's avatar
Anders Carlsson committed
118 119 120 121 122

  current_keyboard_window = g_object_ref (_gdk_root);
}

gboolean
123
_gdk_quartz_display_has_pending (GdkDisplay *display)
Anders Carlsson's avatar
Anders Carlsson committed
124
{
125 126
  return (_gdk_event_queue_find_first (display) ||
         (_gdk_quartz_event_loop_check_pending ()));
Anders Carlsson's avatar
Anders Carlsson committed
127 128
}

129 130
void
_gdk_quartz_events_break_all_grabs (guint32 time)
131
{
132 133
  GdkSeat *seat = gdk_display_get_default_seat (_gdk_display);
  gdk_seat_ungrab (seat);
134 135
}

Anders Carlsson's avatar
Anders Carlsson committed
136 137 138 139 140 141 142 143 144 145 146 147 148
static void
fixup_event (GdkEvent *event)
{
  if (event->any.window)
    g_object_ref (event->any.window);
  if (((event->any.type == GDK_ENTER_NOTIFY) ||
       (event->any.type == GDK_LEAVE_NOTIFY)) &&
      (event->crossing.subwindow != NULL))
    g_object_ref (event->crossing.subwindow);
  event->any.send_event = FALSE;
}

static void
149 150
append_event (GdkEvent *event,
              gboolean  windowing)
Anders Carlsson's avatar
Anders Carlsson committed
151
{
152 153
  GList *node;

Anders Carlsson's avatar
Anders Carlsson committed
154
  fixup_event (event);
155 156 157 158
  node = _gdk_event_queue_append (_gdk_display, event);

  if (windowing)
    _gdk_windowing_got_event (_gdk_display, node, event, 0);
Anders Carlsson's avatar
Anders Carlsson committed
159 160
}

161 162 163
static gint
gdk_event_apply_filters (NSEvent *nsevent,
			 GdkEvent *event,
164
			 GList **filters)
Anders Carlsson's avatar
Anders Carlsson committed
165 166
{
  GList *tmp_list;
167
  GdkFilterReturn result;
Anders Carlsson's avatar
Anders Carlsson committed
168
  
169
  tmp_list = *filters;
170

Anders Carlsson's avatar
Anders Carlsson committed
171 172
  while (tmp_list)
    {
173
      GdkEventFilter *filter = (GdkEventFilter*) tmp_list->data;
174 175 176 177 178 179 180 181 182
      GList *node;

      if ((filter->flags & GDK_EVENT_FILTER_REMOVED) != 0)
        {
          tmp_list = tmp_list->next;
          continue;
        }

      filter->ref_count++;
Anders Carlsson's avatar
Anders Carlsson committed
183
      result = filter->function (nsevent, event, filter->data);
184 185 186 187 188 189 190 191 192 193 194 195 196 197

      /* get the next node after running the function since the
         function may add or remove a next node */
      node = tmp_list;
      tmp_list = tmp_list->next;

      filter->ref_count--;
      if (filter->ref_count == 0)
        {
          *filters = g_list_remove_link (*filters, node);
          g_list_free_1 (node);
          g_free (filter);
        }

198 199
      if (result !=  GDK_FILTER_CONTINUE)
	return result;
Anders Carlsson's avatar
Anders Carlsson committed
200 201
    }

202
  return GDK_FILTER_CONTINUE;
Anders Carlsson's avatar
Anders Carlsson committed
203 204 205
}

static guint32
206
get_time_from_ns_event (NSEvent *event)
Anders Carlsson's avatar
Anders Carlsson committed
207 208
{
  double time = [event timestamp];
209 210 211 212 213

  /* cast via double->uint64 conversion to make sure that it is
   * wrapped on 32-bit machines when it overflows
   */
  return (guint32) (guint64) (time * 1000.0);
Anders Carlsson's avatar
Anders Carlsson committed
214 215 216
}

static int
217
get_mouse_button_from_ns_event (NSEvent *event)
Anders Carlsson's avatar
Anders Carlsson committed
218
{
219
  NSInteger button;
220 221 222

  button = [event buttonNumber];

Anders Carlsson's avatar
Anders Carlsson committed
223 224 225 226 227 228 229 230 231 232 233 234 235
  switch (button)
    {
    case 0:
      return 1;
    case 1:
      return 3;
    case 2:
      return 2;
    default:
      return button + 1;
    }
}

236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
static GdkModifierType
get_mouse_button_modifiers_from_ns_buttons (NSUInteger nsbuttons)
{
  GdkModifierType modifiers = 0;

  if (nsbuttons & (1 << 0))
    modifiers |= GDK_BUTTON1_MASK;
  if (nsbuttons & (1 << 1))
    modifiers |= GDK_BUTTON3_MASK;
  if (nsbuttons & (1 << 2))
    modifiers |= GDK_BUTTON2_MASK;
  if (nsbuttons & (1 << 3))
    modifiers |= GDK_BUTTON4_MASK;
  if (nsbuttons & (1 << 4))
    modifiers |= GDK_BUTTON5_MASK;

  return modifiers;
}

255 256 257 258 259 260 261 262 263 264 265 266 267 268
static GdkModifierType
get_mouse_button_modifiers_from_ns_event (NSEvent *event)
{
  int button;
  GdkModifierType state = 0;

  /* This maps buttons 1 to 5 to GDK_BUTTON[1-5]_MASK */
  button = get_mouse_button_from_ns_event (event);
  if (button >= 1 && button <= 5)
    state = (1 << (button + 7));

  return state;
}

269
static GdkModifierType
270
get_keyboard_modifiers_from_ns_flags (NSUInteger nsflags)
271 272 273
{
  GdkModifierType modifiers = 0;

274
  if (nsflags & GDK_QUARTZ_ALPHA_SHIFT_KEY_MASK)
275
    modifiers |= GDK_LOCK_MASK;
276
  if (nsflags & GDK_QUARTZ_SHIFT_KEY_MASK)
277
    modifiers |= GDK_SHIFT_MASK;
278
  if (nsflags & GDK_QUARTZ_CONTROL_KEY_MASK)
279
    modifiers |= GDK_CONTROL_MASK;
280
  if (nsflags & GDK_QUARTZ_ALTERNATE_KEY_MASK)
281
    modifiers |= GDK_MOD1_MASK;
282
  if (nsflags & GDK_QUARTZ_COMMAND_KEY_MASK)
283
    modifiers |= GDK_MOD2_MASK;
284 285 286 287

  return modifiers;
}

288 289 290 291 292 293
static GdkModifierType
get_keyboard_modifiers_from_ns_event (NSEvent *nsevent)
{
  return get_keyboard_modifiers_from_ns_flags ([nsevent modifierFlags]);
}

Anders Carlsson's avatar
Anders Carlsson committed
294 295 296 297
/* Return an event mask from an NSEvent */
static GdkEventMask
get_event_mask_from_ns_event (NSEvent *nsevent)
{
298
  switch ([nsevent type])
Anders Carlsson's avatar
Anders Carlsson committed
299
    {
300 301 302
    case GDK_QUARTZ_LEFT_MOUSE_DOWN:
    case GDK_QUARTZ_RIGHT_MOUSE_DOWN:
    case GDK_QUARTZ_OTHER_MOUSE_DOWN:
Anders Carlsson's avatar
Anders Carlsson committed
303
      return GDK_BUTTON_PRESS_MASK;
304 305 306
    case GDK_QUARTZ_LEFT_MOUSE_UP:
    case GDK_QUARTZ_RIGHT_MOUSE_UP:
    case GDK_QUARTZ_OTHER_MOUSE_UP:
Anders Carlsson's avatar
Anders Carlsson committed
307
      return GDK_BUTTON_RELEASE_MASK;
308
    case GDK_QUARTZ_MOUSE_MOVED:
309
      return GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK;
310
    case GDK_QUARTZ_SCROLL_WHEEL:
Anders Carlsson's avatar
Anders Carlsson committed
311 312 313 314
      /* Since applications that want button press events can get
       * scroll events on X11 (since scroll wheel events are really
       * button press events there), we need to use GDK_BUTTON_PRESS_MASK too.
       */
315
      return GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK;
316
    case GDK_QUARTZ_LEFT_MOUSE_DRAGGED:
317 318 319
      return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
	      GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | 
	      GDK_BUTTON1_MASK);
320
    case GDK_QUARTZ_RIGHT_MOUSE_DRAGGED:
321 322 323
      return (GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
	      GDK_BUTTON_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | 
	      GDK_BUTTON3_MASK);
324
    case GDK_QUARTZ_OTHER_MOUSE_DRAGGED:
Anders Carlsson's avatar
Anders Carlsson committed
325
      {
326 327 328 329 330 331
	GdkEventMask mask;

	mask = (GDK_POINTER_MOTION_MASK |
		GDK_POINTER_MOTION_HINT_MASK |
		GDK_BUTTON_MOTION_MASK);

332
	if (get_mouse_button_from_ns_event (nsevent) == 2)
333 334
	  mask |= (GDK_BUTTON2_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | 
		   GDK_BUTTON2_MASK);
Anders Carlsson's avatar
Anders Carlsson committed
335 336 337

	return mask;
      }
338 339 340
    case NSEventTypeMagnify:
    case NSEventTypeRotate:
      return GDK_TOUCHPAD_GESTURE_MASK;
341 342 343
    case GDK_QUARTZ_KEY_DOWN:
    case GDK_QUARTZ_KEY_UP:
    case GDK_QUARTZ_FLAGS_CHANGED:
344
      {
345
        switch (_gdk_quartz_keys_event_type (nsevent))
346 347 348 349 350 351 352 353 354 355
	  {
	  case GDK_KEY_PRESS:
	    return GDK_KEY_PRESS_MASK;
	  case GDK_KEY_RELEASE:
	    return GDK_KEY_RELEASE_MASK;
	  case GDK_NOTHING:
	    return 0;
	  default:
	    g_assert_not_reached ();
	  }
356
      }
357 358
      break;

359
    case GDK_QUARTZ_MOUSE_ENTERED:
360 361
      return GDK_ENTER_NOTIFY_MASK;

362
    case GDK_QUARTZ_MOUSE_EXITED:
363 364
      return GDK_LEAVE_NOTIFY_MASK;

Anders Carlsson's avatar
Anders Carlsson committed
365 366 367 368 369 370 371
    default:
      g_assert_not_reached ();
    }

  return 0;
}

372 373 374 375 376 377 378
static void
get_window_point_from_screen_point (GdkWindow *window,
                                    NSPoint    screen_point,
                                    gint      *x,
                                    gint      *y)
{
  NSPoint point;
379
  GdkQuartzNSWindow *nswindow;
380

381
  nswindow = (GdkQuartzNSWindow*)(((GdkWindowImplQuartz *)window->impl)->toplevel);
382

383
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070
384
  point = [nswindow convertScreenToBase:screen_point];
385 386 387
#else
  point = [nswindow convertPointFromScreen:screen_point];
#endif
388 389 390 391
  *x = point.x;
  *y = window->height - point.y;
}

392 393 394 395 396
static gboolean
is_mouse_button_press_event (NSEventType type)
{
  switch (type)
    {
397 398 399
      case GDK_QUARTZ_LEFT_MOUSE_DOWN:
      case GDK_QUARTZ_RIGHT_MOUSE_DOWN:
      case GDK_QUARTZ_OTHER_MOUSE_DOWN:
400
        return TRUE;
401 402
    default:
      return FALSE;
403 404 405 406 407
    }

  return FALSE;
}

408 409 410 411 412 413
static GdkWindow *
get_toplevel_from_ns_event (NSEvent *nsevent,
                            NSPoint *screen_point,
                            gint    *x,
                            gint    *y)
{
414
  GdkWindow *toplevel = NULL;
415

416 417 418
  if ([nsevent window])
    {
      GdkQuartzView *view;
419 420
      NSPoint point, view_point;
      NSRect view_frame;
421

422
      view = (GdkQuartzView *)[[nsevent window] contentView];
423

424
      toplevel = [view gdkWindow];
425

426
      point = [nsevent locationInWindow];
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
      view_point = [view convertPoint:point fromView:nil];
      view_frame = [view frame];

      /* NSEvents come in with a window set, but with window coordinates
       * out of window bounds. For e.g. moved events this is fine, we use
       * this information to properly handle enter/leave notify and motion
       * events. For mouse button press/release, we want to avoid forwarding
       * these events however, because the window they relate to is not the
       * window set in the event. This situation appears to occur when button
       * presses come in just before (or just after?) a window is resized and
       * also when a button press occurs on the OS X window titlebar.
       *
       * By setting toplevel to NULL, we do another attempt to get the right
       * toplevel window below.
       */
      if (is_mouse_button_press_event ([nsevent type]) &&
          (view_point.x < view_frame.origin.x ||
           view_point.x >= view_frame.origin.x + view_frame.size.width ||
           view_point.y < view_frame.origin.y ||
           view_point.y >= view_frame.origin.y + view_frame.size.height))
        {
          toplevel = NULL;

          /* This is a hack for button presses to break all grabs. E.g. if
           * a menu is open and one clicks on the title bar (or anywhere
           * out of window bounds), we really want to pop down the menu (by
           * breaking the grabs) before OS X handles the action of the title
           * bar button.
           *
           * Because we cannot ingest this event into GDK, we have to do it
           * here, not very nice.
           */
          _gdk_quartz_events_break_all_grabs (get_time_from_ns_event (nsevent));
        }
      else
        {
463 464 465 466 467 468
          if (gdk_quartz_osx_version () >= GDK_OSX_LION)
            *screen_point = [(GdkQuartzNSWindow*)[nsevent window] convertPointToScreen:point];
#if MAC_OS_X_VERSION_MIN_REQUIRED < 10700
          else
            *screen_point = [[nsevent window] convertBaseToScreen:point];
#endif
469 470 471
          *x = point.x;
          *y = toplevel->height - point.y;
        }
472
    }
473 474

  if (!toplevel)
475 476 477 478
    {
      /* Fallback used when no NSWindow set.  This happens e.g. when
       * we allow motion events without a window set in gdk_event_translate()
       * that occur immediately after the main menu bar was clicked/used.
479 480
       * This fallback will not return coordinates contained in a window's
       * titlebar.
481 482 483 484 485 486
       */
      *screen_point = [NSEvent mouseLocation];
      toplevel = find_toplevel_under_pointer (_gdk_display,
                                              *screen_point,
                                              x, y);
    }
487 488 489 490

  return toplevel;
}

Anders Carlsson's avatar
Anders Carlsson committed
491 492 493 494 495
static GdkEvent *
create_focus_event (GdkWindow *window,
		    gboolean   in)
{
  GdkEvent *event;
496 497
  GdkDisplay *display = gdk_window_get_display (window);
  GdkSeat *seat = gdk_display_get_default_seat (display);
Anders Carlsson's avatar
Anders Carlsson committed
498 499 500 501 502

  event = gdk_event_new (GDK_FOCUS_CHANGE);
  event->focus_change.window = window;
  event->focus_change.in = in;

503 504
  gdk_event_set_device (event, gdk_seat_get_keyboard (seat));
  gdk_event_set_seat (event, seat);
505

Anders Carlsson's avatar
Anders Carlsson committed
506 507 508
  return event;
}

509 510 511 512 513 514 515

static void
generate_motion_event (GdkWindow *window)
{
  NSPoint screen_point;
  GdkEvent *event;
  gint x, y, x_root, y_root;
516 517
  GdkDisplay *display = gdk_window_get_display (window);
  GdkSeat *seat = gdk_display_get_default_seat (display);
518 519 520 521 522 523 524 525

  event = gdk_event_new (GDK_MOTION_NOTIFY);
  event->any.window = NULL;
  event->any.send_event = TRUE;

  screen_point = [NSEvent mouseLocation];

  _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, &x_root, &y_root);
526
  get_window_point_from_screen_point (window, screen_point, &x, &y);
527 528 529

  event->any.type = GDK_MOTION_NOTIFY;
  event->motion.window = window;
530
  event->motion.time = get_time_from_ns_event ([NSApp currentEvent]);
531 532 533 534 535
  event->motion.x = x;
  event->motion.y = y;
  event->motion.x_root = x_root;
  event->motion.y_root = y_root;
  /* FIXME event->axes */
536 537
  event->motion.state = _gdk_quartz_events_get_current_keyboard_modifiers () |
                        _gdk_quartz_events_get_current_mouse_modifiers ();
538
  event->motion.is_hint = FALSE;
539 540
  event->motion.device = gdk_seat_get_pointer (seat);
  gdk_event_set_seat (event, seat);
541 542 543 544

  append_event (event, TRUE);
}

545
/* Note: Used to both set a new focus window and to unset the old one. */
Anders Carlsson's avatar
Anders Carlsson committed
546
void
547 548
_gdk_quartz_events_update_focus_window (GdkWindow *window,
					gboolean   got_focus)
Anders Carlsson's avatar
Anders Carlsson committed
549
{
550 551 552 553 554
  GdkEvent *event;

  if (got_focus && window == current_keyboard_window)
    return;

555
  /* FIXME: Don't do this when grabbed? Or make GdkQuartzNSWindow
556 557 558 559
   * disallow it in the first place instead?
   */
  
  if (!got_focus && window == current_keyboard_window)
Anders Carlsson's avatar
Anders Carlsson committed
560
    {
561
      event = create_focus_event (current_keyboard_window, FALSE);
562
      append_event (event, FALSE);
563 564
      g_object_unref (current_keyboard_window);
      current_keyboard_window = NULL;
565
    }
Anders Carlsson's avatar
Anders Carlsson committed
566

567 568 569 570 571
  if (got_focus)
    {
      if (current_keyboard_window)
	{
	  event = create_focus_event (current_keyboard_window, FALSE);
572
	  append_event (event, FALSE);
573 574 575 576 577
	  g_object_unref (current_keyboard_window);
	  current_keyboard_window = NULL;
	}
      
      event = create_focus_event (window, TRUE);
578
      append_event (event, FALSE);
579
      current_keyboard_window = g_object_ref (window);
580 581 582 583 584 585 586

      /* We just became the active window.  Unlike X11, Mac OS X does
       * not send us motion events while the window does not have focus
       * ("is not key").  We send a dummy motion notify event now, so that
       * everything in the window is set to correct state.
       */
      generate_motion_event (window);
Anders Carlsson's avatar
Anders Carlsson committed
587 588 589
    }
}

590 591
void
_gdk_quartz_events_send_map_event (GdkWindow *window)
Anders Carlsson's avatar
Anders Carlsson committed
592
{
593
  GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
Anders Carlsson's avatar
Anders Carlsson committed
594

595
  if (!impl->toplevel)
596 597
    return;

598
  if (window->event_mask & GDK_STRUCTURE_MASK)
Anders Carlsson's avatar
Anders Carlsson committed
599
    {
600
      GdkEvent event;
601

602 603
      event.any.type = GDK_MAP;
      event.any.window = window;
604
  
605
      gdk_event_put (&event);
606 607 608
    }
}

609 610 611 612 613 614 615
static GdkWindow *
find_toplevel_under_pointer (GdkDisplay *display,
                             NSPoint     screen_point,
                             gint       *x,
                             gint       *y)
{
  GdkWindow *toplevel;
616
  GdkPointerWindowInfo *info;
617
  GdkSeat *seat = gdk_display_get_default_seat (display);
618

619
  info = _gdk_display_get_pointer_info (display, gdk_seat_get_pointer (seat));
620
  toplevel = info->toplevel_under_pointer;
621
  if (toplevel && WINDOW_IS_TOPLEVEL (toplevel))
622
    get_window_point_from_screen_point (toplevel, screen_point, x, y);
623

624 625 626 627 628 629 630 631 632 633 634 635
  if (toplevel)
    {
      /* If the coordinates are out of window bounds, this toplevel is not
       * under the pointer and we thus return NULL. This can occur when
       * toplevel under pointer has not yet been updated due to a very recent
       * window resize. Alternatively, we should no longer be relying on
       * the toplevel_under_pointer value which is maintained in gdkwindow.c.
       */
      if (*x < 0 || *y < 0 || *x >= toplevel->width || *y >= toplevel->height)
        return NULL;
    }

636 637 638
  return toplevel;
}

639 640 641 642 643 644 645
static GdkWindow *
find_toplevel_for_keyboard_event (NSEvent *nsevent)
{
  GList *list, *l;
  GdkWindow *window;
  GdkDisplay *display;
  GdkQuartzView *view;
646
  GdkSeat *seat;
647 648 649 650

  view = (GdkQuartzView *)[[nsevent window] contentView];
  window = [view gdkWindow];

651
  display = gdk_window_get_display (window);
652
  seat = gdk_display_get_default_seat (display);
653

654
  list = gdk_seat_get_slaves (seat, GDK_SEAT_CAPABILITY_KEYBOARD);
655 656 657 658 659 660 661 662
  for (l = list; l; l = l->next)
    {
      GdkDeviceGrabInfo *grab;
      GdkDevice *device = l->data;

      grab = _gdk_display_get_last_device_grab (display, device);
      if (grab && grab->window && !grab->owner_events)
        {
663
          window = gdk_window_get_effective_toplevel (grab->window);
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682
          break;
        }
    }

  g_list_free (list);

  return window;
}

static GdkWindow *
find_toplevel_for_mouse_event (NSEvent    *nsevent,
                               gint       *x,
                               gint       *y)
{
  NSPoint screen_point;
  NSEventType event_type;
  GdkWindow *toplevel;
  GdkDisplay *display;
  GdkDeviceGrabInfo *grab;
683
  GdkSeat *seat;
684

685
  toplevel = get_toplevel_from_ns_event (nsevent, &screen_point, x, y);
686

687
  display = gdk_window_get_display (toplevel);
688 689
  seat = gdk_display_get_default_seat (_gdk_display);
  
690 691 692 693 694 695 696 697 698 699 700 701
  event_type = [nsevent type];

  /* From the docs for XGrabPointer:
   *
   * If owner_events is True and if a generated pointer event
   * would normally be reported to this client, it is reported
   * as usual. Otherwise, the event is reported with respect to
   * the grab_window and is reported only if selected by
   * event_mask. For either value of owner_events, unreported
   * events are discarded.
   */
  grab = _gdk_display_get_last_device_grab (display,
702
                                            gdk_seat_get_pointer (seat));
703
  if (WINDOW_IS_TOPLEVEL (toplevel) && grab)
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738
    {
      /* Implicit grabs do not go through XGrabPointer and thus the
       * event mask should not be checked.
       */
      if (!grab->implicit
          && (grab->event_mask & get_event_mask_from_ns_event (nsevent)) == 0)
        return NULL;

      if (grab->owner_events)
        {
          /* For owner events, we need to use the toplevel under the
           * pointer, not the window from the NSEvent, since that is
           * reported with respect to the key window, which could be
           * wrong.
           */
          GdkWindow *toplevel_under_pointer;
          gint x_tmp, y_tmp;

          toplevel_under_pointer = find_toplevel_under_pointer (display,
                                                                screen_point,
                                                                &x_tmp, &y_tmp);
          if (toplevel_under_pointer)
            {
              toplevel = toplevel_under_pointer;
              *x = x_tmp;
              *y = y_tmp;
            }

          return toplevel;
        }
      else
        {
          /* Finally check the grab window. */
          GdkWindow *grab_toplevel;

739
          grab_toplevel = gdk_window_get_effective_toplevel (grab->window);
740 741
          get_window_point_from_screen_point (grab_toplevel, screen_point,
                                              x, y);
742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758

          return grab_toplevel;
        }

      return NULL;
    }
  else 
    {
      /* The non-grabbed case. */
      GdkWindow *toplevel_under_pointer;
      gint x_tmp, y_tmp;

      /* Ignore all events but mouse moved that might be on the title
       * bar (above the content view). The reason is that otherwise
       * gdk gets confused about getting e.g. button presses with no
       * window (the title bar is not known to it).
       */
759
      if (event_type != GDK_QUARTZ_MOUSE_MOVED)
760 761 762 763 764 765 766 767 768
        if (*y < 0)
          return NULL;

      /* As for owner events, we need to use the toplevel under the
       * pointer, not the window from the NSEvent.
       */
      toplevel_under_pointer = find_toplevel_under_pointer (display,
                                                            screen_point,
                                                            &x_tmp, &y_tmp);
769 770
      if (toplevel_under_pointer
          && WINDOW_IS_TOPLEVEL (toplevel_under_pointer))
771 772 773 774 775
        {
          GdkWindowImplQuartz *toplevel_impl;

          toplevel = toplevel_under_pointer;

776
          toplevel_impl = (GdkWindowImplQuartz *)toplevel->impl;
777 778 779 780 781 782 783 784 785 786 787

          *x = x_tmp;
          *y = y_tmp;
        }

      return toplevel;
    }

  return NULL;
}

788 789 790 791 792 793
/* This function finds the correct window to send an event to, taking
 * into account grabs, event propagation, and event masks.
 */
static GdkWindow *
find_window_for_ns_event (NSEvent *nsevent, 
                          gint    *x, 
794 795 796
                          gint    *y,
                          gint    *x_root,
                          gint    *y_root)
797
{
798
  GdkQuartzView *view;
799
  GdkWindow *toplevel;
800
  NSPoint screen_point;
801 802
  NSEventType event_type;

803
  view = (GdkQuartzView *)[[nsevent window] contentView];
804

805
  toplevel = get_toplevel_from_ns_event (nsevent, &screen_point, x, y);
806 807
  if (!toplevel)
    return NULL;
808
  _gdk_quartz_window_nspoint_to_gdk_xy (screen_point, x_root, y_root);
809

810
  event_type = [nsevent type];
Anders Carlsson's avatar
Anders Carlsson committed
811 812 813

  switch (event_type)
    {
814 815 816 817 818 819 820 821 822 823 824
    case GDK_QUARTZ_LEFT_MOUSE_DOWN:
    case GDK_QUARTZ_RIGHT_MOUSE_DOWN:
    case GDK_QUARTZ_OTHER_MOUSE_DOWN:
    case GDK_QUARTZ_LEFT_MOUSE_UP:
    case GDK_QUARTZ_RIGHT_MOUSE_UP:
    case GDK_QUARTZ_OTHER_MOUSE_UP:
    case GDK_QUARTZ_MOUSE_MOVED:
    case GDK_QUARTZ_SCROLL_WHEEL:
    case GDK_QUARTZ_LEFT_MOUSE_DRAGGED:
    case GDK_QUARTZ_RIGHT_MOUSE_DRAGGED:
    case GDK_QUARTZ_OTHER_MOUSE_DRAGGED:
825 826
    case NSEventTypeMagnify:
    case NSEventTypeRotate:
827
      return find_toplevel_for_mouse_event (nsevent, x, y);
828 829 830

    case GDK_QUARTZ_MOUSE_ENTERED:
    case GDK_QUARTZ_MOUSE_EXITED:
831 832 833 834 835 836 837
      /* Only handle our own entered/exited events, not the ones for the
       * titlebar buttons.
       */
      if ([view trackingRect] == [nsevent trackingNumber])
        return toplevel;
      else
        return NULL;
Anders Carlsson's avatar
Anders Carlsson committed
838

839 840 841
    case GDK_QUARTZ_KEY_DOWN:
    case GDK_QUARTZ_KEY_UP:
    case GDK_QUARTZ_FLAGS_CHANGED:
842
      return find_toplevel_for_keyboard_event (nsevent);
Anders Carlsson's avatar
Anders Carlsson committed
843 844

    default:
845 846
      /* Ignore everything else. */
      break;
Anders Carlsson's avatar
Anders Carlsson committed
847 848 849 850 851
    }

  return NULL;
}

852 853 854 855 856 857
static void
fill_crossing_event (GdkWindow       *toplevel,
                     GdkEvent        *event,
                     NSEvent         *nsevent,
                     gint             x,
                     gint             y,
858 859
                     gint             x_root,
                     gint             y_root,
860 861 862 863
                     GdkEventType     event_type,
                     GdkCrossingMode  mode,
                     GdkNotifyType    detail)
{
864
  GdkSeat *seat = gdk_display_get_default_seat (_gdk_display);
865

866 867 868 869 870 871
  event->any.type = event_type;
  event->crossing.window = toplevel;
  event->crossing.subwindow = NULL;
  event->crossing.time = get_time_from_ns_event (nsevent);
  event->crossing.x = x;
  event->crossing.y = y;
872 873
  event->crossing.x_root = x_root;
  event->crossing.y_root = y_root;
874 875
  event->crossing.mode = mode;
  event->crossing.detail = detail;
876 877
  event->crossing.state = get_keyboard_modifiers_from_ns_event (nsevent) |
                         _gdk_quartz_events_get_current_mouse_modifiers ();
878

879 880
  gdk_event_set_device (event, gdk_seat_get_pointer (seat));
  gdk_event_set_seat (event, seat);
881

882 883 884
  /* FIXME: Focus and button state? */
}

885 886 887 888 889 890 891 892 893 894 895 896 897
/* fill_pinch_event handles the conversion from the two OSX gesture events
   NSEventTypeMagnfiy and NSEventTypeRotate to the GDK_TOUCHPAD_PINCH event.
   The normal behavior of the OSX events is that they produce as sequence of
     1 x NSEventPhaseBegan,
     n x NSEventPhaseChanged,
     1 x NSEventPhaseEnded
   This can happen for both the Magnify and the Rotate events independently.
   As both events are summarized in one GDK_TOUCHPAD_PINCH event sequence, a
   little state machine handles the case of two NSEventPhaseBegan events in
   a sequence, e.g. Magnify(Began), Magnify(Changed)..., Rotate(Began)...
   such that PINCH(STARTED), PINCH(UPDATE).... will not show a second
   PINCH(STARTED) event.
*/
898
#ifdef AVAILABLE_MAC_OS_X_VERSION_10_8_AND_LATER
899 900 901 902 903 904 905 906 907 908 909 910 911 912
static void
fill_pinch_event (GdkWindow *window,
                  GdkEvent  *event,
                  NSEvent   *nsevent,
                  gint       x,
                  gint       y,
                  gint       x_root,
                  gint       y_root)
{
  static double last_scale = 1.0;
  static enum {
    FP_STATE_IDLE,
    FP_STATE_UPDATE
  } last_state = FP_STATE_IDLE;
913
  GdkSeat *seat = gdk_display_get_default_seat (_gdk_display);
914 915 916 917 918 919 920 921 922 923 924 925

  event->any.type = GDK_TOUCHPAD_PINCH;
  event->touchpad_pinch.window = window;
  event->touchpad_pinch.time = get_time_from_ns_event (nsevent);
  event->touchpad_pinch.x = x;
  event->touchpad_pinch.y = y;
  event->touchpad_pinch.x_root = x_root;
  event->touchpad_pinch.y_root = y_root;
  event->touchpad_pinch.state = get_keyboard_modifiers_from_ns_event (nsevent);
  event->touchpad_pinch.n_fingers = 2;
  event->touchpad_pinch.dx = 0.0;
  event->touchpad_pinch.dy = 0.0;
926
  gdk_event_set_device (event, gdk_seat_get_pointer (seat));
927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991

  switch ([nsevent phase])
    {
    case NSEventPhaseBegan:
      switch (last_state)
        {
        case FP_STATE_IDLE:
          event->touchpad_pinch.phase = GDK_TOUCHPAD_GESTURE_PHASE_BEGIN;
          last_state = FP_STATE_UPDATE;
          last_scale = 1.0;
          break;
        case FP_STATE_UPDATE:
          /* We have already received a PhaseBegan event but no PhaseEnded
             event. This can happen, e.g. Magnify(Began), Magnify(Change)...
             Rotate(Began), Rotate (Change),...., Magnify(End) Rotate(End)
          */
          event->touchpad_pinch.phase = GDK_TOUCHPAD_GESTURE_PHASE_UPDATE;
          break;
        }
      break;
    case NSEventPhaseChanged:
      event->touchpad_pinch.phase = GDK_TOUCHPAD_GESTURE_PHASE_UPDATE;
      break;
    case NSEventPhaseEnded:
      event->touchpad_pinch.phase = GDK_TOUCHPAD_GESTURE_PHASE_END;
      switch (last_state)
        {
        case FP_STATE_IDLE:
          /* We are idle but have received a second PhaseEnded event.
             This can happen because we have Magnify and Rotate OSX
             event sequences. We just send a second end GDK_PHASE_END.
          */
          break;
        case FP_STATE_UPDATE:
          last_state = FP_STATE_IDLE;
          break;
        }
      break;
    case NSEventPhaseCancelled:
      event->touchpad_pinch.phase = GDK_TOUCHPAD_GESTURE_PHASE_CANCEL;
      last_state = FP_STATE_IDLE;
      break;
    case NSEventPhaseMayBegin:
    case NSEventPhaseStationary:
      event->touchpad_pinch.phase = GDK_TOUCHPAD_GESTURE_PHASE_CANCEL;
      break;
    default:
      g_assert_not_reached ();
      break;
    }

  switch ([nsevent type])
    {
    case NSEventTypeMagnify:
      last_scale *= [nsevent magnification] + 1.0;
      event->touchpad_pinch.angle_delta = 0.0;
      break;
    case NSEventTypeRotate:
      event->touchpad_pinch.angle_delta = - [nsevent rotation] * G_PI / 180.0;
      break;
    default:
      g_assert_not_reached ();
    }
  event->touchpad_pinch.scale = last_scale;
}
992
#endif /* OSX Version >= 10.8 */
993

994 995 996 997 998
static void
fill_button_event (GdkWindow *window,
                   GdkEvent  *event,
                   NSEvent   *nsevent,
                   gint       x,
999 1000 1001
                   gint       y,
                   gint       x_root,
                   gint       y_root)
Anders Carlsson's avatar
Anders Carlsson committed
1002 1003
{
  GdkEventType type;
1004
  gint state;
1005
  GdkSeat *seat = gdk_display_get_default_seat (_gdk_display);
1006

1007 1008
  state = get_keyboard_modifiers_from_ns_event (nsevent) |
         _gdk_quartz_events_get_current_mouse_modifiers ();
Anders Carlsson's avatar
Anders Carlsson committed
1009 1010 1011

  switch ([nsevent type])
    {
1012 1013 1014
    case GDK_QUARTZ_LEFT_MOUSE_DOWN:
    case GDK_QUARTZ_RIGHT_MOUSE_DOWN:
    case GDK_QUARTZ_OTHER_MOUSE_DOWN:
Anders Carlsson's avatar
Anders Carlsson committed
1015
      type = GDK_BUTTON_PRESS;
1016
      state &= ~get_mouse_button_modifiers_from_ns_event (nsevent);
Anders Carlsson's avatar
Anders Carlsson committed
1017
      break;
1018

1019 1020 1021
    case GDK_QUARTZ_LEFT_MOUSE_UP:
    case GDK_QUARTZ_RIGHT_MOUSE_UP:
    case GDK_QUARTZ_OTHER_MOUSE_UP:
Anders Carlsson's avatar
Anders Carlsson committed
1022
      type = GDK_BUTTON_RELEASE;
1023
      state |= get_mouse_button_modifiers_from_ns_event (nsevent);
Anders Carlsson's avatar
Anders Carlsson committed
1024
      break;
1025

Anders Carlsson's avatar
Anders Carlsson committed
1026 1027 1028
    default:
      g_assert_not_reached ();
    }
1029

1030
  event->any.type = type;
Anders Carlsson's avatar
Anders Carlsson committed
1031
  event->button.window = window;
1032
  event->button.time = get_time_from_ns_event (nsevent);
Anders Carlsson's avatar
Anders Carlsson committed
1033 1034
  event->button.x = x;
  event->button.y = y;
1035 1036
  event->button.x_root = x_root;
  event->button.y_root = y_root;
Anders Carlsson's avatar
Anders Carlsson committed
1037
  /* FIXME event->axes */
1038
  event->button.state = state;
1039
  event->button.button = get_mouse_button_from_ns_event (nsevent);
1040 1041 1042

  event->button.device = gdk_seat_get_pointer (seat);
  gdk_event_set_seat (event, seat);
Anders Carlsson's avatar
Anders Carlsson committed
1043 1044
}

1045 1046 1047 1048 1049
static void
fill_motion_event (GdkWindow *window,
                   GdkEvent  *event,
                   NSEvent   *nsevent,
                   gint       x,
1050 1051 1052
                   gint       y,
                   gint       x_root,
                   gint       y_root)
Anders Carlsson's avatar
Anders Carlsson committed
1053
{
1054
  GdkSeat *seat = gdk_display_get_default_seat (_gdk_display);
1055

1056
  event->any.type = GDK_MOTION_NOTIFY;
Anders Carlsson's avatar
Anders Carlsson committed
1057
  event->motion.window = window;
1058
  event->motion.time = get_time_from_ns_event (nsevent);
Anders Carlsson's avatar
Anders Carlsson committed
1059 1060
  event->motion.x = x;
  event->motion.y = y;
1061 1062
  event->motion.x_root = x_root;
  event->motion.y_root = y_root;
Anders Carlsson's avatar
Anders Carlsson committed
1063
  /* FIXME event->axes */
1064 1065
  event->motion.state = get_keyboard_modifiers_from_ns_event (nsevent) |
                        _gdk_quartz_events_get_current_mouse_modifiers ();
Anders Carlsson's avatar
Anders Carlsson committed
1066
  event->motion.is_hint = FALSE;
1067 1068
  event->motion.device = gdk_seat_get_pointer (seat);
  gdk_event_set_seat (event, seat);
Anders Carlsson's avatar
Anders Carlsson committed
1069 1070
}

1071 1072 1073 1074
static void
fill_scroll_event (GdkWindow          *window,
                   GdkEvent           *event,
                   NSEvent            *nsevent,
1075 1076
                   gint                x,
                   gint                y,
1077 1078
                   gint                x_root,
                   gint                y_root,
1079 1080
                   gdouble             delta_x,
                   gdouble             delta_y,
1081
                   GdkScrollDirection  direction)
Anders Carlsson's avatar
Anders Carlsson committed
1082
{
1083
  GdkSeat *seat = gdk_display_get_default_seat (_gdk_display);
1084
  NSPoint point;
1085 1086 1087

  point = [nsevent locationInWindow];

1088
  event->any.type = GDK_SCROLL;
Anders Carlsson's avatar
Anders Carlsson committed
1089
  event->scroll.window = window;
1090
  event->scroll.time = get_time_from_ns_event (nsevent);
1091 1092
  event->scroll.x = x;
  event->scroll.y = y;
1093 1094
  event->scroll.x_root = x_root;
  event->scroll.y_root = y_root;
1095
  event->scroll.state = get_keyboard_modifiers_from_ns_event (nsevent);
Anders Carlsson's avatar
Anders Carlsson committed
1096
  event->scroll.direction = direction;
1097
  event->scroll.device = gdk_seat_get_pointer (seat);
1098 1099
  event->scroll.delta_x = delta_x;
  event->scroll.delta_y = delta_y;
1100
  gdk_event_set_seat (event, seat);
Anders Carlsson's avatar
Anders Carlsson committed
1101 1102
}

1103 1104 1105 1106 1107
static void
fill_key_event (GdkWindow    *window,
                GdkEvent     *event,
                NSEvent      *nsevent,
                GdkEventType  type)
Anders Carlsson's avatar
Anders Carlsson committed
1108
{
1109
  GdkEventPrivate *priv;
1110
  GdkSeat *seat = gdk_display_get_default_seat (_gdk_display);
1111 1112
  gchar buf[7];
  gunichar c = 0;
Anders Carlsson's avatar
Anders Carlsson committed
1113

1114 1115 1116
  priv = (GdkEventPrivate *) event;
  priv->windowing_data = [nsevent retain];

1117
  event->any.type = type;
Anders Carlsson's avatar
Anders Carlsson committed
1118
  event->key.window = window;
1119 1120
  event->key.time = get_time_from_ns_event (nsevent);
  event->key.state = get_keyboard_modifiers_from_ns_event (nsevent);
Anders Carlsson's avatar
Anders Carlsson committed
1121
  event->key.hardware_keycode = [nsevent keyCode];
1122
  gdk_event_set_scancode (event, [nsevent keyCode]);
1123
  event->key.group = ([nsevent modifierFlags] & GDK_QUARTZ_ALTERNATE_KEY_MASK) ? 1 : 0;
1124
  event->key.keyval = GDK_KEY_VoidSymbol;
1125

1126 1127 1128
  gdk_event_set_device (event, gdk_seat_get_keyboard (seat));
  gdk_event_set_seat (event, seat);

1129
  gdk_keymap_translate_keyboard_state (gdk_keymap_get_for_display (_gdk_display),
Anders Carlsson's avatar
Anders Carlsson committed
1130
				       event->key.hardware_keycode,
1131
				       event->key.state,
Anders Carlsson's avatar
Anders Carlsson committed
1132 1133 1134 1135
				       event->key.group,
				       &event->key.keyval,
				       NULL, NULL, NULL);

1136
  event->key.is_modifier = _gdk_quartz_keys_is_modifier (event->key.hardware_keycode);
1137

1138 1139 1140 1141 1142 1143 1144 1145 1146 1147
  /* If the key press is a modifier, the state should include the mask
   * for that modifier but only for releases, not presses. This
   * matches the X11 backend behavior.
   */
  if (event->key.is_modifier)
    {
      int mask = 0;

      switch (event->key.keyval)
        {
1148 1149
        case GDK_KEY_Meta_R:
        case GDK_KEY_Meta_L:
1150
          mask = GDK_MOD2_MASK;
1151
          break;
1152 1153
        case GDK_KEY_Shift_R:
        case GDK_KEY_Shift_L:
1154 1155
          mask = GDK_SHIFT_MASK;
          break;
1156
        case GDK_KEY_Caps_Lock:
1157 1158
          mask = GDK_LOCK_MASK;
          break;
1159 1160
        case GDK_KEY_Alt_R:
        case GDK_KEY_Alt_L:
1161
          mask = GDK_MOD1_MASK;
1162
          break;
1163 1164
        case GDK_KEY_Control_R:
        case GDK_KEY_Control_L:
1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176
          mask = GDK_CONTROL_MASK;
          break;
        default:
          mask = 0;
        }

      if (type == GDK_KEY_PRESS)
        event->key.state &= ~mask;
      else if (type == GDK_KEY_RELEASE)
        event->key.state |= mask;
    }

1177
  event->key.state |= _gdk_quartz_events_get_current_mouse_modifiers ();
1178

1179 1180 1181 1182 1183 1184 1185
  /* The X11 backend adds the first virtual modifier MOD2..MOD5 are
   * mapped to. Since we only have one virtual modifier in the quartz
   * backend, calling the standard function will do.
   */
  gdk_keymap_add_virtual_modifiers (gdk_keymap_get_for_display (_gdk_display),
                                    &event->key.state);

1186 1187 1188
  event->key.string = NULL;

  /* Fill in ->string since apps depend on it, taken from the x11 backend. */
1189
  if (event->key.keyval != GDK_KEY_VoidSymbol)
1190 1191
    c = gdk_keyval_to_unicode (event->key.keyval);

1192
  if (c)
1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
    {
      gsize bytes_written;
      gint len;

      len = g_unichar_to_utf8 (c, buf);
      buf[len] = '\0';
      
      event->key.string = g_locale_from_utf8 (buf, len,
					      NULL, &bytes_written,
					      NULL);
      if (event->key.string)
	event->key.length = bytes_written;
    }
1206
  else if (event->key.keyval == GDK_KEY_Escape)
1207 1208 1209 1210
    {
      event->key.length = 1;
      event->key.string = g_strdup ("\033");
    }
1211 1212
  else if (event->key.keyval == GDK_KEY_Return ||
	  event->key.keyval == GDK_KEY_KP_Enter)
1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223
    {
      event->key.length = 1;
      event->key.string = g_strdup ("\r");
    }

  if (!event->key.string)
    {
      event->key.length = 0;
      event->key.string = g_strdup ("");
    }

1224 1225 1226 1227 1228 1229
  GDK_NOTE(EVENTS,
    g_message ("key %s:\t\twindow: %p  key: %12s  %d",
	  type == GDK_KEY_PRESS ? "press" : "release",
	  event->key.window,
	  event->key.keyval ? gdk_keyval_name (event->key.keyval) : "(none)",
	  event->key.keyval));
Anders Carlsson's avatar
Anders Carlsson committed
1230 1231
}

1232 1233 1234 1235 1236
static gboolean
synthesize_crossing_event (GdkWindow *window,
                           GdkEvent  *event,
                           NSEvent   *nsevent,
                           gint       x,
1237 1238 1239
                           gint       y,
                           gint       x_root,
                           gint       y_root)
1240 1241 1242
{
  switch ([nsevent type])
    {
1243
    case GDK_QUARTZ_MOUSE_ENTERED:
1244 1245 1246
      /* Enter events are considered always to be from another toplevel
       * window, this shouldn't negatively affect any app or gtk code,
       * and is the only way to make GtkMenu work. EEK EEK EEK.
1247
       */
1248
      if (!(window->event_mask & GDK_ENTER_NOTIFY_MASK))
1249 1250 1251 1252 1253 1254 1255
        return FALSE;

      fill_crossing_event (window, event, nsevent,
                           x, y,
                           x_root, y_root,
                           GDK_ENTER_NOTIFY,
                           GDK_CROSSING_NORMAL,
1256
                           GDK_NOTIFY_NONLINEAR);
1257 1258
      return TRUE;

1259
    case GDK_QUARTZ_MOUSE_EXITED:
1260
      /* See above */
1261
      if (!(window->event_mask & GDK_LEAVE_NOTIFY_MASK))
1262 1263 1264 1265 1266 1267 1268
        return FALSE;

      fill_crossing_event (window, event, nsevent,
                           x, y,
                           x_root, y_root,
                           GDK_LEAVE_NOTIFY,
                           GDK_CROSSING_NORMAL,
1269
                           GDK_NOTIFY_NONLINEAR);
1270 1271 1272 1273 1274 1275 1276 1277 1278
      return TRUE;

    default:
      break;
    }

  return FALSE;
}

1279 1280 1281 1282
void
_gdk_quartz_synthesize_null_key_event (GdkWindow *window)
{
  GdkEvent *event;
1283
  GdkSeat *seat = gdk_display_get_default_seat (_gdk_display);
1284 1285 1286 1287 1288 1289 1290 1291

  event = gdk_event_new (GDK_KEY_PRESS);
  event->any.type = GDK_KEY_PRESS;
  event->key.window = window;
  event->key.state = 0;
  event->key.hardware_keycode = 0;
  event->key.group = 0;
  event->key.keyval = GDK_KEY_VoidSymbol;
1292 1293 1294

  gdk_event_set_device (event, gdk_seat_get_keyboard (seat));
  gdk_event_set_seat (event, seat);
1295 1296 1297
  append_event(event, FALSE);
}

1298 1299 1300
GdkModifierType
_gdk_quartz_events_get_current_keyboard_modifiers (void)
{
1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322
  if (gdk_quartz_osx_version () >= GDK_OSX_SNOW_LEOPARD)
    {
      return get_keyboard_modifiers_from_ns_flags ([NSClassFromString(@"NSEvent") modifierFlags]);
    }
  else
    {
      guint carbon_modifiers = GetCurrentKeyModifiers ();
      GdkModifierType modifiers = 0;

      if (carbon_modifiers & alphaLock)
        modifiers |= GDK_LOCK_MASK;
      if (carbon_modifiers & shiftKey)
        modifiers |= GDK_SHIFT_MASK;
      if (carbon_modifiers & controlKey)
        modifiers |= GDK_CONTROL_MASK;
      if (carbon_modifiers & optionKey)
        modifiers |= GDK_MOD1_MASK;
      if (carbon_modifiers & cmdKey)
        modifiers |= GDK_MOD2_MASK;

      return modifiers;
    }
1323 1324 1325 1326 1327
}

GdkModifierType
_gdk_quartz_events_get_current_mouse_modifiers (void)
{
1328 1329 1330 1331 1332 1333 1334 1335
  if (gdk_quartz_osx_version () >= GDK_OSX_SNOW_LEOPARD)
    {
      return get_mouse_button_modifiers_from_ns_buttons ([NSClassFromString(@"NSEvent") pressedMouseButtons]);
    }
  else
    {
      return get_mouse_button_modifiers_from_ns_buttons (GetCurrentButtonState ());
    }
1336 1337
}

1338 1339 1340 1341 1342 1343 1344
/* Detect window resizing */

static gboolean
test_resize (NSEvent *event, GdkWindow *toplevel, gint x, gint y)
{
  GdkWindowImplQuartz *toplevel_impl;
  gboolean lion;
1345

1346
  /* Resizing from the resize indicator only begins if an GDK_QUARTZ_LEFT_MOUSE_BUTTON
1347
   * event is received in the resizing area.
1348 1349 1350
   */
  toplevel_impl = (GdkWindowImplQuartz *)toplevel->impl;
  if ([toplevel_impl->toplevel showsResizeIndicator])
1351
  if ([event type] == GDK_QUARTZ_LEFT_MOUSE_DOWN &&
1352
      [toplevel_impl->toplevel showsResizeIndicator])
1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366
    {
      NSRect frame;

      /* If the resize indicator is visible and the event
       * is in the lower right 15x15 corner, we leave these
       * events to Cocoa as to be handled as resize events.
       * Applications may have widgets in this area.  These
       * will most likely be larger than 15x15 and for
       * scroll bars there are also other means to move
       * the scroll bar.  Since the resize indicator is
       * the only way of resizing windows on Mac OS, it
       * is too important to not make functional.
       */
      frame = [toplevel_impl->view bounds];
1367 1368 1369 1370 1371
      if (x > frame.size.width - GRIP_WIDTH &&
          x < frame.size.width &&
          y > frame.size.height - GRIP_HEIGHT &&
          y < frame.size.height)
        return TRUE;
1372
     }
1373

1374 1375 1376 1377 1378 1379
  /* If we're on Lion and within 5 pixels of an edge,
   * then assume that the user wants to resize, and
   * return NULL to let Quartz get on with it. We check
   * the selector isRestorable to see if we're on 10.7.
   * This extra check is in case the user starts
   * dragging before GDK recognizes the grab.
1380 1381 1382 1383 1384 1385 1386
   *
   * We perform this check for a button press of all buttons, because we
   * do receive, for instance, a right mouse down event for a GDK window
   * for x-coordinate range [-3, 0], but we do not want to forward this
   * into GDK. Forwarding such events into GDK will confuse the pointer
   * window finding code, because there are no GdkWindows present in
   * the range [-3, 0].
1387
   */
1388
  lion = gdk_quartz_osx_version () >= GDK_OSX_LION;
1389
  if (lion &&
1390 1391 1392
      ([event type] == GDK_QUARTZ_LEFT_MOUSE_DOWN ||
       [event type] == GDK_QUARTZ_RIGHT_MOUSE_DOWN ||
       [event type] == GDK_QUARTZ_OTHER_MOUSE_DOWN))
1393 1394 1395 1396 1397 1398
    {
      if (x < GDK_LION_RESIZE ||
          x > toplevel->width - GDK_LION_RESIZE ||
          y > toplevel->height - GDK_LION_RESIZE)
        return TRUE;
    }
1399

1400 1401 1402
  return FALSE;
}

1403 1404 1405 1406 1407 1408 1409 1410
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
#define GDK_QUARTZ_APP_KIT_DEFINED NSAppKitDefined
#define GDK_QUARTZ_APPLICATION_DEACTIVATED NSApplicationDeactivatedEventType
#else
#define GDK_QUARTZ_APP_KIT_DEFINED NSEventTypeAppKitDefined
#define GDK_QUARTZ_APPLICATION_DEACTIVATED NSEventSubtypeApplicationDeactivated
#endif

Anders Carlsson's avatar
Anders Carlsson committed
1411
static gboolean
1412 1413
gdk_event_translate (GdkEvent *event,
                     NSEvent  *nsevent)
Anders Carlsson's avatar
Anders Carlsson committed
1414
{
1415
  NSEventType event_type;
1416
  NSWindow *nswindow;
Anders Carlsson's avatar
Anders Carlsson committed
1417 1418
  GdkWindow *window;
  int x, y;
1419
  int x_root, y_root;
1420
  gboolean return_val;
Anders Carlsson's avatar
Anders Carlsson committed
1421

1422 1423 1424
  /* There is no support for real desktop wide grabs, so we break
   * grabs when the application loses focus (gets deactivated).
   */
1425
  event_type = [nsevent type];
1426
  if (event_type == GDK_QUARTZ_APP_KIT_DEFINED)
Anders Carlsson's avatar
Anders Carlsson committed
1427
    {
1428
      if ([nsevent subtype] ==  GDK_QUARTZ_APPLICATION_DEACTIVATED)
1429
        _gdk_quartz_events_break_all_grabs (get_time_from_ns_event (nsevent));
1430 1431 1432 1433 1434

      /* This could potentially be used to break grabs when clicking
       * on the title. The subtype 20 is undocumented so it's probably
       * not a good idea: else if (subtype == 20) break_all_grabs ();
       */
1435 1436 1437

      /* Leave all AppKit events to AppKit. */
      return FALSE;
1438
    }
Anders Carlsson's avatar
Anders Carlsson committed
1439

1440 1441
  if (_gdk_default_filters)
    {
1442 1443
      /* Apply global filters */
      GdkFilterReturn result;
1444

1445
      result = gdk_event_apply_filters (nsevent, event, &_gdk_default_filters);
1446 1447 1448 1449 1450
      if (result != GDK_FILTER_CONTINUE)
        {
          return_val = (result == GDK_FILTER_TRANSLATE) ? TRUE : FALSE;
          goto done;
        }
1451 1452
    }

1453 1454
  nswindow = [nsevent window];

1455 1456
  /* Ignore events for windows not created by GDK. */
  if (nswindow && ![[nswindow contentView] isKindOfClass:[GdkQuartzView class]])
1457 1458
    return FALSE;

1459 1460 1461 1462 1463
  /* Ignore events for ones with no windows */
  if (!nswindow)
    {
      GdkWindow *toplevel = NULL;

1464
      if (event_type == GDK_QUARTZ_MOUSE_MOVED)
1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481
        {
          /* Motion events received after clicking the menu bar do not have the
           * window field set.  Instead of giving up on the event immediately,
           * we first check whether this event is within our window bounds.
           */
          NSPoint screen_point = [NSEvent mouseLocation];
          gint x_tmp, y_tmp;

          toplevel = find_toplevel_under_pointer (_gdk_display,
                                                  screen_point,
                                                  &x_tmp, &y_tmp);
        }