gdkwindow-quartz.c 79.3 KB
Newer Older
Anders Carlsson's avatar
Anders Carlsson committed
1 2 3
/* gdkwindow-quartz.c
 *
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4
 * Copyright (C) 2005-2007 Imendio AB
Anders Carlsson's avatar
Anders Carlsson committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *
 * 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
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

22
#include "config.h"
23
#include <Carbon/Carbon.h>
Anders Carlsson's avatar
Anders Carlsson committed
24 25

#include "gdk.h"
26
#include "gdkdeviceprivate.h"
27
#include "gdkwindowimpl.h"
Anders Carlsson's avatar
Anders Carlsson committed
28
#include "gdkprivate-quartz.h"
29
#include "gdkscreen-quartz.h"
30
#include "gdkinputprivate.h"
Anders Carlsson's avatar
Anders Carlsson committed
31

32 33 34
#include <sys/time.h>
#include <cairo-quartz.h>

Anders Carlsson's avatar
Anders Carlsson committed
35
static gpointer parent_class;
36
static gpointer root_window_parent_class;
Anders Carlsson's avatar
Anders Carlsson committed
37

38 39
static GSList   *update_nswindows;
static gboolean  in_process_all_updates = FALSE;
40 41

static GSList *main_window_stack;
42

43 44
void _gdk_quartz_window_flush (GdkWindowImplQuartz *window_impl);

45 46 47 48 49 50 51 52 53 54
#define FULLSCREEN_DATA "fullscreen-data"

typedef struct
{
  gint            x, y;
  gint            width, height;
  GdkWMDecoration decor;
} FullscreenSavedGeometry;


55
static void update_toplevel_order (void);
56 57 58
static void clear_toplevel_order  (void);

static FullscreenSavedGeometry *get_fullscreen_geometry (GdkWindow *window);
59

60 61 62 63
#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)
64

65 66 67
NSView *
gdk_quartz_window_get_nsview (GdkWindow *window)
{
68 69 70
  if (GDK_WINDOW_DESTROYED (window))
    return NULL;

71
  return ((GdkWindowImplQuartz *)window->impl)->view;
72 73
}

74 75 76 77 78 79
NSWindow *
gdk_quartz_window_get_nswindow (GdkWindow *window)
{
  if (GDK_WINDOW_DESTROYED (window))
    return NULL;

80
  return ((GdkWindowImplQuartz *)window->impl)->toplevel;
81 82
}

Sven Herzberg's avatar
Sven Herzberg committed
83
static CGContextRef
84 85
gdk_window_impl_quartz_get_context (GdkWindowImplQuartz *window_impl,
				    gboolean             antialias)
Sven Herzberg's avatar
Sven Herzberg committed
86 87 88
{
  CGContextRef cg_context;

89
  if (GDK_WINDOW_DESTROYED (window_impl->wrapper))
Sven Herzberg's avatar
Sven Herzberg committed
90 91 92 93 94 95
    return NULL;

  /* Lock focus when not called as part of a drawRect call. This
   * is needed when called from outside "real" expose events, for
   * example for synthesized expose events when realizing windows
   * and for widgets that send fake expose events like the arrow
96
   * buttons in spinbuttons or the position marker in rulers.
Sven Herzberg's avatar
Sven Herzberg committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
   */
  if (window_impl->in_paint_rect_count == 0)
    {
      if (![window_impl->view lockFocusIfCanDraw])
        return NULL;
    }

  cg_context = [[NSGraphicsContext currentContext] graphicsPort];
  CGContextSaveGState (cg_context);
  CGContextSetAllowsAntialiasing (cg_context, antialias);

  /* We'll emulate the clipping caused by double buffering here */
  if (window_impl->begin_paint_count != 0)
    {
      CGRect rect;
      CGRect *cg_rects;
      gint n_rects, i;

Benjamin Otte's avatar
Benjamin Otte committed
115
      n_rects = cairo_region_num_rectangles (window_impl->paint_clip_region);
Sven Herzberg's avatar
Sven Herzberg committed
116 117

      if (n_rects == 1)
Benjamin Otte's avatar
Benjamin Otte committed
118
	cg_rects = &rect;
Sven Herzberg's avatar
Sven Herzberg committed
119
      else
Benjamin Otte's avatar
Benjamin Otte committed
120
	cg_rects = g_new (CGRect, n_rects);
Sven Herzberg's avatar
Sven Herzberg committed
121 122

      for (i = 0; i < n_rects; i++)
Benjamin Otte's avatar
Benjamin Otte committed
123 124 125 126 127 128 129 130 131
	{
          cairo_rectangle_int_t cairo_rect;
          cairo_region_get_rectangle (window_impl->paint_clip_region,
                                      i, &cairo_rect);
	  cg_rects[i].origin.x = cairo_rect.x;
	  cg_rects[i].origin.y = cairo_rect.y;
	  cg_rects[i].size.width = cairo_rect.width;
	  cg_rects[i].size.height = cairo_rect.height;
	}
Sven Herzberg's avatar
Sven Herzberg committed
132

133
      CGContextClipToRects (cg_context, cg_rects, n_rects);
Sven Herzberg's avatar
Sven Herzberg committed
134 135 136 137 138 139 140 141

      if (cg_rects != &rect)
        g_free (cg_rects);
    }

  return cg_context;
}

