[mutter/wip/carlosg/frames-client: 15/32] x11: Add standalone MetaSyncCounter helper struct




commit 7d982f1153d3b9d68a6f75e6c97ba8bb5e153a73
Author: Carlos Garnacho <carlosg gnome org>
Date:   Wed Sep 7 14:24:24 2022 +0200

    x11: Add standalone MetaSyncCounter helper struct
    
    This helper struct takes care of the handling of requests and alarms
    in order to satisfy NET_WM_SYNC_REQUEST. It will be necessary to
    decouple rendering of windows and frames in future commits, so each
    window may need its own synchronization and accounting.

 src/meson.build             |   2 +
 src/x11/meta-sync-counter.c | 351 ++++++++++++++++++++++++++++++++++++++++++++
 src/x11/meta-sync-counter.h |  69 +++++++++
 3 files changed, 422 insertions(+)
---
diff --git a/src/meson.build b/src/meson.build
index f4d5c78dfd..5fa5f5f9d0 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -466,6 +466,8 @@ if have_x11_client
     'x11/meta-selection-source-x11-private.h',
     'x11/meta-startup-notification-x11.c',
     'x11/meta-startup-notification-x11.h',
+    'x11/meta-sync-counter.c',
+    'x11/meta-sync-counter.h',
     'x11/meta-x11-display.c',
     'x11/meta-x11-display-private.h',
     'x11/meta-x11-errors.c',
