gdkwindow.c 328 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GDK - The GIMP Drawing Kit
2 3
 * Copyright (C) 1995-2007 Peter Mattis, Spencer Kimball,
 * Josh MacDonald, Ryan Lortie
Elliot Lee's avatar
Elliot Lee committed
4 5
 *
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
7 8 9 10 11 12
 * 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
13
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
16
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
17
 */
18

19
/*
20
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
21 22
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
23
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
24 25
 */

26 27
#include "config.h"

28 29
#include <cairo-gobject.h>

30 31
#include "gdkwindow.h"

32
#include "gdkrectangle.h"
33 34
#include "gdkinternals.h"
#include "gdkintl.h"
Matthias Clasen's avatar
Matthias Clasen committed
35 36
#include "gdkscreenprivate.h"
#include "gdkdisplayprivate.h"
37
#include "gdkdeviceprivate.h"
38
#include "gdkvisualprivate.h"
39
#include "gdkmarshalers.h"
Owen W. Taylor's avatar
Owen W. Taylor committed
40
#include "gdkframeclockidle.h"
41
#include "gdkwindowimpl.h"
42
#include "gdkglcontextprivate.h"
43
#include "gdk-private.h"
44

45 46
#include <math.h>

47 48
#include <epoxy/gl.h>

49 50 51
/* for the use of round() */
#include "fallback-c89.c"

52 53 54 55
#ifdef GDK_WINDOWING_WAYLAND
#include "wayland/gdkwayland.h"
#endif

56 57 58
#undef DEBUG_WINDOW_PRINTING


59 60 61 62 63
/**
 * SECTION:windows
 * @Short_description: Onscreen display areas in the target window system
 * @Title: Windows
 *
64
 * A #GdkWindow is a (usually) rectangular region on the screen.
65
 * It’s a low-level object, used to implement high-level objects such as
66
 * #GtkWidget and #GtkWindow on the GTK+ level. A #GtkWindow is a toplevel
William Jon McCann's avatar
William Jon McCann committed
67
 * window, the thing a user might think of as a “window” with a titlebar
Matthias Clasen's avatar
Matthias Clasen committed
68 69
 * and so on; a #GtkWindow may contain many #GdkWindows. For example,
 * each #GtkButton has a #GdkWindow associated with it.
70
 *
71
 * # Composited Windows # {#COMPOSITED-WINDOWS}
72
 *
73
 * Normally, the windowing system takes care of rendering the contents
Matthias Clasen's avatar
Matthias Clasen committed
74 75
 * of a child window onto its parent window. This mechanism can be
 * intercepted by calling gdk_window_set_composited() on the child
76
 * window. For a “composited” window it is the
Matthias Clasen's avatar
Matthias Clasen committed
77
 * responsibility of the application to render the window contents at
78
 * the right spot.
79
 *
80
 * # Offscreen Windows # {#OFFSCREEN-WINDOWS}
Matthias Clasen's avatar
Matthias Clasen committed
81
 *
82
 * Offscreen windows are more general than composited windows, since
Matthias Clasen's avatar
Matthias Clasen committed
83
 * they allow not only to modify the rendering of the child window onto
84
 * its parent, but also to apply coordinate transformations.
Matthias Clasen's avatar
Matthias Clasen committed
85 86 87 88 89 90 91 92 93 94 95
 *
 * To integrate an offscreen window into a window hierarchy, one has
 * to call gdk_offscreen_window_set_embedder() and handle a number of
 * signals. The #GdkWindow::pick-embedded-child signal on the embedder
 * window is used to select an offscreen child at given coordinates,
 * and the #GdkWindow::to-embedder and #GdkWindow::from-embedder signals
 * on the offscreen window are used to translate coordinates between
 * the embedder and the offscreen window.
 *
 * For rendering an offscreen window onto its embedder, the contents
 * of the offscreen window are available as a surface, via
96
 * gdk_offscreen_window_get_surface().
97 98
 */

99