142
static void
143 144
gdk_window_impl_quartz_release_context (GdkWindowImplQuartz *window_impl,
                                        CGContextRef         cg_context)
145 146 147 148
{
  CGContextRestoreGState (cg_context);
  CGContextSetAllowsAntialiasing (cg_context, TRUE);

149
  /* See comment in gdk_quartz_window_get_context(). */
150 151
  if (window_impl->in_paint_rect_count == 0)
    {
152
      _gdk_quartz_window_flush (window_impl);
153 154 155 156
      [window_impl->view unlockFocus];
    }
}

157 158 159
static void
check_grab_unmap (GdkWindow *window)
{
160
  GList *list, *l;
161
  GdkDisplay *display = gdk_window_get_display (window);
162
  GdkDeviceManager *device_manager;
163

164 165 166 167
  device_manager = gdk_display_get_device_manager (display);
  list = gdk_device_manager_list_devices (device_manager,
                                          GDK_DEVICE_TYPE_FLOATING);
  for (l = list; l; l = l->next)
168
    {
169
      _gdk_display_end_device_grab (display, l->data, 0, window, TRUE);
170
    }
171 172

  g_list_free (list);
173 174 175 176 177
}

static void
check_grab_destroy (GdkWindow *window)
{
178
  GList *list, *l;
179
  GdkDisplay *display = gdk_window_get_display (window);
180
  GdkDeviceManager *device_manager;
181 182

  /* Make sure there is no lasting grab in this native window */
183 184 185 186 187
  device_manager = gdk_display_get_device_manager (display);
  list = gdk_device_manager_list_devices (device_manager,
                                          GDK_DEVICE_TYPE_MASTER);

  for (l = list; l; l = l->next)
188
    {
189 190 191 192 193 194 195 196 197
      GdkDeviceGrabInfo *grab;

      grab = _gdk_display_get_last_device_grab (display, l->data);
      if (grab && grab->native_window == window)
        {
          /* Serials are always 0 in quartz, but for clarity: */
          grab->serial_end = grab->serial_start;
          grab->implicit_ungrab = TRUE;
        }
198 199
    }

200
  g_list_free (list);
201 202
}

