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

static gpointer parent_class;
33
static gpointer root_window_parent_class;
Anders Carlsson's avatar
Anders Carlsson committed
34

35
36
static GSList   *update_nswindows;
static gboolean  in_process_all_updates = FALSE;
37
38

static GSList *main_window_stack;
39

40
41
42
43
44
45
46
47
48
49
#define FULLSCREEN_DATA "fullscreen-data"

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


50
static void update_toplevel_order (void);
51
52
53
static void clear_toplevel_order  (void);

static FullscreenSavedGeometry *get_fullscreen_geometry (GdkWindow *window);
54

55
56
57
58
#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)
59

60
61
static void gdk_window_impl_iface_init (GdkWindowImplIface *iface);

62
63
64
65
66
NSView *
gdk_quartz_window_get_nsview (GdkWindow *window)
{
  GdkWindowObject *private = (GdkWindowObject *)window;

67
68
69
  if (GDK_WINDOW_DESTROYED (window))
    return NULL;

70
71
72
  return ((GdkWindowImplQuartz *)private->impl)->view;
}

73
74
75
76
77
78
79
80
81
82
83
NSWindow *
gdk_quartz_window_get_nswindow (GdkWindow *window)
{
  GdkWindowObject *private = (GdkWindowObject *)window;

  if (GDK_WINDOW_DESTROYED (window))
    return NULL;

  return ((GdkWindowImplQuartz *)private->impl)->toplevel;
}

Sven Herzberg's avatar
Sven Herzberg committed
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
static CGContextRef
gdk_window_impl_quartz_get_context (GdkDrawable *drawable,
				    gboolean     antialias)
{
  GdkDrawableImplQuartz *drawable_impl = GDK_DRAWABLE_IMPL_QUARTZ (drawable);
  GdkWindowImplQuartz *window_impl = GDK_WINDOW_IMPL_QUARTZ (drawable);
  CGContextRef cg_context;

  if (GDK_WINDOW_DESTROYED (drawable_impl->wrapper))
    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
99
   * buttons in spinbuttons or the position marker in rulers.
Sven Herzberg's avatar
Sven Herzberg committed
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
   */
  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
118
      n_rects = cairo_region_num_rectangles (window_impl->paint_clip_region);
Sven Herzberg's avatar
Sven Herzberg committed
119
120

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

      for (i = 0; i < n_rects; i++)
Benjamin Otte's avatar
Benjamin Otte committed
126
127
128
129
130
131
132
133
134
	{
          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
135

136
      CGContextClipToRects (cg_context, cg_rects, n_rects);
Sven Herzberg's avatar
Sven Herzberg committed
137
138
139
140
141
142
143
144

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

  return cg_context;
}

145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
static void
gdk_window_impl_quartz_release_context (GdkDrawable  *drawable,
                                        CGContextRef  cg_context)
{
  GdkWindowImplQuartz *window_impl = GDK_WINDOW_IMPL_QUARTZ (drawable);

  CGContextRestoreGState (cg_context);
  CGContextSetAllowsAntialiasing (cg_context, TRUE);

  /* See comment in gdk_quartz_drawable_get_context(). */
  if (window_impl->in_paint_rect_count == 0)
    {
      _gdk_quartz_drawable_flush (drawable);
      [window_impl->view unlockFocus];
    }
}

162
163
164
static void
check_grab_unmap (GdkWindow *window)
{
165
  GList *list, *l;
166
  GdkDisplay *display = gdk_window_get_display (window);
167
  GdkDeviceManager *device_manager;
168

169
170
171
172
  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)
173
    {
174
      _gdk_display_end_device_grab (display, l->data, 0, window, TRUE);
175
    }
176
177

  g_list_free (list);
178
179
180
181
182
}

static void
check_grab_destroy (GdkWindow *window)
{
183
  GList *list, *l;
184
  GdkDisplay *display = gdk_window_get_display (window);
185
  GdkDeviceManager *device_manager;
186
187

  /* Make sure there is no lasting grab in this native window */
188
189
190
191
192
  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)
193
    {
194
195
196
197
198
199
200
201
202
      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;
        }
203
204
    }

205
  g_list_free (list);
206
207
}

