[mutter/gbsneto/cleanup-x11-from-window-actor: 5/10] window-actor: Move X11-specific code to MetaWindowActorX11



commit 80e3c1de57101d932e67742a0d06bf705f8907b7
Author: Georges Basile Stavracas Neto <georges stavracas gmail com>
Date:   Fri Dec 21 18:51:02 2018 -0200

    window-actor: Move X11-specific code to MetaWindowActorX11
    
    MetaWindowActor handles sending _NET_WM_FRAME_* X atoms to
    clients - even pure Wayland clients.
    
    Now that we have Wayland- and X11-specific implementations of
    MetaWindowActor, we can delegate this to MetaWindowActorX11,
    and allow pure Wayland apps to not even connect to
    MetaSurfaceActor:repaint-scheduled.
    
    Do that by moving all the X11-specific code to the X11-specific
    MetaWindowActorX11 class. Add vfuncs to MetaWindowActorClass
    that are necessary for the move, namely:
    
     * pre_paint() and post_paint()
     * post_init()
     * frame_complete()
     * set_surface_actor()
     * queue_frame_drawn()
    
    https://gitlab.gnome.org/GNOME/mutter/merge_requests/368

 src/compositor/meta-window-actor-private.h |  16 +
 src/compositor/meta-window-actor-wayland.c |  53 +++
 src/compositor/meta-window-actor-x11.c     | 499 +++++++++++++++++++++++++++++
 src/compositor/meta-window-actor.c         | 412 +-----------------------
 4 files changed, 584 insertions(+), 396 deletions(-)
---
diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h
index af371afd7..955577792 100644
--- a/src/compositor/meta-window-actor-private.h
+++ b/src/compositor/meta-window-actor-private.h
@@ -12,6 +12,22 @@
 struct _MetaWindowActorClass
 {
   ClutterActorClass parent;
+
+  void (*frame_complete) (MetaWindowActor  *actor,
+                          ClutterFrameInfo *frame_info,
+                          int64_t           presentation_time);
+
+  void (*set_surface_actor) (MetaWindowActor  *actor,
+                             MetaSurfaceActor *surface);
+
+  void (*queue_frame_drawn) (MetaWindowActor *actor,
+                             gboolean         skip_sync_delay);
+
+  void (*post_init) (MetaWindowActor *actor);
+
+  void (*pre_paint) (MetaWindowActor *actor);
+  void (*post_paint) (MetaWindowActor *actor);
+  void (*queue_destroy) (MetaWindowActor *actor);
 };
 
 MetaWindowActor *meta_window_actor_new (MetaWindow *window);
diff --git a/src/compositor/meta-window-actor-wayland.c b/src/compositor/meta-window-actor-wayland.c
index 851b413a2..7083d5507 100644
--- a/src/compositor/meta-window-actor-wayland.c
+++ b/src/compositor/meta-window-actor-wayland.c
@@ -20,6 +20,7 @@
  *     Georges Basile Stavracas Neto <gbsneto gnome org>
  */
 
+#include "compositor/meta-surface-actor.h"
 #include "compositor/meta-window-actor-wayland.h"
 #include "meta/meta-window-actor.h"
 
@@ -30,9 +31,61 @@ struct _MetaWindowActorWayland
 
 G_DEFINE_TYPE (MetaWindowActorWayland, meta_window_actor_wayland, META_TYPE_WINDOW_ACTOR)
 
+static void
+meta_window_actor_wayland_frame_complete (MetaWindowActor  *actor,
+                                          ClutterFrameInfo *frame_info,
+                                          int64_t           presentation_time)
+{
+}
+
+static void
+meta_window_actor_wayland_set_surface_actor (MetaWindowActor  *actor,
+                                             MetaSurfaceActor *surface)
+{
+  MetaWindowActorClass *parent_class =
+    META_WINDOW_ACTOR_CLASS (meta_window_actor_wayland_parent_class);
+
+  parent_class->set_surface_actor (actor, surface);
+}
+
+static void
+meta_window_actor_wayland_queue_frame_drawn (MetaWindowActor *actor,
+                                             gboolean         skip_sync_delay)
+{
+}
+
+static void
+meta_window_actor_wayland_post_init (MetaWindowActor *actor)
+{
+}
+
+static void
+meta_window_actor_wayland_pre_paint (MetaWindowActor *actor)
+{
+}
+
+static void
+meta_window_actor_wayland_post_paint (MetaWindowActor *actor)
+{
+}
+
+static void
+meta_window_actor_wayland_queue_destroy (MetaWindowActor *actor)
+{
+}
+
 static void
 meta_window_actor_wayland_class_init (MetaWindowActorWaylandClass *klass)
 {
+  MetaWindowActorClass *window_actor_class = META_WINDOW_ACTOR_CLASS (klass);
+
+  window_actor_class->frame_complete = meta_window_actor_wayland_frame_complete;
+  window_actor_class->set_surface_actor = meta_window_actor_wayland_set_surface_actor;
+  window_actor_class->queue_frame_drawn = meta_window_actor_wayland_queue_frame_drawn;
+  window_actor_class->post_init = meta_window_actor_wayland_post_init;
+  window_actor_class->pre_paint = meta_window_actor_wayland_pre_paint;
+  window_actor_class->post_paint = meta_window_actor_wayland_post_paint;
+  window_actor_class->queue_destroy = meta_window_actor_wayland_queue_destroy;
 }
 
 static void
diff --git a/src/compositor/meta-window-actor-x11.c b/src/compositor/meta-window-actor-x11.c
index fe41e81f0..32548898d 100644
--- a/src/compositor/meta-window-actor-x11.c
+++ b/src/compositor/meta-window-actor-x11.c
@@ -20,19 +20,518 @@
  *     Georges Basile Stavracas Neto <gbsneto gnome org>
  */
 