Anders Carlsson's avatar
Anders Carlsson committed
203 204 205 206 207
static void
gdk_window_impl_quartz_finalize (GObject *object)
{
  GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (object);

208
  check_grab_destroy (GDK_WINDOW_IMPL_QUARTZ (object)->wrapper);
209

210
  if (impl->paint_clip_region)
Benjamin Otte's avatar
Benjamin Otte committed
211
    cairo_region_destroy (impl->paint_clip_region);
212

213 214 215
  if (impl->transient_for)
    g_object_unref (impl->transient_for);

Anders Carlsson's avatar
Anders Carlsson committed
216 217 218
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
/* Help preventing "beam sync penalty" where CG makes all graphics code
 * block until the next vsync if we try to flush (including call display on
 * a view) too often. We do this by limiting the manual flushing done
 * outside of expose calls to less than some frequency when measured over
 * the last 4 flushes. This is a bit arbitray, but seems to make it possible
 * for some quick manual flushes (such as gtkruler or gimp's marching ants)
 * without hitting the max flush frequency.
 *
 * If drawable NULL, no flushing is done, only registering that a flush was
 * done externally.
 */
void
_gdk_quartz_window_flush (GdkWindowImplQuartz *window_impl)
{
  static struct timeval prev_tv;
  static gint intervals[4];
  static gint index;
  struct timeval tv;
  gint ms;

  gettimeofday (&tv, NULL);
  ms = (tv.tv_sec - prev_tv.tv_sec) * 1000 + (tv.tv_usec - prev_tv.tv_usec) / 1000;
  intervals[index++ % 4] = ms;

  if (window_impl)
    {
      ms = intervals[0] + intervals[1] + intervals[2] + intervals[3];

      /* ~25Hz on average. */
      if (ms > 4*40)
        {
          if (window_impl)
            [window_impl->toplevel flushWindow];

          prev_tv = tv;
        }
    }
  else
    prev_tv = tv;
}

static cairo_user_data_key_t gdk_quartz_cairo_key;

typedef struct {
  GdkWindowImplQuartz  *window_impl;
  CGContextRef  cg_context;
} GdkQuartzCairoSurfaceData;

Anders Carlsson's avatar
Anders Carlsson committed
267
static void
268
gdk_quartz_cairo_surface_destroy (void *data)
Anders Carlsson's avatar
Anders Carlsson committed
269
{
270
  GdkQuartzCairoSurfaceData *surface_data = data;
Anders Carlsson's avatar
Anders Carlsson committed
271

272
  surface_data->window_impl->cairo_surface = NULL;
Anders Carlsson's avatar
Anders Carlsson committed
273

274 275
  gdk_quartz_window_release_context (surface_data->window_impl,
                                     surface_data->cg_context);
276

277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
  g_free (surface_data);
}

static cairo_surface_t *
gdk_quartz_create_cairo_surface (GdkWindowImplQuartz *impl,
				 int                  width,
				 int                  height)
{
  CGContextRef cg_context;
  GdkQuartzCairoSurfaceData *surface_data;
  cairo_surface_t *surface;

  cg_context = gdk_quartz_window_get_context (impl, TRUE);

  if (!cg_context)
    return NULL;

  surface_data = g_new (GdkQuartzCairoSurfaceData, 1);
  surface_data->window_impl = impl;
  surface_data->cg_context = cg_context;

  surface = cairo_quartz_surface_create_for_cg_context (cg_context,
                                                        width, height);

  cairo_surface_set_user_data (surface, &gdk_quartz_cairo_key,
                               surface_data,
                               gdk_quartz_cairo_surface_destroy);

  return surface;
}

static cairo_surface_t *
gdk_quartz_ref_cairo_surface (GdkWindow *window)
{
  GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);

  if (GDK_WINDOW_DESTROYED (window))
    return NULL;

  if (!impl->cairo_surface)
    {
      impl->cairo_surface = 
          gdk_quartz_create_cairo_surface (impl,
                                           gdk_window_get_width (impl->wrapper),
                                           gdk_window_get_height (impl->wrapper));
    }
  else
    cairo_surface_reference (impl->cairo_surface);

  return impl->cairo_surface;
Anders Carlsson's avatar
Anders Carlsson committed
327 328 329 330 331
}

static void
gdk_window_impl_quartz_init (GdkWindowImplQuartz *impl)
{
332
  impl->type_hint = GDK_WINDOW_TYPE_HINT_NORMAL;
Anders Carlsson's avatar
Anders Carlsson committed
333 334
}

335
static void
336
gdk_window_impl_quartz_begin_paint_region (GdkPaintable    *paintable,
337
                                           GdkWindow       *window,
338
					   const cairo_region_t *region)
