diff options
author | Stefano Karapetsas <[email protected]> | 2011-12-11 12:55:19 +0100 |
---|---|---|
committer | Stefano Karapetsas <[email protected]> | 2011-12-11 12:55:19 +0100 |
commit | 51175189c6d7313a3b84019e39496f957c4e6164 (patch) | |
tree | e4c2c130fa3140bca28685ef900f04a012e53dcd /src/egg-idletime.c | |
download | mate-power-manager-51175189c6d7313a3b84019e39496f957c4e6164.tar.bz2 mate-power-manager-51175189c6d7313a3b84019e39496f957c4e6164.tar.xz |
moved from Mate-Extra
Diffstat (limited to 'src/egg-idletime.c')
-rw-r--r-- | src/egg-idletime.c | 690 |
1 files changed, 690 insertions, 0 deletions
diff --git a/src/egg-idletime.c b/src/egg-idletime.c new file mode 100644 index 0000000..28b7390 --- /dev/null +++ b/src/egg-idletime.c @@ -0,0 +1,690 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2007-2009 Richard Hughes <[email protected]> + * + * 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 "egg-idletime.h" + +static void egg_idletime_finalize (GObject *object); + +#define EGG_IDLETIME_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), EGG_IDLETIME_TYPE, EggIdletimePrivate)) + +struct EggIdletimePrivate +{ + gint sync_event; + gboolean reset_set; + XSyncCounter idle_counter; + GPtrArray *array; + Display *dpy; +}; + +typedef struct +{ + guint id; + XSyncValue timeout; + XSyncAlarm xalarm; + EggIdletime *idletime; +} EggIdletimeAlarm; + +enum { + SIGNAL_ALARM_EXPIRED, + SIGNAL_RESET, + LAST_SIGNAL +}; + +typedef enum { + EGG_IDLETIME_ALARM_TYPE_POSITIVE, + EGG_IDLETIME_ALARM_TYPE_NEGATIVE, + EGG_IDLETIME_ALARM_TYPE_DISABLED +} EggIdletimeAlarmType; + +static guint signals [LAST_SIGNAL] = { 0 }; +static gpointer egg_idletime_object = NULL; + +G_DEFINE_TYPE (EggIdletime, egg_idletime, G_TYPE_OBJECT) + +/** + * egg_idletime_xsyncvalue_to_int64: + */ +static gint64 +egg_idletime_xsyncvalue_to_int64 (XSyncValue value) +{ + return ((guint64) XSyncValueHigh32 (value)) << 32 | (guint64) XSyncValueLow32 (value); +} + +/** + * egg_idletime_get_time: + */ +gint64 +egg_idletime_get_time (EggIdletime *idletime) +{ + XSyncValue value; + XSyncQueryCounter (idletime->priv->dpy, idletime->priv->idle_counter, &value); + return egg_idletime_xsyncvalue_to_int64 (value); +} + +/** + * egg_idletime_xsync_alarm_set: + */ +static void +egg_idletime_xsync_alarm_set (EggIdletime *idletime, EggIdletimeAlarm *alarm, EggIdletimeAlarmType alarm_type) +{ + XSyncAlarmAttributes attr; + XSyncValue delta; + unsigned int flags; + XSyncTestType test; + + /* just remove it */ + if (alarm_type == EGG_IDLETIME_ALARM_TYPE_DISABLED) { + if (alarm->xalarm) { + XSyncDestroyAlarm (idletime->priv->dpy, alarm->xalarm); + alarm->xalarm = None; + } + return; + } + + /* which way do we do the test? */ + if (alarm_type == EGG_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->timeout; + attr.delta = delta; + + flags = XSyncCACounter | XSyncCAValueType | XSyncCATestType | XSyncCAValue | XSyncCADelta; + + if (alarm->xalarm) + XSyncChangeAlarm (idletime->priv->dpy, alarm->xalarm, flags, &attr); + else + alarm->xalarm = XSyncCreateAlarm (idletime->priv->dpy, flags, &attr); +} + +/** + * egg_idletime_alarm_reset_all: + */ +void +egg_idletime_alarm_reset_all (EggIdletime *idletime) +{ + guint i; + EggIdletimeAlarm *alarm; + + g_return_if_fail (EGG_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 = g_ptr_array_index (idletime->priv->array, i); + egg_idletime_xsync_alarm_set (idletime, alarm, EGG_IDLETIME_ALARM_TYPE_POSITIVE); + } + + /* set the reset alarm to be disabled */ + alarm = g_ptr_array_index (idletime->priv->array, 0); + egg_idletime_xsync_alarm_set (idletime, alarm, EGG_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; +} + +/** + * egg_idletime_alarm_find_id: + */ +static EggIdletimeAlarm * +egg_idletime_alarm_find_id (EggIdletime *idletime, guint id) +{ + guint i; + EggIdletimeAlarm *alarm; + for (i=0; i<idletime->priv->array->len; i++) { + alarm = g_ptr_array_index (idletime->priv->array, i); + if (alarm->id == id) + return alarm; + } + return NULL; +} + +/** + * egg_idletime_set_reset_alarm: + */ +static void +egg_idletime_set_reset_alarm (EggIdletime *idletime, XSyncAlarmNotifyEvent *alarm_event) +{ + EggIdletimeAlarm *alarm; + int overflow; + XSyncValue add; + gint64 current, reset_threshold; + + alarm = egg_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->timeout, alarm_event->counter_value, add, &overflow); + + /* set the reset alarm to fire the next time + * idletime->priv->idle_counter < the current counter value */ + egg_idletime_xsync_alarm_set (idletime, alarm, EGG_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 = egg_idletime_get_time (idletime); + reset_threshold = egg_idletime_xsyncvalue_to_int64 (alarm->timeout); + if (current < reset_threshold) { + /* We've missed the alarm already */ + egg_idletime_alarm_reset_all (idletime); + } + } +} + +/** + * egg_idletime_alarm_find_event: + */ +static EggIdletimeAlarm * +egg_idletime_alarm_find_event (EggIdletime *idletime, XSyncAlarmNotifyEvent *alarm_event) +{ + guint i; + EggIdletimeAlarm *alarm; + for (i=0; i<idletime->priv->array->len; i++) { + alarm = g_ptr_array_index (idletime->priv->array, i); + if (alarm_event->alarm == alarm->xalarm) + return alarm; + } + return NULL; +} + +/** + * egg_idletime_event_filter_cb: + */ +static GdkFilterReturn +egg_idletime_event_filter_cb (GdkXEvent *gdkxevent, GdkEvent *event, gpointer data) +{ + EggIdletimeAlarm *alarm; + XEvent *xevent = (XEvent *) gdkxevent; + EggIdletime *idletime = (EggIdletime *) 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 = egg_idletime_alarm_find_event (idletime, alarm_event); + if (alarm == NULL) + return GDK_FILTER_CONTINUE; + + /* are we the reset alarm? */ + if (alarm->id == 0) { + egg_idletime_alarm_reset_all (idletime); + goto out; + } + + /* emit */ + g_signal_emit (alarm->idletime, signals [SIGNAL_ALARM_EXPIRED], 0, alarm->id); + + /* we need the first alarm to go off to set the reset alarm */ + egg_idletime_set_reset_alarm (idletime, alarm_event); +out: + /* don't propagate */ + return GDK_FILTER_REMOVE; +} + +/** + * egg_idletime_alarm_new: + */ +static EggIdletimeAlarm * +egg_idletime_alarm_new (EggIdletime *idletime, guint id) +{ + EggIdletimeAlarm *alarm; + + /* create a new alarm */ + alarm = g_new0 (EggIdletimeAlarm, 1); + + /* set the default values */ + alarm->id = id; + alarm->xalarm = None; + alarm->idletime = g_object_ref (idletime); + + return alarm; +} + +/** + * egg_idletime_alarm_set: + */ +gboolean +egg_idletime_alarm_set (EggIdletime *idletime, guint id, guint timeout) +{ + EggIdletimeAlarm *alarm; + + g_return_val_if_fail (EGG_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 = egg_idletime_alarm_find_id (idletime, id); + if (alarm == NULL) { + /* create a new alarm */ + alarm = egg_idletime_alarm_new (idletime, id); + + /* add to array */ + g_ptr_array_add (idletime->priv->array, alarm); + } + + /* set the timeout */ + XSyncIntToValue (&alarm->timeout, (gint)timeout); + + /* set, and start the timer */ + egg_idletime_xsync_alarm_set (idletime, alarm, EGG_IDLETIME_ALARM_TYPE_POSITIVE); + return TRUE; +} + +/** + * egg_idletime_alarm_free: + */ +static gboolean +egg_idletime_alarm_free (EggIdletime *idletime, EggIdletimeAlarm *alarm) +{ + g_return_val_if_fail (EGG_IS_IDLETIME (idletime), FALSE); + g_return_val_if_fail (alarm != NULL, FALSE); + + if (alarm->xalarm) + XSyncDestroyAlarm (idletime->priv->dpy, alarm->xalarm); + g_object_unref (alarm->idletime); + g_free (alarm); + g_ptr_array_remove (idletime->priv->array, alarm); + return TRUE; +} + +/** + * egg_idletime_alarm_free: + */ +gboolean +egg_idletime_alarm_remove (EggIdletime *idletime, guint id) +{ + EggIdletimeAlarm *alarm; + + g_return_val_if_fail (EGG_IS_IDLETIME (idletime), FALSE); + + alarm = egg_idletime_alarm_find_id (idletime, id); + if (alarm == NULL) + return FALSE; + egg_idletime_alarm_free (idletime, alarm); + return TRUE; +} + +/** + * egg_idletime_class_init: + **/ +static void +egg_idletime_class_init (EggIdletimeClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = egg_idletime_finalize; + g_type_class_add_private (klass, sizeof (EggIdletimePrivate)); + + signals [SIGNAL_ALARM_EXPIRED] = + g_signal_new ("alarm-expired", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EggIdletimeClass, 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 (EggIdletimeClass, reset), + NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +/** + * egg_idletime_init: + **/ +static void +egg_idletime_init (EggIdletime *idletime) +{ + int sync_error; + int ncounters; + XSyncSystemCounter *counters; + EggIdletimeAlarm *alarm; + guint i; + + idletime->priv = EGG_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, egg_idletime_event_filter_cb, idletime); + + /* create a reset alarm */ + alarm = egg_idletime_alarm_new (idletime, 0); + g_ptr_array_add (idletime->priv->array, alarm); +} + +/** + * egg_idletime_finalize: + **/ +static void +egg_idletime_finalize (GObject *object) +{ + guint i; + EggIdletime *idletime; + EggIdletimeAlarm *alarm; + + g_return_if_fail (object != NULL); + g_return_if_fail (EGG_IS_IDLETIME (object)); + + idletime = EGG_IDLETIME (object); + idletime->priv = EGG_IDLETIME_GET_PRIVATE (idletime); + + /* free all counters, including reset counter */ + for (i=0; i<idletime->priv->array->len; i++) { + alarm = g_ptr_array_index (idletime->priv->array, i); + egg_idletime_alarm_free (idletime, alarm); + } + g_ptr_array_free (idletime->priv->array, TRUE); + + G_OBJECT_CLASS (egg_idletime_parent_class)->finalize (object); +} + +/** + * egg_idletime_new: + **/ +EggIdletime * +egg_idletime_new (void) +{ + if (egg_idletime_object != NULL) { + g_object_ref (egg_idletime_object); + } else { + egg_idletime_object = g_object_new (EGG_IDLETIME_TYPE, NULL); + g_object_add_weak_pointer (egg_idletime_object, &egg_idletime_object); + } + return EGG_IDLETIME (egg_idletime_object); +} + +/*************************************************************************** + *** MAKE CHECK TESTS *** + ***************************************************************************/ +#ifdef EGG_TEST +#include "egg-test.h" + +static void +egg_test_egg_idletime_wait (guint time_ms) +{ + GTimer *ltimer = g_timer_new (); + gfloat goal = time_ms / (gfloat) 1000.0f; + do { + g_main_context_iteration (NULL, FALSE); + } while (g_timer_elapsed (ltimer, NULL) < goal); + g_timer_destroy (ltimer); +} + +static guint last_alarm = 0; +static guint event_time; +GTimer *timer; + +static void +gpm_alarm_expired_cb (EggIdletime *idletime, guint alarm, gpointer data) +{ + last_alarm = alarm; + event_time = g_timer_elapsed (timer, NULL) * (gfloat) 1000.0f; +// g_print ("[evt %i in %ims]\n", alarm, event_time); +} + +static void +wait_until_alarm (void) +{ + g_print ("*****************************\n"); + g_print ("*** DO NOT MOVE THE MOUSE ***\n"); + g_print ("*****************************\n"); + while (last_alarm == 0) + g_main_context_iteration (NULL, FALSE); +} + +static void +wait_until_reset (void) +{ + if (last_alarm == 0) + return; + g_print ("*****************************\n"); + g_print ("*** MOVE THE MOUSE ***\n"); + g_print ("*****************************\n"); + while (last_alarm != 0) + g_main_context_iteration (NULL, FALSE); + egg_test_egg_idletime_wait (1000); +} + +void +egg_idletime_test (gpointer data) +{ + EggIdletime *idletime; + gboolean ret; + guint i; + EggTest *test = (EggTest *) data; + + if (egg_test_start (test, "EggIdletime") == FALSE) + return; + + timer = g_timer_new (); + gdk_init (NULL, NULL); + + /* warn */ + + g_timer_start (timer); + /************************************************************/ + egg_test_title (test, "check to see if delay works as expected"); + egg_test_egg_idletime_wait (2000); + event_time = g_timer_elapsed (timer, NULL) * (gfloat) 1000.0f; + if (event_time > 1800 && event_time < 2200) { + egg_test_success (test, "time %i~=%i", 2000, event_time); + } else { + egg_test_failed (test, "time not the same! %i != %i", event_time, 2000); + } + + /************************************************************/ + egg_test_title (test, "make sure we get a non null device"); + idletime = egg_idletime_new (); + if (idletime != NULL) { + egg_test_success (test, "got EggIdletime"); + } else { + egg_test_failed (test, "could not get EggIdletime"); + } + g_signal_connect (idletime, "alarm-expired", + G_CALLBACK (gpm_alarm_expired_cb), NULL); + + /************************************************************/ + egg_test_title (test, "check if we are alarm zero with no alarms"); + if (last_alarm == 0) { + egg_test_success (test, NULL); + } else { + egg_test_failed (test, "alarm %i set!", last_alarm); + } + + /************************************************************/ + egg_test_title (test, "check if we can set an reset alarm"); + ret = egg_idletime_alarm_set (idletime, 0, 100); + if (!ret) { + egg_test_success (test, "ignored reset alarm"); + } else { + egg_test_failed (test, "did not ignore reset alarm"); + } + + /************************************************************/ + egg_test_title (test, "check if we can set an alarm timeout of zero"); + ret = egg_idletime_alarm_set (idletime, 999, 0); + if (!ret) { + egg_test_success (test, "ignored invalid alarm"); + } else { + egg_test_failed (test, "did not ignore invalid alarm"); + } + + /************************************************************/ + g_timer_start (timer); + egg_test_title (test, "check if we can set an alarm"); + ret = egg_idletime_alarm_set (idletime, 101, 5000); + if (ret) { + egg_test_success (test, "set alarm okay"); + } else { + egg_test_failed (test, "could not set alarm"); + } + + egg_idletime_alarm_set (idletime, 101, 5000); + wait_until_alarm (); + + /* loop this two times */ + for (i=0; i<2; i++) { + /* just let it time out, and wait for human input */ + wait_until_reset (); + g_timer_start (timer); + + /************************************************************/ + g_timer_start (timer); + egg_test_title (test, "check if we can set an alarm"); + ret = egg_idletime_alarm_set (idletime, 101, 5000); + if (ret) { + egg_test_success (test, "set alarm 5000ms okay"); + } else { + egg_test_failed (test, "could not set alarm 5000ms"); + } + + /* wait for alarm to go off */ + wait_until_alarm (); + g_timer_start (timer); + + /************************************************************/ + egg_test_title (test, "check if correct alarm has gone off"); + if (last_alarm == 101) { + egg_test_success (test, "correct alarm"); + } else { + egg_test_failed (test, "alarm %i set!", last_alarm); + } + + /************************************************************/ + egg_test_title (test, "check if alarm has gone off in correct time"); + if (event_time > 3000 && event_time < 6000) { + egg_test_success (test, "correct, timeout ideally %ims (we did after %ims)", 5000, event_time); + } else { + egg_test_failed (test, "alarm %i did not timeout correctly !", last_alarm); + } + } + + /* just let it time out, and wait for human input */ + wait_until_reset (); + g_timer_start (timer); + + /************************************************************/ + g_timer_start (timer); + egg_test_title (test, "check if we can set an existing alarm"); + ret = egg_idletime_alarm_set (idletime, 101, 10000); + if (ret) { + egg_test_success (test, "set alarm 10000ms okay"); + } else { + egg_test_failed (test, "could not set alarm 10000ms"); + } + + /* wait for alarm to go off */ + wait_until_alarm (); + g_timer_start (timer); + + /************************************************************/ + egg_test_title (test, "check if alarm has gone off in the old time"); + if (event_time > 5000) { + egg_test_success (test, "last timeout value used"); + } else { + egg_test_failed (test, "incorrect timeout used %ims", event_time); + } + + /************************************************************/ + egg_test_title (test, "check if we can remove an invalid alarm"); + ret = egg_idletime_alarm_remove (idletime, 202); + if (!ret) { + egg_test_success (test, "ignored invalid alarm"); + } else { + egg_test_failed (test, "removed invalid alarm"); + } + + /************************************************************/ + egg_test_title (test, "check if we can remove an valid alarm"); + ret = egg_idletime_alarm_remove (idletime, 101); + if (ret) { + egg_test_success (test, "removed valid alarm"); + } else { + egg_test_failed (test, "failed to remove valid alarm"); + } + + g_timer_destroy (timer); + g_object_unref (idletime); + + egg_test_end (test); +} + +#endif + |