+#include "backends/meta-logical-monitor.h"
+#include "compositor/compositor-private.h"
+#include "compositor/meta-surface-actor.h"
 #include "compositor/meta-window-actor-x11.h"
+#include "core/window-private.h"
+#include "meta/compositor.h"
 #include "meta/meta-window-actor.h"
+#include "meta/meta-x11-errors.h"
+#include "meta/window.h"
+#include "x11/meta-x11-display-private.h"
 
 struct _MetaWindowActorX11
 {
   MetaWindowActor parent;
+
+  /* List of FrameData for recent frames */
+  GList *frames;
+
+  guint send_frame_messages_timer;
+  int64_t frame_drawn_time;
+
+  guint repaint_scheduled_id;
+
+  /* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
+   * client message for one or more messages in ->frames */
+  gboolean needs_frame_drawn;
+  gboolean repaint_scheduled;
 };
 
 G_DEFINE_TYPE (MetaWindowActorX11, meta_window_actor_x11, META_TYPE_WINDOW_ACTOR)
 
+/* Each time the application updates the sync request counter to a new even value
+ * value, we queue a frame into the windows list of frames. Once we're painting
+ * an update "in response" to the window, we fill in frame_counter with the
+ * Cogl counter for that frame, and send _NET_WM_FRAME_DRAWN at the end of the
+ * frame. _NET_WM_FRAME_TIMINGS is sent when we get a frame_complete callback.
+ *
+ * As an exception, if a window is completely obscured, we try to throttle drawning
+ * to a slower frame rate. In this case, frame_counter stays -1 until
+ * send_frame_message_timeout() runs, at which point we send both the
+ * _NET_WM_FRAME_DRAWN and _NET_WM_FRAME_TIMINGS messages.
+ */
+typedef struct
+{
+  uint64_t sync_request_serial;
+  int64_t frame_counter;
+  int64_t frame_drawn_time;
+} FrameData;
+
+static void
+frame_data_free (FrameData *frame)
+{
+  g_slice_free (FrameData, frame);
+}
+
+static void
+surface_repaint_scheduled (MetaSurfaceActor *actor,
+                           gpointer          user_data)
+{
+  MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (user_data);
+
+  actor_x11->repaint_scheduled = TRUE;
+}
+
+static void
+remove_frame_messages_timer (MetaWindowActorX11 *actor_x11)
+{
+  g_assert (actor_x11->send_frame_messages_timer != 0);
+
+  g_source_remove (actor_x11->send_frame_messages_timer);
+  actor_x11->send_frame_messages_timer = 0;
+}
+
+static void
+do_send_frame_drawn (MetaWindowActorX11 *actor_x11,
+                     FrameData          *frame)
+{
+  MetaWindow *window =
+    meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
+  MetaDisplay *display = meta_window_get_display (window);
+  Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
+
+  XClientMessageEvent ev = { 0, };
+
+  frame->frame_drawn_time =
+    meta_compositor_monotonic_time_to_server_time (display,
+                                                   g_get_monotonic_time ());
+  actor_x11->frame_drawn_time = frame->frame_drawn_time;
+
+  ev.type = ClientMessage;
+  ev.window = meta_window_get_xwindow (window);
+  ev.message_type = display->x11_display->atom__NET_WM_FRAME_DRAWN;
+  ev.format = 32;
+  ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT (0xffffffff);
+  ev.data.l[1] = frame->sync_request_serial >> 32;
+  ev.data.l[2] = frame->frame_drawn_time & G_GUINT64_CONSTANT (0xffffffff);
+  ev.data.l[3] = frame->frame_drawn_time >> 32;
+
+  meta_x11_error_trap_push (display->x11_display);
+  XSendEvent (xdisplay, ev.window, False, 0, (XEvent *) &ev);
+  XFlush (xdisplay);
+  meta_x11_error_trap_pop (display->x11_display);
+}
+
+static void
+do_send_frame_timings (MetaWindowActorX11 *actor_x11,
+                       FrameData          *frame,
+                       int                 refresh_interval,
+                       int64_t             presentation_time)
+{
+  MetaWindow *window =
+    meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
+  MetaDisplay *display = meta_window_get_display (window);
+  Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
+
+  XClientMessageEvent ev = { 0, };
+
+  ev.type = ClientMessage;
+  ev.window = meta_window_get_xwindow (window);
+  ev.message_type = display->x11_display->atom__NET_WM_FRAME_TIMINGS;
+  ev.format = 32;
+  ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT (0xffffffff);
+  ev.data.l[1] = frame->sync_request_serial >> 32;
+
+  if (presentation_time != 0)
+    {
+      int64_t presentation_time_server =
+        meta_compositor_monotonic_time_to_server_time (display,
+                                                       presentation_time);
+      int64_t presentation_time_offset = presentation_time_server - frame->frame_drawn_time;
+      if (presentation_time_offset == 0)
+        presentation_time_offset = 1;
+
+      if ((int32_t)presentation_time_offset == presentation_time_offset)
+        ev.data.l[2] = presentation_time_offset;
+    }
+
+  ev.data.l[3] = refresh_interval;
+  ev.data.l[4] = 1000 * META_SYNC_DELAY;
+
+  meta_x11_error_trap_push (display->x11_display);
+  XSendEvent (xdisplay, ev.window, False, 0, (XEvent *) &ev);
+  XFlush (xdisplay);
+  meta_x11_error_trap_pop (display->x11_display);
+}
+
+static void
+send_frame_timings (MetaWindowActorX11 *actor_x11,
+                    FrameData          *frame,
+                    ClutterFrameInfo   *frame_info,
+                    int64_t             presentation_time)
+{
+  float refresh_rate;
+  int refresh_interval;
+
+  refresh_rate = frame_info->refresh_rate;
+  /* 0.0 is a flag for not known, but sanity-check against other odd numbers */
+  if (refresh_rate >= 1.0)
+    refresh_interval = (int) (0.5 + 1000000 / refresh_rate);
+  else
+    refresh_interval = 0;
+
+  do_send_frame_timings (actor_x11, frame, refresh_interval, presentation_time);
+}
+
+static gboolean
+send_frame_messages_timeout (gpointer data)
+{
+  MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (data);
+  GList *l;
+
+  for (l = actor_x11->frames; l;)
+    {
+      GList *l_next = l->next;
+      FrameData *frame = l->data;
+
+      if (frame->frame_counter == -1)
+        {
+          do_send_frame_drawn (actor_x11, frame);
+          do_send_frame_timings (actor_x11, frame, 0, 0);
+
+          actor_x11->frames = g_list_delete_link (actor_x11->frames, l);
+          frame_data_free (frame);
+        }
+
+      l = l_next;
+    }
+
+  actor_x11->needs_frame_drawn = FALSE;
+  actor_x11->send_frame_messages_timer = 0;
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+queue_send_frame_messages_timeout (MetaWindowActorX11 *actor_x11)
+{
+  MetaWindow *window =
+    meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
+  MetaDisplay *display = meta_window_get_display (window);
+  MetaLogicalMonitor *logical_monitor;
+  int64_t current_time;
+  float refresh_rate;
+  int interval, offset;
+
+  if (actor_x11->send_frame_messages_timer != 0)
+    return;
+
+  logical_monitor = meta_window_get_main_logical_monitor (window);
+  if (logical_monitor)
+    {
+      GList *monitors = meta_logical_monitor_get_monitors (logical_monitor);
+      MetaMonitor *monitor;
+      MetaMonitorMode *mode;
+
+      monitor = g_list_first (monitors)->data;
+      mode = meta_monitor_get_current_mode (monitor);
+
+      refresh_rate = meta_monitor_mode_get_refresh_rate (mode);
+    }
+  else
+    {
+      refresh_rate = 60.0f;
+    }
+
+  current_time =
+    meta_compositor_monotonic_time_to_server_time (display,
+                                                   g_get_monotonic_time ());
+  interval = (int) (1000000 / refresh_rate) * 6;
+  offset = MAX (0, actor_x11->frame_drawn_time + interval - current_time) / 1000;
+
+ /* The clutter master clock source has already been added with META_PRIORITY_REDRAW,
+  * so the timer will run *after* the clutter frame handling, if a frame is ready
+  * to be drawn when the timer expires.
+  */
+  actor_x11->send_frame_messages_timer =
+    g_timeout_add_full (META_PRIORITY_REDRAW, offset,
+                        send_frame_messages_timeout,
+                        actor_x11, NULL);
+  g_source_set_name_by_id (actor_x11->send_frame_messages_timer,
+                           "[mutter] send_frame_messages_timeout");
+}
+
+static void
+assign_frame_counter_to_frames (MetaWindowActorX11 *actor_x11)
+{
+  MetaWindow *window =
+    meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11));
+  MetaCompositor *compositor = window->display->compositor;
+  ClutterStage *stage = CLUTTER_STAGE (compositor->stage);
+  GList *l;
+
+  /* If the window is obscured, then we're expecting to deal with sending
+   * frame messages in a timeout, rather than in this paint cycle.
+   */
+  if (actor_x11->send_frame_messages_timer != 0)
+    return;
+
+  for (l = actor_x11->frames; l; l = l->next)
+    {
+      FrameData *frame = l->data;
+
+      if (frame->frame_counter == -1)
+        frame->frame_counter = clutter_stage_get_frame_counter (stage);
+    }
+}
+
+static void
+meta_window_actor_x11_frame_complete (MetaWindowActor  *actor,
+                                      ClutterFrameInfo *frame_info,
+                                      int64_t           presentation_time)
+{
+  MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
+  GList *l;
+
+  if (meta_window_actor_is_destroyed (actor))
+    return;
+
+  for (l = actor_x11->frames; l;)
+    {
+      GList *l_next = l->next;
+      FrameData *frame = l->data;
+      int64_t frame_counter = frame_info->frame_counter;
+
+      if (frame->frame_counter != -1 && frame->frame_counter <= frame_counter)
+        {
+          MetaWindow *window =
+            meta_window_actor_get_meta_window (actor);
+
+          if (G_UNLIKELY (frame->frame_drawn_time == 0))
+            g_warning ("%s: Frame has assigned frame counter but no frame drawn time",
+                       window->desc);
+          if (G_UNLIKELY (frame->frame_counter < frame_counter))
+            g_warning ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
+                       window->desc, frame->frame_counter);
+
+          actor_x11->frames = g_list_delete_link (actor_x11->frames, l);
+          send_frame_timings (actor_x11, frame, frame_info, presentation_time);
+          frame_data_free (frame);
+        }
+
+      l = l_next;
+    }
+}
+
+static void
+meta_window_actor_x11_set_surface_actor (MetaWindowActor  *actor,
+                                         MetaSurfaceActor *surface)
+{
+  MetaWindowActorClass *parent_class =
+    META_WINDOW_ACTOR_CLASS (meta_window_actor_x11_parent_class);
+  MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
+  MetaSurfaceActor *old_surface;
+
+  old_surface = meta_window_actor_get_surface (actor);
+
+  if (old_surface)
+    {
+      g_signal_handler_disconnect (old_surface,
+                                   actor_x11->repaint_scheduled_id);
+      actor_x11->repaint_scheduled_id = 0;
+    }
+
+  parent_class->set_surface_actor (actor, surface);
+
+  if (surface)
+    {
+      actor_x11->repaint_scheduled_id =
+        g_signal_connect (surface, "repaint-scheduled",
+                          G_CALLBACK (surface_repaint_scheduled),
+                          actor_x11);
+    }
+}
+
+static void
+meta_window_actor_x11_queue_frame_drawn (MetaWindowActor *actor,
+                                         gboolean         skip_sync_delay)
+{
+  MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
+  MetaWindow *window =
+    meta_window_actor_get_meta_window (actor);
+  FrameData *frame;
+
+  if (meta_window_actor_is_destroyed (actor))
+    return;
+
+  frame = g_slice_new0 (FrameData);
+  frame->frame_counter = -1;
+  frame->sync_request_serial = window->sync_request_serial;
+
+  actor_x11->frames = g_list_prepend (actor_x11->frames, frame);
+
+  actor_x11->needs_frame_drawn = TRUE;
+
+  if (skip_sync_delay)
+    {
+      ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (actor_x11));
+      clutter_stage_skip_sync_delay (CLUTTER_STAGE (stage));
+    }
+
+  if (!actor_x11->repaint_scheduled)
+    {
+      MetaSurfaceActor *surface;
+      gboolean is_obscured;
+
+      surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11));
+
+      if (surface)
+        is_obscured = meta_surface_actor_is_obscured (surface);
+      else
+        is_obscured = FALSE;
+
+      /* A frame was marked by the client without actually doing any
+       * damage or any unobscured, or while we had the window frozen
+       * (e.g. during an interactive resize.) We need to make sure that the
+       * pre_paint/post_paint functions get called, enabling us to
+       * send a _NET_WM_FRAME_DRAWN. We do a 1-pixel redraw to get
+       * consistent timing with non-empty frames. If the window
+       * is completely obscured we fire off the send_frame_messages timeout.
+       */
+      if (is_obscured)
+        {
+          queue_send_frame_messages_timeout (actor_x11);
+        }
+      else if (surface)
+        {
+          const cairo_rectangle_int_t clip = { 0, 0, 1, 1 };
+          clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (surface), &clip);
+          actor_x11->repaint_scheduled = TRUE;
+        }
+    }
+}
+
+static void
+meta_window_actor_x11_post_init (MetaWindowActor *actor)
+{
+  MetaWindow *window = meta_window_actor_get_meta_window (actor);
+
+  /* If a window doesn't start off with updates frozen, we should
+   * we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn.
+   */
+  if (window->extended_sync_request_counter &&
+      !meta_window_updates_are_frozen (window))
+    meta_window_actor_queue_frame_drawn (actor, FALSE);
+}
+
+static void
+meta_window_actor_x11_pre_paint (MetaWindowActor *actor)
+{
+  MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
+
+  assign_frame_counter_to_frames (actor_x11);
+}
+
+static void
+meta_window_actor_x11_post_paint (MetaWindowActor *actor)
+{
+  MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
+
+  actor_x11->repaint_scheduled = FALSE;
+
+  if (meta_window_actor_is_destroyed (actor))
+    return;
+
+  /* If the window had damage, but wasn't actually redrawn because
+   * it is obscured, we should wait until timer expiration before
+   * sending _NET_WM_FRAME_* messages.
+   */
+  if (actor_x11->send_frame_messages_timer == 0 &&
+      actor_x11->needs_frame_drawn)
+    {
+      GList *l;
+
+      for (l = actor_x11->frames; l; l = l->next)
+        {
+          FrameData *frame = l->data;
+
+          if (frame->frame_drawn_time == 0)
+            do_send_frame_drawn (actor_x11, frame);
+        }
+
+      actor_x11->needs_frame_drawn = FALSE;
+    }
+}
+
+static void
+meta_window_actor_x11_queue_destroy (MetaWindowActor *actor)
+{
+  MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
+
+  if (actor_x11->send_frame_messages_timer != 0)
+    remove_frame_messages_timer (actor_x11);
+}
+
+static void
+meta_window_actor_x11_paint (ClutterActor *actor)
+{
+  MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor);
+
+ /* This window got damage when obscured; we set up a timer
+  * to send frame completion events, but since we're drawing
+  * the window now (for some other reason) cancel the timer
+  * and send the completion events normally */
+  if (actor_x11->send_frame_messages_timer != 0)
+    {
+      remove_frame_messages_timer (actor_x11);
+      assign_frame_counter_to_frames (actor_x11);
+    }
+
+  CLUTTER_ACTOR_CLASS (meta_window_actor_x11_parent_class)->paint (actor);
+}
+
+static void
+meta_window_actor_x11_dispose (GObject *object)
+{
+  MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object);
+
+  if (actor_x11->send_frame_messages_timer != 0)
+    remove_frame_messages_timer (actor_x11);
+
+  G_OBJECT_CLASS (meta_window_actor_x11_parent_class)->dispose (object);
+}
+
+static void
+meta_window_actor_x11_finalize (GObject *object)
+{
+  MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object);
+
+  g_list_free_full (actor_x11->frames, (GDestroyNotify) frame_data_free);
+
+  G_OBJECT_CLASS (meta_window_actor_x11_parent_class)->finalize (object);
+}
+
 static void
 meta_window_actor_x11_class_init (MetaWindowActorX11Class *klass)
 {
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
+  MetaWindowActorClass *window_actor_class = META_WINDOW_ACTOR_CLASS (klass);
+
+  window_actor_class->frame_complete = meta_window_actor_x11_frame_complete;
+  window_actor_class->set_surface_actor = meta_window_actor_x11_set_surface_actor;
+  window_actor_class->queue_frame_drawn = meta_window_actor_x11_queue_frame_drawn;
+  window_actor_class->post_init = meta_window_actor_x11_post_init;
+  window_actor_class->pre_paint = meta_window_actor_x11_pre_paint;
+  window_actor_class->post_paint = meta_window_actor_x11_post_paint;
+  window_actor_class->queue_destroy = meta_window_actor_x11_queue_destroy;
+
+  actor_class->paint = meta_window_actor_x11_paint;
+
+  object_class->dispose = meta_window_actor_x11_dispose;
+  object_class->finalize = meta_window_actor_x11_finalize;
 }
 
 static void
diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c
index 5d9352419..2e28a2019 100644
--- a/src/compositor/meta-window-actor.c
+++ b/src/compositor/meta-window-actor.c
@@ -12,9 +12,6 @@
 #include <math.h>
 #include <string.h>
 
-#include "backends/meta-backend-private.h"
-#include "backends/meta-logical-monitor.h"
-#include "backends/meta-monitor-manager-private.h"
 #include "backends/meta-screen-cast-window.h"
 #include "clutter/clutter-mutter.h"
 #include "clutter/x11/clutter-x11.h"
@@ -34,9 +31,7 @@
 #include "meta/meta-enum-types.h"
 #include "meta/meta-shadow-factory.h"
 #include "meta/meta-shaped-texture.h"
-#include "meta/meta-x11-errors.h"
 #include "meta/window.h"
-#include "x11/meta-x11-display-private.h"
 
 #ifdef HAVE_WAYLAND
 #include "compositor/meta-surface-actor-wayland.h"
@@ -83,10 +78,6 @@ typedef struct _MetaWindowActorPrivate
 
   MetaShadowMode    shadow_mode;
 
-  guint             send_frame_messages_timer;
-  gint64            frame_drawn_time;
-
-  guint             repaint_scheduled_id;
   guint             size_changed_id;
 
   /*
@@ -100,18 +91,11 @@ typedef struct _MetaWindowActorPrivate
   gint              map_in_progress;
   gint              destroy_in_progress;
 
-  /* List of FrameData for recent frames */
-  GList            *frames;
   guint             freeze_count;
 
   guint                    visible                : 1;
   guint                    disposed               : 1;
 