339 340
{
  GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (paintable);
341
  cairo_region_t *clipped_and_offset_region;
342
  cairo_t *cr;
343

Benjamin Otte's avatar
Benjamin Otte committed
344
  clipped_and_offset_region = cairo_region_copy (region);
345

Benjamin Otte's avatar
Benjamin Otte committed
346
  cairo_region_intersect (clipped_and_offset_region,
347
                        window->clip_region_with_children);
Benjamin Otte's avatar
Benjamin Otte committed
348
  cairo_region_translate (clipped_and_offset_region,
349
                     window->abs_x, window->abs_y);
350 351

  if (impl->begin_paint_count == 0)
352
    impl->paint_clip_region = cairo_region_reference (clipped_and_offset_region);
353
  else
Benjamin Otte's avatar
Benjamin Otte committed
354
    cairo_region_union (impl->paint_clip_region, clipped_and_offset_region);
355

356
  impl->begin_paint_count++;
357

358
  if (cairo_region_is_empty (clipped_and_offset_region))
359
    goto done;
360

361
  cr = gdk_cairo_create (window);
362

363
  cairo_translate (cr, -window->abs_x, -window->abs_y);
364 365 366

  gdk_cairo_region (cr, clipped_and_offset_region);
  cairo_clip (cr);
367

368
  while (window->background == NULL && window->parent)
369
    {
370 371
      cairo_translate (cr, -window->x, window->y);
      window = window->parent;
372
    }
373
  
374 375
  if (window->background)
    cairo_set_source (cr, window->background);
376
  else
377
    cairo_set_source_rgba (cr, 0, 0, 0, 0);
378

379 380 381 382 383 384 385
  /* Can use cairo_paint() here, we clipped above */
  cairo_paint (cr);

  cairo_destroy (cr);

done:
  cairo_region_destroy (clipped_and_offset_region);
386 387 388 389 390 391 392
}

static void
gdk_window_impl_quartz_end_paint (GdkPaintable *paintable)
{
  GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (paintable);

393
  impl->begin_paint_count--;
394 395 396

  if (impl->begin_paint_count == 0)
    {
Benjamin Otte's avatar
Benjamin Otte committed
397
      cairo_region_destroy (impl->paint_clip_region);
398 399
      impl->paint_clip_region = NULL;
    }
400 401
}

402
void
Benjamin Otte's avatar
Benjamin Otte committed
403
_gdk_quartz_window_set_needs_display_in_region (GdkWindow    *window,
404
                                                cairo_region_t    *region)
405 406
{
  GdkWindowImplQuartz *impl;
Benjamin Otte's avatar
Benjamin Otte committed
407
  int i, n_rects;
408

409
  impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
410 411

  if (!impl->needs_display_region)
Benjamin Otte's avatar
Benjamin Otte committed
412
    impl->needs_display_region = cairo_region_create ();
413

Benjamin Otte's avatar
Benjamin Otte committed
414
  cairo_region_union (impl->needs_display_region, region);
415

Benjamin Otte's avatar
Benjamin Otte committed
416 417 418 419 420 421 422 423
  n_rects = cairo_region_num_rectangles (region);
  for (i = 0; i < n_rects; i++)
    {
      cairo_rectangle_int_t rect;
      cairo_region_get_rectangle (region, i, &rect);
      [impl->view setNeedsDisplayInRect:NSMakeRect (rect.x, rect.y,
                                                    rect.width, rect.height)];
    }
424 425
}

426 427
void
_gdk_windowing_window_process_updates_recurse (GdkWindow *window,
428
                                               cairo_region_t *region)
429
{
430 431 432 433
  /* Make sure to only flush each toplevel at most once if we're called
   * from process_all_updates.
   */
  if (in_process_all_updates)
434
    {
435 436
      GdkWindow *toplevel;

437 438
      toplevel = gdk_window_get_effective_toplevel (window);
      if (toplevel && WINDOW_IS_TOPLEVEL (toplevel))
439
        {
440
          GdkWindowImplQuartz *toplevel_impl;
441 442
          NSWindow *nswindow;

443
          toplevel_impl = (GdkWindowImplQuartz *)toplevel->impl;
444
          nswindow = toplevel_impl->toplevel;
445

446 447 448
          /* In theory, we could skip the flush disabling, since we only
           * have one NSView.
           */
449 450
          if (nswindow && ![nswindow isFlushWindowDisabled]) 
            {
451
              [nswindow retain];
452
              [nswindow disableFlushWindow];
453
              update_nswindows = g_slist_prepend (update_nswindows, nswindow);
454
            }
455
        }
456 457
    }

458 459 460 461
  if (WINDOW_IS_TOPLEVEL (window))
    _gdk_quartz_window_set_needs_display_in_region (window, region);
  else
    _gdk_window_process_updates_recurse (window, region);
462

463 464 465 466
  /* NOTE: I'm not sure if we should displayIfNeeded here. It slows down a
   * lot (since it triggers the beam syncing) and things seem to work
   * without it.
   */
467 468
}

469 470
void
_gdk_windowing_before_process_all_updates (void)
471
{
472
  in_process_all_updates = TRUE;
473 474

  NSDisableScreenUpdates ();
475 476
}

