+/*
+ * Copyright © 2009 Christopher Eby <kreed@kreed.org>
+ *
+ * This file is part of Inertia.
+ *
+ * Inertia is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation, either version 3 of
+ * the License, or (at your option) any later version.
+ *
+ * Inertia 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 <http://www.gnu.org/licenses/> for the full license text.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <X11/Xlib.h>
+#include <X11/extensions/sync.h>
+#include <X11/extensions/scrnsaver.h>
+
+static Display *dpy = NULL;
+
+static void get_alarm(XSyncAlarm *alarm, XSyncCounter counter, XSyncTestType type, XSyncValue value)
+{
+ XSyncAlarmAttributes attrs;
+
+ XSyncValue delta;
+ XSyncIntToValue(&delta, 0);
+
+ static const unsigned long flags = XSyncCACounter | XSyncCATestType | XSyncCAValue | XSyncCADelta;
+
+ attrs.trigger.counter = counter;
+ attrs.trigger.test_type = type;
+ attrs.trigger.wait_value = value;
+ attrs.delta = delta;
+
+ if (*alarm)
+ XSyncChangeAlarm(dpy, *alarm, flags, &attrs);
+ else
+ *alarm = XSyncCreateAlarm(dpy, flags, &attrs);
+}
+
+static void die(const char *errstr)
+{
+ fputs(errstr, stderr);
+ fflush(stderr);
+ exit(EXIT_FAILURE);
+}
+
+bool wait_for_idle_time(int idle_time)
+{
+ static int xsync_event_base;
+ static XSyncAlarm idle_alarm = None;
+ static XSyncAlarm reset_alarm = None;
+ static XSyncCounter idle = None;
+ static XSyncValue idle_timeout;
+
+ if (idle == None) {
+ if (!(dpy = XOpenDisplay(NULL)))
+ die("Could not open X11 display; exiting.\n");
+
+ int dummy;
+ if (!XQueryExtension(dpy, "XTEST", &dummy, &dummy, &dummy))
+ die("XTEST extension not available; cannot reset idle time\n");
+
+ int xsync_error_base;
+ int xsync_major = SYNC_MAJOR_VERSION;
+ int xsync_minor = SYNC_MINOR_VERSION;
+
+ if (!XSyncQueryExtension(dpy, &xsync_event_base, &xsync_error_base) || !XSyncInitialize(dpy, &xsync_major, &xsync_minor))
+ die("No XSync extension; exiting.\n");
+
+ int i;
+ XSyncSystemCounter *counters = XSyncListSystemCounters(dpy, &i);
+ while (i--)
+ if (!strcmp(counters[i].name, "IDLETIME"))
+ idle = counters[i].counter;
+ XSyncFreeSystemCounterList(counters);
+
+ if (idle == None)
+ die("No IDLETIME counter! xorg-server 1.3 and higher should support it. Exiting.\n");
+
+ XSyncIntToValue(&idle_timeout, idle_time * 1000);
+ get_alarm(&idle_alarm, idle, XSyncPositiveComparison, idle_timeout);
+ }
+
+ XEvent ev;
+
+ while (!XNextEvent(dpy, &ev)) {
+ if (ev.type == xsync_event_base + XSyncAlarmNotify) {
+ XSyncAlarmNotifyEvent *e = (XSyncAlarmNotifyEvent*)&ev;
+
+ if (e->alarm == idle_alarm) {
+ int overflow;
+ XSyncValue reset_timeout;
+ XSyncValue minus_one;
+
+ XSyncIntToValue(&minus_one, -1);
+ XSyncValueAdd(&reset_timeout, e->counter_value, minus_one, &overflow);
+ get_alarm(&reset_alarm, idle, XSyncNegativeComparison, reset_timeout);
+
+ return true;
+ } else if (e->alarm == reset_alarm) {
+ get_alarm(&idle_alarm, idle, XSyncPositiveComparison, idle_timeout);
+ }
+ }
+ }
+
+ return false;
+}
+
+void reset_idle_time()
+{
+ if (!dpy) {
+ if (!(dpy = XOpenDisplay(NULL))) {
+ fputs("Could not open X11 display.\n", stderr);
+ return;
+ }
+ }
+
+ XScreenSaverSuspend(dpy, True);
+ XSync(dpy, False);
+ XScreenSaverSuspend(dpy, False);
+ XSync(dpy, False);
+}