-  /* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
-   * client message for one or more messages in ->frames */
-  guint             needs_frame_drawn      : 1;
-  guint             repaint_scheduled      : 1;
-
   guint             needs_reshape          : 1;
   guint             recompute_focused_shadow   : 1;
   guint             recompute_unfocused_shadow : 1;
@@ -122,26 +106,6 @@ typedef struct _MetaWindowActorPrivate
   guint             first_frame_state      : 2; /* FirstFrameState */
 } MetaWindowActorPrivate;
 
-typedef struct _FrameData FrameData;
-
-/* Each time the application updates the sync request counter to a new even value
- * value, we queue a frame into the windows list of frames. Once we're painting
- * an update "in response" to the window, we fill in frame_counter with the
- * Cogl counter for that frame, and send _NET_WM_FRAME_DRAWN at the end of the
- * frame. _NET_WM_FRAME_TIMINGS is sent when we get a frame_complete callback.
- *
- * As an exception, if a window is completely obscured, we try to throttle drawning
- * to a slower frame rate. In this case, frame_counter stays -1 until
- * send_frame_message_timeout() runs, at which point we send both the
- * _NET_WM_FRAME_DRAWN and _NET_WM_FRAME_TIMINGS messages.
- */
-struct _FrameData
-{
-  guint64 sync_request_serial;
-  int64_t frame_counter;
-  gint64 frame_drawn_time;
-};
-
 enum
 {
   FIRST_FRAME,
@@ -160,7 +124,6 @@ enum
 };
 
 static void meta_window_actor_dispose    (GObject *object);