100 101 102 103
/* Historically a GdkWindow always matches a platform native window,
 * be it a toplevel window or a child window. In this setup the
 * GdkWindow (and other GdkDrawables) were platform independent classes,
 * and the actual platform specific implementation was in a delegate
William Jon McCann's avatar
William Jon McCann committed
104
 * object available as “impl” in the window object.
105
 *
106 107 108
 * With the addition of client side windows and offscreen windows this
 * changes a bit. The application-visible GdkWindow object behaves as
 * it did before, but not all such windows now have a corresponding native
William Jon McCann's avatar
William Jon McCann committed
109
 * window. Instead windows that are “client side” are emulated by the gdk
110 111
 * code such that clipping, drawing, moving, events etc work as expected.
 *
William Jon McCann's avatar
William Jon McCann committed
112
 * For GdkWindows that have a native window the “impl” object is the
113 114 115 116 117 118 119
 * same as before. However, for all client side windows the impl object
 * is shared with its parent (i.e. all client windows descendants of one
 * native window has the same impl.
 *
 * Additionally there is a new type of platform independent impl object,
 * GdkOffscreenWindow. All windows of type GDK_WINDOW_OFFSCREEN get an impl
 * of this type (while their children are generally GDK_WINDOW_CHILD virtual
120 121
 * windows). Such windows work by allocating a #cairo_surface_t as the backing
 * store for drawing operations, which is resized with the window.
122
 *
William Jon McCann's avatar
William Jon McCann committed
123 124
 * GdkWindows have a pointer to the “impl window” they are in, i.e.
 * the topmost GdkWindow which have the same “impl” value. This is stored
125 126 127 128 129
 * in impl_window, which is different from the window itself only for client
 * side windows.
 * All GdkWindows (native or not) track the position of the window in the parent
 * (x, y), the size of the window (width, height), the position of the window
 * with respect to the impl window (abs_x, abs_y). We also track the clip
130
 * region of the window wrt parent windows, in window-relative coordinates (clip_region).
131 132 133 134 135 136 137 138 139
 *
 * All toplevel windows are native windows, but also child windows can be
 * native (although not children of offscreens). We always listen to
 * a basic set of events (see get_native_event_mask) for these windows
 * so that we can emulate events for any client side children.
 *
 * For native windows we apply the calculated clip region as a window shape
 * so that eg. client side siblings that overlap the native child properly
 * draws over the native child window.
140
 */
141

142 143 144
/* This adds a local value to the GdkVisibilityState enum */
#define GDK_VISIBILITY_NOT_VIEWABLE 3

145
enum {
Alexander Larsson's avatar
Alexander Larsson committed
146 147 148
  PICK_EMBEDDED_CHILD, /* only called if children are embedded */
  TO_EMBEDDER,
  FROM_EMBEDDER,
149
  CREATE_SURFACE,
150 151 152
  LAST_SIGNAL
};

Cody Russell's avatar
Cody Russell committed
153 154
enum {
  PROP_0,
155
  PROP_CURSOR
Cody Russell's avatar
Cody Russell committed
156 157
};

158
/* Global info */
159

160
static void gdk_window_finalize   (GObject              *object);
Cody Russell's avatar
Cody Russell committed
161 162 163 164 165 166 167 168 169 170

static void gdk_window_set_property (GObject      *object,
                                     guint         prop_id,
                                     const GValue *value,
                                     GParamSpec   *pspec);
static void gdk_window_get_property (GObject      *object,
                                     guint         prop_id,
                                     GValue       *value,
                                     GParamSpec   *pspec);

171
static void gdk_window_clear_backing_region (GdkWindow *window);
172

173
static void recompute_visible_regions   (GdkWindow *private,
174
					 gboolean recalculate_children);
175 176
static void gdk_window_invalidate_in_parent (GdkWindow *private);
static void move_native_children        (GdkWindow *private);
177 178
static void update_cursor               (GdkDisplay *display,
                                         GdkDevice  *device);
179
static void impl_window_add_update_area (GdkWindow *impl_window,
180
					 cairo_region_t *region);
181
static void gdk_window_invalidate_region_full (GdkWindow       *window,
182
					       const cairo_region_t *region,
183
					       gboolean         invalidate_children);
184 185
static void gdk_window_invalidate_rect_full (GdkWindow          *window,
					     const GdkRectangle *rect,
186
					     gboolean            invalidate_children);
187
static cairo_surface_t *gdk_window_ref_impl_surface (GdkWindow *window);
188

189 190
static void gdk_window_set_frame_clock (GdkWindow      *window,
                                        GdkFrameClock  *clock);
Owen W. Taylor's avatar
Owen W. Taylor committed
191

192 193 194 195 196
static void draw_ugly_color (GdkWindow       *window,
                             const cairo_region_t *region,
                             int color);


197 198
static guint signals[LAST_SIGNAL] = { 0 };

199 200
static gpointer parent_class = NULL;

201 202
static const cairo_user_data_key_t gdk_window_cairo_key;

Benjamin Otte's avatar
Benjamin Otte committed
203
G_DEFINE_ABSTRACT_TYPE (GdkWindow, gdk_window, G_TYPE_OBJECT)
204