Anders Carlsson's avatar
Anders Carlsson committed
208
209
210
211
212
static void
gdk_window_impl_quartz_finalize (GObject *object)
{
  GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (object);

213
214
  check_grab_destroy (GDK_DRAWABLE_IMPL_QUARTZ (object)->wrapper);

215
  if (impl->paint_clip_region)
Benjamin Otte's avatar
Benjamin Otte committed
216
    cairo_region_destroy (impl->paint_clip_region);
217

218
219
220
  if (impl->transient_for)
    g_object_unref (impl->transient_for);

Anders Carlsson's avatar
Anders Carlsson committed
221
222
223
224
225
226
  G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
gdk_window_impl_quartz_class_init (GdkWindowImplQuartzClass *klass)
{
227
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
228
  GdkDrawableImplQuartzClass *drawable_quartz_class = GDK_DRAWABLE_IMPL_QUARTZ_CLASS (klass);
Anders Carlsson's avatar
Anders Carlsson committed
229
230
231

  parent_class = g_type_class_peek_parent (klass);

232
233
  object_class->finalize = gdk_window_impl_quartz_finalize;

Sven Herzberg's avatar
Sven Herzberg committed
234
  drawable_quartz_class->get_context = gdk_window_impl_quartz_get_context;
235
  drawable_quartz_class->release_context = gdk_window_impl_quartz_release_context;
Anders Carlsson's avatar
Anders Carlsson committed
236
237
238
239
240
}

static void
gdk_window_impl_quartz_init (GdkWindowImplQuartz *impl)
{
241
  impl->type_hint = GDK_WINDOW_TYPE_HINT_NORMAL;
Anders Carlsson's avatar
Anders Carlsson committed
242
243
}

244
static void
245
gdk_window_impl_quartz_begin_paint_region (GdkPaintable    *paintable,
246
                                           GdkWindow       *window,
247
					   const cairo_region_t *region)
248
249
{
  GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (paintable);
250
  GdkWindowObject *private = (GdkWindowObject*) window;
251
  cairo_region_t *clipped_and_offset_region;
252
  cairo_t *cr;
253

Benjamin Otte's avatar
Benjamin Otte committed
254
  clipped_and_offset_region = cairo_region_copy (region);
255

Benjamin Otte's avatar
Benjamin Otte committed
256
  cairo_region_intersect (clipped_and_offset_region,
257
                        private->clip_region_with_children);
Benjamin Otte's avatar
Benjamin Otte committed
258
  cairo_region_translate (clipped_and_offset_region,
259
                     private->abs_x, private->abs_y);
260
261

  if (impl->begin_paint_count == 0)
262
    impl->paint_clip_region = cairo_region_reference (clipped_and_offset_region);
263
  else
Benjamin Otte's avatar
Benjamin Otte committed
264
    cairo_region_union (impl->paint_clip_region, clipped_and_offset_region);
265

266
  impl->begin_paint_count++;
267

268
  if (cairo_region_is_empty (clipped_and_offset_region))
269
    goto done;
270

271
  cr = gdk_cairo_create (window);
272

273
274
275
276
  cairo_translate (cr, -private->abs_x, -private->abs_y);

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

278
  while (private->background == NULL && private->parent)
279
    {
280
281
      cairo_translate (cr, -private->x, private->y);
      private = private->parent;
282
    }
283
284
285
  
  if (private->background)
    cairo_set_source (cr, private->background);
286
  else
287
    cairo_set_source_rgba (cr, 0, 0, 0, 0);
288

289
290
291
292
293
294
295
  /* Can use cairo_paint() here, we clipped above */
  cairo_paint (cr);

  cairo_destroy (cr);

done:
  cairo_region_destroy (clipped_and_offset_region);
296
297
298
299
300
301
302
}

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

303
  impl->begin_paint_count--;
304
305
306

  if (impl->begin_paint_count == 0)
    {
Benjamin Otte's avatar
Benjamin Otte committed
307
      cairo_region_destroy (impl->paint_clip_region);
308
309
      impl->paint_clip_region = NULL;
    }
310
311
}

312
void
Benjamin Otte's avatar
Benjamin Otte committed
313
_gdk_quartz_window_set_needs_display_in_region (GdkWindow    *window,
314
                                                cairo_region_t    *region)
315
316
317
{
  GdkWindowObject *private;
  GdkWindowImplQuartz *impl;
Benjamin Otte's avatar
Benjamin Otte committed
318
  int i, n_rects;
319
320
321
322
323

  private = GDK_WINDOW_OBJECT (window);
  impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);

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

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

Benjamin Otte's avatar
Benjamin Otte committed
328
329
330
331
332
333
334
335
  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)];
    }
336
337
}

338
339
void
_gdk_windowing_window_process_updates_recurse (GdkWindow *window,
340
                                               cairo_region_t *region)