477 478
void
_gdk_windowing_after_process_all_updates (void)
479
{
480 481
  GSList *old_update_nswindows = update_nswindows;
  GSList *tmp_list = update_nswindows;
482

483
  update_nswindows = NULL;
484

485
  while (tmp_list)
486
    {
487 488
      NSWindow *nswindow = tmp_list->data;

489 490
      [[nswindow contentView] displayIfNeeded];

491
      _gdk_quartz_window_flush (NULL);
492

493 494 495
      [nswindow enableFlushWindow];
      [nswindow flushWindow];
      [nswindow release];
496

497
      tmp_list = tmp_list->next;
498
    }
499

500
  g_slist_free (old_update_nswindows);
501

502
  in_process_all_updates = FALSE;
503 504

  NSEnableScreenUpdates ();
505 506 507 508 509 510 511 512 513
}

static void
gdk_window_impl_quartz_paintable_init (GdkPaintableIface *iface)
{
  iface->begin_paint_region = gdk_window_impl_quartz_begin_paint_region;
  iface->end_paint = gdk_window_impl_quartz_end_paint;
}

Anders Carlsson's avatar
Anders Carlsson committed
514 515 516 517 518 519 520 521 522 523 524 525
static const gchar *
get_default_title (void)
{
  const char *title;

  title = g_get_application_name ();
  if (!title)
    title = g_get_prgname ();

  return title;
}

526 527 528 529 530 531 532 533
static void
get_ancestor_coordinates_from_child (GdkWindow *child_window,
				     gint       child_x,
				     gint       child_y,
				     GdkWindow *ancestor_window, 
				     gint      *ancestor_x, 
				     gint      *ancestor_y)
{
534
  while (child_window != ancestor_window)
535
    {
536 537
      child_x += child_window->x;
      child_y += child_window->y;
538

539
      child_window = child_window->parent;
540 541 542 543 544 545 546
    }

  *ancestor_x = child_x;
  *ancestor_y = child_y;
}

void
547
_gdk_quartz_window_debug_highlight (GdkWindow *window, gint number)
548 549
{
  gint x, y;
550
  gint gx, gy;
551 552
  GdkWindow *toplevel;
  gint tx, ty;
553 554
  static NSWindow *debug_window[10];
  static NSRect old_rect[10];
555
  NSRect rect;
556 557 558
  NSColor *color;

  g_return_if_fail (number >= 0 && number <= 9);
559 560 561 562 563

  if (window == _gdk_root)
    return;

  if (window == NULL)
564 565 566 567 568 569 570
    {
      if (debug_window[number])
        [debug_window[number] close];
      debug_window[number] = NULL;

      return;
    }
571 572 573 574 575 576 577 578

  toplevel = gdk_window_get_toplevel (window);
  get_ancestor_coordinates_from_child (window, 0, 0, toplevel, &x, &y);

  gdk_window_get_origin (toplevel, &tx, &ty);
  x += tx;
  y += ty;

579
  _gdk_quartz_window_gdk_xy_to_xy (x, y + window->height,
580 581
                                   &gx, &gy);

582
  rect = NSMakeRect (gx, gy, window->width, window->height);
583

584 585 586 587
  if (debug_window[number] && NSEqualRects (rect, old_rect[number]))
    return;

  old_rect[number] = rect;
588

589 590
  if (debug_window[number])
    [debug_window[number] close];
591

592 593 594 595
  debug_window[number] = [[NSWindow alloc] initWithContentRect:rect
                                                     styleMask:NSBorderlessWindowMask
			                               backing:NSBackingStoreBuffered
			                                 defer:NO];
596

597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
  switch (number)
    {
    case 0:
      color = [NSColor redColor];
      break;
    case 1:
      color = [NSColor blueColor];
      break;
    case 2:
      color = [NSColor greenColor];
      break;
    case 3:
      color = [NSColor yellowColor];
      break;
    case 4:
      color = [NSColor brownColor];
      break;
    case 5:
      color = [NSColor purpleColor];
      break;
    default:
      color = [NSColor blackColor];
      break;
    }
621

622 623 624 625 626 627
  [debug_window[number] setBackgroundColor:color];
  [debug_window[number] setAlphaValue:0.4];
  [debug_window[number] setOpaque:NO];
  [debug_window[number] setReleasedWhenClosed:YES];
  [debug_window[number] setIgnoresMouseEvents:YES];
  [debug_window[number] setLevel:NSFloatingWindowLevel];
628

629
  [debug_window[number] orderFront:nil];
630 631
}