-static void meta_window_actor_finalize   (GObject *object);
 static void meta_window_actor_constructed (GObject *object);
 static void meta_window_actor_set_property (GObject       *object,
                                             guint         prop_id,
@@ -175,7 +138,8 @@ static void meta_window_actor_paint (ClutterActor *actor);
 
 static gboolean meta_window_actor_get_paint_volume (ClutterActor       *actor,
                                                     ClutterPaintVolume *volume);
-
+static void set_surface (MetaWindowActor  *actor,
+                         MetaSurfaceActor *surface);
 
 static gboolean meta_window_actor_has_shadow (MetaWindowActor *self);
 
@@ -183,12 +147,6 @@ static void meta_window_actor_handle_updates (MetaWindowActor *self);
 
 static void check_needs_reshape (MetaWindowActor *self);
 
-static void do_send_frame_drawn (MetaWindowActor *self, FrameData *frame);
-static void do_send_frame_timings (MetaWindowActor  *self,
-                                   FrameData        *frame,
-                                   gint             refresh_interval,
-                                   gint64           presentation_time);
-
 static void cullable_iface_init (MetaCullableInterface *iface);
 
 static void screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface);
@@ -199,9 +157,10 @@ G_DEFINE_TYPE_WITH_CODE (MetaWindowActor, meta_window_actor, CLUTTER_TYPE_ACTOR,
                          G_IMPLEMENT_INTERFACE (META_TYPE_SCREEN_CAST_WINDOW, 
screen_cast_window_iface_init));
 
 static void
-frame_data_free (FrameData *frame)
+meta_window_actor_real_set_surface_actor (MetaWindowActor  *actor,
+                                          MetaSurfaceActor *surface)
 {
-  g_slice_free (FrameData, frame);
+  set_surface (actor, surface);
 }
 
 static void
@@ -212,7 +171,6 @@ meta_window_actor_class_init (MetaWindowActorClass *klass)
   GParamSpec   *pspec;
 
   object_class->dispose      = meta_window_actor_dispose;
-  object_class->finalize     = meta_window_actor_finalize;
   object_class->set_property = meta_window_actor_set_property;
   object_class->get_property = meta_window_actor_get_property;
   object_class->constructed  = meta_window_actor_constructed;