205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
#ifdef DEBUG_WINDOW_PRINTING
char *
print_region (cairo_region_t *region)
{
  GString *s = g_string_new ("{");
  if (cairo_region_is_empty (region))
    {
      g_string_append (s, "empty");
    }
  else
    {
      int num = cairo_region_num_rectangles (region);
      cairo_rectangle_int_t r;

      if (num == 1)
	{
	  cairo_region_get_rectangle (region, 0, &r);
	  g_string_append_printf (s, "%dx%d @%d,%d", r.width, r.height, r.x, r.y);
	}
      else
	{
226
	  int i;
227 228
	  cairo_region_get_extents (region, &r);
	  g_string_append_printf (s, "extent: %dx%d @%d,%d, details: ", r.width, r.height, r.x, r.y);
229
	  for (i = 0; i < num; i++)
230
	    {
231
              cairo_region_get_rectangle (region, i, &r);
232 233 234 235 236 237 238 239 240 241 242
	      g_string_append_printf (s, "[%dx%d @%d,%d]", r.width, r.height, r.x, r.y);
	      if (i != num -1)
		g_string_append (s, ", ");
	    }
	}
    }
  g_string_append (s, "}");
  return g_string_free (s, FALSE);
}
#endif

243
static void
244
gdk_window_init (GdkWindow *window)
245 246
{
  /* 0-initialization is good for all other fields. */
rhlabs's avatar
rhlabs committed
247

248
  window->window_type = GDK_WINDOW_CHILD;
Elliot Lee's avatar
Elliot Lee committed
249

Havoc Pennington's avatar
Havoc Pennington committed
250
  window->state = GDK_WINDOW_STATE_WITHDRAWN;
251
  window->fullscreen_mode = GDK_FULLSCREEN_ON_CURRENT_MONITOR;
252 253 254
  window->width = 1;
  window->height = 1;
  window->toplevel_window_type = -1;
255 256 257 258 259
  /* starts hidden */
  window->effective_visibility = GDK_VISIBILITY_NOT_VIEWABLE;
  window->visibility = GDK_VISIBILITY_FULLY_OBSCURED;
  /* Default to unobscured since some backends don't send visibility events */
  window->native_visibility = GDK_VISIBILITY_UNOBSCURED;
260 261 262

  window->device_cursor = g_hash_table_new_full (NULL, NULL,
                                                 NULL, g_object_unref);
263
}
264

265
/* Stop and return on the first non-NULL parent */
266
static gboolean
267 268 269 270
accumulate_get_window (GSignalInvocationHint *ihint,
		       GValue		       *return_accu,
		       const GValue	       *handler_return,
		       gpointer               data)
271 272 273
{
  g_value_copy (handler_return, return_accu);
  /* Continue while returning NULL */
274
  return g_value_get_object (handler_return) == NULL;
275 276
}

277 278 279 280 281 282 283 284 285 286 287 288
static gboolean
create_surface_accumulator (GSignalInvocationHint *ihint,
                            GValue                *return_accu,
                            const GValue          *handler_return,
                            gpointer               data)
{
  g_value_copy (handler_return, return_accu);

  /* Stop on the first non-NULL return value */
  return g_value_get_boxed (handler_return) == NULL;
}

289 290
static GQuark quark_pointer_window = 0;

291
static void
292
gdk_window_class_init (GdkWindowClass *klass)
293 294
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
295

296 297
  parent_class = g_type_class_peek_parent (klass);

298
  object_class->finalize = gdk_window_finalize;
Cody Russell's avatar
Cody Russell committed
299 300
  object_class->set_property = gdk_window_set_property;
  object_class->get_property = gdk_window_get_property;
301

302 303
  klass->create_surface = _gdk_offscreen_window_create_surface;

304
  quark_pointer_window = g_quark_from_static_string ("gtk-pointer-window");
305 306


Cody Russell's avatar
Cody Russell committed
307
  /* Properties */
308 309 310 311 312 313 314 315 316

  /**
   * GdkWindow:cursor:
   *
   * The mouse pointer for a #GdkWindow. See gdk_window_set_cursor() and
   * gdk_window_get_cursor() for details.
   *
   * Since: 2.18
   */
Cody Russell's avatar
Cody Russell committed
317 318
  g_object_class_install_property (object_class,
                                   PROP_CURSOR,
Benjamin Otte's avatar
Benjamin Otte committed
319 320 321 322 323
                                   g_param_spec_object ("cursor",
                                                        P_("Cursor"),
                                                        P_("Cursor"),
                                                        GDK_TYPE_CURSOR,
                                                        G_PARAM_READWRITE));
Cody Russell's avatar
Cody Russell committed
324

Matthias Clasen's avatar
Matthias Clasen committed
325 326 327 328 329 330 331 332 333
  /**
   * GdkWindow::pick-embedded-child:
   * @window: the window on which the signal is emitted
   * @x: x coordinate in the window
   * @y: y coordinate in the window
   *
   * The ::pick-embedded-child signal is emitted to find an embedded
   * child at the given position.
   *
334 335
   * Returns: (nullable) (transfer none): the #GdkWindow of the
   *     embedded child at @x, @y, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
336 337 338
   *
   * Since: 2.18
   */
