[gnome-settings-daemon] power: Add a helper GObject to interface with X11 IDLETIME counters
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-settings-daemon] power: Add a helper GObject to interface with X11 IDLETIME counters
- Date: Tue, 5 Jul 2011 10:14:34 +0000 (UTC)
commit d4dbfb8bdcfbb2b9a67438a8db5d04374b0c559d
Author: Richard Hughes <richard hughsie com>
Date: Mon Jul 4 14:48:23 2011 +0100
power: Add a helper GObject to interface with X11 IDLETIME counters
plugins/power/gpm-idletime.c | 467 ++++++++++++++++++++++++++++++++++++++++++
plugins/power/gpm-idletime.h | 65 ++++++
2 files changed, 532 insertions(+), 0 deletions(-)
---
diff --git a/plugins/power/gpm-idletime.c b/plugins/power/gpm-idletime.c
new file mode 100644
index 0000000..61380ba
--- /dev/null
+++ b/plugins/power/gpm-idletime.c
@@ -0,0 +1,467 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007-2011 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <glib.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/sync.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdk.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gpm-idletime.h"
+
+static void gpm_idletime_finalize (GObject *object);
+
+#define GPM_IDLETIME_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GPM_IDLETIME_TYPE, GpmIdletimePrivate))
+
+struct GpmIdletimePrivate
+{
+ gint sync_event;
+ gboolean reset_set;
+ XSyncCounter idle_counter;
+ GPtrArray *array;
+ Display *dpy;
+};
+
+typedef struct
+{
+ guint id;
+ XSyncValue timeout;
+ XSyncAlarm xalarm;
+ GpmIdletime *idletime;
+} GpmIdletimeAlarm;
+
+enum {
+ SIGNAL_ALARM_EXPIRED,
+ SIGNAL_RESET,
+ LAST_SIGNAL
+};
+
+typedef enum {
+ GPM_IDLETIME_ALARM_TYPE_POSITIVE,
+ GPM_IDLETIME_ALARM_TYPE_NEGATIVE,
+ GPM_IDLETIME_ALARM_TYPE_DISABLED
+} GpmIdletimeAlarmType;
+
+static guint signals [LAST_SIGNAL] = { 0 };
+static gpointer gpm_idletime_object = NULL;
+
+G_DEFINE_TYPE (GpmIdletime, gpm_idletime, G_TYPE_OBJECT)
+
+static gint64
+gpm_idletime_xsyncvalue_to_int64 (XSyncValue value)
+{
+ return ((guint64) XSyncValueHigh32 (value)) << 32 |
+ (guint64) XSyncValueLow32 (value);
+}
+
+gint64
+gpm_idletime_get_time (GpmIdletime *idletime)
+{
+ XSyncValue value;
+ XSyncQueryCounter (idletime->priv->dpy,
+ idletime->priv->idle_counter,
+ &value);
+ return gpm_idletime_xsyncvalue_to_int64 (value);
+}
+
+static void
+gpm_idletime_xsync_alarm_set (GpmIdletime *idletime,
+ GpmIdletimeAlarm *alarm_item,
+ GpmIdletimeAlarmType alarm_type)
+{
+ XSyncAlarmAttributes attr;
+ XSyncValue delta;
+ unsigned int flags;
+ XSyncTestType test;
+
+ /* just remove it */
+ if (alarm_type == GPM_IDLETIME_ALARM_TYPE_DISABLED) {
+ if (alarm_item->xalarm) {
+ XSyncDestroyAlarm (idletime->priv->dpy,
+ alarm_item->xalarm);
+ alarm_item->xalarm = None;
+ }
+ return;
+ }
+
+ /* which way do we do the test? */
+ if (alarm_type == GPM_IDLETIME_ALARM_TYPE_POSITIVE)
+ test = XSyncPositiveTransition;
+ else
+ test = XSyncNegativeTransition;
+
+ XSyncIntToValue (&delta, 0);
+
+ attr.trigger.counter = idletime->priv->idle_counter;
+ attr.trigger.value_type = XSyncAbsolute;
+ attr.trigger.test_type = test;
+ attr.trigger.wait_value = alarm_item->timeout;
+ attr.delta = delta;
+
+ flags = XSyncCACounter |
+ XSyncCAValueType |
+ XSyncCATestType |
+ XSyncCAValue |
+ XSyncCADelta;
+
+ if (alarm_item->xalarm) {
+ XSyncChangeAlarm (idletime->priv->dpy,
+ alarm_item->xalarm,
+ flags,
+ &attr);
+ } else {
+ alarm_item->xalarm = XSyncCreateAlarm (idletime->priv->dpy,
+ flags,
+ &attr);
+ }
+}
+
+void
+gpm_idletime_alarm_reset_all (GpmIdletime *idletime)
+{
+ guint i;
+ GpmIdletimeAlarm *alarm_item;
+
+ g_return_if_fail (GPM_IS_IDLETIME (idletime));
+
+ if (!idletime->priv->reset_set)
+ return;
+
+ /* reset all the alarms (except the reset alarm) to their timeouts */
+ for (i=1; i < idletime->priv->array->len; i++) {
+ alarm_item = g_ptr_array_index (idletime->priv->array, i);
+ gpm_idletime_xsync_alarm_set (idletime,
+ alarm_item,
+ GPM_IDLETIME_ALARM_TYPE_POSITIVE);
+ }
+
+ /* set the reset alarm to be disabled */
+ alarm_item = g_ptr_array_index (idletime->priv->array, 0);
+ gpm_idletime_xsync_alarm_set (idletime,
+ alarm_item,
+ GPM_IDLETIME_ALARM_TYPE_DISABLED);
+
+ /* emit signal so say we've reset all timers */
+ g_signal_emit (idletime, signals [SIGNAL_RESET], 0);
+
+ /* we need to be reset again on the next event */
+ idletime->priv->reset_set = FALSE;
+}
+
+static GpmIdletimeAlarm *
+gpm_idletime_alarm_find_id (GpmIdletime *idletime, guint id)
+{
+ guint i;
+ GpmIdletimeAlarm *alarm_item;
+ for (i = 0; i < idletime->priv->array->len; i++) {
+ alarm_item = g_ptr_array_index (idletime->priv->array, i);
+ if (alarm_item->id == id)
+ return alarm_item;
+ }
+ return NULL;
+}
+
+static void
+gpm_idletime_set_reset_alarm (GpmIdletime *idletime,
+ XSyncAlarmNotifyEvent *alarm_event)
+{
+ GpmIdletimeAlarm *alarm_item;
+ int overflow;
+ XSyncValue add;
+ gint64 current, reset_threshold;
+
+ alarm_item = gpm_idletime_alarm_find_id (idletime, 0);
+
+ if (!idletime->priv->reset_set) {
+ /* don't match on the current value because
+ * XSyncNegativeComparison means less or equal. */
+ XSyncIntToValue (&add, -1);
+ XSyncValueAdd (&alarm_item->timeout,
+ alarm_event->counter_value,
+ add,
+ &overflow);
+
+ /* set the reset alarm to fire the next time
+ * idletime->priv->idle_counter < the current counter value */
+ gpm_idletime_xsync_alarm_set (idletime,
+ alarm_item,
+ GPM_IDLETIME_ALARM_TYPE_NEGATIVE);
+
+ /* don't try to set this again if multiple timers are
+ * going off in sequence */
+ idletime->priv->reset_set = TRUE;
+
+ current = gpm_idletime_get_time (idletime);
+ reset_threshold = gpm_idletime_xsyncvalue_to_int64 (alarm_item->timeout);
+ if (current < reset_threshold) {
+ /* We've missed the alarm already */
+ gpm_idletime_alarm_reset_all (idletime);
+ }
+ }
+}
+
+static GpmIdletimeAlarm *
+gpm_idletime_alarm_find_event (GpmIdletime *idletime,
+ XSyncAlarmNotifyEvent *alarm_event)
+{
+ guint i;
+ GpmIdletimeAlarm *alarm_item;
+ for (i = 0; i < idletime->priv->array->len; i++) {
+ alarm_item = g_ptr_array_index (idletime->priv->array, i);
+ if (alarm_event->alarm == alarm_item->xalarm)
+ return alarm_item;
+ }
+ return NULL;
+}
+
+static GdkFilterReturn
+gpm_idletime_event_filter_cb (GdkXEvent *gdkxevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ GpmIdletimeAlarm *alarm_item;
+ XEvent *xevent = (XEvent *) gdkxevent;
+ GpmIdletime *idletime = (GpmIdletime *) data;
+ XSyncAlarmNotifyEvent *alarm_event;
+
+ /* no point continuing */
+ if (xevent->type != idletime->priv->sync_event + XSyncAlarmNotify)
+ return GDK_FILTER_CONTINUE;
+
+ alarm_event = (XSyncAlarmNotifyEvent *) xevent;
+
+ /* did we match one of our alarms? */
+ alarm_item = gpm_idletime_alarm_find_event (idletime, alarm_event);
+ if (alarm_item == NULL)
+ return GDK_FILTER_CONTINUE;
+
+ /* are we the reset alarm? */
+ if (alarm_item->id == 0) {
+ gpm_idletime_alarm_reset_all (idletime);
+ goto out;
+ }
+
+ /* emit */
+ g_signal_emit (alarm_item->idletime,
+ signals[SIGNAL_ALARM_EXPIRED],
+ 0, alarm_item->id);
+
+ /* we need the first alarm to go off to set the reset alarm */
+ gpm_idletime_set_reset_alarm (idletime, alarm_event);
+out:
+ /* don't propagate */
+ return GDK_FILTER_REMOVE;
+}
+
+static GpmIdletimeAlarm *
+gpm_idletime_alarm_new (GpmIdletime *idletime, guint id)
+{
+ GpmIdletimeAlarm *alarm_item;
+
+ /* create a new alarm */
+ alarm_item = g_new0 (GpmIdletimeAlarm, 1);
+
+ /* set the default values */
+ alarm_item->id = id;
+ alarm_item->xalarm = None;
+ alarm_item->idletime = g_object_ref (idletime);
+
+ return alarm_item;
+}
+
+gboolean
+gpm_idletime_alarm_set (GpmIdletime *idletime,
+ guint id,
+ guint timeout)
+{
+ GpmIdletimeAlarm *alarm_item;
+
+ g_return_val_if_fail (GPM_IS_IDLETIME (idletime), FALSE);
+ g_return_val_if_fail (id != 0, FALSE);
+ g_return_val_if_fail (timeout != 0, FALSE);
+
+ /* see if we already created an alarm with this ID */
+ alarm_item = gpm_idletime_alarm_find_id (idletime, id);
+ if (alarm_item == NULL) {
+ /* create a new alarm */
+ alarm_item = gpm_idletime_alarm_new (idletime, id);
+ g_ptr_array_add (idletime->priv->array, alarm_item);
+ }
+
+ /* set the timeout */
+ XSyncIntToValue (&alarm_item->timeout, (gint)timeout);
+
+ /* set, and start the timer */
+ gpm_idletime_xsync_alarm_set (idletime,
+ alarm_item,
+ GPM_IDLETIME_ALARM_TYPE_POSITIVE);
+ return TRUE;
+}
+
+static gboolean
+gpm_idletime_alarm_free (GpmIdletime *idletime,
+ GpmIdletimeAlarm *alarm_item)
+{
+ g_return_val_if_fail (GPM_IS_IDLETIME (idletime), FALSE);
+ g_return_val_if_fail (alarm_item != NULL, FALSE);
+
+ if (alarm_item->xalarm) {
+ XSyncDestroyAlarm (idletime->priv->dpy,
+ alarm_item->xalarm);
+ }
+ g_object_unref (alarm_item->idletime);
+ g_free (alarm_item);
+ g_ptr_array_remove (idletime->priv->array, alarm_item);
+ return TRUE;
+}
+
+gboolean
+gpm_idletime_alarm_remove (GpmIdletime *idletime, guint id)
+{
+ GpmIdletimeAlarm *alarm_item;
+
+ g_return_val_if_fail (GPM_IS_IDLETIME (idletime), FALSE);
+
+ alarm_item = gpm_idletime_alarm_find_id (idletime, id);
+ if (alarm_item == NULL)
+ return FALSE;
+ gpm_idletime_alarm_free (idletime, alarm_item);
+ return TRUE;
+}
+
+static void
+gpm_idletime_class_init (GpmIdletimeClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->finalize = gpm_idletime_finalize;
+ g_type_class_add_private (klass, sizeof (GpmIdletimePrivate));
+
+ signals [SIGNAL_ALARM_EXPIRED] =
+ g_signal_new ("alarm-expired",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GpmIdletimeClass, alarm_expired),
+ NULL, NULL, g_cclosure_marshal_VOID__UINT,
+ G_TYPE_NONE, 1, G_TYPE_UINT);
+ signals [SIGNAL_RESET] =
+ g_signal_new ("reset",
+ G_TYPE_FROM_CLASS (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GpmIdletimeClass, reset),
+ NULL, NULL, g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+gpm_idletime_init (GpmIdletime *idletime)
+{
+ int sync_error;
+ int ncounters;
+ XSyncSystemCounter *counters;
+ GpmIdletimeAlarm *alarm_item;
+ guint i;
+
+ idletime->priv = GPM_IDLETIME_GET_PRIVATE (idletime);
+
+ idletime->priv->array = g_ptr_array_new ();
+
+ idletime->priv->reset_set = FALSE;
+ idletime->priv->idle_counter = None;
+ idletime->priv->sync_event = 0;
+ idletime->priv->dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default());
+
+ /* get the sync event */
+ if (!XSyncQueryExtension (idletime->priv->dpy,
+ &idletime->priv->sync_event,
+ &sync_error)) {
+ g_warning ("No Sync extension.");
+ return;
+ }
+
+ /* gtk_init should do XSyncInitialize for us */
+ counters = XSyncListSystemCounters (idletime->priv->dpy,
+ &ncounters);
+ for (i = 0; i < ncounters && !idletime->priv->idle_counter; i++) {
+ if (strcmp(counters[i].name, "IDLETIME") == 0)
+ idletime->priv->idle_counter = counters[i].counter;
+ }
+ XSyncFreeSystemCounterList (counters);
+
+ /* arh. we don't have IDLETIME support */
+ if (!idletime->priv->idle_counter) {
+ g_warning ("No idle counter");
+ return;
+ }
+
+ /* catch the timer alarm */
+ gdk_window_add_filter (NULL,
+ gpm_idletime_event_filter_cb,
+ idletime);
+
+ /* create a reset alarm */
+ alarm_item = gpm_idletime_alarm_new (idletime, 0);
+ g_ptr_array_add (idletime->priv->array, alarm_item);
+}
+
+static void
+gpm_idletime_finalize (GObject *object)
+{
+ guint i;
+ GpmIdletime *idletime;
+ GpmIdletimeAlarm *alarm_item;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GPM_IS_IDLETIME (object));
+
+ idletime = GPM_IDLETIME (object);
+ idletime->priv = GPM_IDLETIME_GET_PRIVATE (idletime);
+
+ /* free all counters, including reset counter */
+ for (i = 0; i < idletime->priv->array->len; i++) {
+ alarm_item = g_ptr_array_index (idletime->priv->array, i);
+ gpm_idletime_alarm_free (idletime, alarm_item);
+ }
+ g_ptr_array_free (idletime->priv->array, TRUE);
+
+ G_OBJECT_CLASS (gpm_idletime_parent_class)->finalize (object);
+}
+
+GpmIdletime *
+gpm_idletime_new (void)
+{
+ if (gpm_idletime_object != NULL) {
+ g_object_ref (gpm_idletime_object);
+ } else {
+ gpm_idletime_object = g_object_new (GPM_IDLETIME_TYPE, NULL);
+ g_object_add_weak_pointer (gpm_idletime_object,
+ &gpm_idletime_object);
+ }
+ return GPM_IDLETIME (gpm_idletime_object);
+}
+
diff --git a/plugins/power/gpm-idletime.h b/plugins/power/gpm-idletime.h
new file mode 100644
index 0000000..a61fe4b
--- /dev/null
+++ b/plugins/power/gpm-idletime.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 Richard Hughes <richard hughsie com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __GPM_IDLETIME_H
+#define __GPM_IDLETIME_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GPM_IDLETIME_TYPE (gpm_idletime_get_type ())
+#define GPM_IDLETIME(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GPM_IDLETIME_TYPE, GpmIdletime))
+#define GPM_IDLETIME_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GPM_IDLETIME_TYPE, GpmIdletimeClass))
+#define GPM_IS_IDLETIME(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GPM_IDLETIME_TYPE))
+#define GPM_IS_IDLETIME_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GPM_IDLETIME_TYPE))
+#define GPM_IDLETIME_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GPM_IDLETIME_TYPE, GpmIdletimeClass))
+
+typedef struct GpmIdletimePrivate GpmIdletimePrivate;
+
+typedef struct
+{
+ GObject parent;
+ GpmIdletimePrivate *priv;
+} GpmIdletime;
+
+typedef struct
+{
+ GObjectClass parent_class;
+ void (* alarm_expired) (GpmIdletime *idletime,
+ guint timer_id);
+ void (* reset) (GpmIdletime *idletime);
+} GpmIdletimeClass;
+
+GType gpm_idletime_get_type (void);
+GpmIdletime *gpm_idletime_new (void);
+
+void gpm_idletime_alarm_reset_all (GpmIdletime *idletime);
+gboolean gpm_idletime_alarm_set (GpmIdletime *idletime,
+ guint alarm_id,
+ guint timeout);
+gboolean gpm_idletime_alarm_remove (GpmIdletime *idletime,
+ guint alarm_id);
+gint64 gpm_idletime_get_time (GpmIdletime *idletime);
+
+G_END_DECLS
+
+#endif /* __GPM_IDLETIME_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]