gdkwindow-wayland.c 137 KB
Newer Older
Kristian Høgsberg's avatar
Kristian Høgsberg committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14
/*
 * Copyright © 2010 Intel Corporation
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
Javier Jardon's avatar
Javier Jardon committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Kristian Høgsberg's avatar
Kristian Høgsberg committed
16 17 18 19 20 21 22 23 24 25 26 27 28
 */

#include "config.h"

#include <netinet/in.h>
#include <unistd.h>

#include "gdk.h"
#include "gdkwayland.h"

#include "gdkwindow.h"
#include "gdkwindowimpl.h"
#include "gdkdisplay-wayland.h"
29
#include "gdkglcontext-wayland.h"
30
#include "gdkframeclockprivate.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
31 32 33
#include "gdkprivate-wayland.h"
#include "gdkinternals.h"
#include "gdkdeviceprivate.h"
34 35
#include "gdkprivate-wayland.h"
#include "xdg-shell-unstable-v6-client-protocol.h"
Kristian Høgsberg's avatar
Kristian Høgsberg committed
36 37 38 39

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
40
#include <errno.h>
Kristian Høgsberg's avatar
Kristian Høgsberg committed
41

42 43 44 45 46 47 48 49
enum {
  COMMITTED,

  LAST_SIGNAL
};

static guint signals[LAST_SIGNAL];

Kristian Høgsberg's avatar
Kristian Høgsberg committed
50 51 52 53
#define WINDOW_IS_TOPLEVEL_OR_FOREIGN(window) \
  (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD &&   \
   GDK_WINDOW_TYPE (window) != GDK_WINDOW_OFFSCREEN)

Matthias Clasen's avatar
Matthias Clasen committed
54
#define WINDOW_IS_TOPLEVEL(window)                   \
Kristian Høgsberg's avatar
Kristian Høgsberg committed
55 56 57 58
  (GDK_WINDOW_TYPE (window) != GDK_WINDOW_CHILD &&   \
   GDK_WINDOW_TYPE (window) != GDK_WINDOW_FOREIGN && \
   GDK_WINDOW_TYPE (window) != GDK_WINDOW_OFFSCREEN)

59 60
#define MAX_WL_BUFFER_SIZE (4083) /* 4096 minus header, string argument length and NUL byte */

Kristian Høgsberg's avatar
Kristian Høgsberg committed
61 62 63
typedef struct _GdkWaylandWindow GdkWaylandWindow;
typedef struct _GdkWaylandWindowClass GdkWaylandWindowClass;

Matthias Clasen's avatar
Matthias Clasen committed
64 65
struct _GdkWaylandWindow
{
Kristian Høgsberg's avatar
Kristian Høgsberg committed
66 67 68
  GdkWindow parent;
};

Matthias Clasen's avatar
Matthias Clasen committed
69 70
struct _GdkWaylandWindowClass
{
Kristian Høgsberg's avatar
Kristian Høgsberg committed
71 72 73
  GdkWindowClass parent_class;
};

74
G_DEFINE_TYPE (GdkWaylandWindow, gdk_wayland_window, GDK_TYPE_WINDOW)
Kristian Høgsberg's avatar
Kristian Høgsberg committed
75 76

static void
77
gdk_wayland_window_class_init (GdkWaylandWindowClass *wayland_window_class)
Kristian Høgsberg's avatar
Kristian Høgsberg committed
78 79 80 81
{
}

static void
82
gdk_wayland_window_init (GdkWaylandWindow *wayland_window)
Kristian Høgsberg's avatar
Kristian Høgsberg committed
83 84 85
{
}

86 87 88 89 90 91 92 93 94 95
#define GDK_TYPE_WINDOW_IMPL_WAYLAND              (_gdk_window_impl_wayland_get_type ())
#define GDK_WINDOW_IMPL_WAYLAND(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), GDK_TYPE_WINDOW_IMPL_WAYLAND, GdkWindowImplWayland))
#define GDK_WINDOW_IMPL_WAYLAND_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), GDK_TYPE_WINDOW_IMPL_WAYLAND, GdkWindowImplWaylandClass))
#define GDK_IS_WINDOW_IMPL_WAYLAND(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDK_TYPE_WINDOW_IMPL_WAYLAND))
#define GDK_IS_WINDOW_IMPL_WAYLAND_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), GDK_TYPE_WINDOW_IMPL_WAYLAND))
#define GDK_WINDOW_IMPL_WAYLAND_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), GDK_TYPE_WINDOW_IMPL_WAYLAND, GdkWindowImplWaylandClass))

typedef struct _GdkWindowImplWayland GdkWindowImplWayland;
typedef struct _GdkWindowImplWaylandClass GdkWindowImplWaylandClass;

Jonas Ådahl's avatar
Jonas Ådahl committed
96 97 98 99 100 101 102
typedef enum _PositionMethod
{
  POSITION_METHOD_NONE,
  POSITION_METHOD_MOVE_RESIZE,
  POSITION_METHOD_MOVE_TO_RECT
} PositionMethod;