diff --git a/src/x11/meta-sync-counter.c b/src/x11/meta-sync-counter.c
new file mode 100644
index 0000000000..1e3b3c4cd9
--- /dev/null
+++ b/src/x11/meta-sync-counter.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2022 Red Hat Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+#include "config.h"
+
+#include "meta-sync-counter.h"
+
+#include "core/window-private.h"
+#include "meta/meta-x11-errors.h"
+#include "x11/meta-x11-display-private.h"
+
+void
+meta_sync_counter_init (MetaSyncCounter *sync_counter,
+                        MetaWindow      *window,
+                        Window           xwindow)
+{
+  sync_counter->window = window;
+  sync_counter->xwindow = xwindow;
+}
+
+void
+meta_sync_counter_clear (MetaSyncCounter *sync_counter)
+{
+  g_clear_handle_id (&sync_counter->sync_request_timeout_id, g_source_remove);
+  meta_sync_counter_destroy_sync_alarm (sync_counter);
+  sync_counter->window = NULL;
+  sync_counter->xwindow = None;
+}
+
+void
+meta_sync_counter_set_counter (MetaSyncCounter *sync_counter,
+                               XSyncCounter     counter,
+                               gboolean         extended)
+{
+  meta_sync_counter_destroy_sync_alarm (sync_counter);
+  sync_counter->sync_request_counter = None;
+
+  sync_counter->sync_request_counter = counter;
+  sync_counter->extended_sync_request_counter = extended;
+
+  if (sync_counter->sync_request_counter != None)
+    {
+      meta_verbose ("Window has _NET_WM_SYNC_REQUEST_COUNTER 0x%lx (extended=%s)",
+                    sync_counter->sync_request_counter,
+                    sync_counter->extended_sync_request_counter ? "true" : "false");
+    }
+
+  if (sync_counter->extended_sync_request_counter)
+    meta_sync_counter_create_sync_alarm (sync_counter);
+}
+
+void
+meta_sync_counter_create_sync_alarm (MetaSyncCounter *sync_counter)
+{
+  MetaWindow *window = sync_counter->window;
+  MetaX11Display *x11_display = window->display->x11_display;
+  XSyncAlarmAttributes values;
+  XSyncValue init;
+
+  if (sync_counter->sync_request_counter == None ||
+      sync_counter->sync_request_alarm != None)
+    return;
+
+  meta_x11_error_trap_push (x11_display);
+
+  /* In the new (extended style), the counter value is initialized by
+   * the client before mapping the window. In the old style, we're
+   * responsible for setting the initial value of the counter.
+   */
+  if (sync_counter->extended_sync_request_counter)
+    {
+      if (!XSyncQueryCounter (x11_display->xdisplay,
+                              sync_counter->sync_request_counter,
+                              &init))
+        {
+          meta_x11_error_trap_pop_with_return (x11_display);
+          sync_counter->sync_request_counter = None;
+          return;
+        }
+
+      sync_counter->sync_request_serial =
+        XSyncValueLow32 (init) + ((gint64) XSyncValueHigh32 (init) << 32);
+    }
+  else
+    {
+      XSyncIntToValue (&init, 0);
+      XSyncSetCounter (x11_display->xdisplay,
+                       sync_counter->sync_request_counter, init);
+      sync_counter->sync_request_serial = 0;
+    }
+
+  values.trigger.counter = sync_counter->sync_request_counter;
+  values.trigger.test_type = XSyncPositiveComparison;
+
+  /* Initialize to one greater than the current value */
+  values.trigger.value_type = XSyncRelative;
+  XSyncIntToValue (&values.trigger.wait_value, 1);
+
+  /* After triggering, increment test_value by this until
+   * until the test condition is false */
+  XSyncIntToValue (&values.delta, 1);
+
+  /* we want events (on by default anyway) */
+  values.events = True;
+
+  sync_counter->sync_request_alarm = XSyncCreateAlarm (x11_display->xdisplay,
+                                                       XSyncCACounter |
+                                                       XSyncCAValueType |
+                                                       XSyncCAValue |
+                                                       XSyncCATestType |
+                                                       XSyncCADelta |
+                                                       XSyncCAEvents,
+                                                       &values);
+
+  if (meta_x11_error_trap_pop_with_return (x11_display) == Success)
+    {
+      meta_x11_display_register_sync_alarm (x11_display,
+                                            &sync_counter->sync_request_alarm,
+                                            window);
+    }
+  else
+    {
+      sync_counter->sync_request_alarm = None;
+      sync_counter->sync_request_counter = None;
+    }
+}
+
+void
+meta_sync_counter_destroy_sync_alarm (MetaSyncCounter *sync_counter)
+{
+  MetaWindow *window = sync_counter->window;
+  MetaX11Display *x11_display = window->display->x11_display;
+
+  if (sync_counter->sync_request_alarm == None)
+    return;
+
+  /* Has to be unregistered _before_ clearing the structure field */
+  meta_x11_display_unregister_sync_alarm (x11_display,
+                                          sync_counter->sync_request_alarm);
+  XSyncDestroyAlarm (x11_display->xdisplay,
+                     sync_counter->sync_request_alarm);
+  sync_counter->sync_request_alarm = None;
+}
+
+gboolean
+meta_sync_counter_has_sync_alarm (MetaSyncCounter *sync_counter)
+{
+  return (!sync_counter->disabled &&
+          sync_counter->sync_request_alarm != None);
+}
+
+static gboolean
+sync_request_timeout (gpointer data)
+{
+  MetaSyncCounter *sync_counter = data;
+  MetaWindow *window = sync_counter->window;
+
+  sync_counter->sync_request_timeout_id = 0;
+
+  /* We have now waited for more than a second for the
+   * application to respond to the sync request
+   */
+  sync_counter->disabled = TRUE;
+
+  /* Reset the wait serial, so we don't continue freezing
+   * window updates
+   */
+  sync_counter->sync_request_wait_serial = 0;
+  meta_compositor_sync_updates_frozen (window->display->compositor, window);
+
+  if (window == window->display->grab_window &&
+      meta_grab_op_is_resizing (window->display->grab_op))
+    {
+      meta_window_update_resize (window,
+                                 window->display->grab_last_edge_resistance_flags,
+                                 window->display->grab_latest_motion_x,
+                                 window->display->grab_latest_motion_y,
+                                 TRUE);
+    }
+
+  return G_SOURCE_REMOVE;
+}
+
+void
+meta_sync_counter_send_request (MetaSyncCounter *sync_counter)
+{
+  MetaWindow *window = sync_counter->window;
+  MetaX11Display *x11_display = window->display->x11_display;
+  XClientMessageEvent ev;
+  gint64 wait_serial;
+
+  if (sync_counter->sync_request_counter == None ||
+      sync_counter->sync_request_alarm == None ||
+      sync_counter->sync_request_timeout_id != 0 ||
+      sync_counter->disabled)
+    return;
+
+  /* For the old style of _NET_WM_SYNC_REQUEST_COUNTER, we just have to
+   * increase the value, but for the new "extended" style we need to
+   * pick an even (unfrozen) value sufficiently ahead of the last serial
+   * that we received from the client; the same code still works
+   * for the old style. The increment of 240 is specified by the EWMH
+   * and is (1 second) * (60fps) * (an increment of 4 per frame).
+   */
+  wait_serial = sync_counter->sync_request_serial + 240;
+
+  sync_counter->sync_request_wait_serial = wait_serial;
+
+  ev.type = ClientMessage;
+  ev.window = sync_counter->xwindow;
+  ev.message_type = x11_display->atom_WM_PROTOCOLS;
+  ev.format = 32;
+  ev.data.l[0] = x11_display->atom__NET_WM_SYNC_REQUEST;
+  /* FIXME: meta_display_get_current_time() is bad, but since calls
+   * come from meta_window_move_resize_internal (which in turn come
+   * from all over), I'm not sure what we can do to fix it.  Do we
+   * want to use _roundtrip, though?
+   */
+  ev.data.l[1] = meta_display_get_current_time (window->display);
+  ev.data.l[2] = wait_serial & G_GUINT64_CONSTANT (0xffffffff);
+  ev.data.l[3] = wait_serial >> 32;
+  ev.data.l[4] = sync_counter->extended_sync_request_counter ? 1 : 0;
+
+  /* We don't need to trap errors here as we are already
+   * inside an error_trap_push()/pop() pair.
+   */
+  XSendEvent (x11_display->xdisplay,
+             sync_counter->xwindow, False, 0, (XEvent*) &ev);
+
+  /* We give the window 1 sec to respond to _NET_WM_SYNC_REQUEST;
+   * if this time expires, we consider the window unresponsive
+   * and resize it unsynchonized.
+   */
+  sync_counter->sync_request_timeout_id = g_timeout_add (1000,
+                                                         sync_request_timeout,
+                                                         sync_counter);
+  g_source_set_name_by_id (sync_counter->sync_request_timeout_id,
+                           "[mutter] sync_request_timeout");
+
+  meta_compositor_sync_updates_frozen (window->display->compositor, window);
+}
+
+void
+meta_sync_counter_update (MetaSyncCounter *sync_counter,
+                          int64_t          new_counter_value)
+{
+  MetaWindow *window = sync_counter->window;
+  gboolean needs_frame_drawn = FALSE;
+  gboolean no_delay_frame = FALSE;
+
+  COGL_TRACE_BEGIN (MetaWindowSyncRequestCounter, "X11: Sync request counter");
+
+  if (sync_counter->extended_sync_request_counter && new_counter_value % 2 == 0)
+    {
+      needs_frame_drawn = TRUE;
+      no_delay_frame = new_counter_value == sync_counter->sync_request_serial + 1;
+    }
+
+  sync_counter->sync_request_serial = new_counter_value;
+  meta_compositor_sync_updates_frozen (window->display->compositor, window);
+
+  if (new_counter_value >= sync_counter->sync_request_wait_serial &&
+      sync_counter->sync_request_timeout_id)
+    {
+      if (!sync_counter->extended_sync_request_counter ||
+          new_counter_value % 2 == 0)
+        {
+          g_clear_handle_id (&sync_counter->sync_request_timeout_id,
+                             g_source_remove);
+        }
+
+      if (window == window->display->grab_window &&
+          meta_grab_op_is_resizing (window->display->grab_op) &&
+          (!sync_counter->extended_sync_request_counter ||
+           new_counter_value % 2 == 0))
+        {
+          meta_topic (META_DEBUG_RESIZING,
+                      "Alarm event received last motion x = %d y = %d",
+                      window->display->grab_latest_motion_x,
+                      window->display->grab_latest_motion_y);
+
+          /* This means we are ready for another configure;
+           * no pointer round trip here, to keep in sync */
+          meta_window_update_resize (window,
+                                     window->display->grab_last_edge_resistance_flags,
+                                     window->display->grab_latest_motion_x,
+                                     window->display->grab_latest_motion_y,
+                                     TRUE);
+        }
+    }
+
+  /* If sync was previously disabled, turn it back on and hope
+   * the application has come to its senses (maybe it was just
+   * busy with a pagefault or a long computation).
+   */
+  sync_counter->disabled = FALSE;
+
+  if (needs_frame_drawn)
+    meta_compositor_queue_frame_drawn (window->display->compositor, window,
+                                       no_delay_frame);
+
+#ifdef COGL_HAS_TRACING
+  if (G_UNLIKELY (cogl_is_tracing_enabled ()))
+    {
+      g_autofree char *description = NULL;
+
+      description =
+        g_strdup_printf ("sync request serial: %" G_GINT64_FORMAT ", "
+                         "needs frame drawn: %s",
+                         new_counter_value,
+                         needs_frame_drawn ? "yes" : "no");
+      COGL_TRACE_DESCRIBE (MetaWindowSyncRequestCounter, description);
+      COGL_TRACE_END (MetaWindowSyncRequestCounter);
+    }
+#endif
+}
+
+gboolean
+meta_sync_counter_is_waiting (MetaSyncCounter *sync_counter)
+{
+  if (sync_counter->extended_sync_request_counter &&
+      sync_counter->sync_request_serial % 2 == 1)
+    return TRUE;
+
+  if (sync_counter->sync_request_serial < sync_counter->sync_request_wait_serial)
+    return TRUE;
+
+  return FALSE;
+}
+
+gboolean
+meta_sync_counter_is_waiting_response (MetaSyncCounter *sync_counter)
+{
+  return sync_counter->sync_request_timeout_id != 0;
+}
diff --git a/src/x11/meta-sync-counter.h b/src/x11/meta-sync-counter.h
new file mode 100644
index 0000000000..975ac5dee6
--- /dev/null
+++ b/src/x11/meta-sync-counter.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 Red Hat Inc
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+
+#ifndef META_SYNC_COUNTER_H
+#define META_SYNC_COUNTER_H
+
+#include "meta/window.h"
+
+#include <string.h>
+#include <X11/extensions/sync.h>
+
+typedef struct {
+  MetaWindow *window;
+  Window xwindow;
+  /* XSync update counter */
+  XSyncCounter sync_request_counter;
+  int64_t sync_request_serial;
+  int64_t sync_request_wait_serial;
+  guint sync_request_timeout_id;
+  /* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */
+  XSyncAlarm sync_request_alarm;
+  /* if TRUE, the we have the new form of sync request counter which
+   * also handles application frames */
+  guint extended_sync_request_counter : 1;
+  guint disabled : 1;
+} MetaSyncCounter;
+
+void meta_sync_counter_init (MetaSyncCounter *sync_counter,
+                             MetaWindow      *window,
+                             Window           xwindow);
+
+void meta_sync_counter_clear (MetaSyncCounter *sync_counter);
+
+void meta_sync_counter_set_counter (MetaSyncCounter *sync_counter,
+                                    XSyncCounter     counter,
+                                    gboolean         extended);
+
+void meta_sync_counter_create_sync_alarm (MetaSyncCounter *sync_counter);
+
+void meta_sync_counter_destroy_sync_alarm (MetaSyncCounter *sync_counter);
+
+gboolean meta_sync_counter_has_sync_alarm (MetaSyncCounter *sync_counter);
+
+void meta_sync_counter_send_request (MetaSyncCounter *sync_counter);
+
+void meta_sync_counter_update (MetaSyncCounter *sync_counter,
+                               int64_t          new_counter_value);
+
+gboolean meta_sync_counter_is_waiting (MetaSyncCounter *sync_counter);
+
+gboolean meta_sync_counter_is_waiting_response (MetaSyncCounter *sync_counter);
+
+#endif /* META_SYNC_COUNTER_H */


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