@@ -220,6 +178,8 @@ meta_window_actor_class_init (MetaWindowActorClass *klass)
   actor_class->paint = meta_window_actor_paint;
   actor_class->get_paint_volume = meta_window_actor_get_paint_volume;
 
+  klass->set_surface_actor = meta_window_actor_real_set_surface_actor;
+
   /**
    * MetaWindowActor::first-frame:
    * @actor: the #MetaWindowActor instance
@@ -314,17 +274,6 @@ surface_size_changed (MetaSurfaceActor *actor,
   meta_window_actor_update_shape (self);
 }
 
-static void
-surface_repaint_scheduled (MetaSurfaceActor *actor,
-                           gpointer          user_data)
-{
-  MetaWindowActor *self = META_WINDOW_ACTOR (user_data);
-  MetaWindowActorPrivate *priv =
-    meta_window_actor_get_instance_private (self);
-
-  priv->repaint_scheduled = TRUE;
-}
-
 static gboolean
 is_argb32 (MetaWindowActor *self)
 {
@@ -421,9 +370,7 @@ set_surface (MetaWindowActor  *self,
 
   if (priv->surface)
     {
-      g_signal_handler_disconnect (priv->surface, priv->repaint_scheduled_id);
       g_signal_handler_disconnect (priv->surface, priv->size_changed_id);
-      priv->repaint_scheduled_id = 0;
       clutter_actor_remove_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (priv->surface));
       g_object_unref (priv->surface);
     }
@@ -433,8 +380,6 @@ set_surface (MetaWindowActor  *self,
   if (priv->surface)
     {
       g_object_ref_sink (priv->surface);
-      priv->repaint_scheduled_id = g_signal_connect (priv->surface, "repaint-scheduled",
-                                                     G_CALLBACK (surface_repaint_scheduled), self);
       priv->size_changed_id = g_signal_connect (priv->surface, "size-changed",
                                                 G_CALLBACK (surface_size_changed), self);
       clutter_actor_add_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (priv->surface));
@@ -501,12 +446,6 @@ meta_window_actor_dispose (GObject *object)
 
   priv->disposed = TRUE;
 
-  if (priv->send_frame_messages_timer != 0)
-    {
-      g_source_remove (priv->send_frame_messages_timer);
-      priv->send_frame_messages_timer = 0;
-    }
-
   g_clear_pointer (&priv->shape_region, cairo_region_destroy);
   g_clear_pointer (&priv->shadow_clip, cairo_region_destroy);
 
@@ -524,18 +463,6 @@ meta_window_actor_dispose (GObject *object)
   G_OBJECT_CLASS (meta_window_actor_parent_class)->dispose (object);
 }
 
-static void
-meta_window_actor_finalize (GObject *object)
-{
-  MetaWindowActor *self = META_WINDOW_ACTOR (object);
-  MetaWindowActorPrivate *priv =
-    meta_window_actor_get_instance_private (self);
-
-  g_list_free_full (priv->frames, (GDestroyNotify) frame_data_free);
-
-  G_OBJECT_CLASS (meta_window_actor_parent_class)->finalize (object);
-}
-
 static void
 meta_window_actor_set_property (GObject      *object,
                                 guint         prop_id,
@@ -706,30 +633,6 @@ clip_shadow_under_window (MetaWindowActor *self)
   return is_non_opaque (self) && priv->window->frame;
 }
 
-static void
-assign_frame_counter_to_frames (MetaWindowActor *self)
-{
-  MetaWindowActorPrivate *priv =
-    meta_window_actor_get_instance_private (self);
-  MetaCompositor *compositor = priv->compositor;
-  ClutterStage *stage = CLUTTER_STAGE (compositor->stage);
-  GList *l;
-
-  /* If the window is obscured, then we're expecting to deal with sending
-   * frame messages in a timeout, rather than in this paint cycle.
-   */
-  if (priv->send_frame_messages_timer != 0)
-    return;
-
-  for (l = priv->frames; l; l = l->next)
-    {
-      FrameData *frame = l->data;
-
-      if (frame->frame_counter == -1)
-        frame->frame_counter = clutter_stage_get_frame_counter (stage);
-    }
-}
-
 static void
 meta_window_actor_paint (ClutterActor *actor)
 {
@@ -742,18 +645,6 @@ meta_window_actor_paint (ClutterActor *actor)
 
   shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow;
 
- /* This window got damage when obscured; we set up a timer
-  * to send frame completion events, but since we're drawing
-  * the window now (for some other reason) cancel the timer
-  * and send the completion events normally */
-  if (priv->send_frame_messages_timer != 0)
-    {
-      g_source_remove (priv->send_frame_messages_timer);
-      priv->send_frame_messages_timer = 0;
-
-      assign_frame_counter_to_frames (self);
-    }
-
   if (shadow != NULL)
     {
       MetaShadowParams params;
@@ -968,140 +859,12 @@ meta_window_actor_is_destroyed (MetaWindowActor *self)
   return priv->disposed || priv->needs_destroy;
 }
 
-static gboolean
-send_frame_messages_timeout (gpointer data)
-{
-  MetaWindowActor *self = (MetaWindowActor *) data;
-  MetaWindowActorPrivate *priv =
-    meta_window_actor_get_instance_private (self);
-  GList *l;
-
-  for (l = priv->frames; l;)
-    {
-      GList *l_next = l->next;
-      FrameData *frame = l->data;
-
-      if (frame->frame_counter == -1)
-        {
-          do_send_frame_drawn (self, frame);
-          do_send_frame_timings (self, frame, 0, 0);
-
-          priv->frames = g_list_delete_link (priv->frames, l);
-          frame_data_free (frame);
-        }
-
-      l = l_next;
-    }
-
-  priv->needs_frame_drawn = FALSE;
-  priv->send_frame_messages_timer = 0;
-
-  return FALSE;
-}
-
-static void
-queue_send_frame_messages_timeout (MetaWindowActor *self)
-{
-  MetaWindowActorPrivate *priv =
-    meta_window_actor_get_instance_private (self);
-  MetaWindow *window = priv->window;
-  MetaDisplay *display = meta_window_get_display (priv->window);
-  MetaLogicalMonitor *logical_monitor;
-  int64_t current_time;
-  float refresh_rate;
-  int interval, offset;
-
-  if (priv->send_frame_messages_timer != 0)
-    return;
-
-  logical_monitor = meta_window_get_main_logical_monitor (window);
-  if (logical_monitor)
-    {
-      GList *monitors = meta_logical_monitor_get_monitors (logical_monitor);
-      MetaMonitor *monitor;
-      MetaMonitorMode *mode;
-
-      monitor = g_list_first (monitors)->data;
-      mode = meta_monitor_get_current_mode (monitor);
-
-      refresh_rate = meta_monitor_mode_get_refresh_rate (mode);
-    }
-  else
-    {
-      refresh_rate = 60.0f;
-    }
-
-  current_time =
-    meta_compositor_monotonic_time_to_server_time (display,
-                                                   g_get_monotonic_time ());
-  interval = (int)(1000000 / refresh_rate) * 6;
-  offset = MAX (0, priv->frame_drawn_time + interval - current_time) / 1000;
-
- /* The clutter master clock source has already been added with META_PRIORITY_REDRAW,
-  * so the timer will run *after* the clutter frame handling, if a frame is ready
-  * to be drawn when the timer expires.
-  */
-  priv->send_frame_messages_timer = g_timeout_add_full (META_PRIORITY_REDRAW, offset, 
send_frame_messages_timeout, self, NULL);
-  g_source_set_name_by_id (priv->send_frame_messages_timer, "[mutter] send_frame_messages_timeout");
-}
-
 void
 meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
                                      gboolean         no_delay_frame)
 {
-  MetaWindowActorPrivate *priv =
-    meta_window_actor_get_instance_private (self);
-  FrameData *frame;
-
-  if (meta_window_actor_is_destroyed (self))
-    return;
-
-  frame = g_slice_new0 (FrameData);
-  frame->frame_counter = -1;
-
-  priv->needs_frame_drawn = TRUE;
-
-  frame->sync_request_serial = priv->window->sync_request_serial;
-
-  priv->frames = g_list_prepend (priv->frames, frame);
-
-  if (no_delay_frame)
-    {
-      ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (self));
-      clutter_stage_skip_sync_delay (CLUTTER_STAGE (stage));
-    }
-
-  if (!priv->repaint_scheduled)
-    {
-      gboolean is_obscured;
-
-      if (priv->surface)
-        is_obscured = meta_surface_actor_is_obscured (priv->surface);
-      else
-        is_obscured = FALSE;
-
-      /* A frame was marked by the client without actually doing any
-       * damage or any unobscured, or while we had the window frozen
-       * (e.g. during an interactive resize.) We need to make sure that the
-       * pre_paint/post_paint functions get called, enabling us to
-       * send a _NET_WM_FRAME_DRAWN. We do a 1-pixel redraw to get
-       * consistent timing with non-empty frames. If the window
-       * is completely obscured we fire off the send_frame_messages timeout.
-       */
-      if (is_obscured)
-        {
-          queue_send_frame_messages_timeout (self);
-        }
-      else
-        {
-          if (priv->surface)
-            {
-              const cairo_rectangle_int_t clip = { 0, 0, 1, 1 };
-              clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (priv->surface), &clip);
-              priv->repaint_scheduled = TRUE;
-            }
-        }
-    }
+  META_WINDOW_ACTOR_GET_CLASS (self)->queue_frame_drawn (self,
+                                                         no_delay_frame);
 }
 
 gboolean