103 104 105 106 107 108 109
typedef struct _ExportedClosure
{
  GdkWaylandWindowExported callback;
  gpointer user_data;
  GDestroyNotify destroy_func;
} ExportedClosure;

110 111 112 113 114 115
struct _GdkWindowImplWayland
{
  GdkWindowImpl parent_instance;

  GdkWindow *wrapper;

116 117 118 119 120
  struct {
    /* The wl_outputs that this window currently touches */
    GSList               *outputs;

    struct wl_surface    *wl_surface;
121 122 123
    struct zxdg_surface_v6 *xdg_surface;
    struct zxdg_toplevel_v6 *xdg_toplevel;
    struct zxdg_popup_v6 *xdg_popup;
124
    struct gtk_surface1  *gtk_surface;
125 126 127
    struct wl_subsurface *wl_subsurface;
    struct wl_egl_window *egl_window;
    struct wl_egl_window *dummy_egl_window;
128
    struct zxdg_exported_v1 *xdg_exported;
129
    struct org_kde_kwin_server_decoration *server_decoration;
130
  } display_server;
131

132
  EGLSurface egl_surface;
133 134
  EGLSurface dummy_egl_surface;

135
  unsigned int initial_configure_received : 1;
136
  unsigned int mapped : 1;
137
  unsigned int use_custom_surface : 1;
138
  unsigned int pending_buffer_attached : 1;
139
  unsigned int pending_commit : 1;
140
  unsigned int awaiting_frame : 1;
141
  GdkWindowTypeHint hint;
142
  GdkWindow *transient_for;
143
  GdkWindow *popup_parent;
Jonas Ådahl's avatar
Jonas Ådahl committed
144
  PositionMethod position_method;
145

146 147 148 149
  cairo_surface_t *staging_cairo_surface;
  cairo_surface_t *committed_cairo_surface;
  cairo_surface_t *backfill_cairo_surface;

150 151
  int pending_buffer_offset_x;
  int pending_buffer_offset_y;
152

153 154
  gchar *title;

155 156 157 158 159 160 161 162 163 164 165
  struct {
    gboolean was_set;

    gchar *application_id;
    gchar *app_menu_path;
    gchar *menubar_path;
    gchar *window_object_path;
    gchar *application_object_path;
    gchar *unique_bus_name;
  } application;

166 167
  GdkGeometry geometry_hints;
  GdkWindowHints geometry_mask;
168

169
  GdkSeat *grab_input_seat;
170

171
  gint64 pending_frame_counter;
172
  guint32 scale;
173 174 175 176 177

  int margin_left;
  int margin_right;
  int margin_top;
  int margin_bottom;
178
  gboolean margin_dirty;
179

180
  int initial_fullscreen_monitor;
181 182

  cairo_region_t *opaque_region;
183 184
  gboolean opaque_region_dirty;

185
  cairo_region_t *input_region;
186 187
  gboolean input_region_dirty;

188
  cairo_region_t *staged_updates_region;
189 190 191

  int saved_width;
  int saved_height;
192 193

  gulong parent_surface_committed_handler;
194 195 196 197 198 199 200 201 202

  struct {
    GdkRectangle rect;
    GdkGravity rect_anchor;
    GdkGravity window_anchor;
    GdkAnchorHints anchor_hints;
    gint rect_anchor_dx;
    gint rect_anchor_dy;
  } pending_move_to_rect;
203 204

  struct {
205 206
    int width;
    int height;
207 208
    GdkWindowState state;
  } pending;
209 210

  struct {
211 212 213 214
    char *handle;
    int export_count;
    GList *closures;
    guint idle_source_id;
215
  } exported;
216 217

  struct zxdg_imported_v1 *imported_transient_for;
218
  GHashTable *shortcuts_inhibitors;
219 220 221 222 223 224 225
};

struct _GdkWindowImplWaylandClass
{
  GdkWindowImplClass parent_class;
};

226 227 228 229
static void gdk_wayland_window_maybe_configure (GdkWindow *window,
                                                int        width,
                                                int        height,
                                                int        scale);
230

231
static void maybe_set_gtk_surface_dbus_properties (GdkWindow *window);
232
static void maybe_set_gtk_surface_modal (GdkWindow *window);
233

234 235
static void gdk_window_request_transient_parent_commit (GdkWindow *window);

236 237 238 239
static void gdk_wayland_window_sync_margin (GdkWindow *window);
static void gdk_wayland_window_sync_input_region (GdkWindow *window);
static void gdk_wayland_window_sync_opaque_region (GdkWindow *window);

240 241
static void unset_transient_for_exported (GdkWindow *window);

242 243 244 245 246 247 248 249 250 251
static void calculate_moved_to_rect_result (GdkWindow    *window,
                                            int           x,
                                            int           y,
                                            int           width,
                                            int           height,
                                            GdkRectangle *flipped_rect,
                                            GdkRectangle *final_rect,
                                            gboolean     *flipped_x,
                                            gboolean     *flipped_y);

252
static gboolean gdk_wayland_window_is_exported (GdkWindow *window);
253
static void gdk_wayland_window_unexport (GdkWindow *window);
254

255 256
GType _gdk_window_impl_wayland_get_type (void);