341
{
342
343
344
345
  /* Make sure to only flush each toplevel at most once if we're called
   * from process_all_updates.
   */
  if (in_process_all_updates)
346
    {
347
348
      GdkWindow *toplevel;

349
350
      toplevel = gdk_window_get_effective_toplevel (window);
      if (toplevel && WINDOW_IS_TOPLEVEL (toplevel))
351
        {
352
353
          GdkWindowObject *toplevel_private;
          GdkWindowImplQuartz *toplevel_impl;
354
355
          NSWindow *nswindow;

356
357
358
          toplevel_private = (GdkWindowObject *)toplevel;
          toplevel_impl = (GdkWindowImplQuartz *)toplevel_private->impl;
          nswindow = toplevel_impl->toplevel;
359

360
361
362
          /* In theory, we could skip the flush disabling, since we only
           * have one NSView.
           */
363
364
          if (nswindow && ![nswindow isFlushWindowDisabled]) 
            {
365
              [nswindow retain];
366
              [nswindow disableFlushWindow];
367
              update_nswindows = g_slist_prepend (update_nswindows, nswindow);
368
            }
369
        }
370
371
    }

372
373
374
375
  if (WINDOW_IS_TOPLEVEL (window))
    _gdk_quartz_window_set_needs_display_in_region (window, region);
  else
    _gdk_window_process_updates_recurse (window, region);
376

377
378
379
380
  /* 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.
   */
381
382
}

383
384
void
_gdk_windowing_before_process_all_updates (void)
385
{
386
  in_process_all_updates = TRUE;
387
388

  NSDisableScreenUpdates ();
389
390
}

391
392
void
_gdk_windowing_after_process_all_updates (void)
393
{
394
395
  GSList *old_update_nswindows = update_nswindows;
  GSList *tmp_list = update_nswindows;
396

397
  update_nswindows = NULL;
398

399
  while (tmp_list)
400
    {
401
402
      NSWindow *nswindow = tmp_list->data;

403
404
      [[nswindow contentView] displayIfNeeded];

405
406
      _gdk_quartz_drawable_flush (NULL);

407
408
409
      [nswindow enableFlushWindow];
      [nswindow flushWindow];
      [nswindow release];
410

411
      tmp_list = tmp_list->next;
412
    }
413

414
  g_slist_free (old_update_nswindows);
415

416
  in_process_all_updates = FALSE;
417
418

  NSEnableScreenUpdates ();
419
420
421
422
423
424
425
426
427
}

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
428
429
430
431
432
433
434
GType
_gdk_window_impl_quartz_get_type (void)
{
  static GType object_type = 0;

  if (!object_type)
    {
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
      const GTypeInfo object_info =
	{
	  sizeof (GdkWindowImplQuartzClass),
	  (GBaseInitFunc) NULL,
	  (GBaseFinalizeFunc) NULL,
	  (GClassInitFunc) gdk_window_impl_quartz_class_init,
	  NULL,           /* class_finalize */
	  NULL,           /* class_data */
	  sizeof (GdkWindowImplQuartz),
	  0,              /* n_preallocs */
	  (GInstanceInitFunc) gdk_window_impl_quartz_init,
	};

      const GInterfaceInfo paintable_info = 
	{
	  (GInterfaceInitFunc) gdk_window_impl_quartz_paintable_init,
	  NULL,
	  NULL
	};
454

455
456
457
458
459
460
461
      const GInterfaceInfo window_impl_info = 
	{
	  (GInterfaceInitFunc) gdk_window_impl_iface_init,
	  NULL,
	  NULL
	};

Anders Carlsson's avatar
Anders Carlsson committed
462
463
464
      object_type = g_type_register_static (GDK_TYPE_DRAWABLE_IMPL_QUARTZ,
                                            "GdkWindowImplQuartz",
                                            &object_info, 0);
465
466
467
      g_type_add_interface_static (object_type,
				   GDK_TYPE_PAINTABLE,
				   &paintable_info);
468
469
470
      g_type_add_interface_static (object_type,
				   GDK_TYPE_WINDOW_IMPL,
				   &window_impl_info);
Anders Carlsson's avatar
Anders Carlsson committed
471
    }
472

Anders Carlsson's avatar
Anders Carlsson committed
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
  return object_type;
}

GType
_gdk_window_impl_get_type (void)
{
  return _gdk_window_impl_quartz_get_type ();
}

static const gchar *
get_default_title (void)
{
  const char *title;

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

  return title;
}

