[gnome-flashback] idle-monitor: use GSource to detect idle time



commit 5a6887294809a21791e366ad2b03c9977aa87494
Author: Alberts Muktupāvels <alberts muktupavels gmail com>
Date:   Tue May 24 21:29:25 2022 +0300

    idle-monitor: use GSource to detect idle time
    
    Based on mutter commits:
    https://gitlab.gnome.org/GNOME/mutter/-/commit/7945ee5bebbc84340e1a
    https://gitlab.gnome.org/GNOME/mutter/-/commit/84e575be775c49f1e13c
    https://gitlab.gnome.org/GNOME/mutter/-/commit/23a8ea2821f21697a39d
    https://gitlab.gnome.org/GNOME/mutter/-/commit/1ca0fdc928511edea57b
    https://gitlab.gnome.org/GNOME/mutter/-/commit/0e69fe07819a289f3156

 daemons/idle-monitor/flashback-idle-monitor.c | 124 ++++++++++-
 daemons/idle-monitor/meta-idle-monitor.c      | 291 +++++++-------------------
 daemons/idle-monitor/meta-idle-monitor.h      |   7 +-
 3 files changed, 187 insertions(+), 235 deletions(-)
---
diff --git a/daemons/idle-monitor/flashback-idle-monitor.c b/daemons/idle-monitor/flashback-idle-monitor.c
index c726004..e229993 100644
--- a/daemons/idle-monitor/flashback-idle-monitor.c
+++ b/daemons/idle-monitor/flashback-idle-monitor.c
@@ -23,6 +23,7 @@
 
 #include <gdk/gdk.h>
 #include <gdk/gdkx.h>
+#include <X11/Xlib.h>
 #include <X11/extensions/sync.h>
 
 #include "flashback-idle-monitor.h"