Kristian Høgsberg's avatar
Kristian Høgsberg committed
257 258 259 260
G_DEFINE_TYPE (GdkWindowImplWayland, _gdk_window_impl_wayland, GDK_TYPE_WINDOW_IMPL)

static void
_gdk_window_impl_wayland_init (GdkWindowImplWayland *impl)
261
{
262
  impl->scale = 1;
263
  impl->initial_fullscreen_monitor = -1;
264 265
  impl->saved_width = -1;
  impl->saved_height = -1;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
266 267
}

268 269 270
static void
_gdk_wayland_screen_add_orphan_dialog (GdkWindow *window)
{
271 272 273 274 275 276
  GdkWaylandDisplay *display_wayland =
    GDK_WAYLAND_DISPLAY (gdk_window_get_display (window));

  if (!g_list_find (display_wayland->orphan_dialogs, window))
    display_wayland->orphan_dialogs =
      g_list_prepend (display_wayland->orphan_dialogs, window);
277 278
}

279 280 281 282 283 284 285 286 287 288 289 290 291 292
static void
drop_cairo_surfaces (GdkWindow *window)
{
  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);

  g_clear_pointer (&impl->staging_cairo_surface, cairo_surface_destroy);
  g_clear_pointer (&impl->backfill_cairo_surface, cairo_surface_destroy);

  /* We nullify this so if a buffer release comes in later, we won't
   * try to reuse that buffer since it's no longer suitable
   */
  impl->committed_cairo_surface = NULL;
}

293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
static void
_gdk_wayland_window_save_size (GdkWindow *window)
{
  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);

  if (window->state & (GDK_WINDOW_STATE_FULLSCREEN | GDK_WINDOW_STATE_MAXIMIZED))
    return;

  impl->saved_width = window->width - impl->margin_left - impl->margin_right;
  impl->saved_height = window->height - impl->margin_top - impl->margin_bottom;
}

static void
_gdk_wayland_window_clear_saved_size (GdkWindow *window)
{
  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);

  if (window->state & (GDK_WINDOW_STATE_FULLSCREEN | GDK_WINDOW_STATE_MAXIMIZED))
    return;

  impl->saved_width = -1;
  impl->saved_height = -1;
}

317
/*
318
 * gdk_wayland_window_update_size:
Kristian Høgsberg's avatar
Kristian Høgsberg committed
319
 * @drawable: a #GdkDrawableImplWayland.
Matthias Clasen's avatar
Matthias Clasen committed
320
 *
Kristian Høgsberg's avatar
Kristian Høgsberg committed
321 322
 * Updates the state of the drawable (in particular the drawable's
 * cairo surface) when its size has changed.
Matthias Clasen's avatar
Matthias Clasen committed
323
 */
324 325
static void
gdk_wayland_window_update_size (GdkWindow *window,
Matthias Clasen's avatar
Matthias Clasen committed
326
                                int32_t    width,
327 328
                                int32_t    height,
                                int        scale)
Kristian Høgsberg's avatar
Kristian Høgsberg committed
329
{
330
  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
331 332
  GdkRectangle area;
  cairo_region_t *region;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
333

334 335 336 337 338
  if ((window->width == width) &&
      (window->height == height) &&
      (impl->scale == scale))
    return;

339
  drop_cairo_surfaces (window);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
340

341 342
  window->width = width;
  window->height = height;
343
  impl->scale = scale;
344

345
  if (impl->display_server.egl_window)
346 347 348
    wl_egl_window_resize (impl->display_server.egl_window, width * scale, height * scale, 0, 0);
  if (impl->display_server.wl_surface)
    wl_surface_set_buffer_scale (impl->display_server.wl_surface, scale);
349

350 351 352 353 354 355 356 357
  area.x = 0;
  area.y = 0;
  area.width = window->width;
  area.height = window->height;

  region = cairo_region_create_rectangle (&area);
  _gdk_window_invalidate_for_expose (window, region);
  cairo_region_destroy (region);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
358 359 360 361
}

GdkWindow *
_gdk_wayland_screen_create_root_window (GdkScreen *screen,
Matthias Clasen's avatar
Matthias Clasen committed
362 363
                                        int        width,
                                        int        height)
Kristian Høgsberg's avatar
Kristian Høgsberg committed
364
{
365
  GdkWaylandDisplay *display_wayland =
366
    GDK_WAYLAND_DISPLAY (gdk_screen_get_display (screen));
Kristian Høgsberg's avatar
Kristian Høgsberg committed
367 368 369
  GdkWindow *window;
  GdkWindowImplWayland *impl;

370
  window = _gdk_display_create_window (GDK_DISPLAY (display_wayland));
Kristian Høgsberg's avatar
Kristian Høgsberg committed
371 372 373 374 375 376 377
  window->impl = g_object_new (GDK_TYPE_WINDOW_IMPL_WAYLAND, NULL);
  window->impl_window = window;
  window->visual = gdk_screen_get_system_visual (screen);

  impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);

  impl->wrapper = GDK_WINDOW (window);