494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
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)
{
  GdkWindowObject *child_private = GDK_WINDOW_OBJECT (child_window);
  GdkWindowObject *ancestor_private = GDK_WINDOW_OBJECT (ancestor_window);

  while (child_private != ancestor_private)
    {
      child_x += child_private->x;
      child_y += child_private->y;

      child_private = child_private->parent;
    }

  *ancestor_x = child_x;
  *ancestor_y = child_y;
}

void
518
_gdk_quartz_window_debug_highlight (GdkWindow *window, gint number)
519
520
521
{
  GdkWindowObject *private = GDK_WINDOW_OBJECT (window);
  gint x, y;
522
  gint gx, gy;
523
524
  GdkWindow *toplevel;
  gint tx, ty;
525
526
  static NSWindow *debug_window[10];
  static NSRect old_rect[10];
527
  NSRect rect;
528
529
530
  NSColor *color;

  g_return_if_fail (number >= 0 && number <= 9);
531
532
533
534
535

  if (window == _gdk_root)
    return;

  if (window == NULL)
536
537
538
539
540
541
542
    {
      if (debug_window[number])
        [debug_window[number] close];
      debug_window[number] = NULL;

      return;
    }
543
544
545
546
547
548
549
550

  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;

551
552
553
554
  _gdk_quartz_window_gdk_xy_to_xy (x, y + private->height,
                                   &gx, &gy);

  rect = NSMakeRect (gx, gy, private->width, private->height);
555

556
557
558
559
  if (debug_window[number] && NSEqualRects (rect, old_rect[number]))
    return;

  old_rect[number] = rect;
560

561
562
  if (debug_window[number])
    [debug_window[number] close];
563

564
565
566
567
  debug_window[number] = [[NSWindow alloc] initWithContentRect:rect
                                                     styleMask:NSBorderlessWindowMask
			                               backing:NSBackingStoreBuffered
			                                 defer:NO];
568

569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
  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;
    }
593

594
595
596
597
598
599
  [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];
600

601
  [debug_window[number] orderFront:nil];
602
603
}

604
605
606
607
608
609
610
611
612
613
614
615
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)));
}

616

617
/* See notes on top of gdkscreen-quartz.c */
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
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
638
{
639
  GdkScreenQuartz *screen_quartz = GDK_SCREEN_QUARTZ (_gdk_screen);
Anders Carlsson's avatar
Anders Carlsson committed
640

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

644
645
646
  if (gdk_x)
    *gdk_x = ns_x - screen_quartz->min_x;
}
647

648
649
650
651
652
653
654
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
655
656
}

657
658
659
660
661
static GdkWindow *
find_child_window_helper (GdkWindow *window,
			  gint       x,
			  gint       y,
			  gint       x_offset,
662
663
			  gint       y_offset,
                          gboolean   get_toplevel)
664
{
665
  GdkWindowImplQuartz *impl;
666
667
  GList *l;

668
669
670
671
672
673
  impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (window)->impl);

  if (window == _gdk_root)
    update_toplevel_order ();

  for (l = impl->sorted_children; l; l = l->next)
674
    {
675
676
      GdkWindowObject *child_private = l->data;
      GdkWindowImplQuartz *child_impl = GDK_WINDOW_IMPL_QUARTZ (child_private->impl);
677
678
      int temp_x, temp_y;

679
      if (!GDK_WINDOW_IS_MAPPED (child_private))
680
681
	continue;

682
683
684
685
686
687
688
689
690
691
692
693
694
      temp_x = x_offset + child_private->x;
      temp_y = y_offset + child_private->y;

      /* 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;
695
          NSUInteger mask;
696
697
698
699
700
701
702
703
704
705
706
          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 &&
707
              x < temp_x + child_private->width && y < temp_y)
708
709
710
711
712
713
714
715
            {
              /* The root means "unknown" i.e. a window not managed by
               * GDK.
               */
              return (GdkWindow *)_gdk_root;
            }
        }

716
717
      if ((!get_toplevel || (get_toplevel && window == _gdk_root)) &&
          x >= temp_x && y >= temp_y &&
718
	  x < temp_x + child_private->width && y < temp_y + child_private->height)
719
720
721
722
	{
	  /* Look for child windows. */
	  return find_child_window_helper (l->data,
					   x, y,
723
724
					   temp_x, temp_y,
                                           get_toplevel);
725
726
727
728
729
730
731
	}
    }
  
  return window;
}