@@ -33,6 +34,10 @@ struct _FlashbackIdleMonitor
 {
   GObject                   parent;
 
+  Display                  *xdisplay;
+  XSyncCounter              counter;
+  XSyncAlarm                user_active_alarm;
+
   MetaIdleMonitor          *monitor;
 
   gint                      dbus_name_id;
@@ -252,6 +257,32 @@ on_name_lost (GDBusConnection *connection,
 {
 }
 
+static void
+ensure_alarm_rescheduled (Display    *dpy,
+                          XSyncAlarm  alarm)
+{
+  XSyncAlarmAttributes attr;
+
+  /* Some versions of Xorg have an issue where alarms aren't
+   * always rescheduled. Calling XSyncChangeAlarm, even
+   * without any attributes, will reschedule the alarm.
+   */
+  XSyncChangeAlarm (dpy, alarm, 0, &attr);
+}
+
+static void
+handle_alarm_notify (FlashbackIdleMonitor  *self,
+                     XSyncAlarmNotifyEvent *alarm_event)
+{
+  if (alarm_event->state != XSyncAlarmActive ||
+      alarm_event->alarm != self->user_active_alarm)
+    return;
+
+  ensure_alarm_rescheduled (self->xdisplay, self->user_active_alarm);
+
+  meta_idle_monitor_reset_idletime (self->monitor);
+}
+
 static GdkFilterReturn
 filter_func (GdkXEvent *xevent,
              GdkEvent  *event,
@@ -264,12 +295,64 @@ filter_func (GdkXEvent *xevent,
   xev = (XEvent *) xevent;
 
   if (xev->type == (monitor->xsync_event_base + XSyncAlarmNotify))
+    handle_alarm_notify (monitor, (XSyncAlarmNotifyEvent *) xev);
+
+  return GDK_FILTER_CONTINUE;
+}
+
+static void
+uint64_to_xsync_value (uint64_t    value,
+                       XSyncValue *xsync_value)
+{
+  XSyncIntsToValue (xsync_value, value & 0xffffffff, value >> 32);
+}
+
+static XSyncAlarm
+_xsync_alarm_set (FlashbackIdleMonitor *self,
+                  XSyncTestType         test_type,
+                  guint64               interval,
+                  gboolean              want_events)
+{
+  XSyncAlarmAttributes attr;
+  XSyncValue delta;
+  guint flags;
+
+  flags = XSyncCACounter | XSyncCAValueType | XSyncCATestType |
+          XSyncCAValue | XSyncCADelta | XSyncCAEvents;
+
+  XSyncIntToValue (&delta, 0);
+  attr.trigger.counter = self->counter;
+  attr.trigger.value_type = XSyncAbsolute;
+  attr.trigger.test_type = test_type;
+  attr.delta = delta;
+  attr.events = want_events;
+
+  uint64_to_xsync_value (interval, &attr.trigger.wait_value);
+
+  return XSyncCreateAlarm (self->xdisplay, flags, &attr);
+}
+
+static XSyncCounter
+find_idletime_counter (FlashbackIdleMonitor *self)
+{
+  int i;
+  int ncounters;
+  XSyncSystemCounter *counters;
+  XSyncCounter counter = None;
+
+  counters = XSyncListSystemCounters (self->xdisplay, &ncounters);
+  for (i = 0; i < ncounters; i++)
     {
-      meta_idle_monitor_handle_xevent (monitor->monitor,
-                                       (XSyncAlarmNotifyEvent*) xev);
+      if (g_strcmp0 (counters[i].name, "IDLETIME") == 0)
+        {
+          counter = counters[i].counter;
+          break;
+        }
     }
 
-  return GDK_FILTER_CONTINUE;
+  XSyncFreeSystemCounterList (counters);
+
+  return counter;
 }
 
 static void
@@ -303,6 +386,12 @@ flashback_idle_monitor_finalize (GObject *object)
 
   gdk_window_remove_filter (NULL, (GdkFilterFunc) filter_func, monitor);
 
+  if (monitor->user_active_alarm != None)
+    {
+      XSyncDestroyAlarm (monitor->xdisplay, monitor->user_active_alarm);
+      monitor->user_active_alarm = None;
+    }
+
   g_object_unref (monitor->monitor);
 
   G_OBJECT_CLASS (flashback_idle_monitor_parent_class)->finalize (object);
@@ -312,7 +401,6 @@ static void
 flashback_idle_monitor_init (FlashbackIdleMonitor *monitor)
 {
   GdkDisplay *display;
-  Display *xdisplay;
   gint event_base;
   gint error_base;
   gint major;
@@ -331,16 +419,34 @@ flashback_idle_monitor_init (FlashbackIdleMonitor *monitor)
                                           monitor, NULL);
 
   display = gdk_display_get_default ();
-  xdisplay = gdk_x11_display_get_xdisplay (display);
+  monitor->xdisplay = gdk_x11_display_get_xdisplay (display);
 
-  if (!XSyncQueryExtension (xdisplay, &event_base, &error_base))
-    g_critical ("Could not query XSync extension");
+  if (!XSyncQueryExtension (monitor->xdisplay, &event_base, &error_base))
+    {
+      g_critical ("Could not query XSync extension");
+      return;
+    }
 
   monitor->xsync_event_base = event_base;
   monitor->xsync_error_base = error_base;
 
-  if (!XSyncInitialize (xdisplay, &major, &minor))
-    g_critical ("Could not initialize XSync");
+  if (!XSyncInitialize (monitor->xdisplay, &major, &minor))
+    {
+      g_critical ("Could not initialize XSync");
+      return;
+    }
+
+  monitor->counter = find_idletime_counter (monitor);
+  if (monitor->counter == None)
+    {
+      g_critical ("IDLETIME counter not found");
+      return;
+    }
+
+  monitor->user_active_alarm = _xsync_alarm_set (monitor,
+                                                 XSyncNegativeTransition,
+                                                 1,
+                                                 TRUE);
 
   gdk_window_add_filter (NULL, (GdkFilterFunc) filter_func, monitor);
 }
diff --git a/daemons/idle-monitor/meta-idle-monitor.c b/daemons/idle-monitor/meta-idle-monitor.c
index b264e48..feb0322 100644
--- a/daemons/idle-monitor/meta-idle-monitor.c
+++ b/daemons/idle-monitor/meta-idle-monitor.c
@@ -26,7 +26,6 @@
 
 #include "config.h"
 
-#include <gdk/gdkx.h>
 #include <string.h>
 
 #include "meta-idle-monitor.h"
@@ -37,10 +36,7 @@ struct _MetaIdleMonitor
 
   GHashTable   *watches;
 
-  GHashTable   *alarms;
-  Display      *display;
-  XSyncCounter  counter;
-  XSyncAlarm    user_active_alarm;
+  guint64       last_event_time;
 };
 
 typedef struct
@@ -51,8 +47,7 @@ typedef struct
   gpointer                  user_data;
   GDestroyNotify            notify;
   guint64                   timeout_msec;
-  int                       idle_source_id;
-  XSyncAlarm                xalarm;
+  GSource                  *timeout_source;
 } MetaIdleMonitorWatch;
 
 G_STATIC_ASSERT(sizeof(unsigned long) == sizeof(gpointer));
@@ -69,12 +64,6 @@ meta_idle_monitor_watch_fire (MetaIdleMonitorWatch *watch)
   monitor = watch->monitor;
   g_object_ref (monitor);
 