378
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
379
  if (display_wayland->compositor_version >= WL_SURFACE_HAS_BUFFER_SCALE &&
Matthias Clasen's avatar
Matthias Clasen committed
380
      gdk_screen_get_n_monitors (screen) > 0)
381
    impl->scale = gdk_screen_get_monitor_scale_factor (screen, 0);
382
G_GNUC_END_IGNORE_DEPRECATIONS
383 384

  /* logical 1x1 fake buffer */
385 386 387
  impl->staging_cairo_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
                                                            impl->scale,
                                                            impl->scale);
388

389
  cairo_surface_set_device_scale (impl->staging_cairo_surface, impl->scale, impl->scale);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421

  window->window_type = GDK_WINDOW_ROOT;
  window->depth = 32;

  window->x = 0;
  window->y = 0;
  window->abs_x = 0;
  window->abs_y = 0;
  window->width = width;
  window->height = height;
  window->viewable = TRUE;

  /* see init_randr_support() in gdkscreen-wayland.c */
  window->event_mask = GDK_STRUCTURE_MASK;

  return window;
}

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

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

  return title;
}

422 423 424 425 426 427 428 429 430 431
static void
fill_presentation_time_from_frame_time (GdkFrameTimings *timings,
                                        guint32          frame_time)
{
  /* The timestamp in a wayland frame is a msec time value that in some
   * way reflects the time at which the server started drawing the frame.
   * This is not useful from our perspective.
   *
   * However, for the DRM backend of Weston, on reasonably recent
   * Linux, we know that the time is the
Matthias Clasen's avatar
Matthias Clasen committed
432
   * clock_gettime (CLOCK_MONOTONIC) value at the vblank, and that
433 434 435 436
   * backend starts drawing immediately after receiving the vblank
   * notification. If we detect this, and make the assumption that the
   * compositor will finish drawing before the next vblank, we can
   * then determine the presentation time as the frame time we
Matthias Clasen's avatar
Matthias Clasen committed
437
   * received plus one refresh interval.
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
   *
   * If a backend is using clock_gettime(CLOCK_MONOTONIC), but not
   * picking values right at the vblank, then the presentation times
   * we compute won't be accurate, but not really worse than then
   * the alternative of not providing presentation times at all.
   *
   * The complexity here is dealing with the fact that we receive
   * only the low 32 bits of the CLOCK_MONOTONIC value in milliseconds.
   */
  gint64 now_monotonic = g_get_monotonic_time ();
  gint64 now_monotonic_msec = now_monotonic / 1000;
  uint32_t now_monotonic_low = (uint32_t)now_monotonic_msec;

  if (frame_time - now_monotonic_low < 1000 ||
      frame_time - now_monotonic_low > (uint32_t)-1000)
    {
      /* Timestamp we received is within one second of the current time.
       */
      gint64 last_frame_time = now_monotonic + (gint64)1000 * (gint32)(frame_time - now_monotonic_low);
      if ((gint32)now_monotonic_low < 0 && (gint32)frame_time > 0)
        last_frame_time += (gint64)1000 * G_GINT64_CONSTANT(0x100000000);
      else if ((gint32)now_monotonic_low > 0 && (gint32)frame_time < 0)
        last_frame_time -= (gint64)1000 * G_GINT64_CONSTANT(0x100000000);

      timings->presentation_time = last_frame_time + timings->refresh_interval;
    }
}

466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
static void
read_back_cairo_surface (GdkWindow *window)
{
  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
  cairo_t *cr;
  cairo_region_t *paint_region = NULL;

  if (!impl->backfill_cairo_surface)
    goto out;

  paint_region = cairo_region_copy (window->clip_region);
  cairo_region_subtract (paint_region, impl->staged_updates_region);

  if (cairo_region_is_empty (paint_region))
    goto out;

  cr = cairo_create (impl->staging_cairo_surface);
  cairo_set_source_surface (cr, impl->backfill_cairo_surface, 0, 0);
  gdk_cairo_region (cr, paint_region);
  cairo_clip (cr);
  cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
  cairo_paint (cr);
  cairo_destroy (cr);
  cairo_surface_flush (impl->staging_cairo_surface);

out:
  g_clear_pointer (&paint_region, cairo_region_destroy);
  g_clear_pointer (&impl->staged_updates_region, cairo_region_destroy);
  g_clear_pointer (&impl->backfill_cairo_surface, cairo_surface_destroy);
}