@@ -1317,11 +1080,7 @@ meta_window_actor_queue_destroy (MetaWindowActor *self)
 
   meta_window_set_compositor_private (window, NULL);
 
-  if (priv->send_frame_messages_timer != 0)
-    {
-      g_source_remove (priv->send_frame_messages_timer);
-      priv->send_frame_messages_timer = 0;
-    }
+  META_WINDOW_ACTOR_GET_CLASS (self)->queue_destroy (self);
 
   if (window_type == META_WINDOW_DROPDOWN_MENU ||
       window_type == META_WINDOW_POPUP_MENU ||
@@ -1507,11 +1266,7 @@ meta_window_actor_new (MetaWindow *window)
   else
     priv->first_frame_state = DRAWING_FIRST_FRAME;
 
-  /* If a window doesn't start off with updates frozen, we should
-   * we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn.
-   */
-  if (priv->window->extended_sync_request_counter && !priv->updates_frozen)
-    meta_window_actor_queue_frame_drawn (self, FALSE);
+  META_WINDOW_ACTOR_GET_CLASS (self)->post_init (self);
 
   meta_window_actor_sync_actor_geometry (self, priv->window->placed);
 
@@ -2043,36 +1798,7 @@ meta_window_actor_pre_paint (MetaWindowActor *self)
 
   meta_window_actor_handle_updates (self);
 
-  assign_frame_counter_to_frames (self);
-}
-
-static void
-do_send_frame_drawn (MetaWindowActor *self, FrameData *frame)
-{
-  MetaWindowActorPrivate *priv =
-    meta_window_actor_get_instance_private (self);
-  MetaDisplay *display = meta_window_get_display (priv->window);
-  Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
-
-  XClientMessageEvent ev = { 0, };
-
-  frame->frame_drawn_time = meta_compositor_monotonic_time_to_server_time (display,
-                                                                           g_get_monotonic_time ());
-  priv->frame_drawn_time = frame->frame_drawn_time;
-
-  ev.type = ClientMessage;
-  ev.window = meta_window_get_xwindow (priv->window);
-  ev.message_type = display->x11_display->atom__NET_WM_FRAME_DRAWN;
-  ev.format = 32;
-  ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT(0xffffffff);
-  ev.data.l[1] = frame->sync_request_serial >> 32;
-  ev.data.l[2] = frame->frame_drawn_time & G_GUINT64_CONSTANT(0xffffffff);
-  ev.data.l[3] = frame->frame_drawn_time >> 32;
-
-  meta_x11_error_trap_push (display->x11_display);
-  XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev);
-  XFlush (xdisplay);
-  meta_x11_error_trap_pop (display->x11_display);
+  META_WINDOW_ACTOR_GET_CLASS (self)->pre_paint (self);
 }
 
 void