-  if (watch->idle_source_id)
-    {
-      g_source_remove (watch->idle_source_id);
-      watch->idle_source_id = 0;
-    }
-
   id = watch->id;
   is_user_active_watch = (watch->timeout_msec == 0);
 
@@ -87,13 +76,6 @@ meta_idle_monitor_watch_fire (MetaIdleMonitorWatch *watch)
   g_object_unref (monitor);
 }
 
-static gint64
-_xsyncvalue_to_int64 (XSyncValue value)
-{
-  return ((guint64) XSyncValueHigh32 (value)) << 32 |
-         (guint64) XSyncValueLow32 (value);
-}
-
 static void
 free_watch (gpointer data)
 {
@@ -102,104 +84,16 @@ free_watch (gpointer data)
 
   g_object_ref (monitor);
 
-  if (watch->idle_source_id)
-    {
-      g_source_remove (watch->idle_source_id);
-      watch->idle_source_id = 0;
-    }
-
   if (watch->notify != NULL)
     watch->notify (watch->user_data);
 
-  if (watch->xalarm != monitor->user_active_alarm &&
-      watch->xalarm != None)
-    {
-      XSyncDestroyAlarm (monitor->display, watch->xalarm);
-      g_hash_table_remove (monitor->alarms, (gpointer) watch->xalarm);
-    }
+  if (watch->timeout_source != NULL)
+    g_source_destroy (watch->timeout_source);
 
   g_object_unref (monitor);
   g_free (watch);
 }
 
-#define GUINT64_TO_XSYNCVALUE(value, ret) XSyncIntsToValue (ret, (value) & 0xFFFFFFFF, ((guint64)(value)) >> 
32)
-
-static XSyncAlarm
-_xsync_alarm_set (MetaIdleMonitor *self,
-                  XSyncTestType    test_type,
-                  guint64          interval,
-                  gboolean         want_events)
-{
-  XSyncAlarmAttributes attr;
-  XSyncValue delta;
-  guint flags;
-
-  flags = XSyncCACounter | XSyncCAValueType | XSyncCATestType |
-          XSyncCAValue | XSyncCADelta | XSyncCAEvents;
-
-  XSyncIntToValue (&delta, 0);
-  attr.trigger.counter = self->counter;
-  attr.trigger.value_type = XSyncAbsolute;
-  attr.delta = delta;
-  attr.events = want_events;
-
-  GUINT64_TO_XSYNCVALUE (interval, &attr.trigger.wait_value);
-  attr.trigger.test_type = test_type;
-  return XSyncCreateAlarm (self->display, flags, &attr);
-}
-
-static XSyncCounter
-find_idletime_counter (MetaIdleMonitor *self)
-{
-  int i;
-  int ncounters;
-  XSyncSystemCounter *counters;
-  XSyncCounter counter = None;
-
-  counters = XSyncListSystemCounters (self->display, &ncounters);
-  for (i = 0; i < ncounters; i++)
-    {
-      if (counters[i].name != NULL && strcmp (counters[i].name, "IDLETIME") == 0)
-        {
-          counter = counters[i].counter;
-          break;
-        }
-    }
-  XSyncFreeSystemCounterList (counters);
-
-  return counter;
-}
-
-static void
-init_xsync (MetaIdleMonitor *self)
-{
-  self->counter = find_idletime_counter (self);
-
-  /* IDLETIME counter not found? */
-  if (self->counter == None)
-    {
-      g_warning ("IDLETIME counter not found\n");
-      return;
-    }
-
-  self->user_active_alarm = _xsync_alarm_set (self, XSyncNegativeTransition, 1, FALSE);
-}
-
-static void
-meta_idle_monitor_constructed (GObject *object)
-{
-  MetaIdleMonitor *self;
-  GdkDisplay *display;
-
-  self = META_IDLE_MONITOR (object);
-
-  G_OBJECT_CLASS (meta_idle_monitor_parent_class)->constructed (object);
-
-  display = gdk_display_get_default ();
-  self->display = gdk_x11_display_get_xdisplay (display);
-  init_xsync (self);
-}
-
 static void
 meta_idle_monitor_dispose (GObject *object)
 {
@@ -207,14 +101,6 @@ meta_idle_monitor_dispose (GObject *object)
 
   g_clear_pointer (&monitor->watches, g_hash_table_destroy);
 
-  if (monitor->user_active_alarm != None)
-    {
-      XSyncDestroyAlarm (monitor->display, monitor->user_active_alarm);
-      monitor->user_active_alarm = None;
-    }
-
-  g_clear_pointer (&monitor->alarms, g_hash_table_destroy);
-
   G_OBJECT_CLASS (meta_idle_monitor_parent_class)->dispose (object);
 }
 
