gdkwindow.c 338 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 156
  PROP_CURSOR,
  LAST_PROP
Cody Russell's avatar
Cody Russell committed
157 158
};

159
/* Global info */
160

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

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);

172
static void gdk_window_clear_backing_region (GdkWindow *window);
173

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

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

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


198
static guint signals[LAST_SIGNAL] = { 0 };
199
static GParamSpec *properties[LAST_PROP] = { NULL, };
200

Benjamin Otte's avatar
Benjamin Otte committed
201
G_DEFINE_ABSTRACT_TYPE (GdkWindow, gdk_window, G_TYPE_OBJECT)
202

203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
#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
	{
224
	  int i;
225 226
	  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);
227
	  for (i = 0; i < num; i++)
228
	    {
229
              cairo_region_get_rectangle (region, i, &r);
230 231 232 233 234 235 236 237 238 239 240
	      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

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 267 268 269
static GList *
list_insert_link_before (GList *list,
                         GList *sibling,
                         GList *link)
{
  if (list == NULL || sibling == list)
    {
      link->prev = NULL;
      link->next = list;
      if (list)
        list->prev = link;
      return link;
    }
  else if (sibling == NULL)
    {
      GList *last = g_list_last (list);

      last->next = link;
      link->prev = last;
      link->next = NULL;

      return list;
    }
  else
    {
      link->next = sibling;
      link->prev = sibling->prev;
      sibling->prev = link;

270 271 272
      if (link->prev)
        link->prev->next = link;

273 274 275 276
      return list;
    }
}

277
static void
278
gdk_window_init (GdkWindow *window)
279 280
{
  /* 0-initialization is good for all other fields. */
rhlabs's avatar
rhlabs committed
281

282
  window->window_type = GDK_WINDOW_CHILD;
Elliot Lee's avatar
Elliot Lee committed
283

Havoc Pennington's avatar
Havoc Pennington committed
284
  window->state = GDK_WINDOW_STATE_WITHDRAWN;
285
  window->fullscreen_mode = GDK_FULLSCREEN_ON_CURRENT_MONITOR;
286 287 288
  window->width = 1;
  window->height = 1;
  window->toplevel_window_type = -1;
289 290 291 292 293
  /* 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;
294
  window->children_list_node.data = window;
295 296 297

  window->device_cursor = g_hash_table_new_full (NULL, NULL,
                                                 NULL, g_object_unref);
298
}
299

300
/* Stop and return on the first non-NULL parent */
301
static gboolean
302 303 304 305
accumulate_get_window (GSignalInvocationHint *ihint,
		       GValue		       *return_accu,
		       const GValue	       *handler_return,
		       gpointer               data)
306 307 308
{
  g_value_copy (handler_return, return_accu);
  /* Continue while returning NULL */
309
  return g_value_get_object (handler_return) == NULL;
310 311
}

312 313 314 315 316 317 318 319 320 321 322 323
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;
}

324 325
static GQuark quark_pointer_window = 0;

326
static void
327
gdk_window_class_init (GdkWindowClass *klass)
328 329
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
330

331
  object_class->finalize = gdk_window_finalize;
Cody Russell's avatar
Cody Russell committed
332 333
  object_class->set_property = gdk_window_set_property;
  object_class->get_property = gdk_window_get_property;
334

335 336
  klass->create_surface = _gdk_offscreen_window_create_surface;

337
  quark_pointer_window = g_quark_from_static_string ("gtk-pointer-window");
338 339


Cody Russell's avatar
Cody Russell committed
340
  /* Properties */
341 342 343 344 345 346 347 348 349

  /**
   * GdkWindow:cursor:
   *
   * The mouse pointer for a #GdkWindow. See gdk_window_set_cursor() and
   * gdk_window_get_cursor() for details.
   *
   * Since: 2.18
   */
350 351 352 353 354 355 356
  properties[PROP_CURSOR] =
      g_param_spec_object ("cursor",
                           P_("Cursor"),
                           P_("Cursor"),
                           GDK_TYPE_CURSOR,
                           G_PARAM_READWRITE);
  g_object_class_install_properties (object_class, LAST_PROP, properties);
Cody Russell's avatar
Cody Russell committed
357

Matthias Clasen's avatar
Matthias Clasen committed
358 359 360 361 362 363 364 365 366
  /**
   * 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.
   *
367 368
   * Returns: (nullable) (transfer none): the #GdkWindow of the
   *     embedded child at @x, @y, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
369 370 371
   *
   * Since: 2.18
   */
Alexander Larsson's avatar
Alexander Larsson committed
372 373
  signals[PICK_EMBEDDED_CHILD] =
    g_signal_new (g_intern_static_string ("pick-embedded-child"),
374 375
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_LAST,
376
                  G_STRUCT_OFFSET (GdkWindowClass, pick_embedded_child),
377
		  accumulate_get_window, NULL,
Matthias Clasen's avatar
Matthias Clasen committed
378
		  _gdk_marshal_OBJECT__DOUBLE_DOUBLE,
379 380 381 382
		  GDK_TYPE_WINDOW,
		  2,
		  G_TYPE_DOUBLE,
		  G_TYPE_DOUBLE);
Matthias Clasen's avatar
Matthias Clasen committed
383 384 385 386

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

  /**
   * GdkWindow::from-embedder:
   * @window: the offscreen window on which the signal is emitted
418 419 420
   * @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
421
   *     coordinate in the offscreen window
422
   * @offscreen_y: (out) (type double): return location for the y
423
   *     coordinate in the offscreen window
Matthias Clasen's avatar
Matthias Clasen committed
424 425 426 427
   *
   * The ::from-embedder signal is emitted to translate coordinates
   * in the embedder of an offscreen window to the offscreen window.
   *
428
   * See also #GdkWindow::to-embedder.
Matthias Clasen's avatar
Matthias Clasen committed
429
   *
Matthias Clasen's avatar
Matthias Clasen committed
430 431
   * Since: 2.18
   */
Alexander Larsson's avatar
Alexander Larsson committed
432 433
  signals[FROM_EMBEDDER] =
    g_signal_new (g_intern_static_string ("from-embedder"),
434 435
		  G_OBJECT_CLASS_TYPE (object_class),
		  G_SIGNAL_RUN_LAST,
436
                  G_STRUCT_OFFSET (GdkWindowClass, from_embedder),
437
		  NULL, NULL,
Matthias Clasen's avatar
Matthias Clasen committed
438
		  _gdk_marshal_VOID__DOUBLE_DOUBLE_POINTER_POINTER,
439 440 441 442 443 444
		  G_TYPE_NONE,
		  4,
		  G_TYPE_DOUBLE,
		  G_TYPE_DOUBLE,
		  G_TYPE_POINTER,
		  G_TYPE_POINTER);
445 446 447 448 449 450 451 452

  /**
   * 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
453
   * needs its surface (re)created, which happens either when the
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
   * 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,
471
                  G_STRUCT_OFFSET (GdkWindowClass, create_surface),
472 473 474 475 476 477
                  create_surface_accumulator, NULL,
                  _gdk_marshal_BOXED__INT_INT,
                  CAIRO_GOBJECT_TYPE_SURFACE,
                  2,
                  G_TYPE_INT,
                  G_TYPE_INT);
478
}
479

480 481 482 483 484
static void
device_removed_cb (GdkDeviceManager *device_manager,
                   GdkDevice        *device,
                   GdkWindow        *window)
{
485 486
  window->devices_inside = g_list_remove (window->devices_inside, device);
  g_hash_table_remove (window->device_cursor, device);
487

488 489
  if (window->device_events)
    g_hash_table_remove (window->device_events, device);
490 491
}

492 493 494 495
static void
gdk_window_finalize (GObject *object)
{
  GdkWindow *window = GDK_WINDOW (object);
496 497
  GdkDeviceManager *device_manager;

498
  device_manager = gdk_display_get_device_manager (gdk_window_get_display (window));
499
  g_signal_handlers_disconnect_by_func (device_manager, device_removed_cb, window);
500

501 502 503 504 505 506 507 508 509 510 511 512 513
  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);
    }
514

515
  if (window->impl)
516
    {
517 518
      g_object_unref (window->impl);
      window->impl = NULL;
519 520
    }

521
  if (window->impl_window != window)
522
    {
523 524
      g_object_unref (window->impl_window);
      window->impl_window = NULL;
525
    }
526

527 528
  if (window->shape)
    cairo_region_destroy (window->shape);
Alexander Larsson's avatar
Alexander Larsson committed
529

530 531
  if (window->input_shape)
    cairo_region_destroy (window->input_shape);
Alexander Larsson's avatar
Alexander Larsson committed
532

533
  if (window->cursor)
534
    g_object_unref (window->cursor);
535

536 537
  if (window->device_cursor)
    g_hash_table_destroy (window->device_cursor);
538

539 540
  if (window->device_events)
    g_hash_table_destroy (window->device_events);
541

542 543 544
  if (window->source_event_masks)
    g_hash_table_destroy (window->source_event_masks);

545 546
  if (window->devices_inside)
    g_list_free (window->devices_inside);
547

548
  G_OBJECT_CLASS (gdk_window_parent_class)->finalize (object);
Elliot Lee's avatar
Elliot Lee committed
549 550
}

Cody Russell's avatar
Cody Russell committed
551 552 553 554 555 556 557 558 559 560 561
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
562
      gdk_window_set_cursor (window, g_value_get_object (value));
Cody Russell's avatar
Cody Russell committed
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
      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
582
      g_value_set_object (value, gdk_window_get_cursor (window));
Cody Russell's avatar
Cody Russell committed
583 584 585 586 587 588 589 590
      break;

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

591
static gboolean
592
gdk_window_is_offscreen (GdkWindow *window)
593
{
594
  return window->window_type == GDK_WINDOW_OFFSCREEN;
595 596
}

597 598
static GdkWindow *
gdk_window_get_impl_window (GdkWindow *window)
599
{
600
  return window->impl_window;
601 602 603 604 605
}

GdkWindow *
_gdk_window_get_impl_window (GdkWindow *window)
{
606
  return gdk_window_get_impl_window (window);
607 608 609
}

static gboolean
610
gdk_window_has_impl (GdkWindow *window)
611
{
612
  return window->impl_window == window;
613 614
}

615
static gboolean
616
gdk_window_is_toplevel (GdkWindow *window)
617 618 619 620 621 622
{
  return
    window->parent == NULL ||
    window->parent->window_type == GDK_WINDOW_ROOT;
}

623 624 625
gboolean
_gdk_window_has_impl (GdkWindow *window)
{
626
  return gdk_window_has_impl (window);
627 628 629
}

static gboolean
630
gdk_window_has_no_impl (GdkWindow *window)
631
{
632
  return window->impl_window != window;
633 634 635
}

static void
636 637
remove_sibling_overlapped_area (GdkWindow *window,
				cairo_region_t *region)
638
{
639 640
  GdkWindow *parent;
  GdkWindow *sibling;
641 642 643
  cairo_region_t *child_region;
  GdkRectangle r;
  GList *l;
644
  cairo_region_t *shape;
645

646 647
  parent = window->parent;

648
  if (gdk_window_is_toplevel (window))
649 650 651 652 653 654
    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)
655
    {
656
      sibling = l->data;
657

658
      if (sibling == window)
659 660
	break;

661
      if (!GDK_WINDOW_IS_MAPPED (sibling) || sibling->input_only || sibling->composited)
662 663 664 665
	continue;

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

669 670 671 672
      r.x = sibling->x;
      r.y = sibling->y;
      r.width = sibling->width;
      r.height = sibling->height;
673 674

      child_region = cairo_region_create_rectangle (&r);
675 676

      if (sibling->shape)
677 678
	{
	  /* Adjust shape region to parent window coords */
679 680 681 682 683 684 685 686 687 688 689 690
	  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);
	    }