@@ -2081,31 +1807,11 @@ meta_window_actor_post_paint (MetaWindowActor *self)
   MetaWindowActorPrivate *priv =
     meta_window_actor_get_instance_private (self);
 
-  priv->repaint_scheduled = FALSE;
+  META_WINDOW_ACTOR_GET_CLASS (self)->post_paint (self);
 
   if (meta_window_actor_is_destroyed (self))
     return;
 
-  /* If the window had damage, but wasn't actually redrawn because
-   * it is obscured, we should wait until timer expiration before
-   * sending _NET_WM_FRAME_* messages.
-   */
-  if (priv->send_frame_messages_timer == 0 &&
-      priv->needs_frame_drawn)
-    {
-      GList *l;
-
-      for (l = priv->frames; l; l = l->next)
-        {
-          FrameData *frame = l->data;
-
-          if (frame->frame_drawn_time == 0)
-            do_send_frame_drawn (self, frame);
-        }
-
-      priv->needs_frame_drawn = FALSE;
-    }
-
   if (priv->first_frame_state == DRAWING_FIRST_FRAME)
     {
       priv->first_frame_state = EMITTED_FIRST_FRAME;
@@ -2113,100 +1819,14 @@ meta_window_actor_post_paint (MetaWindowActor *self)
     }
 }
 
-static void
-do_send_frame_timings (MetaWindowActor  *self,
-                       FrameData        *frame,
-                       gint             refresh_interval,
-                       gint64           presentation_time)
-{
-  MetaWindowActorPrivate *priv =
-    meta_window_actor_get_instance_private (self);
-  MetaDisplay *display = meta_window_get_display (priv->window);
-  Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display);
-
-  XClientMessageEvent ev = { 0, };
-
-  ev.type = ClientMessage;
-  ev.window = meta_window_get_xwindow (priv->window);
-  ev.message_type = display->x11_display->atom__NET_WM_FRAME_TIMINGS;
-  ev.format = 32;
-  ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT(0xffffffff);
-  ev.data.l[1] = frame->sync_request_serial >> 32;
-
-  if (presentation_time != 0)
-    {
-      gint64 presentation_time_server = meta_compositor_monotonic_time_to_server_time (display,
-                                                                                       presentation_time);
-      gint64 presentation_time_offset = presentation_time_server - frame->frame_drawn_time;
-      if (presentation_time_offset == 0)
-        presentation_time_offset = 1;
-
-      if ((gint32)presentation_time_offset == presentation_time_offset)
-        ev.data.l[2] = presentation_time_offset;
-    }
-
-  ev.data.l[3] = refresh_interval;
-  ev.data.l[4] = 1000 * META_SYNC_DELAY;
-
-  meta_x11_error_trap_push (display->x11_display);
-  XSendEvent (xdisplay, ev.window, False, 0, (XEvent*) &ev);
-  XFlush (xdisplay);
-  meta_x11_error_trap_pop (display->x11_display);
-}
-
-static void
-send_frame_timings (MetaWindowActor  *self,
-                    FrameData        *frame,
-                    ClutterFrameInfo *frame_info,
-                    gint64            presentation_time)
-{
-  float refresh_rate;
-  int refresh_interval;
-
-  refresh_rate = frame_info->refresh_rate;
-  /* 0.0 is a flag for not known, but sanity-check against other odd numbers */
-  if (refresh_rate >= 1.0)
-    refresh_interval = (int) (0.5 + 1000000 / refresh_rate);
-  else
-    refresh_interval = 0;
-
-  do_send_frame_timings (self, frame, refresh_interval, presentation_time);
-}
-
 void
 meta_window_actor_frame_complete (MetaWindowActor  *self,
                                   ClutterFrameInfo *frame_info,
                                   gint64            presentation_time)
 {
-  MetaWindowActorPrivate *priv =
-    meta_window_actor_get_instance_private (self);
-  GList *l;
-
-  if (meta_window_actor_is_destroyed (self))
-    return;
-
-  for (l = priv->frames; l;)
-    {
-      GList *l_next = l->next;
-      FrameData *frame = l->data;
-      gint64 frame_counter = frame_info->frame_counter;
-
-      if (frame->frame_counter != -1 && frame->frame_counter <= frame_counter)
-        {
-          if (G_UNLIKELY (frame->frame_drawn_time == 0))
-            g_warning ("%s: Frame has assigned frame counter but no frame drawn time",
-                       priv->window->desc);
-          if (G_UNLIKELY (frame->frame_counter < frame_counter))
-            g_warning ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT,
-                       priv->window->desc, frame->frame_counter);
-
-          priv->frames = g_list_delete_link (priv->frames, l);
-          send_frame_timings (self, frame, frame_info, presentation_time);
-          frame_data_free (frame);
-        }
-
-      l = l_next;
-    }
+  META_WINDOW_ACTOR_GET_CLASS (self)->frame_complete (self,
+                                                      frame_info,
+                                                      presentation_time);
 }
 
 void



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]