1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3
* Copyright (C) 2007-2009 Richard Hughes <richard@hughsie.com>
5
* Licensed under the GNU General Public License Version 2
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28
#include <X11/extensions/sync.h>
36
#include "gpm-idletime.h"
38
static void gpm_idletime_finalize (GObject *object);
40
#define GPM_IDLETIME_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GPM_IDLETIME_TYPE, GpmIdletimePrivate))
42
struct GpmIdletimePrivate
46
XSyncCounter idle_counter;
56
GpmIdletime *idletime;
66
GPM_IDLETIME_ALARM_TYPE_POSITIVE,
67
GPM_IDLETIME_ALARM_TYPE_NEGATIVE,
68
GPM_IDLETIME_ALARM_TYPE_DISABLED
69
} GpmIdletimeAlarmType;
71
static guint signals [LAST_SIGNAL] = { 0 };
72
static gpointer gpm_idletime_object = NULL;
74
G_DEFINE_TYPE (GpmIdletime, gpm_idletime, G_TYPE_OBJECT)
77
* gpm_idletime_xsyncvalue_to_int64:
80
gpm_idletime_xsyncvalue_to_int64 (XSyncValue value)
82
return ((guint64) XSyncValueHigh32 (value)) << 32 | (guint64) XSyncValueLow32 (value);
86
* gpm_idletime_get_time:
89
gpm_idletime_get_time (GpmIdletime *idletime)
92
XSyncQueryCounter (idletime->priv->dpy, idletime->priv->idle_counter, &value);
93
return gpm_idletime_xsyncvalue_to_int64 (value);
97
* gpm_idletime_xsync_alarm_set:
100
gpm_idletime_xsync_alarm_set (GpmIdletime *idletime, GpmIdletimeAlarm *alarm_item, GpmIdletimeAlarmType alarm_type)
102
XSyncAlarmAttributes attr;
108
if (alarm_type == GPM_IDLETIME_ALARM_TYPE_DISABLED) {
109
if (alarm_item->xalarm) {
110
XSyncDestroyAlarm (idletime->priv->dpy, alarm_item->xalarm);
111
alarm_item->xalarm = None;
116
/* which way do we do the test? */
117
if (alarm_type == GPM_IDLETIME_ALARM_TYPE_POSITIVE)
118
test = XSyncPositiveTransition;
120
test = XSyncNegativeTransition;
122
XSyncIntToValue (&delta, 0);
124
attr.trigger.counter = idletime->priv->idle_counter;
125
attr.trigger.value_type = XSyncAbsolute;
126
attr.trigger.test_type = test;
127
attr.trigger.wait_value = alarm_item->timeout;
130
flags = XSyncCACounter | XSyncCAValueType | XSyncCATestType | XSyncCAValue | XSyncCADelta;
132
if (alarm_item->xalarm)
133
XSyncChangeAlarm (idletime->priv->dpy, alarm_item->xalarm, flags, &attr);
135
alarm_item->xalarm = XSyncCreateAlarm (idletime->priv->dpy, flags, &attr);
139
* gpm_idletime_alarm_reset_all:
142
gpm_idletime_alarm_reset_all (GpmIdletime *idletime)
145
GpmIdletimeAlarm *alarm_item;
147
g_return_if_fail (GPM_IS_IDLETIME (idletime));
149
if (!idletime->priv->reset_set)
152
/* reset all the alarms (except the reset alarm) to their timeouts */
153
for (i=1; i<idletime->priv->array->len; i++) {
154
alarm_item = g_ptr_array_index (idletime->priv->array, i);
155
gpm_idletime_xsync_alarm_set (idletime, alarm_item, GPM_IDLETIME_ALARM_TYPE_POSITIVE);
158
/* set the reset alarm to be disabled */
159
alarm_item = g_ptr_array_index (idletime->priv->array, 0);
160
gpm_idletime_xsync_alarm_set (idletime, alarm_item, GPM_IDLETIME_ALARM_TYPE_DISABLED);
162
/* emit signal so say we've reset all timers */
163
g_signal_emit (idletime, signals [SIGNAL_RESET], 0);
165
/* we need to be reset again on the next event */
166
idletime->priv->reset_set = FALSE;
170
* gpm_idletime_alarm_find_id:
172
static GpmIdletimeAlarm *
173
gpm_idletime_alarm_find_id (GpmIdletime *idletime, guint id)
176
GpmIdletimeAlarm *alarm_item;
177
for (i=0; i<idletime->priv->array->len; i++) {
178
alarm_item = g_ptr_array_index (idletime->priv->array, i);
179
if (alarm_item->id == id)
186
* gpm_idletime_set_reset_alarm:
189
gpm_idletime_set_reset_alarm (GpmIdletime *idletime, XSyncAlarmNotifyEvent *alarm_event)
191
GpmIdletimeAlarm *alarm_item;
194
gint64 current, reset_threshold;
196
alarm_item = gpm_idletime_alarm_find_id (idletime, 0);
198
if (!idletime->priv->reset_set) {
199
/* don't match on the current value because
200
* XSyncNegativeComparison means less or equal. */
201
XSyncIntToValue (&add, -1);
202
XSyncValueAdd (&alarm_item->timeout, alarm_event->counter_value, add, &overflow);
204
/* set the reset alarm to fire the next time
205
* idletime->priv->idle_counter < the current counter value */
206
gpm_idletime_xsync_alarm_set (idletime, alarm_item, GPM_IDLETIME_ALARM_TYPE_NEGATIVE);
208
/* don't try to set this again if multiple timers are going off in sequence */
209
idletime->priv->reset_set = TRUE;
211
current = gpm_idletime_get_time (idletime);
212
reset_threshold = gpm_idletime_xsyncvalue_to_int64 (alarm_item->timeout);
213
if (current < reset_threshold) {
214
/* We've missed the alarm already */
215
gpm_idletime_alarm_reset_all (idletime);
221
* gpm_idletime_alarm_find_event:
223
static GpmIdletimeAlarm *
224
gpm_idletime_alarm_find_event (GpmIdletime *idletime, XSyncAlarmNotifyEvent *alarm_event)
227
GpmIdletimeAlarm *alarm_item;
228
for (i=0; i<idletime->priv->array->len; i++) {
229
alarm_item = g_ptr_array_index (idletime->priv->array, i);
230
if (alarm_event->alarm == alarm_item->xalarm)
237
* gpm_idletime_event_filter_cb:
239
static GdkFilterReturn
240
gpm_idletime_event_filter_cb (GdkXEvent *gdkxevent, GdkEvent *event, gpointer data)
242
GpmIdletimeAlarm *alarm_item;
243
XEvent *xevent = (XEvent *) gdkxevent;
244
GpmIdletime *idletime = (GpmIdletime *) data;
245
XSyncAlarmNotifyEvent *alarm_event;
247
/* no point continuing */
248
if (xevent->type != idletime->priv->sync_event + XSyncAlarmNotify)
249
return GDK_FILTER_CONTINUE;
251
alarm_event = (XSyncAlarmNotifyEvent *) xevent;
253
/* did we match one of our alarms? */
254
alarm_item = gpm_idletime_alarm_find_event (idletime, alarm_event);
255
if (alarm_item == NULL)
256
return GDK_FILTER_CONTINUE;
258
/* are we the reset alarm? */
259
if (alarm_item->id == 0) {
260
gpm_idletime_alarm_reset_all (idletime);
265
g_signal_emit (alarm_item->idletime, signals [SIGNAL_ALARM_EXPIRED], 0, alarm_item->id);
267
/* we need the first alarm to go off to set the reset alarm */
268
gpm_idletime_set_reset_alarm (idletime, alarm_event);
270
/* don't propagate */
271
return GDK_FILTER_REMOVE;
275
* gpm_idletime_alarm_new:
277
static GpmIdletimeAlarm *
278
gpm_idletime_alarm_new (GpmIdletime *idletime, guint id)
280
GpmIdletimeAlarm *alarm_item;
282
/* create a new alarm */
283
alarm_item = g_new0 (GpmIdletimeAlarm, 1);
285
/* set the default values */
287
alarm_item->xalarm = None;
288
alarm_item->idletime = g_object_ref (idletime);
294
* gpm_idletime_alarm_set:
297
gpm_idletime_alarm_set (GpmIdletime *idletime, guint id, guint timeout)
299
GpmIdletimeAlarm *alarm_item;
301
g_return_val_if_fail (GPM_IS_IDLETIME (idletime), FALSE);
302
g_return_val_if_fail (id != 0, FALSE);
303
g_return_val_if_fail (timeout != 0, FALSE);
305
/* see if we already created an alarm with this ID */
306
alarm_item = gpm_idletime_alarm_find_id (idletime, id);
307
if (alarm_item == NULL) {
308
/* create a new alarm */
309
alarm_item = gpm_idletime_alarm_new (idletime, id);
312
g_ptr_array_add (idletime->priv->array, alarm_item);
315
/* set the timeout */
316
XSyncIntToValue (&alarm_item->timeout, (gint)timeout);
318
/* set, and start the timer */
319
gpm_idletime_xsync_alarm_set (idletime, alarm_item, GPM_IDLETIME_ALARM_TYPE_POSITIVE);
324
* gpm_idletime_alarm_free:
327
gpm_idletime_alarm_free (GpmIdletime *idletime, GpmIdletimeAlarm *alarm_item)
329
g_return_val_if_fail (GPM_IS_IDLETIME (idletime), FALSE);
330
g_return_val_if_fail (alarm_item != NULL, FALSE);
332
if (alarm_item->xalarm)
333
XSyncDestroyAlarm (idletime->priv->dpy, alarm_item->xalarm);
334
g_object_unref (alarm_item->idletime);
336
g_ptr_array_remove (idletime->priv->array, alarm_item);
341
* gpm_idletime_alarm_free:
344
gpm_idletime_alarm_remove (GpmIdletime *idletime, guint id)
346
GpmIdletimeAlarm *alarm_item;
348
g_return_val_if_fail (GPM_IS_IDLETIME (idletime), FALSE);
350
alarm_item = gpm_idletime_alarm_find_id (idletime, id);
351
if (alarm_item == NULL)
353
gpm_idletime_alarm_free (idletime, alarm_item);
358
* gpm_idletime_class_init:
361
gpm_idletime_class_init (GpmIdletimeClass *klass)
363
GObjectClass *object_class = G_OBJECT_CLASS (klass);
364
object_class->finalize = gpm_idletime_finalize;
365
g_type_class_add_private (klass, sizeof (GpmIdletimePrivate));
367
signals [SIGNAL_ALARM_EXPIRED] =
368
g_signal_new ("alarm-expired",
369
G_TYPE_FROM_CLASS (object_class),
371
G_STRUCT_OFFSET (GpmIdletimeClass, alarm_expired),
372
NULL, NULL, g_cclosure_marshal_VOID__UINT,
373
G_TYPE_NONE, 1, G_TYPE_UINT);
374
signals [SIGNAL_RESET] =
375
g_signal_new ("reset",
376
G_TYPE_FROM_CLASS (object_class),
378
G_STRUCT_OFFSET (GpmIdletimeClass, reset),
379
NULL, NULL, g_cclosure_marshal_VOID__VOID,
387
gpm_idletime_init (GpmIdletime *idletime)
391
XSyncSystemCounter *counters;
392
GpmIdletimeAlarm *alarm_item;
395
idletime->priv = GPM_IDLETIME_GET_PRIVATE (idletime);
397
idletime->priv->array = g_ptr_array_new ();
399
idletime->priv->reset_set = FALSE;
400
idletime->priv->idle_counter = None;
401
idletime->priv->sync_event = 0;
402
idletime->priv->dpy = GDK_DISPLAY_XDISPLAY (gdk_display_get_default());
404
/* get the sync event */
405
if (!XSyncQueryExtension (idletime->priv->dpy, &idletime->priv->sync_event, &sync_error)) {
406
g_warning ("No Sync extension.");
410
/* gtk_init should do XSyncInitialize for us */
411
counters = XSyncListSystemCounters (idletime->priv->dpy, &ncounters);
412
for (i=0; i < ncounters && !idletime->priv->idle_counter; i++) {
413
if (strcmp(counters[i].name, "IDLETIME") == 0)
414
idletime->priv->idle_counter = counters[i].counter;
416
XSyncFreeSystemCounterList (counters);
418
/* arh. we don't have IDLETIME support */
419
if (!idletime->priv->idle_counter) {
420
g_warning ("No idle counter.");
424
/* catch the timer alarm */
425
gdk_window_add_filter (NULL, gpm_idletime_event_filter_cb, idletime);
427
/* create a reset alarm */
428
alarm_item = gpm_idletime_alarm_new (idletime, 0);
429
g_ptr_array_add (idletime->priv->array, alarm_item);
433
* gpm_idletime_finalize:
436
gpm_idletime_finalize (GObject *object)
439
GpmIdletime *idletime;
440
GpmIdletimeAlarm *alarm_item;
442
g_return_if_fail (object != NULL);
443
g_return_if_fail (GPM_IS_IDLETIME (object));
445
idletime = GPM_IDLETIME (object);
446
idletime->priv = GPM_IDLETIME_GET_PRIVATE (idletime);
448
/* free all counters, including reset counter */
449
for (i=0; i<idletime->priv->array->len; i++) {
450
alarm_item = g_ptr_array_index (idletime->priv->array, i);
451
gpm_idletime_alarm_free (idletime, alarm_item);
453
g_ptr_array_free (idletime->priv->array, TRUE);
455
G_OBJECT_CLASS (gpm_idletime_parent_class)->finalize (object);
462
gpm_idletime_new (void)
464
if (gpm_idletime_object != NULL) {
465
g_object_ref (gpm_idletime_object);
467
gpm_idletime_object = g_object_new (GPM_IDLETIME_TYPE, NULL);
468
g_object_add_weak_pointer (gpm_idletime_object, &gpm_idletime_object);
470
return GPM_IDLETIME (gpm_idletime_object);