497 498 499 500 501 502 503
static void
frame_callback (void               *data,
                struct wl_callback *callback,
                uint32_t            time)
{
  GdkWindow *window = data;
  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
504 505
  GdkWaylandDisplay *display_wayland =
    GDK_WAYLAND_DISPLAY (gdk_window_get_display (window));
506 507 508
  GdkFrameClock *clock = gdk_window_get_frame_clock (window);
  GdkFrameTimings *timings;

Matthias Clasen's avatar
Matthias Clasen committed
509 510 511
  GDK_NOTE (EVENTS,
            g_message ("frame %p", window));

512
  wl_callback_destroy (callback);
513 514 515 516

  if (GDK_WINDOW_DESTROYED (window))
    return;

517 518 519 520
  if (!impl->awaiting_frame)
    return;

  impl->awaiting_frame = FALSE;
521 522 523 524 525 526 527 528
  _gdk_frame_clock_thaw (clock);

  timings = gdk_frame_clock_get_timings (clock, impl->pending_frame_counter);
  impl->pending_frame_counter = 0;

  if (timings == NULL)
    return;

529
  timings->refresh_interval = 16667; /* default to 1/60th of a second */
530
  if (impl->display_server.outputs)
531 532 533
    {
      /* We pick a random output out of the outputs that the window touches
       * The rate here is in milli-hertz */
534 535 536
      int refresh_rate =
        _gdk_wayland_screen_get_output_refresh_rate (display_wayland->screen,
                                                     impl->display_server.outputs->data);
537 538 539 540
      if (refresh_rate != 0)
        timings->refresh_interval = G_GINT64_CONSTANT(1000000000) / refresh_rate;
    }

541 542
  fill_presentation_time_from_frame_time (timings, time);

543 544 545 546 547 548 549 550
  timings->complete = TRUE;

#ifdef G_ENABLE_DEBUG
  if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0)
    _gdk_frame_clock_debug_print_timings (clock, timings);
#endif
}

551
static const struct wl_callback_listener frame_listener = {
552 553 554 555 556 557 558
  frame_callback
};

static void
on_frame_clock_before_paint (GdkFrameClock *clock,
                             GdkWindow     *window)
{
559 560 561 562
  GdkFrameTimings *timings = gdk_frame_clock_get_current_timings (clock);
  gint64 presentation_time;
  gint64 refresh_interval;

563 564 565
  if (window->update_freeze_count > 0)
    return;

566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585
  gdk_frame_clock_get_refresh_info (clock,
                                    timings->frame_time,
                                    &refresh_interval, &presentation_time);

  if (presentation_time != 0)
    {
      /* Assume the algorithm used by the DRM backend of Weston - it
       * starts drawing at the next vblank after receiving the commit
       * for this frame, and presentation occurs at the vblank
       * after that.
       */
      timings->predicted_presentation_time = presentation_time + refresh_interval;
    }
  else
    {
      /* As above, but we don't actually know the phase of the vblank,
       * so just assume that we're half way through a refresh cycle.
       */
      timings->predicted_presentation_time = timings->frame_time + refresh_interval / 2 + refresh_interval;
    }
586 587 588 589 590 591 592 593 594 595 596 597
}

static void
on_frame_clock_after_paint (GdkFrameClock *clock,
                            GdkWindow     *window)
{
  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
  struct wl_callback *callback;

  if (!impl->pending_commit)
    return;

598 599 600
  if (window->update_freeze_count > 0)
    return;

601
  callback = wl_surface_frame (impl->display_server.wl_surface);
602
  wl_callback_add_listener (callback, &frame_listener, window);
603 604
  _gdk_frame_clock_freeze (clock);

605 606 607
  /* Before we commit a new buffer, make sure we've backfilled
   * undrawn parts from any old committed buffer
   */
608 609
  if (impl->pending_buffer_attached)
    read_back_cairo_surface (window);
610 611 612 613 614 615 616 617 618

  /* From this commit forward, we can't write to the buffer,
   * it's "live".  In the future, if we need to stage more changes
   * we have to allocate a new staging buffer and draw to it instead.
   *
   * Our one saving grace is if the compositor releases the buffer
   * before we need to stage any changes, then we can take it back and
   * use it again.
   */
619
  wl_surface_commit (impl->display_server.wl_surface);
620 621 622 623 624 625 626 627

  if (impl->pending_buffer_attached)
    impl->committed_cairo_surface = g_steal_pointer (&impl->staging_cairo_surface);

  impl->pending_buffer_attached = FALSE;
  impl->pending_commit = FALSE;
  impl->pending_frame_counter = gdk_frame_clock_get_frame_counter (clock);
  impl->awaiting_frame = TRUE;
628 629

  g_signal_emit (impl, signals[COMMITTED], 0);
630 631
}

632 633 634 635
static void
window_update_scale (GdkWindow *window)
{
  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
636 637
  GdkWaylandDisplay *display_wayland =
    GDK_WAYLAND_DISPLAY (gdk_window_get_display (window));
638 639 640
  guint32 scale;
  GSList *l;

641
  if (display_wayland->compositor_version < WL_SURFACE_HAS_BUFFER_SCALE)
642 643 644 645 646
    {
      /* We can't set the scale on this surface */
      return;
    }

647
  scale = 1;
648
  for (l = impl->display_server.outputs; l != NULL; l = l->next)
649 650
    {
      guint32 output_scale =
651
        _gdk_wayland_screen_get_output_scale (display_wayland->screen, l->data);
652 653 654
      scale = MAX (scale, output_scale);
    }

655
  /* Notify app that scale changed */
656
  gdk_wayland_window_maybe_configure (window, window->width, window->height, scale);
657 658 659 660
}

static void
on_monitors_changed (GdkScreen *screen,
Matthias Clasen's avatar
Matthias Clasen committed
661
                     GdkWindow *window)
662 663 664 665
{
  window_update_scale (window);
}