632 633 634 635 636 637 638 639 640 641 642 643
gboolean
_gdk_quartz_window_is_ancestor (GdkWindow *ancestor,
                                GdkWindow *window)
{
  if (ancestor == NULL || window == NULL)
    return FALSE;

  return (gdk_window_get_parent (window) == ancestor ||
          _gdk_quartz_window_is_ancestor (ancestor, 
                                          gdk_window_get_parent (window)));
}

644

645
/* See notes on top of gdkscreen-quartz.c */
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
void
_gdk_quartz_window_gdk_xy_to_xy (gint  gdk_x,
                                 gint  gdk_y,
                                 gint *ns_x,
                                 gint *ns_y)
{
  GdkScreenQuartz *screen_quartz = GDK_SCREEN_QUARTZ (_gdk_screen);

  if (ns_y)
    *ns_y = screen_quartz->height - gdk_y + screen_quartz->min_y;

  if (ns_x)
    *ns_x = gdk_x + screen_quartz->min_x;
}

void
_gdk_quartz_window_xy_to_gdk_xy (gint  ns_x,
                                 gint  ns_y,
                                 gint *gdk_x,
                                 gint *gdk_y)
Anders Carlsson's avatar
Anders Carlsson committed
666
{
667
  GdkScreenQuartz *screen_quartz = GDK_SCREEN_QUARTZ (_gdk_screen);
Anders Carlsson's avatar
Anders Carlsson committed
668

669 670
  if (gdk_y)
    *gdk_y = screen_quartz->height - ns_y + screen_quartz->min_y;
671

672 673 674
  if (gdk_x)
    *gdk_x = ns_x - screen_quartz->min_x;
}
675

676 677 678 679 680 681 682
void
_gdk_quartz_window_nspoint_to_gdk_xy (NSPoint  point,
                                      gint    *x,
                                      gint    *y)
{
  _gdk_quartz_window_xy_to_gdk_xy (point.x, point.y,
                                   x, y);
Anders Carlsson's avatar
Anders Carlsson committed
683 684
}

685 686 687 688 689
static GdkWindow *
find_child_window_helper (GdkWindow *window,
			  gint       x,
			  gint       y,
			  gint       x_offset,
690 691
			  gint       y_offset,
                          gboolean   get_toplevel)
692
{
693
  GdkWindowImplQuartz *impl;
694 695
  GList *l;

696
  impl = GDK_WINDOW_IMPL_QUARTZ (window->impl);
697 698 699 700 701

  if (window == _gdk_root)
    update_toplevel_order ();

  for (l = impl->sorted_children; l; l = l->next)
702
    {
703 704
      GdkWindow *child = l->data;
      GdkWindowImplQuartz *child_impl = GDK_WINDOW_IMPL_QUARTZ (child->impl);
705 706
      int temp_x, temp_y;

707
      if (!GDK_WINDOW_IS_MAPPED (child))
708 709
	continue;

710 711
      temp_x = x_offset + child->x;
      temp_y = y_offset + child->y;
712 713 714 715 716 717 718 719 720 721 722

      /* Special-case the root window. We have to include the title
       * bar in the checks, otherwise the window below the title bar
       * will be found i.e. events punch through. (If we can find a
       * better way to deal with the events in gdkevents-quartz, this
       * might not be needed.)
       */
      if (window == _gdk_root)
        {
          NSRect frame = NSMakeRect (0, 0, 100, 100);
          NSRect content;
723
          NSUInteger mask;
724 725 726 727 728 729 730 731 732 733 734
          int titlebar_height;

          mask = [child_impl->toplevel styleMask];

          /* Get the title bar height. */
          content = [NSWindow contentRectForFrameRect:frame
                                            styleMask:mask];
          titlebar_height = frame.size.height - content.size.height;

          if (titlebar_height > 0 &&
              x >= temp_x && y >= temp_y - titlebar_height &&
735
              x < temp_x + child->width && y < temp_y)
736 737 738 739 740 741 742 743
            {
              /* The root means "unknown" i.e. a window not managed by
               * GDK.
               */
              return (GdkWindow *)_gdk_root;
            }
        }

744 745
      if ((!get_toplevel || (get_toplevel && window == _gdk_root)) &&
          x >= temp_x && y >= temp_y &&
746
	  x < temp_x + child->width && y < temp_y + child->height)
747 748 749 750
	{
	  /* Look for child windows. */
	  return find_child_window_helper (l->data,
					   x, y,
751 752
					   temp_x, temp_y,
                                           get_toplevel);
753 754 755 756 757 758 759
	}
    }
  
  return window;
}