/* Given a GdkWindow and coordinates relative to it, returns the
732
733
 * innermost subwindow that contains the point. If the coordinates are
 * outside the passed in window, NULL is returned.
734
735
736
737
 */
GdkWindow *
_gdk_quartz_window_find_child (GdkWindow *window,
			       gint       x,
738
739
			       gint       y,
                               gboolean   get_toplevel)
740
741
742
{
  GdkWindowObject *private = GDK_WINDOW_OBJECT (window);

743
  if (x >= 0 && y >= 0 && x < private->width && y < private->height)
744
    return find_child_window_helper (window, x, y, 0, 0, get_toplevel);
745
746
747
748

  return NULL;
}

749

750
751
752
753
754
755
void
_gdk_quartz_window_did_become_main (GdkWindow *window)
{
  main_window_stack = g_slist_remove (main_window_stack, window);

  if (GDK_WINDOW_OBJECT (window)->window_type != GDK_WINDOW_TEMP)
756
    main_window_stack = g_slist_prepend (main_window_stack, window);
757
758

  clear_toplevel_order ();
759
760
761
762
763
764
765
766
767
768
769
770
771
}

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;

772
      toplevels = gdk_screen_get_toplevel_windows (gdk_screen_get_default ());
773
774
775
776
777
778
779
780
      if (toplevels)
        new_window = toplevels->data;
      g_list_free (toplevels);
    }

  if (new_window &&
      new_window != window &&
      GDK_WINDOW_IS_MAPPED (new_window) &&
781
      WINDOW_IS_TOPLEVEL (new_window))
782
783
784
785
786
787
    {
      GdkWindowObject *private = (GdkWindowObject *) new_window;
      GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);

      [impl->toplevel makeKeyAndOrderFront:impl->toplevel];
    }
788
789

  clear_toplevel_order ();
790
791
}

792
static NSScreen *
Kristian Rietveld's avatar
Kristian Rietveld committed
793
get_nsscreen_for_point (gint x, gint y)
794
795
796
{
  int i;
  NSArray *screens;
797
  NSScreen *screen = NULL;
798
799
800
801
802
803
804
805
806

  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
807
808
      if (x >= rect.origin.x && x <= rect.origin.x + rect.size.width &&
          y >= rect.origin.y && y <= rect.origin.y + rect.size.height)
809
810
811
812
        {
          screen = [screens objectAtIndex:i];
          break;
        }
813
814
815
816
    }

  GDK_QUARTZ_RELEASE_POOL;

817
  return screen;
818
819
}

820
821
822
823
824
825
826
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
827
828
829
830
{
  GdkWindowObject *private;
  GdkWindowImplQuartz *impl;
  GdkDrawableImplQuartz *draw_impl;
831
  GdkWindowImplQuartz *parent_impl;
Anders Carlsson's avatar
Anders Carlsson committed
832

833
  GDK_QUARTZ_ALLOC_POOL;
Anders Carlsson's avatar
Anders Carlsson committed
834
835

  private = (GdkWindowObject *)window;
836

837
838
839
  impl = g_object_new (_gdk_window_impl_get_type (), NULL);
  private->impl = (GdkDrawable *)impl;
  draw_impl = GDK_DRAWABLE_IMPL_QUARTZ (impl);
Anders Carlsson's avatar
Anders Carlsson committed
840
841
  draw_impl->wrapper = GDK_DRAWABLE (window);

842
  parent_impl = GDK_WINDOW_IMPL_QUARTZ (private->parent->impl);
Anders Carlsson's avatar
Anders Carlsson committed
843
844
845
846
847

  switch (private->window_type)
    {
    case GDK_WINDOW_TOPLEVEL:
    case GDK_WINDOW_TEMP:
848
      if (GDK_WINDOW_TYPE (private->parent) != GDK_WINDOW_ROOT)
Anders Carlsson's avatar
Anders Carlsson committed
849
	{
850
851
	  /* The common code warns for this case */
          parent_impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (_gdk_root)->impl);
Anders Carlsson's avatar
Anders Carlsson committed
852
853
854
	}
    }

855
  /* Maintain the z-ordered list of children. */
856
  if (private->parent != (GdkWindowObject *)_gdk_root)
857
858
859
    parent_impl->sorted_children = g_list_prepend (parent_impl->sorted_children, window);
  else
    clear_toplevel_order ();
Anders Carlsson's avatar
Anders Carlsson committed
860
861
862
863
864

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

865
866
  impl->view = NULL;

867
  switch (attributes->window_type)