666

Matthias Clasen's avatar
Matthias Clasen committed
667
static void gdk_wayland_window_create_surface (GdkWindow *window);
668

Kristian Høgsberg's avatar
Kristian Høgsberg committed
669 670
void
_gdk_wayland_display_create_window_impl (GdkDisplay    *display,
Matthias Clasen's avatar
Matthias Clasen committed
671 672 673 674 675 676
                                         GdkWindow     *window,
                                         GdkWindow     *real_parent,
                                         GdkScreen     *screen,
                                         GdkEventMask   event_mask,
                                         GdkWindowAttr *attributes,
                                         gint           attributes_mask)
Kristian Høgsberg's avatar
Kristian Høgsberg committed
677
{
678
  GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
679
  GdkWindowImplWayland *impl;
680
  GdkFrameClock *frame_clock;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
681 682 683 684 685
  const char *title;

  impl = g_object_new (GDK_TYPE_WINDOW_IMPL_WAYLAND, NULL);
  window->impl = GDK_WINDOW_IMPL (impl);
  impl->wrapper = GDK_WINDOW (window);
686
  impl->shortcuts_inhibitors = g_hash_table_new (NULL, NULL);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
687

Matthias Clasen's avatar
Matthias Clasen committed
688
  if (window->width > 65535)
Kristian Høgsberg's avatar
Kristian Høgsberg committed
689
    {
Matthias Clasen's avatar
Matthias Clasen committed
690 691 692 693 694 695 696
      g_warning ("Native Windows wider than 65535 pixels are not supported");
      window->width = 65535;
    }
  if (window->height > 65535)
    {
      g_warning ("Native Windows taller than 65535 pixels are not supported");
      window->height = 65535;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
697
    }
698

Kristian Høgsberg's avatar
Kristian Høgsberg committed
699 700
  g_object_ref (window);

701
  /* More likely to be right than just assuming 1 */
702
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
703
  if (display_wayland->compositor_version >= WL_SURFACE_HAS_BUFFER_SCALE &&
704
      gdk_screen_get_n_monitors (screen) > 0)
705
    impl->scale = gdk_screen_get_monitor_scale_factor (screen, 0);
706
G_GNUC_END_IGNORE_DEPRECATIONS
707

708 709
  impl->title = NULL;

Kristian Høgsberg's avatar
Kristian Høgsberg committed
710 711 712 713 714
  switch (GDK_WINDOW_TYPE (window))
    {
    case GDK_WINDOW_TOPLEVEL:
    case GDK_WINDOW_TEMP:
      if (attributes_mask & GDK_WA_TITLE)
Matthias Clasen's avatar
Matthias Clasen committed
715
        title = attributes->title;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
716
      else
Matthias Clasen's avatar
Matthias Clasen committed
717
        title = get_default_title ();
Kristian Høgsberg's avatar
Kristian Høgsberg committed
718 719 720 721 722 723 724 725 726

      gdk_window_set_title (window, title);
      break;

    case GDK_WINDOW_CHILD:
    default:
      break;
    }

727 728
  gdk_wayland_window_create_surface (window);

Kristian Høgsberg's avatar
Kristian Høgsberg committed
729 730
  if (attributes_mask & GDK_WA_TYPE_HINT)
    gdk_window_set_type_hint (window, attributes->type_hint);
731 732 733 734 735 736 737

  frame_clock = gdk_window_get_frame_clock (window);

  g_signal_connect (frame_clock, "before-paint",
                    G_CALLBACK (on_frame_clock_before_paint), window);
  g_signal_connect (frame_clock, "after-paint",
                    G_CALLBACK (on_frame_clock_after_paint), window);
738 739 740

  g_signal_connect (screen, "monitors-changed",
                    G_CALLBACK (on_monitors_changed), window);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
741 742
}

743 744 745
static void
gdk_wayland_window_attach_image (GdkWindow *window)
{
746
  GdkWaylandDisplay *display;
747
  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
748

749 750 751
  if (GDK_WINDOW_DESTROYED (window))
    return;

752
  g_assert (_gdk_wayland_is_shm_surface (impl->staging_cairo_surface));
753

754
  /* Attach this new buffer to the surface */
755
  wl_surface_attach (impl->display_server.wl_surface,
756
                     _gdk_wayland_shm_surface_get_wl_buffer (impl->staging_cairo_surface),
757 758 759 760
                     impl->pending_buffer_offset_x,
                     impl->pending_buffer_offset_y);
  impl->pending_buffer_offset_x = 0;
  impl->pending_buffer_offset_y = 0;
761 762 763 764

  /* Only set the buffer scale if supported by the compositor */
  display = GDK_WAYLAND_DISPLAY (gdk_window_get_display (window));
  if (display->compositor_version >= WL_SURFACE_HAS_BUFFER_SCALE)
765
    wl_surface_set_buffer_scale (impl->display_server.wl_surface, impl->scale);
766

767
  impl->pending_buffer_attached = TRUE;
768
  impl->pending_commit = TRUE;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
769 770
}

771 772
static const cairo_user_data_key_t gdk_wayland_window_cairo_key;