/* Given a GdkWindow and coordinates relative to it, returns the
760 761
 * innermost subwindow that contains the point. If the coordinates are
 * outside the passed in window, NULL is returned.
762 763 764 765
 */
GdkWindow *
_gdk_quartz_window_find_child (GdkWindow *window,
			       gint       x,
766 767
			       gint       y,
                               gboolean   get_toplevel)
768
{
769
  if (x >= 0 && y >= 0 && x < window->width && y < window->height)
770
    return find_child_window_helper (window, x, y, 0, 0, get_toplevel);
771 772 773 774

  return NULL;
}

775

776 777 778 779 780
void
_gdk_quartz_window_did_become_main (GdkWindow *window)
{
  main_window_stack = g_slist_remove (main_window_stack, window);

781
  if (window->window_type != GDK_WINDOW_TEMP)
782
    main_window_stack = g_slist_prepend (main_window_stack, window);
783 784

  clear_toplevel_order ();
785 786 787 788 789 790 791 792 793 794 795 796 797
}

void
_gdk_quartz_window_did_resign_main (GdkWindow *window)
{
  GdkWindow *new_window = NULL;

  if (main_window_stack)
    new_window = main_window_stack->data;
  else
    {
      GList *toplevels;

798
      toplevels = gdk_screen_get_toplevel_windows (gdk_screen_get_default ());
799 800 801 802 803 804 805 806
      if (toplevels)
        new_window = toplevels->data;
      g_list_free (toplevels);
    }

  if (new_window &&
      new_window != window &&
      GDK_WINDOW_IS_MAPPED (new_window) &&
807
      WINDOW_IS_TOPLEVEL (new_window))
808
    {
809
      GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (new_window->impl);
810 811 812

      [impl->toplevel makeKeyAndOrderFront:impl->toplevel];
    }
813 814

  clear_toplevel_order ();
815 816
}

817
static NSScreen *
Kristian Rietveld's avatar
Kristian Rietveld committed
818
get_nsscreen_for_point (gint x, gint y)
819 820 821
{
  int i;
  NSArray *screens;
822
  NSScreen *screen = NULL;
823 824 825 826 827 828 829 830 831

  GDK_QUARTZ_ALLOC_POOL;

  screens = [NSScreen screens];

  for (i = 0; i < [screens count]; i++)
    {
      NSRect rect = [[screens objectAtIndex:i] frame];

Kristian Rietveld's avatar
Kristian Rietveld committed
832 833
      if (x >= rect.origin.x && x <= rect.origin.x + rect.size.width &&
          y >= rect.origin.y && y <= rect.origin.y + rect.size.height)
834 835 836 837
        {
          screen = [screens objectAtIndex:i];
          break;
        }
838 839 840 841
    }

  GDK_QUARTZ_RELEASE_POOL;

842
  return screen;
843 844
}

845 846 847 848 849 850 851
void
_gdk_window_impl_new (GdkWindow     *window,
		      GdkWindow     *real_parent,
		      GdkScreen     *screen,
		      GdkEventMask   event_mask,
		      GdkWindowAttr *attributes,
		      gint           attributes_mask)