Anders Carlsson's avatar
Anders Carlsson committed
868
869
870
871
    {
    case GDK_WINDOW_TOPLEVEL:
    case GDK_WINDOW_TEMP:
      {
872
873
        NSScreen *screen;
        NSRect screen_rect;
874
        NSRect content_rect;
875
        NSUInteger style_mask;
876
        int nx, ny;
877
878
        const char *title;

879
880
881
882
883
884
885
        /* 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.
         */
        _gdk_quartz_window_gdk_xy_to_xy (private->x, private->y, &nx, &ny);

Kristian Rietveld's avatar
Kristian Rietveld committed
886
        screen = get_nsscreen_for_point (nx, ny);
887
888
889
890
891
        screen_rect = [screen frame];
        nx -= screen_rect.origin.x;
        ny -= screen_rect.origin.y;

        content_rect = NSMakeRect (nx, ny - private->height,
892
893
                                   private->width,
                                   private->height);
Anders Carlsson's avatar
Anders Carlsson committed
894

895
896
        if (attributes->window_type == GDK_WINDOW_TEMP ||
            attributes->type_hint == GDK_WINDOW_TYPE_HINT_SPLASHSCREEN)
897
898
          {
            style_mask = NSBorderlessWindowMask;
899
900
901
          }
        else
          {
902
903
904
905
906
            style_mask = (NSTitledWindowMask |
                          NSClosableWindowMask |
                          NSMiniaturizableWindowMask |
                          NSResizableWindowMask);
          }
Anders Carlsson's avatar
Anders Carlsson committed
907
908

	impl->toplevel = [[GdkQuartzWindow alloc] initWithContentRect:content_rect 
Anders Carlsson's avatar
Anders Carlsson committed
909
			                                    styleMask:style_mask
Michael Natterer's avatar
Michael Natterer committed
910
			                                      backing:NSBackingStoreBuffered
911
912
			                                        defer:NO
                                                                screen:screen];
Michael Natterer's avatar
Michael Natterer committed
913

Anders Carlsson's avatar
Anders Carlsson committed
914
915
916
917
918
919
	if (attributes_mask & GDK_WA_TITLE)
	  title = attributes->title;
	else
	  title = get_default_title ();

	gdk_window_set_title (window, title);
920
  
921
	if (gdk_window_get_visual (window) == gdk_screen_get_rgba_visual (_gdk_screen))
Anders Carlsson's avatar
Anders Carlsson committed
922
923
924
925
926
	  {
	    [impl->toplevel setOpaque:NO];
	    [impl->toplevel setBackgroundColor:[NSColor clearColor]];
	  }

927
928
929
        content_rect.origin.x = 0;
        content_rect.origin.y = 0;

Anders Carlsson's avatar
Anders Carlsson committed
930
931
932
	impl->view = [[GdkQuartzView alloc] initWithFrame:content_rect];
	[impl->view setGdkWindow:window];
	[impl->toplevel setContentView:impl->view];
933
	[impl->view release];
Anders Carlsson's avatar
Anders Carlsson committed
934
935
      }
      break;
936

Anders Carlsson's avatar
Anders Carlsson committed
937
938
    case GDK_WINDOW_CHILD:
      {
939
	GdkWindowImplQuartz *parent_impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (private->parent)->impl);
Anders Carlsson's avatar
Anders Carlsson committed
940

941
	if (!private->input_only)
Anders Carlsson's avatar
Anders Carlsson committed
942
	  {
943
944
945
946
	    NSRect frame_rect = NSMakeRect (private->x + private->parent->abs_x,
                                            private->y + private->parent->abs_y,
                                            private->width,
                                            private->height);
Anders Carlsson's avatar
Anders Carlsson committed
947
948
949
950
951
952
953
954
	
	    impl->view = [[GdkQuartzView alloc] initWithFrame:frame_rect];
	    
	    [impl->view setGdkWindow:window];

	    /* GdkWindows should be hidden by default */
	    [impl->view setHidden:YES];
	    [parent_impl->view addSubview:impl->view];
955
	    [impl->view release];
Anders Carlsson's avatar
Anders Carlsson committed
956
957
958
	  }
      }
      break;
959

Anders Carlsson's avatar
Anders Carlsson committed
960
961
962
963
964
965
    default:
      g_assert_not_reached ();
    }

  GDK_QUARTZ_RELEASE_POOL;

Cody Russell's avatar
Cody Russell committed
966
967
  if (attributes_mask & GDK_WA_TYPE_HINT)
    gdk_window_set_type_hint (window, attributes->type_hint);