773 774 775 776 777
static void
buffer_release_callback (void             *_data,
                         struct wl_buffer *wl_buffer)
{
  cairo_surface_t *cairo_surface = _data;
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795
  GdkWindowImplWayland *impl = cairo_surface_get_user_data (cairo_surface, &gdk_wayland_window_cairo_key);

  g_return_if_fail (GDK_IS_WINDOW_IMPL_WAYLAND (impl));

  /* The released buffer isn't the latest committed one, we have no further
   * use for it, so clean it up.
   */
  if (impl->committed_cairo_surface != cairo_surface)
    {
      /* If this fails, then the surface buffer got reused before it was
       * released from the compositor
       */
      g_warn_if_fail (impl->staging_cairo_surface != cairo_surface);

      cairo_surface_destroy (cairo_surface);
      return;
    }

796
  if (impl->staged_updates_region != NULL)
797
    {
798 799
      /* If this fails, then we're tracking staged updates on a staging surface
       * that doesn't exist.
800
       */
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821
      g_warn_if_fail (impl->staging_cairo_surface != NULL);

      /* If we've staged updates into a new buffer before the release for this
       * buffer came in, then we can't reuse this buffer, so unref it. It may still
       * be alive as a readback buffer though (via impl->backfill_cairo_surface).
       *
       * It's possible a staging surface was allocated but no updates were staged.
       * If that happened, clean up that staging surface now, since the old commit
       * buffer is available again, and reusing the old commit buffer for future
       * updates will save having to do a read back later.
       */
      if (!cairo_region_is_empty (impl->staged_updates_region))
        {
          g_clear_pointer (&impl->committed_cairo_surface, cairo_surface_destroy);
          return;
        }
      else
        {
          g_clear_pointer (&impl->staged_updates_region, cairo_region_destroy);
          g_clear_pointer (&impl->staging_cairo_surface, cairo_surface_destroy);
        }
822
    }
823

824 825 826 827
  /* Release came in, we haven't done any interim updates, so we can just use
   * the old committed buffer again.
   */
  impl->staging_cairo_surface = g_steal_pointer (&impl->committed_cairo_surface);
828 829 830 831 832 833
}

static const struct wl_buffer_listener buffer_listener = {
  buffer_release_callback
};

834 835
static void
gdk_wayland_window_ensure_cairo_surface (GdkWindow *window)
Kristian Høgsberg's avatar
Kristian Høgsberg committed
836 837
{
  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
838 839

  /* If we are drawing using OpenGL then we only need a logical 1x1 surface. */
840
  if (impl->display_server.egl_window)
841
    {
842 843
      if (impl->staging_cairo_surface &&
          _gdk_wayland_is_shm_surface (impl->staging_cairo_surface))
844
        g_clear_pointer (&impl->staging_cairo_surface, cairo_surface_destroy);
845

846 847 848 849 850 851 852 853
      if (!impl->staging_cairo_surface)
        {
          impl->staging_cairo_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
                                                                    impl->scale,
                                                                    impl->scale);
          cairo_surface_set_device_scale (impl->staging_cairo_surface,
                                          impl->scale, impl->scale);
        }
854
    }
855
  else if (!impl->staging_cairo_surface)
Kristian Høgsberg's avatar
Kristian Høgsberg committed
856
    {
857
      GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (gdk_window_get_display (impl->wrapper));
858
      struct wl_buffer *buffer;
859

860 861 862 863 864 865 866 867 868 869 870
      impl->staging_cairo_surface = _gdk_wayland_display_create_shm_surface (display_wayland,
                                                                             impl->wrapper->width,
                                                                             impl->wrapper->height,
                                                                             impl->scale);
      cairo_surface_set_user_data (impl->staging_cairo_surface,
                                   &gdk_wayland_window_cairo_key,
                                   g_object_ref (impl),
                                   (cairo_destroy_func_t)
                                   g_object_unref);
      buffer = _gdk_wayland_shm_surface_get_wl_buffer (impl->staging_cairo_surface);
      wl_buffer_add_listener (buffer, &buffer_listener, impl->staging_cairo_surface);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
871
    }
872 873
}

874 875 876 877 878 879
/* The cairo surface returned here uses a memory segment that's shared
 * with the display server.  This is not a temporary buffer that gets
 * copied to the display server, but the actual buffer the display server
 * will ultimately end up sending to the GPU. At the time this happens
 * impl->committed_cairo_surface gets set to impl->staging_cairo_surface, and
 * impl->staging_cairo_surface gets nullified.
880 881 882 883 884 885 886 887 888 889
 */
static cairo_surface_t *
gdk_wayland_window_ref_cairo_surface (GdkWindow *window)
{
  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);

  if (GDK_WINDOW_DESTROYED (impl->wrapper))
    return NULL;

  gdk_wayland_window_ensure_cairo_surface (window);
890

891
  cairo_surface_reference (impl->staging_cairo_surface);
Kristian Høgsberg's avatar
Kristian Høgsberg committed
892

893
  return impl->staging_cairo_surface;
Kristian Høgsberg's avatar
Kristian Høgsberg committed
894 895
}