@@ -223,7 +109,6 @@ meta_idle_monitor_class_init (MetaIdleMonitorClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-  object_class->constructed = meta_idle_monitor_constructed;
   object_class->dispose = meta_idle_monitor_dispose;
 }
 
@@ -231,84 +116,7 @@ static void
 meta_idle_monitor_init (MetaIdleMonitor *monitor)
 {
   monitor->watches = g_hash_table_new_full (NULL, NULL, NULL, free_watch);
-  monitor->alarms = g_hash_table_new (NULL, NULL);
-}
-
-static void
-check_x11_watches (MetaIdleMonitor *self,
-                   XSyncAlarm       alarm)
-{
-  GList *node, *watch_ids;
-
-  /* we get the keys and do explicit look ups in case
-   * an early iteration of the loop ends up leading
-   * to watches from later iterations getting invalidated
-   */
-  watch_ids = g_hash_table_get_keys (self->watches);
-
-  for (node = watch_ids; node != NULL; node = node->next)
-    {
-      guint watch_id = GPOINTER_TO_UINT (node->data);
-      MetaIdleMonitorWatch *watch;
-
-      watch = g_hash_table_lookup (self->watches, GUINT_TO_POINTER (watch_id));
-
-      if (watch && watch->xalarm == alarm)
-        meta_idle_monitor_watch_fire (watch);
-    }
-
-  g_list_free (watch_ids);
-}
-
-static void
-ensure_alarm_rescheduled (Display    *dpy,
-                          XSyncAlarm  alarm)
-{
-  XSyncAlarmAttributes attr;
-
-  /* Some versions of Xorg have an issue where alarms aren't
-   * always rescheduled. Calling XSyncChangeAlarm, even
-   * without any attributes, will reschedule the alarm. */
-  XSyncChangeAlarm (dpy, alarm, 0, &attr);
-}
-
-static void
-set_alarm_enabled (Display    *dpy,
-                   XSyncAlarm  alarm,
-                   gboolean    enabled)
-{
-  XSyncAlarmAttributes attr;
-  attr.events = enabled;
-  XSyncChangeAlarm (dpy, alarm, XSyncCAEvents, &attr);
-}
-
-void
-meta_idle_monitor_handle_xevent (MetaIdleMonitor       *self,
-                                 XSyncAlarmNotifyEvent *alarm_event)
-{
-  XSyncAlarm alarm;
-  gboolean has_alarm;
-
-  if (alarm_event->state != XSyncAlarmActive)
-    return;
-
-  alarm = alarm_event->alarm;
-
-  has_alarm = FALSE;
-
-  if (alarm == self->user_active_alarm)
-    {
-      set_alarm_enabled (self->display, alarm, FALSE);
-      has_alarm = TRUE;
-    }
-  else if (g_hash_table_contains (self->alarms, (gpointer) alarm))
-    {
-      ensure_alarm_rescheduled (self->display, alarm);
-      has_alarm = TRUE;
-    }
-
-  if (has_alarm)
-    check_x11_watches (self, alarm);
+  monitor->last_event_time = g_get_monotonic_time ();
 }
 
 static guint32
@@ -320,16 +128,33 @@ get_next_watch_serial (void)
 }
 
 static gboolean
-fire_watch_idle (gpointer data)
+idle_monitor_dispatch_timeout (GSource     *source,
+                               GSourceFunc  callback,
+                               gpointer     user_data)
 {
-  MetaIdleMonitorWatch *watch = data;
+  MetaIdleMonitorWatch *watch = (MetaIdleMonitorWatch *) user_data;
+  int64_t now;
+  int64_t ready_time;
+
+  now = g_source_get_time (source);
+  ready_time = g_source_get_ready_time (source);
+  if (ready_time > now)
+    return G_SOURCE_CONTINUE;
 
-  watch->idle_source_id = 0;
+  g_source_set_ready_time (watch->timeout_source, -1);
   meta_idle_monitor_watch_fire (watch);
 
-  return FALSE;
+  return G_SOURCE_CONTINUE;
 }
 