Anders Carlsson's avatar
Anders Carlsson committed
968
969
}

970
void
971
_gdk_quartz_window_update_position (GdkWindow *window)
972
{
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
  NSRect frame_rect;
  NSRect content_rect;
  GdkWindowObject *private = (GdkWindowObject *)window;
  GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);

  GDK_QUARTZ_ALLOC_POOL;

  frame_rect = [impl->toplevel frame];
  content_rect = [impl->toplevel contentRectForFrameRect:frame_rect];

  _gdk_quartz_window_xy_to_gdk_xy (content_rect.origin.x,
                                   content_rect.origin.y + content_rect.size.height,
                                   &private->x, &private->y);


  GDK_QUARTZ_RELEASE_POOL;
}

void
_gdk_windowing_update_window_sizes (GdkScreen *screen)
{
  GList *windows, *list;
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
  GdkWindowObject *private = (GdkWindowObject *)_gdk_root;

  /* The size of the root window is so that it can contain all
   * monitors attached to this machine.  The monitors are laid out
   * within this root window.  We calculate the size of the root window
   * and the positions of the different monitors in gdkscreen-quartz.c.
   *
   * This data is updated when the monitor configuration is changed.
   */
  private->x = 0;
  private->y = 0;
  private->abs_x = 0;
  private->abs_y = 0;
  private->width = gdk_screen_get_width (screen);
  private->height = gdk_screen_get_height (screen);
1010
1011
1012
1013
1014
1015
1016

  windows = gdk_screen_get_toplevel_windows (screen);

  for (list = windows; list; list = list->next)
    _gdk_quartz_window_update_position (list->data);

  g_list_free (windows);
1017
1018
}

Anders Carlsson's avatar
Anders Carlsson committed
1019
1020
1021
1022
void
_gdk_windowing_window_init (void)
{
  GdkWindowObject *private;
Richard Hult's avatar
Richard Hult committed
1023
  GdkWindowImplQuartz *impl;
1024
  GdkDrawableImplQuartz *drawable_impl;
Anders Carlsson's avatar
Anders Carlsson committed
1025
1026
1027
1028

  g_assert (_gdk_root == NULL);

  _gdk_root = g_object_new (GDK_TYPE_WINDOW, NULL);
Anders Carlsson's avatar
Anders Carlsson committed
1029

1030
  private = (GdkWindowObject *)_gdk_root;
1031
  private->impl = g_object_new (_gdk_root_window_impl_quartz_get_type (), NULL);
1032
  private->impl_window = private;
1033
  private->visual = gdk_screen_get_system_visual (_gdk_screen);
1034

Richard Hult's avatar
Richard Hult committed
1035
  impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (_gdk_root)->impl);
1036

1037
  _gdk_windowing_update_window_sizes (_gdk_screen);
Richard Hult's avatar
Richard Hult committed
1038

Anders Carlsson's avatar
Anders Carlsson committed
1039
  private->state = 0; /* We don't want GDK_WINDOW_STATE_WITHDRAWN here */
Anders Carlsson's avatar
Anders Carlsson committed
1040
1041
  private->window_type = GDK_WINDOW_ROOT;
  private->depth = 24;
1042
  private->viewable = TRUE;
1043
1044
1045
1046

  drawable_impl = GDK_DRAWABLE_IMPL_QUARTZ (private->impl);
  
  drawable_impl->wrapper = GDK_DRAWABLE (private);
Anders Carlsson's avatar
Anders Carlsson committed
1047
1048
}

1049
1050
1051
1052
static void
_gdk_quartz_window_destroy (GdkWindow *window,
                            gboolean   recursing,
                            gboolean   foreign_destroy)
Anders Carlsson's avatar
Anders Carlsson committed
1053
{
1054
1055
1056
1057
1058
1059
1060
  GdkWindowObject *private;
  GdkWindowImplQuartz *impl;
  GdkWindowObject *parent;

  private = GDK_WINDOW_OBJECT (window);
  impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);

1061
  main_window_stack = g_slist_remove (main_window_stack, window);
1062

1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
  g_list_free (impl->sorted_children);
  impl->sorted_children = NULL;

  parent = private->parent;
  if (parent)
    {
      GdkWindowImplQuartz *parent_impl = GDK_WINDOW_IMPL_QUARTZ (parent->impl);

      parent_impl->sorted_children = g_list_remove (parent_impl->sorted_children, window);
    }