691 692 693 694 695
	}

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

697 698
  remove_sibling_overlapped_area (parent, region);

699 700
  /* Convert back to window coords */
  cairo_region_translate (region, -window->x, -window->y);
701 702 703 704
}

static void
remove_child_area (GdkWindow *window,
Alexander Larsson's avatar
Alexander Larsson committed
705
		   gboolean for_input,
706
		   cairo_region_t *region)
707
{
708
  GdkWindow *child;
709
  cairo_region_t *child_region;
710 711
  GdkRectangle r;
  GList *l;
712
  cairo_region_t *shape;
713

714
  for (l = window->children; l; l = l->next)
715 716 717
    {
      child = l->data;

718 719
      /* If region is empty already, no need to do
	 anything potentially costly */
Benjamin Otte's avatar
Benjamin Otte committed
720
      if (cairo_region_is_empty (region))
721 722
	break;

723
      if (!GDK_WINDOW_IS_MAPPED (child) || child->input_only || child->composited)
724 725 726 727 728 729 730 731 732 733 734
	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
735

736
      /* Bail early if child totally outside region */
Benjamin Otte's avatar
Benjamin Otte committed
737
      if (cairo_region_contains_rectangle (region, &r) == CAIRO_REGION_OVERLAP_OUT)
738 739
	continue;

Benjamin Otte's avatar
Benjamin Otte committed
740
      child_region = cairo_region_create_rectangle (&r);
741

Alexander Larsson's avatar
Alexander Larsson committed
742
      if (child->shape)
743 744
	{
	  /* Adjust shape region to parent window coords */
Benjamin Otte's avatar
Benjamin Otte committed
745 746 747
	  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);
748
	}
749
      else if (window->window_type == GDK_WINDOW_FOREIGN)
Alexander Larsson's avatar
Alexander Larsson committed
750
	{
751
	  shape = GDK_WINDOW_IMPL_GET_CLASS (child)->get_shape (child);
Alexander Larsson's avatar
Alexander Larsson committed
752 753
	  if (shape)
	    {
Benjamin Otte's avatar
Benjamin Otte committed
754 755
	      cairo_region_intersect (child_region, shape);
	      cairo_region_destroy (shape);
Alexander Larsson's avatar
Alexander Larsson committed
756 757
	    }
	}
Alexander Larsson's avatar
Alexander Larsson committed
758 759 760 761

      if (for_input)
	{
	  if (child->input_shape)
Benjamin Otte's avatar
Benjamin Otte committed
762
	    cairo_region_intersect (child_region, child->input_shape);
763
	  else if (window->window_type == GDK_WINDOW_FOREIGN)
Alexander Larsson's avatar
Alexander Larsson committed
764
	    {
765
	      shape = GDK_WINDOW_IMPL_GET_CLASS (child)->get_input_shape (child);
Alexander Larsson's avatar
Alexander Larsson committed
766 767
	      if (shape)
		{
Benjamin Otte's avatar
Benjamin Otte committed
768 769
		  cairo_region_intersect (child_region, shape);
		  cairo_region_destroy (shape);
Alexander Larsson's avatar
Alexander Larsson committed
770 771 772
		}
	    }
	}
773

774
      cairo_region_subtract (region, child_region);
Benjamin Otte's avatar
Benjamin Otte committed
775
      cairo_region_destroy (child_region);
776 777 778
    }
}