896 897 898 899 900 901 902 903
static cairo_surface_t *
gdk_wayland_window_create_similar_image_surface (GdkWindow *     window,
                                                 cairo_format_t  format,
                                                 int             width,
                                                 int             height)
{
  return cairo_image_surface_create (format, width, height);
}
904

905
static gboolean
906
gdk_window_impl_wayland_begin_paint (GdkWindow *window)
907 908
{
  gdk_wayland_window_ensure_cairo_surface (window);
Matthias Clasen's avatar
Matthias Clasen committed
909

910
  return FALSE;
911 912
}

913 914 915 916 917 918 919
static void
gdk_window_impl_wayland_end_paint (GdkWindow *window)
{
  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
  cairo_rectangle_int_t rect;
  int i, n;

920 921 922
  if (impl->staging_cairo_surface &&
      _gdk_wayland_is_shm_surface (impl->staging_cairo_surface) &&
      !window->current_paint.use_gl &&
923
      !cairo_region_is_empty (window->current_paint.region))
924
    {
925 926
      gdk_wayland_window_attach_image (window);

927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944
      /* If there's a committed buffer pending, then track which
       * updates are staged until the next frame, so we can back
       * fill the unstaged parts of the staging buffer with the
       * last frame.
       */
      if (impl->committed_cairo_surface != NULL)
        {
          if (impl->staged_updates_region == NULL)
            {
              impl->staged_updates_region = cairo_region_copy (window->current_paint.region);
              impl->backfill_cairo_surface = cairo_surface_reference (impl->committed_cairo_surface);
            }
          else
            {
              cairo_region_union (impl->staged_updates_region, window->current_paint.region);
            }
        }

945 946 947 948
      n = cairo_region_num_rectangles (window->current_paint.region);
      for (i = 0; i < n; i++)
        {
          cairo_region_get_rectangle (window->current_paint.region, i, &rect);
949
          wl_surface_damage (impl->display_server.wl_surface, rect.x, rect.y, rect.width, rect.height);
950
        }
951 952

      impl->pending_commit = TRUE;
953
    }
954 955 956 957

  gdk_wayland_window_sync_margin (window);
  gdk_wayland_window_sync_opaque_region (window);
  gdk_wayland_window_sync_input_region (window);
958 959
}

960 961 962
static gboolean
gdk_window_impl_wayland_beep (GdkWindow *window)
{
963 964
  gdk_wayland_display_system_bell (gdk_window_get_display (window),
                                   window);
965 966 967 968

  return TRUE;
}

969 970 971
static void
gdk_window_impl_wayland_finalize (GObject *object)
{
972
  GdkWindow *window = GDK_WINDOW (object);
973 974 975 976 977 978
  GdkWindowImplWayland *impl;

  g_return_if_fail (GDK_IS_WINDOW_IMPL_WAYLAND (object));

  impl = GDK_WINDOW_IMPL_WAYLAND (object);

979 980 981
  if (gdk_wayland_window_is_exported (window))
    gdk_wayland_window_unexport_handle (window);

982 983
  g_free (impl->title);

984 985 986 987 988 989 990
  g_free (impl->application.application_id);
  g_free (impl->application.app_menu_path);
  g_free (impl->application.menubar_path);
  g_free (impl->application.window_object_path);
  g_free (impl->application.application_object_path);
  g_free (impl->application.unique_bus_name);

991
  g_clear_pointer (&impl->opaque_region, cairo_region_destroy);
992
  g_clear_pointer (&impl->input_region, cairo_region_destroy);
993
  g_clear_pointer (&impl->staged_updates_region, cairo_region_destroy);
994

995 996
  g_hash_table_destroy (impl->shortcuts_inhibitors);

997 998 999
  G_OBJECT_CLASS (_gdk_window_impl_wayland_parent_class)->finalize (object);
}

1000
static void
1001
gdk_wayland_window_configure (GdkWindow *window,
Matthias Clasen's avatar
Matthias Clasen committed
1002
                              int        width,
1003 1004
                              int        height,
                              int        scale)
1005 1006 1007 1008 1009
{
  GdkDisplay *display;
  GdkEvent *event;

  event = gdk_event_new (GDK_CONFIGURE);
1010
  event->configure.window = g_object_ref (window);
1011 1012 1013 1014
  event->configure.send_event = FALSE;
  event->configure.width = width;
  event->configure.height = height;

1015
  gdk_wayland_window_update_size (window, width, height, scale);
1016
  _gdk_window_update_size (window);
1017

1018
  display = gdk_window_get_display (window);
1019 1020 1021
  _gdk_wayland_display_deliver_event (display, event);
}

1022 1023 1024 1025 1026 1027 1028
static void
gdk_wayland_window_maybe_configure (GdkWindow *window,
                                    int        width,
                                    int        height,
                                    int        scale)
{
  GdkWindowImplWayland *impl = GDK_WINDOW_IMPL_WAYLAND (window->impl);
1029 1030
  gboolean is_xdg_popup;
  gboolean is_visible;
1031 1032 1033 1034 1035 1036

  if (window->width == width &&
      window->height == height &&
      impl->scale == scale)
    return;