1074
  _gdk_quartz_drawable_finish (GDK_DRAWABLE (impl));
Anders Carlsson's avatar
Anders Carlsson committed
1075

1076
1077
  if (!recursing && !foreign_destroy)
    {
1078
1079
      GDK_QUARTZ_ALLOC_POOL;

Anders Carlsson's avatar
Anders Carlsson committed
1080
1081
1082
      if (impl->toplevel)
	[impl->toplevel close];
      else if (impl->view)
1083
	[impl->view removeFromSuperview];
1084
1085

      GDK_QUARTZ_RELEASE_POOL;
Anders Carlsson's avatar
Anders Carlsson committed
1086
1087
1088
    }
}

1089
static cairo_surface_t *
Benjamin Otte's avatar
Benjamin Otte committed
1090
gdk_window_quartz_resize_cairo_surface (GdkDrawable     *drawable,
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
                                        cairo_surface_t *surface,
                                        gint             width,
                                        gint             height)
{
  /* Quartz surfaces cannot be resized */
  cairo_surface_destroy (surface);

  return NULL;
}

Anders Carlsson's avatar
Anders Carlsson committed
1101
1102
1103
void
_gdk_windowing_window_destroy_foreign (GdkWindow *window)
{
1104
  /* Foreign windows aren't supported in OSX. */
Anders Carlsson's avatar
Anders Carlsson committed
1105
1106
}

1107
1108
1109
/* FIXME: This might be possible to simplify with client-side windows. Also
 * note that already_mapped is not used yet, see the x11 backend.
*/
Anders Carlsson's avatar
Anders Carlsson committed
1110
static void
1111
gdk_window_quartz_show (GdkWindow *window, gboolean already_mapped)
Anders Carlsson's avatar
Anders Carlsson committed
1112
{
1113
1114
  GdkWindowObject *private = (GdkWindowObject *)window;
  GdkWindowImplQuartz *impl = GDK_WINDOW_IMPL_QUARTZ (private->impl);
1115
  gboolean focus_on_map;
Anders Carlsson's avatar
Anders Carlsson committed
1116
1117
1118

  GDK_QUARTZ_ALLOC_POOL;

1119
1120
1121
1122
1123
  if (!GDK_WINDOW_IS_MAPPED (window))
    focus_on_map = private->focus_on_map;
  else
    focus_on_map = TRUE;

1124
  if (WINDOW_IS_TOPLEVEL (window) && impl->toplevel)
Anders Carlsson's avatar
Anders Carlsson committed
1125
    {
1126
1127
      gboolean make_key;

1128
      make_key = (private->accept_focus && focus_on_map &&
1129
                  private->window_type != GDK_WINDOW_TEMP);
1130

1131
      [(GdkQuartzWindow*)impl->toplevel showAndMakeKey:make_key];
1132
      clear_toplevel_order ();
1133

1134
      _gdk_quartz_events_send_map_event (window);
Anders Carlsson's avatar
Anders Carlsson committed
1135
1136
1137
1138
1139
1140
    }
  else
    {
      [impl->view setHidden:NO];
    }

1141
1142
  [impl->view setNeedsDisplay:YES];

Anders Carlsson's avatar
Anders Carlsson committed
1143
1144
  gdk_synthesize_window_state (window, GDK_WINDOW_STATE_WITHDRAWN, 0);

1145
1146
1147
1148
1149
1150
  if (private->state & GDK_WINDOW_STATE_MAXIMIZED)
    gdk_window_maximize (window);

  if (private->state & GDK_WINDOW_STATE_ICONIFIED)
    gdk_window_iconify (window);

1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
  if (impl->transient_for && !GDK_WINDOW_DESTROYED (impl->transient_for))
    _gdk_quartz_window_attach_to_parent (window);

  GDK_QUARTZ_RELEASE_POOL;
}

/* Temporarily unsets the parent window, if the window is a
 * transient. 
 */
void
_gdk_quartz_window_detach_from_parent (GdkWindow *window)
{
  GdkWindowImplQuartz *impl;

  g_return_if_fail (GDK_IS_WINDOW (window));

  impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (window)->impl);
  
  g_return_if_fail (impl->toplevel != NULL);

1171
1172
1173
1174
1175
  if (impl->transient_for && !GDK_WINDOW_DESTROYED (impl->transient_for))
    {
      GdkWindowImplQuartz *parent_impl;

      parent_impl = GDK_WINDOW_IMPL_QUARTZ (GDK_WINDOW_OBJECT (impl->transient_for)->impl);