779
static GdkVisibilityState
780
effective_visibility (GdkWindow *window)
781 782 783
{
  GdkVisibilityState native;

784
  if (!gdk_window_is_viewable (window))
785 786
    return GDK_VISIBILITY_NOT_VIEWABLE;

787
  native = window->impl_window->native_visibility;
788 789

  if (native == GDK_VISIBILITY_FULLY_OBSCURED ||
790
      window->visibility == GDK_VISIBILITY_FULLY_OBSCURED)
791 792
    return GDK_VISIBILITY_FULLY_OBSCURED;
  else if (native == GDK_VISIBILITY_UNOBSCURED)
793
    return window->visibility;
794 795 796 797 798
  else /* native PARTIAL, private partial or unobscured  */
    return GDK_VISIBILITY_PARTIAL;
}

static void
799
gdk_window_update_visibility (GdkWindow *window)
800 801 802 803
{
  GdkVisibilityState new_visibility;
  GdkEvent *event;

804
  new_visibility = effective_visibility (window);
805

806
  if (new_visibility != window->effective_visibility)
807
    {
808
      window->effective_visibility = new_visibility;
809 810

      if (new_visibility != GDK_VISIBILITY_NOT_VIEWABLE &&
811
	  window->event_mask & GDK_VISIBILITY_NOTIFY_MASK)
812
	{
813
	  event = _gdk_make_event (window, GDK_VISIBILITY_NOTIFY,
814 815 816 817 818 819 820
				   NULL, FALSE);
	  event->visibility.state = new_visibility;
	}
    }
}