Anders Carlsson's avatar
Anders Carlsson committed
852 853
{
  GdkWindowImplQuartz *impl;
854
  GdkWindowImplQuartz *parent_impl;
Anders Carlsson's avatar
Anders Carlsson committed
855

856
  GDK_QUARTZ_ALLOC_POOL;
Anders Carlsson's avatar
Anders Carlsson committed
857

858
  impl = g_object_new (GDK_TYPE_WINDOW_IMPL_QUARTZ, NULL);
859 860
  window->impl = GDK_WINDOW_IMPL (impl);
  impl->wrapper = window;
Anders Carlsson's avatar
Anders Carlsson committed
861

862
  parent_impl = GDK_WINDOW_IMPL_QUARTZ (window->parent->impl);
Anders Carlsson's avatar
Anders Carlsson committed
863

864
  switch (window->window_type)
Anders Carlsson's avatar
Anders Carlsson committed
865 866 867
    {
    case GDK_WINDOW_TOPLEVEL:
    case GDK_WINDOW_TEMP:
868
      if (GDK_WINDOW_TYPE (window->parent) != GDK_WINDOW_ROOT)
Anders Carlsson's avatar
Anders Carlsson committed
869
	{
870
	  /* The common code warns for this case */
871
          parent_impl = GDK_WINDOW_IMPL_QUARTZ (_gdk_root->impl);
Anders Carlsson's avatar
Anders Carlsson committed
872 873 874
	}
    }

875
  /* Maintain the z-ordered list of children. */
876
  if (window->parent != _gdk_root)
877 878 879
    parent_impl->sorted_children = g_list_prepend (parent_impl->sorted_children, window);
  else
    clear_toplevel_order ();
Anders Carlsson's avatar
Anders Carlsson committed
880 881 882 883 884

  gdk_window_set_cursor (window, ((attributes_mask & GDK_WA_CURSOR) ?
				  (attributes->cursor) :
				  NULL));

885 886
  impl->view = NULL;

887
  switch (attributes->window_type)
Anders Carlsson's avatar
Anders Carlsson committed
888 889 890 891
    {
    case GDK_WINDOW_TOPLEVEL:
    case GDK_WINDOW_TEMP:
      {
892 893
        NSScreen *screen;
        NSRect screen_rect;
894
        NSRect content_rect;
895
        NSUInteger style_mask;
896
        int nx, ny;
897 898
        const char *title;

899 900 901 902 903
        /* initWithContentRect will place on the mainScreen by default.
         * We want to select the screen to place on ourselves.  We need
         * to find the screen the window will be on and correct the
         * content_rect coordinates to be relative to that screen.
         */
904
        _gdk_quartz_window_gdk_xy_to_xy (window->x, window->y, &nx, &ny);
905

Kristian Rietveld's avatar
Kristian Rietveld committed
906
        screen = get_nsscreen_for_point (nx, ny);
907 908 909 910
        screen_rect = [screen frame];
        nx -= screen_rect.origin.x;
        ny -= screen_rect.origin.y;

911 912 913
        content_rect = NSMakeRect (nx, ny - window->height,
                                   window->width,
                                   window->height);
Anders Carlsson's avatar
Anders Carlsson committed
914

915 916
        if (attributes->window_type == GDK_WINDOW_TEMP ||
            attributes->type_hint == GDK_WINDOW_TYPE_HINT_SPLASHSCREEN)
917 918
          {
            style_mask = NSBorderlessWindowMask;
919 920 921
          }
        else
          {
922 923 924 925 926
            style_mask = (NSTitledWindowMask |
                          NSClosableWindowMask |
                          NSMiniaturizableWindowMask |
                          NSResizableWindowMask);
          }
Anders Carlsson's avatar
Anders Carlsson committed
927 928

	impl->toplevel = [[GdkQuartzWindow alloc] initWithContentRect:content_rect 
Anders Carlsson's avatar
Anders Carlsson committed
929
			                                    styleMask:style_mask
Michael Natterer's avatar
Michael Natterer committed
930
			                                      backing:NSBackingStoreBuffered
931 932
			                                        defer:NO
                                                                screen:screen];
Michael Natterer's avatar
Michael Natterer committed
933

Anders Carlsson's avatar
Anders Carlsson committed
934 935 936 937 938 939
	if (attributes_mask & GDK_WA_TITLE)
	  title = attributes->title;
	else
	  title = get_default_title ();

	gdk_window_set_title (window, title);
940
  
941
	if (gdk_window_get_visual (window) == gdk_screen_get_rgba_visual (_gdk_screen))
Anders Carlsson's avatar
Anders Carlsson committed
942 943 944 945 946
	  {
	    [impl->toplevel setOpaque:NO];
	    [impl->toplevel setBackgroundColor:[NSColor clearColor]];
	  }

947 948 949
        content_rect.origin.x = 0;
        content_rect.origin.y = 0;

Anders Carlsson's avatar
Anders Carlsson committed
950 951 952
	impl->view = [[GdkQuartzView alloc] initWithFrame:content_rect];
	[impl->view setGdkWindow:window];
	[impl->toplevel setContentView:impl->view];
953
	[impl->view release];
Anders Carlsson's avatar
Anders Carlsson committed
954 955
      }
      break;