Alexander Larsson's avatar
Alexander Larsson committed
339 340
  signals[PICK_EMBEDDED_CHILD] =
    g_signal_new (g_intern_static_string ("pick-embedded-child"),
341 342
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_LAST,
343
                  G_STRUCT_OFFSET (GdkWindowClass, pick_embedded_child),
344
		  accumulate_get_window, NULL,
Matthias Clasen's avatar
Matthias Clasen committed
345
		  _gdk_marshal_OBJECT__DOUBLE_DOUBLE,
346 347 348 349
		  GDK_TYPE_WINDOW,
		  2,
		  G_TYPE_DOUBLE,
		  G_TYPE_DOUBLE);
Matthias Clasen's avatar
Matthias Clasen committed
350 351 352 353

  /**
   * GdkWindow::to-embedder:
   * @window: the offscreen window on which the signal is emitted
354 355 356
   * @offscreen_x: x coordinate in the offscreen window
   * @offscreen_y: y coordinate in the offscreen window
   * @embedder_x: (out) (type double): return location for the x
357
   *     coordinate in the embedder window
358
   * @embedder_y: (out) (type double): return location for the y
359
   *     coordinate in the embedder window
Matthias Clasen's avatar
Matthias Clasen committed
360 361 362 363
   *
   * The ::to-embedder signal is emitted to translate coordinates
   * in an offscreen window to its embedder.
   *
364
   * See also #GdkWindow::from-embedder.
Matthias Clasen's avatar
Matthias Clasen committed
365
   *
Matthias Clasen's avatar
Matthias Clasen committed
366 367
   * Since: 2.18
   */
Alexander Larsson's avatar
Alexander Larsson committed
368 369
  signals[TO_EMBEDDER] =
    g_signal_new (g_intern_static_string ("to-embedder"),
370 371
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_LAST,
372
                  G_STRUCT_OFFSET (GdkWindowClass, to_embedder),
373
		  NULL, NULL,
Matthias Clasen's avatar
Matthias Clasen committed
374
		  _gdk_marshal_VOID__DOUBLE_DOUBLE_POINTER_POINTER,
375 376 377 378 379 380
		  G_TYPE_NONE,
		  4,
		  G_TYPE_DOUBLE,
		  G_TYPE_DOUBLE,
		  G_TYPE_POINTER,
		  G_TYPE_POINTER);
Matthias Clasen's avatar
Matthias Clasen committed
381 382 383 384

  /**
   * GdkWindow::from-embedder:
   * @window: the offscreen window on which the signal is emitted
385 386 387
   * @embedder_x: x coordinate in the embedder window
   * @embedder_y: y coordinate in the embedder window
   * @offscreen_x: (out) (type double): return location for the x
388
   *     coordinate in the offscreen window
389
   * @offscreen_y: (out) (type double): return location for the y
390
   *     coordinate in the offscreen window
Matthias Clasen's avatar
Matthias Clasen committed
391 392 393 394
   *
   * The ::from-embedder signal is emitted to translate coordinates
   * in the embedder of an offscreen window to the offscreen window.
   *
395
   * See also #GdkWindow::to-embedder.
Matthias Clasen's avatar
Matthias Clasen committed
396
   *
Matthias Clasen's avatar
Matthias Clasen committed
397 398
   * Since: 2.18
   */
Alexander Larsson's avatar
Alexander Larsson committed
399 400
  signals[FROM_EMBEDDER] =
    g_signal_new (g_intern_static_string ("from-embedder"),
401 402
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_LAST,
403
                  G_STRUCT_OFFSET (GdkWindowClass, from_embedder),
404
		  NULL, NULL,
Matthias Clasen's avatar
Matthias Clasen committed
405
		  _gdk_marshal_VOID__DOUBLE_DOUBLE_POINTER_POINTER,
406 407 408 409 410 411
		  G_TYPE_NONE,
		  4,
		  G_TYPE_DOUBLE,
		  G_TYPE_DOUBLE,
		  G_TYPE_POINTER,
		  G_TYPE_POINTER);
412 413 414 415 416 417 418 419

  /**
   * GdkWindow::create-surface:
   * @window: the offscreen window on which the signal is emitted
   * @width: the width of the offscreen surface to create
   * @height: the height of the offscreen surface to create
   *
   * The ::create-surface signal is emitted when an offscreen window
420
   * needs its surface (re)created, which happens either when the
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
   * window is first drawn to, or when the window is being
   * resized. The first signal handler that returns a non-%NULL
   * surface will stop any further signal emission, and its surface
   * will be used.
   *
   * Note that it is not possible to access the window's previous
   * surface from within any callback of this signal. Calling
   * gdk_offscreen_window_get_surface() will lead to a crash.
   *
   * Returns: the newly created #cairo_surface_t for the offscreen window
   *
   * Since: 3.0
   */
  signals[CREATE_SURFACE] =
    g_signal_new (g_intern_static_string ("create-surface"),
                  G_OBJECT_CLASS_TYPE (object_class),
                  G_SIGNAL_RUN_LAST,
438
                  G_STRUCT_OFFSET (GdkWindowClass, create_surface),
439 440 441 442 443 444
                  create_surface_accumulator, NULL,
                  _gdk_marshal_BOXED__INT_INT,
                  CAIRO_GOBJECT_TYPE_SURFACE,
                  2,
                  G_TYPE_INT,
                  G_TYPE_INT);