static void
821 822
gdk_window_update_visibility_recursively (GdkWindow *window,
					  GdkWindow *only_for_impl)
823
{
824
  GdkWindow *child;
825 826
  GList *l;

827 828
  gdk_window_update_visibility (window);
  for (l = window->children; l != NULL; l = l->next)
829 830 831 832 833 834 835 836
    {
      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
837
static gboolean
838
should_apply_clip_as_shape (GdkWindow *window)
Alexander Larsson's avatar
Alexander Larsson committed
839 840
{
  return
841
    gdk_window_has_impl (window) &&
Alexander Larsson's avatar
Alexander Larsson committed
842
    /* Not for offscreens */
843
    !gdk_window_is_offscreen (window) &&
Alexander Larsson's avatar
Alexander Larsson committed
844 845 846
    /* or for non-shaped toplevels */
    (!gdk_window_is_toplevel (window) ||
     window->shape != NULL || window->applied_shape) &&
Alexander Larsson's avatar
Alexander Larsson committed
847
    /* or for foreign windows */
848
    window->window_type != GDK_WINDOW_FOREIGN &&
Alexander Larsson's avatar
Alexander Larsson committed
849
    /* or for the root window */
850
    window->window_type != GDK_WINDOW_ROOT;
Alexander Larsson's avatar
Alexander Larsson committed
851 852 853
}

static void
854
apply_shape (GdkWindow *window,
855
	     cairo_region_t *region)
Alexander Larsson's avatar
Alexander Larsson committed
856
{
857
  GdkWindowImplClass *impl_class;
Alexander Larsson's avatar
Alexander Larsson committed
858 859 860 861 862

  /* 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 */
863
  impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
Alexander Larsson's avatar
Alexander Larsson committed
864
  if (region)
865
    impl_class->shape_combine_region (window,
Alexander Larsson's avatar
Alexander Larsson committed
866
				      region, 0, 0);
867 868
  else if (window->applied_shape)
    impl_class->shape_combine_region (window,
Alexander Larsson's avatar
Alexander Larsson committed
869 870
				      NULL, 0, 0);

871
  window->applied_shape = region != NULL;
Alexander Larsson's avatar
Alexander Larsson committed
872 873
}

Benjamin Otte's avatar
Benjamin Otte committed
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
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
891
static void
892
apply_clip_as_shape (GdkWindow *window)
Alexander Larsson's avatar
Alexander Larsson committed
893 894
{
  GdkRectangle r;
895
  cairo_region_t *region;
Alexander Larsson's avatar
Alexander Larsson committed
896 897

  r.x = r.y = 0;
898 899
  r.width = window->width;
  r.height = window->height;
Alexander Larsson's avatar
Alexander Larsson committed
900

901
  region = cairo_region_copy (window->clip_region);
902
  remove_sibling_overlapped_area (window, region);
903

Alexander Larsson's avatar
Alexander Larsson committed
904 905 906 907
  /* 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 */
908 909
  if (!region_rect_equal (region, &r))
    apply_shape (window, region);
Alexander Larsson's avatar
Alexander Larsson committed
910
  else
911
    apply_shape (window, NULL);
912 913

  cairo_region_destroy (region);
Alexander Larsson's avatar
Alexander Larsson committed
914 915
}

916
static void
917 918 919
recompute_visible_regions_internal (GdkWindow *private,
				    gboolean   recalculate_clip,
				    gboolean   recalculate_children)
920 921 922
{
  GdkRectangle r;
  GList *l;
923
  GdkWindow *child;
924
  cairo_region_t *new_clip;
925 926 927 928 929 930
  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;
931

932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950
  /* 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
951
   * window size/position
952 953
   */
  clip_region_changed = FALSE;
954
  if (recalculate_clip)
955
    {
956
      if (private->viewable)
957
	{
958 959 960 961 962
	  /* 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
963
	  new_clip = cairo_region_create_rectangle (&r);
964

965
	  if (!gdk_window_is_toplevel (private))
966
	    cairo_region_intersect (new_clip, private->parent->clip_region);
967

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

971
	  if (should_apply_clip_as_shape (private) && private->shape)
Benjamin Otte's avatar
Benjamin Otte committed
972
	    cairo_region_intersect (new_clip, private->shape);
973 974
	}
      else
975
	  new_clip = cairo_region_create ();
Alexander Larsson's avatar
Alexander Larsson committed
976

977
      if (private->clip_region == NULL ||
Benjamin Otte's avatar
Benjamin Otte committed