gnome-shell/src/shell-idle-monitor.c
Jasper St. Pierre 8f71920622 messageTray: Correct idleness
If the user is already active when the notification pops up, we
won't get an idle watcher because there's no transition from
active to idle or vice versa. Correct this by initializing the
state correctly from XSync.
2012-08-19 21:15:47 -04:00

430 lines
13 KiB
C

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Adapted from gnome-session/gnome-session/gs-idle-monitor.c
*
* Copyright (C) 2012 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Authors: William Jon McCann <mccann@jhu.edu>
*
*/
#include "config.h"
#include <time.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/extensions/sync.h>
#include <glib.h>
#include <gdk/gdkx.h>
#include <gdk/gdk.h>
#include "shell-idle-monitor.h"
#define SHELL_IDLE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SHELL_TYPE_IDLE_MONITOR, ShellIdleMonitorPrivate))
struct ShellIdleMonitorPrivate
{
Display *display;
GHashTable *watches;
int sync_event_base;
XSyncCounter counter;
};
typedef struct
{
Display *display;
guint id;
XSyncValue interval;
ShellIdleMonitorWatchFunc callback;
gpointer user_data;
GDestroyNotify notify;
XSyncAlarm xalarm_positive;
XSyncAlarm xalarm_negative;
} ShellIdleMonitorWatch;
static guint32 watch_serial = 1;
G_DEFINE_TYPE (ShellIdleMonitor, shell_idle_monitor, G_TYPE_OBJECT)
static gint64
_xsyncvalue_to_int64 (XSyncValue value)
{
return ((guint64) XSyncValueHigh32 (value)) << 32
| (guint64) XSyncValueLow32 (value);
}
static XSyncValue
_int64_to_xsyncvalue (gint64 value)
{
XSyncValue ret;
XSyncIntsToValue (&ret, value, ((guint64)value) >> 32);
return ret;
}
static void
shell_idle_monitor_dispose (GObject *object)
{
ShellIdleMonitor *monitor;
monitor = SHELL_IDLE_MONITOR (object);
if (monitor->priv->watches != NULL) {
g_hash_table_destroy (monitor->priv->watches);
monitor->priv->watches = NULL;
}
G_OBJECT_CLASS (shell_idle_monitor_parent_class)->dispose (object);
}
static gboolean
_find_alarm (gpointer key,
ShellIdleMonitorWatch *watch,
XSyncAlarm *alarm)
{
/* g_debug ("Searching for %d in %d,%d", (int)*alarm, (int)watch->xalarm_positive, (int)watch->xalarm_negative); */
if (watch->xalarm_positive == *alarm
|| watch->xalarm_negative == *alarm) {
return TRUE;
}
return FALSE;
}
static ShellIdleMonitorWatch *
find_watch_for_alarm (ShellIdleMonitor *monitor,
XSyncAlarm alarm)
{
ShellIdleMonitorWatch *watch;
watch = g_hash_table_find (monitor->priv->watches,
(GHRFunc)_find_alarm,
&alarm);
return watch;
}
static void
handle_alarm_notify_event (ShellIdleMonitor *monitor,
XSyncAlarmNotifyEvent *alarm_event)
{
ShellIdleMonitorWatch *watch;
gboolean condition;
if (alarm_event->state == XSyncAlarmDestroyed) {
return;
}
watch = find_watch_for_alarm (monitor, alarm_event->alarm);
if (watch == NULL) {
/* g_debug ("Unable to find watch for alarm %d", (int)alarm_event->alarm); */
return;
}
/* g_debug ("Watch %d fired, idle time = %" G_GINT64_FORMAT,
watch->id,
_xsyncvalue_to_int64 (alarm_event->counter_value)); */
if (alarm_event->alarm == watch->xalarm_positive) {
condition = TRUE;
} else {
condition = FALSE;
}
if (watch->callback != NULL) {
watch->callback (monitor,
watch->id,
condition,
watch->user_data);
}
}
static GdkFilterReturn
xevent_filter (GdkXEvent *xevent,
GdkEvent *event,
ShellIdleMonitor *monitor)
{
XEvent *ev;
XSyncAlarmNotifyEvent *alarm_event;
ev = xevent;
if (ev->xany.type != monitor->priv->sync_event_base + XSyncAlarmNotify) {
return GDK_FILTER_CONTINUE;
}
alarm_event = xevent;
handle_alarm_notify_event (monitor, alarm_event);
return GDK_FILTER_CONTINUE;
}
static gboolean
init_xsync (ShellIdleMonitor *monitor)
{
int sync_error_base;
int res;
int major;
int minor;
int i;
int ncounters;
XSyncSystemCounter *counters;
res = XSyncQueryExtension (monitor->priv->display,
&monitor->priv->sync_event_base,
&sync_error_base);
if (! res) {
g_warning ("ShellIdleMonitor: Sync extension not present");
return FALSE;
}
res = XSyncInitialize (monitor->priv->display, &major, &minor);
if (! res) {
g_warning ("ShellIdleMonitor: Unable to initialize Sync extension");
return FALSE;
}
counters = XSyncListSystemCounters (monitor->priv->display, &ncounters);
for (i = 0; i < ncounters; i++) {
if (counters[i].name != NULL
&& strcmp (counters[i].name, "IDLETIME") == 0) {
monitor->priv->counter = counters[i].counter;
break;
}
}
XSyncFreeSystemCounterList (counters);
if (monitor->priv->counter == None) {
g_warning ("ShellIdleMonitor: IDLETIME counter not found");
return FALSE;
}
gdk_window_add_filter (NULL, (GdkFilterFunc)xevent_filter, monitor);
return TRUE;
}
static GObject *
shell_idle_monitor_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
ShellIdleMonitor *monitor;
monitor = SHELL_IDLE_MONITOR (G_OBJECT_CLASS (shell_idle_monitor_parent_class)->constructor (type,
n_construct_properties,
construct_properties));
monitor->priv->display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
if (! init_xsync (monitor)) {
g_object_unref (monitor);
return NULL;
}
return G_OBJECT (monitor);
}
static void
shell_idle_monitor_class_init (ShellIdleMonitorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = shell_idle_monitor_dispose;
object_class->constructor = shell_idle_monitor_constructor;
g_type_class_add_private (klass, sizeof (ShellIdleMonitorPrivate));
}
static guint32
get_next_watch_serial (void)
{
guint32 serial;
serial = watch_serial++;
if ((gint32)watch_serial < 0) {
watch_serial = 1;
}
/* FIXME: make sure it isn't in the hash */
return serial;
}
static ShellIdleMonitorWatch *
idle_monitor_watch_new (guint interval)
{
ShellIdleMonitorWatch *watch;
watch = g_slice_new0 (ShellIdleMonitorWatch);
watch->interval = _int64_to_xsyncvalue ((gint64)interval);
watch->id = get_next_watch_serial ();
watch->xalarm_positive = None;
watch->xalarm_negative = None;
return watch;
}
static void
idle_monitor_watch_free (ShellIdleMonitorWatch *watch)
{
if (watch == NULL) {
return;
}
if (watch->notify != NULL) {
watch->notify (watch->user_data);
}
if (watch->xalarm_positive != None) {
XSyncDestroyAlarm (watch->display, watch->xalarm_positive);
}
if (watch->xalarm_negative != None) {
XSyncDestroyAlarm (watch->display, watch->xalarm_negative);
}
g_slice_free (ShellIdleMonitorWatch, watch);
}
static void
shell_idle_monitor_init (ShellIdleMonitor *monitor)
{
monitor->priv = SHELL_IDLE_MONITOR_GET_PRIVATE (monitor);
monitor->priv->watches = g_hash_table_new_full (NULL,
NULL,
NULL,
(GDestroyNotify)idle_monitor_watch_free);
monitor->priv->counter = None;
}
/**
* shell_idle_monitor_get:
*
* Returns: (transfer none): the global #ShellIdleMonitor.
*/
ShellIdleMonitor *
shell_idle_monitor_get (void)
{
static ShellIdleMonitor *idle_monitor;
if (G_UNLIKELY (idle_monitor == NULL))
idle_monitor = g_object_new (SHELL_TYPE_IDLE_MONITOR,
NULL);
return idle_monitor;
}
static gboolean
_xsync_alarm_set (ShellIdleMonitor *monitor,
ShellIdleMonitorWatch *watch)
{
XSyncAlarmAttributes attr;
XSyncValue delta;
guint flags;
flags = XSyncCACounter
| XSyncCAValueType
| XSyncCATestType
| XSyncCAValue
| XSyncCADelta
| XSyncCAEvents;
XSyncIntToValue (&delta, 0);
attr.trigger.counter = monitor->priv->counter;
attr.trigger.value_type = XSyncAbsolute;
attr.trigger.wait_value = watch->interval;
attr.delta = delta;
attr.events = TRUE;
attr.trigger.test_type = XSyncPositiveTransition;
if (watch->xalarm_positive != None) {
/* g_debug ("ShellIdleMonitor: updating alarm for positive transition wait=%" G_GINT64_FORMAT,
_xsyncvalue_to_int64 (attr.trigger.wait_value)); */
XSyncChangeAlarm (monitor->priv->display, watch->xalarm_positive, flags, &attr);
} else {
/* g_debug ("ShellIdleMonitor: creating new alarm for positive transition wait=%" G_GINT64_FORMAT,
_xsyncvalue_to_int64 (attr.trigger.wait_value)); */
watch->xalarm_positive = XSyncCreateAlarm (monitor->priv->display, flags, &attr);
}
attr.trigger.wait_value = _int64_to_xsyncvalue (_xsyncvalue_to_int64 (watch->interval) - 1);
attr.trigger.test_type = XSyncNegativeTransition;
if (watch->xalarm_negative != None) {
/* g_debug ("ShellIdleMonitor: updating alarm for negative transition wait=%" G_GINT64_FORMAT,
_xsyncvalue_to_int64 (attr.trigger.wait_value)); */
XSyncChangeAlarm (monitor->priv->display, watch->xalarm_negative, flags, &attr);
} else {
/* g_debug ("ShellIdleMonitor: creating new alarm for negative transition wait=%" G_GINT64_FORMAT,
_xsyncvalue_to_int64 (attr.trigger.wait_value)); */
watch->xalarm_negative = XSyncCreateAlarm (monitor->priv->display, flags, &attr);
}
return TRUE;
}
guint
shell_idle_monitor_add_watch (ShellIdleMonitor *monitor,
guint interval,
ShellIdleMonitorWatchFunc callback,
gpointer user_data,
GDestroyNotify notify)
{
ShellIdleMonitorWatch *watch;
g_return_val_if_fail (SHELL_IS_IDLE_MONITOR (monitor), 0);
g_return_val_if_fail (callback != NULL, 0);
watch = idle_monitor_watch_new (interval);
watch->display = monitor->priv->display;
watch->callback = callback;
watch->user_data = user_data;
watch->notify = notify;
_xsync_alarm_set (monitor, watch);
g_hash_table_insert (monitor->priv->watches,
GUINT_TO_POINTER (watch->id),
watch);
return watch->id;
}
void
shell_idle_monitor_remove_watch (ShellIdleMonitor *monitor,
guint id)
{
g_return_if_fail (SHELL_IS_IDLE_MONITOR (monitor));
g_hash_table_remove (monitor->priv->watches,
GUINT_TO_POINTER (id));
}
gint64
shell_idle_monitor_get_idletime (ShellIdleMonitor *monitor)
{
XSyncValue value;
if (!XSyncQueryCounter (monitor->priv->display, monitor->priv->counter, &value))
return FALSE;
return _xsyncvalue_to_int64 (value);
}