445
}
446

447 448 449 450 451
static void
device_removed_cb (GdkDeviceManager *device_manager,
                   GdkDevice        *device,
                   GdkWindow        *window)
{
452 453
  window->devices_inside = g_list_remove (window->devices_inside, device);
  g_hash_table_remove (window->device_cursor, device);
454

455 456
  if (window->device_events)
    g_hash_table_remove (window->device_events, device);
457 458
}

459 460 461 462
static void
gdk_window_finalize (GObject *object)
{
  GdkWindow *window = GDK_WINDOW (object);
463 464
  GdkDeviceManager *device_manager;

465
  device_manager = gdk_display_get_device_manager (gdk_window_get_display (window));
466
  g_signal_handlers_disconnect_by_func (device_manager, device_removed_cb, window);
467

468 469 470 471 472 473 474 475 476 477 478 479 480
  if (!GDK_WINDOW_DESTROYED (window))
    {
      if (GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN)
	{
	  g_warning ("losing last reference to undestroyed window\n");
	  _gdk_window_destroy (window, FALSE);
	}
      else
	/* We use TRUE here, to keep us from actually calling
	 * XDestroyWindow() on the window
	 */
	_gdk_window_destroy (window, TRUE);
    }
481

482
  if (window->impl)
483
    {
484 485
      g_object_unref (window->impl);
      window->impl = NULL;
486 487
    }

488
  if (window->impl_window != window)
489
    {
490 491
      g_object_unref (window->impl_window);
      window->impl_window = NULL;
492
    }
493

494 495
  if (window->shape)
    cairo_region_destroy (window->shape);
Alexander Larsson's avatar
Alexander Larsson committed
496

497 498
  if (window->input_shape)
    cairo_region_destroy (window->input_shape);
Alexander Larsson's avatar
Alexander Larsson committed
499

500
  if (window->cursor)
501
    g_object_unref (window->cursor);
502

503 504
  if (window->device_cursor)
    g_hash_table_destroy (window->device_cursor);
505

506 507
  if (window->device_events)
    g_hash_table_destroy (window->device_events);
508

509 510 511
  if (window->source_event_masks)
    g_hash_table_destroy (window->source_event_masks);

512 513
  if (window->devices_inside)
    g_list_free (window->devices_inside);
514

515
  G_OBJECT_CLASS (parent_class)->finalize (object);
Elliot Lee's avatar
Elliot Lee committed
516 517
}