+static GSourceFuncs idle_monitor_source_funcs =
+  {
+    .prepare = NULL,
+    .check = NULL,
+    .dispatch = idle_monitor_dispatch_timeout,
+    .finalize = NULL,
+  };
+
 static MetaIdleMonitorWatch *
 make_watch (MetaIdleMonitor           *monitor,
             guint64                    timeout_msec,
@@ -348,26 +173,21 @@ make_watch (MetaIdleMonitor           *monitor,
   watch->notify = notify;
   watch->timeout_msec = timeout_msec;
 
-  if (monitor->user_active_alarm != None)
+  if (timeout_msec != 0)
     {
-      if (timeout_msec != 0)
-        {
-          watch->xalarm = _xsync_alarm_set (monitor, XSyncPositiveTransition, timeout_msec, TRUE);
+      GSource *source;
 
-          g_hash_table_add (monitor->alarms, (gpointer) watch->xalarm);
+      source = g_source_new (&idle_monitor_source_funcs, sizeof (GSource));
 
-          if (meta_idle_monitor_get_idletime (monitor) > (gint64) timeout_msec)
-            {
-              watch->idle_source_id = g_idle_add (fire_watch_idle, watch);
-              g_source_set_name_by_id (watch->idle_source_id, "[mutter] fire_watch_idle");
-            }
-        }
-      else
-        {
-          watch->xalarm = monitor->user_active_alarm;
+      g_source_set_callback (source, NULL, watch, NULL);
+      g_source_set_ready_time (source,
+                               monitor->last_event_time +
+                               timeout_msec * 1000);
 
-          set_alarm_enabled (monitor->display, monitor->user_active_alarm, TRUE);
-        }
+      g_source_attach (source, NULL);
+      g_source_unref (source);
+
+      watch->timeout_source = source;
     }
 
   g_hash_table_insert (monitor->watches,
@@ -484,10 +304,39 @@ meta_idle_monitor_remove_watch (MetaIdleMonitor *monitor,
 gint64
 meta_idle_monitor_get_idletime (MetaIdleMonitor *monitor)
 {
-  XSyncValue value;
+  return (g_get_monotonic_time () - monitor->last_event_time) / 1000;
+}
+
+void
+meta_idle_monitor_reset_idletime (MetaIdleMonitor *self)
+{
+  GList *node, *watch_ids;
+
+  self->last_event_time = g_get_monotonic_time ();
+
+  watch_ids = g_hash_table_get_keys (self->watches);
+
+  for (node = watch_ids; node != NULL; node = node->next)
+    {
+      guint watch_id = GPOINTER_TO_UINT (node->data);
+      MetaIdleMonitorWatch *watch;
+
+      watch = g_hash_table_lookup (self->watches,
+                                   GUINT_TO_POINTER (watch_id));
+      if (!watch)
+        continue;
 
-  if (!XSyncQueryCounter (monitor->display, monitor->counter, &value))
-    return -1;
+      if (watch->timeout_msec == 0)
+        {
+          meta_idle_monitor_watch_fire (watch);
+        }
+      else
+        {
+          g_source_set_ready_time (watch->timeout_source,
+                                   self->last_event_time +
+                                   watch->timeout_msec * 1000);
+        }
+    }
 
-  return _xsyncvalue_to_int64 (value);
+  g_list_free (watch_ids);
 }
diff --git a/daemons/idle-monitor/meta-idle-monitor.h b/daemons/idle-monitor/meta-idle-monitor.h
index a1c4b7a..e756ff9 100644
--- a/daemons/idle-monitor/meta-idle-monitor.h
+++ b/daemons/idle-monitor/meta-idle-monitor.h
@@ -19,8 +19,6 @@
 #define META_IDLE_MONITOR_H
 
 #include <glib-object.h>
-#include <X11/Xlib.h>
-#include <X11/extensions/sync.h>
 
 #define META_TYPE_IDLE_MONITOR            (meta_idle_monitor_get_type ())
 #define META_IDLE_MONITOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_IDLE_MONITOR, 
MetaIdleMonitor))
@@ -43,9 +41,6 @@ struct _MetaIdleMonitorClass
 
 GType meta_idle_monitor_get_type (void);
 
-void          meta_idle_monitor_handle_xevent         (MetaIdleMonitor          *self,
-                                                       XSyncAlarmNotifyEvent    *xevent);
-
 guint         meta_idle_monitor_add_idle_watch        (MetaIdleMonitor          *monitor,
                                                       guint64                   interval_msec,
                                                       MetaIdleMonitorWatchFunc  callback,
@@ -61,4 +56,6 @@ void          meta_idle_monitor_remove_watch          (MetaIdleMonitor
                                                       guint                     id);
 gint64        meta_idle_monitor_get_idletime          (MetaIdleMonitor          *monitor);
 
+void          meta_idle_monitor_reset_idletime        (MetaIdleMonitor          *monitor);
+
 #endif


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