30ca25e978
If the user was inactive while a notification was shown, we show the summary when the user becomes active again. This ensures that we inform the user of the existance of new notifications that the user might have missed. When the user comes back from away, the summary is now only shown if it has new notifications. https://bugzilla.gnome.org/show_bug.cgi?id=643014
413 lines
13 KiB
C
413 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;
|
|
}
|
|
|
|
ShellIdleMonitor *
|
|
shell_idle_monitor_new (void)
|
|
{
|
|
GObject *idle_monitor;
|
|
|
|
idle_monitor = g_object_new (SHELL_TYPE_IDLE_MONITOR,
|
|
NULL);
|
|
|
|
return SHELL_IDLE_MONITOR (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));
|
|
}
|