Cody Russell's avatar
Cody Russell committed
518 519 520 521 522 523 524 525 526 527 528
static void
gdk_window_set_property (GObject      *object,
                         guint         prop_id,
                         const GValue *value,
                         GParamSpec   *pspec)
{
  GdkWindow *window = (GdkWindow *)object;

  switch (prop_id)
    {
    case PROP_CURSOR:
Benjamin Otte's avatar
Benjamin Otte committed
529
      gdk_window_set_cursor (window, g_value_get_object (value));
Cody Russell's avatar
Cody Russell committed
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gdk_window_get_property (GObject    *object,
                         guint       prop_id,
                         GValue     *value,
                         GParamSpec *pspec)
{
  GdkWindow *window = (GdkWindow *) object;

  switch (prop_id)
    {
    case PROP_CURSOR:
Benjamin Otte's avatar
Benjamin Otte committed
549
      g_value_set_object (value, gdk_window_get_cursor (window));
Cody Russell's avatar
Cody Russell committed
550 551 552 553 554 555 556 557
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

558
static gboolean
559
gdk_window_is_offscreen (GdkWindow *window)
560
{
561
  return window->window_type == GDK_WINDOW_OFFSCREEN;
562 563
}

564 565
static GdkWindow *
gdk_window_get_impl_window (GdkWindow *window)
566
{
567
  return window->impl_window;
568 569 570 571 572
}

GdkWindow *
_gdk_window_get_impl_window (GdkWindow *window)
{
573
  return gdk_window_get_impl_window (window);
574 575 576
}

static gboolean
577
gdk_window_has_impl (GdkWindow *window)
578
{
579
  return window->impl_window == window;
580 581
}

582
static gboolean
583
gdk_window_is_toplevel (GdkWindow *window)
584 585 586 587 588 589
{
  return
    window->parent == NULL ||
    window->parent->window_type == GDK_WINDOW_ROOT;
}

590 591 592
gboolean
_gdk_window_has_impl (GdkWindow *window)
{
593
  return gdk_window_has_impl (window);
594 595 596
}

static gboolean
597
gdk_window_has_no_impl (GdkWindow *window)
598
{
599
  return window->impl_window != window;
600 601 602
}

static void
603 604
remove_sibling_overlapped_area (GdkWindow *window,
				cairo_region_t *region)
605
{
606 607
  GdkWindow *parent;
  GdkWindow *sibling;
608 609 610
  cairo_region_t *child_region;
  GdkRectangle r;
  GList *l;
611
  cairo_region_t *shape;
612

613 614
  parent = window->parent;

615
  if (gdk_window_is_toplevel (window))
616 617 618 619 620 621
    return;

  /* Convert from from window coords to parent coords */
  cairo_region_translate (region, window->x, window->y);

  for (l = parent->children; l; l = l->next)
622
    {
623
      sibling = l->data;
624

625
      if (sibling == window)
626 627
	break;

628
      if (!GDK_WINDOW_IS_MAPPED (sibling) || sibling->input_only || sibling->composited)
629 630 631 632
	continue;

      /* Ignore offscreen children, as they don't draw in their parent and
       * don't take part in the clipping */
633
      if (gdk_window_is_offscreen (sibling))
634 635
	continue;

636 637 638 639
      r.x = sibling->x;
      r.y = sibling->y;
      r.width = sibling->width;
      r.height = sibling->height;
640 641

      child_region = cairo_region_create_rectangle (&r);
642 643

      if (sibling->shape)
644 645
	{
	  /* Adjust shape region to parent window coords */
646 647 648 649 650 651 652 653 654 655 656 657
	  cairo_region_translate (sibling->shape, sibling->x, sibling->y);
	  cairo_region_intersect (child_region, sibling->shape);
	  cairo_region_translate (sibling->shape, -sibling->x, -sibling->y);
	}
      else if (window->window_type == GDK_WINDOW_FOREIGN)
	{
	  shape = GDK_WINDOW_IMPL_GET_CLASS (sibling)->get_shape (sibling);
	  if (shape)
	    {
	      cairo_region_intersect (child_region, shape);
	      cairo_region_destroy (shape);
	    }
658 659 660 661 662
	}

      cairo_region_subtract (region, child_region);
      cairo_region_destroy (child_region);
    }
663

664 665
  remove_sibling_overlapped_area (parent, region);

666 667
  /* Convert back to window coords */
  cairo_region_translate (region, -window->x, -window->y);
668 669 670 671
}

static void
remove_child_area (GdkWindow *window,
Alexander Larsson's avatar
Alexander Larsson committed
672
		   gboolean for_input,
673
		   cairo_region_t *region)
674
{
675
  GdkWindow *child;
676
  cairo_region_t *child_region;
677 678
  GdkRectangle r;
  GList *l;
679
  cairo_region_t *shape;
680

681
  for (l = window->children; l; l = l->next)
682 683 684
    {
      child = l->data;

685 686
      /* If region is empty already, no need to do
	 anything potentially costly */
Benjamin Otte's avatar
Benjamin Otte committed
687
      if (cairo_region_is_empty (region))
688 689
	break;

690
      if (!GDK_WINDOW_IS_MAPPED (child) || child->input_only || child->composited)
691 692 693 694 695 696 697 698 699 700 701
	continue;

      /* Ignore offscreen children, as they don't draw in their parent and
       * don't take part in the clipping */
      if (gdk_window_is_offscreen (child))
	continue;

      r.x = child->x;
      r.y = child->y;
      r.width = child->width;
      r.height = child->height;
Alexander Larsson's avatar
Alexander Larsson committed
702

703
      /* Bail early if child totally outside region */
Benjamin Otte's avatar
Benjamin Otte committed
704
      if (cairo_region_contains_rectangle (region, &r) == CAIRO_REGION_OVERLAP_OUT)
705 706
	continue;

Benjamin Otte's avatar
Benjamin Otte committed
707
      child_region = cairo_region_create_rectangle (&r);
708

Alexander Larsson's avatar
Alexander Larsson committed
709
      if (child->shape)
710 711
	{
	  /* Adjust shape region to parent window coords */
Benjamin Otte's avatar
Benjamin Otte committed
712 713 714
	  cairo_region_translate (child->shape, child->x, child->y);
	  cairo_region_intersect (child_region, child->shape);
	  cairo_region_translate (child->shape, -child->x, -child->y);
715
	}
716
      else if (window->window_type == GDK_WINDOW_FOREIGN)
Alexander Larsson's avatar
Alexander Larsson committed
717
	{
718
	  shape = GDK_WINDOW_IMPL_GET_CLASS (child)->get_shape (child);
Alexander Larsson's avatar
Alexander Larsson committed
719 720
	  if (shape)
	    {
Benjamin Otte's avatar
Benjamin Otte committed
721 722
	      cairo_region_intersect (child_region, shape);
	      cairo_region_destroy (shape);
Alexander Larsson's avatar
Alexander Larsson committed
723 724
	    }
	}
Alexander Larsson's avatar
Alexander Larsson committed
725 726 727 728

      if (for_input)
	{
	  if (child->input_shape)
Benjamin Otte's avatar
Benjamin Otte committed
729
	    cairo_region_intersect (child_region, child->input_shape);
730
	  else if (window->window_type == GDK_WINDOW_FOREIGN)
Alexander Larsson's avatar
Alexander Larsson committed
731
	    {
732
	      shape = GDK_WINDOW_IMPL_GET_CLASS (child)->get_input_shape (child);
Alexander Larsson's avatar
Alexander Larsson committed
733 734
	      if (shape)
		{
Benjamin Otte's avatar
Benjamin Otte committed
735 736
		  cairo_region_intersect (child_region, shape);
		  cairo_region_destroy (shape);
Alexander Larsson's avatar
Alexander Larsson committed
737 738 739
		}
	    }
	}
740

741
      cairo_region_subtract (region, child_region);
Benjamin Otte's avatar
Benjamin Otte committed
742
      cairo_region_destroy (child_region);
743 744 745
    }
}

746
static GdkVisibilityState
747
effective_visibility (GdkWindow *window)
748 749 750
{
  GdkVisibilityState native;

751
  if (!gdk_window_is_viewable (window))
752 753
    return GDK_VISIBILITY_NOT_VIEWABLE;

754
  native = window->impl_window->native_visibility;
755 756

  if (native == GDK_VISIBILITY_FULLY_OBSCURED ||
757
      window->visibility == GDK_VISIBILITY_FULLY_OBSCURED)
758 759
    return GDK_VISIBILITY_FULLY_OBSCURED;
  else if (native == GDK_VISIBILITY_UNOBSCURED)
760
    return window->visibility;
761 762 763 764 765
  else /* native PARTIAL, private partial or unobscured  */
    return GDK_VISIBILITY_PARTIAL;
}

static void
766
gdk_window_update_visibility (GdkWindow *window)
767 768 769 770
{
  GdkVisibilityState new_visibility;
  GdkEvent *event;

771
  new_visibility = effective_visibility (window);
772

773
  if (new_visibility != window->effective_visibility)
774
    {
775
      window->effective_visibility = new_visibility;
776 777

      if (new_visibility != GDK_VISIBILITY_NOT_VIEWABLE &&
778
	  window->event_mask & GDK_VISIBILITY_NOTIFY_MASK)
779
	{
780
	  event = _gdk_make_event (window, GDK_VISIBILITY_NOTIFY,
781 782 783 784 785 786 787
				   NULL, FALSE);
	  event->visibility.state = new_visibility;
	}
    }
}

static void
788 789
gdk_window_update_visibility_recursively (GdkWindow *window,
					  GdkWindow *only_for_impl)
790
{
791
  GdkWindow *child;
792 793
  GList *l;

794 795
  gdk_window_update_visibility (window);
  for (l = window->children; l != NULL; l = l->next)
796 797 798 799 800 801 802 803
    {
      child = l->data;
      if ((only_for_impl == NULL) ||
	  (only_for_impl == child->impl_window))
	gdk_window_update_visibility_recursively (child, only_for_impl);
    }
}

Alexander Larsson's avatar
Alexander Larsson committed
804
static gboolean
805
should_apply_clip_as_shape (GdkWindow *window)
Alexander Larsson's avatar
Alexander Larsson committed
806 807
{
  return
808
    gdk_window_has_impl (window) &&
Alexander Larsson's avatar
Alexander Larsson committed
809
    /* Not for offscreens */
810
    !gdk_window_is_offscreen (window) &&
Alexander Larsson's avatar
Alexander Larsson committed
811 812 813
    /* or for non-shaped toplevels */
    (!gdk_window_is_toplevel (window) ||
     window->shape != NULL || window->applied_shape) &&
Alexander Larsson's avatar
Alexander Larsson committed
814
    /* or for foreign windows */
815
    window->window_type != GDK_WINDOW_FOREIGN &&
Alexander Larsson's avatar
Alexander Larsson committed
816
    /* or for the root window */
817
    window->window_type != GDK_WINDOW_ROOT;
Alexander Larsson's avatar
Alexander Larsson committed
818 819 820
}

static void
821
apply_shape (GdkWindow *window,
822
	     cairo_region_t *region)
Alexander Larsson's avatar
Alexander Larsson committed
823
{
824
  GdkWindowImplClass *impl_class;
Alexander Larsson's avatar
Alexander Larsson committed
825 826 827 828 829

  /* We trash whether we applied a shape so that
     we can avoid unsetting it many times, which
     could happen in e.g. apply_clip_as_shape as
     windows get resized */
830
  impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
Alexander Larsson's avatar
Alexander Larsson committed
831
  if (region)
832
    impl_class->shape_combine_region (window,
Alexander Larsson's avatar
Alexander Larsson committed
833
				      region, 0, 0);
834 835
  else if (window->applied_shape)
    impl_class->shape_combine_region (window,
Alexander Larsson's avatar
Alexander Larsson committed
836 837
				      NULL, 0, 0);

838
  window->applied_shape = region != NULL;
Alexander Larsson's avatar
Alexander Larsson committed
839 840
}

Benjamin Otte's avatar
Benjamin Otte committed
841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
static gboolean
region_rect_equal (const cairo_region_t *region,
                   const GdkRectangle *rect)
{
    GdkRectangle extents;

    if (cairo_region_num_rectangles (region) != 1)
        return FALSE;

    cairo_region_get_extents (region, &extents);

    return extents.x == rect->x &&
        extents.y == rect->y &&
        extents.width == rect->width &&
        extents.height == rect->height;
}

Alexander Larsson's avatar
Alexander Larsson committed
858
static void
859
apply_clip_as_shape (GdkWindow *window)
Alexander Larsson's avatar
Alexander Larsson committed
860 861
{
  GdkRectangle r;
862
  cairo_region_t *region;
Alexander Larsson's avatar
Alexander Larsson committed
863 864

  r.x = r.y = 0;
865 866
  r.width = window->width;
  r.height = window->height;
Alexander Larsson's avatar
Alexander Larsson committed
867

868
  region = cairo_region_copy (window->clip_region);
869
  remove_sibling_overlapped_area (window, region);
870

Alexander Larsson's avatar
Alexander Larsson committed
871 872 873 874
  /* We only apply the clip region if would differ
     from the actual clip region implied by the size
     of the window. This is to avoid unneccessarily
     adding meaningless shapes to all native subwindows */
875 876
  if (!region_rect_equal (region, &r))
    apply_shape (window, region);
Alexander Larsson's avatar
Alexander Larsson committed
877
  else
878
    apply_shape (window, NULL);
879 880

  cairo_region_destroy (region);
Alexander Larsson's avatar
Alexander Larsson committed
881 882
}

883
static void
884 885 886
recompute_visible_regions_internal (GdkWindow *private,
				    gboolean   recalculate_clip,
				    gboolean   recalculate_children)
887 888 889
{
  GdkRectangle r;
  GList *l;
890
  GdkWindow *child;
891
  cairo_region_t *new_clip;
892 893 894 895 896 897
  gboolean clip_region_changed;
  gboolean abs_pos_changed;
  int old_abs_x, old_abs_y;

  old_abs_x = private->abs_x;
  old_abs_y = private->abs_y;
898

899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917
  /* Update absolute position */
  if (gdk_window_has_impl (private))
    {
      /* Native window starts here */
      private->abs_x = 0;
      private->abs_y = 0;
    }
  else
    {
      private->abs_x = private->parent->abs_x + private->x;
      private->abs_y = private->parent->abs_y + private->y;
    }

  abs_pos_changed =
    private->abs_x != old_abs_x ||
    private->abs_y != old_abs_y;

  /* Update clip region based on:
   * parent clip
918
   * window size/position
919 920
   */
  clip_region_changed = FALSE;
921
  if (recalculate_clip)
922
    {
923
      if (private->viewable)
924
	{
925 926 927 928 929
	  /* Calculate visible region (sans children) in parent window coords */
	  r.x = private->x;
	  r.y = private->y;
	  r.width = private->width;
	  r.height = private->height;
Benjamin Otte's avatar
Benjamin Otte committed
930
	  new_clip = cairo_region_create_rectangle (&r);
931

932
	  if (!gdk_window_is_toplevel (private))
933
	    cairo_region_intersect (new_clip, private->parent->clip_region);
934

935
	  /* Convert from parent coords to window coords */
Benjamin Otte's avatar
Benjamin Otte committed
936
	  cairo_region_translate (new_clip, -private->x, -private->y);
937

938
	  if (should_apply_clip_as_shape (private) && private->shape)
Benjamin Otte's avatar
Benjamin Otte committed
939
	    cairo_region_intersect (new_clip, private->shape);
940 941
	}
      else
942
	  new_clip = cairo_region_create ();
Alexander Larsson's avatar
Alexander Larsson committed
943

944
      if (private->clip_region == NULL ||
Benjamin Otte's avatar
Benjamin Otte committed
945
	  !cairo_region_equal (private->clip_region, new_clip))
946
	clip_region_changed = TRUE;
947

948
      if (private->clip_region)
Benjamin Otte's avatar
Benjamin Otte committed
949
	cairo_region_destroy (private->clip_region);
950 951 952
      private->clip_region = new_clip;
    }

953 954 955 956 957
  if (clip_region_changed)
    {
      GdkVisibilityState visibility;
      gboolean fully_visible;