From cca674789bbaa10f365ca8da87db6263b67af023 Mon Sep 17 00:00:00 2001
From: Přemysl Janouch
Date: Mon, 15 May 2017 20:12:27 +0200
Subject: wmstatus: add a sleep timer
Since GNOME Settings Daemon doesn't work.
---
wmstatus.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 129 insertions(+), 13 deletions(-)
diff --git a/wmstatus.c b/wmstatus.c
index 8f7a97b..c500d7a 100644
--- a/wmstatus.c
+++ b/wmstatus.c
@@ -35,6 +35,7 @@
#include
#include
#include
+#include
#include
#include
@@ -1136,7 +1137,7 @@ static struct simple_config_item g_config_table[] =
{ "nut_load_power", NULL, "ups.realpower.nominal override" },
{ "command", NULL, "command to run for more info" },
-
+ { "sleep_timer", NULL, "idle seconds to suspend after" },
{ NULL, NULL, NULL }
};
@@ -1148,7 +1149,7 @@ struct app_context
struct backend *backend; ///< WM backend
Display *dpy; ///< X display handle
- int xkb_base_event_code; ///< Xkb base event code
+ struct poller_fd x_event; ///< X11 event
const char *prefix; ///< User-defined prefix
struct poller poller; ///< Poller
@@ -1156,6 +1157,15 @@ struct app_context
struct poller_timer make_context; ///< Start PulseAudio communication
struct poller_timer refresh_rest; ///< Refresh unpollable information
+ // Sleep timer:
+
+ int xsync_base_event_code; ///< XSync base event code
+ XSyncCounter idle_counter; ///< XSync IDLETIME counter
+ XSyncValue idle_timeout; ///< Sleep timeout
+
+ XSyncAlarm idle_alarm_inactive; ///< User is inactive
+ XSyncAlarm idle_alarm_active; ///< User is active
+
// Command:
struct poller_timer command_start; ///< Start the command
@@ -1167,7 +1177,7 @@ struct app_context
// Hotkeys:
- struct poller_fd x_event; ///< X11 event
+ int xkb_base_event_code; ///< Xkb base event code
char *layout; ///< Keyboard layout
// Insomnia:
@@ -1215,6 +1225,29 @@ str_map_destroy (void *self)
free (self);
}
+static void
+app_context_init_xsync (struct app_context *self)
+{
+ int n;
+ if (!XSyncQueryExtension (self->dpy, &self->xsync_base_event_code, &n)
+ || !XSyncInitialize (self->dpy, &n, &n))
+ {
+ print_error ("cannot initialize XSync");
+ return;
+ }
+
+ // The idle counter is not guaranteed to exist, only SERVERTIME is
+ XSyncSystemCounter *counters = XSyncListSystemCounters (self->dpy, &n);
+ while (n--)
+ {
+ if (!strcmp (counters[n].name, "IDLETIME"))
+ self->idle_counter = counters[n].counter;
+ }
+ if (!self->idle_counter)
+ print_error ("idle counter is missing");
+ XSyncFreeSystemCounterList (counters);
+}
+
static void
app_context_init (struct app_context *self)
{
@@ -1241,6 +1274,8 @@ app_context_init (struct app_context *self)
poller_fd_init (&self->x_event, &self->poller,
ConnectionNumber (self->dpy));
+ app_context_init_xsync (self);
+
// So far we don't necessarily need DBus to function,
// and we have no desire to process any incoming messages either
DBusError err = DBUS_ERROR_INIT;
@@ -1551,6 +1586,77 @@ on_refresh_rest (void *user_data)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+static void
+suspend (struct app_context *ctx)
+{
+ DBusMessage *msg = dbus_message_new_method_call
+ ("org.freedesktop.login1", "/org/freedesktop/login1",
+ "org.freedesktop.login1.Manager", "Suspend");
+ hard_assert (msg != NULL);
+
+ dbus_bool_t interactive = false;
+ hard_assert (dbus_message_append_args (msg,
+ DBUS_TYPE_BOOLEAN, &interactive,
+ DBUS_TYPE_INVALID));
+
+ DBusError err = DBUS_ERROR_INIT;
+ DBusMessage *reply = dbus_connection_send_with_reply_and_block
+ (ctx->system_bus, msg, 1000, &err);
+ dbus_message_unref (msg);
+ if (!reply)
+ {
+ print_error ("%s: %s", "suspend", err.message);
+ dbus_error_free (&err);
+ }
+ else
+ dbus_message_unref (reply);
+}
+
+static void
+set_idle_alarm (struct app_context *ctx,
+ XSyncAlarm *alarm, XSyncTestType test, XSyncValue value)
+{
+ XSyncAlarmAttributes attr;
+ attr.trigger.counter = ctx->idle_counter;
+ attr.trigger.test_type = test;
+ attr.trigger.wait_value = value;
+ XSyncIntToValue (&attr.delta, 0);
+
+ long flags = XSyncCACounter | XSyncCATestType | XSyncCAValue | XSyncCADelta;
+ if (*alarm)
+ XSyncChangeAlarm (ctx->dpy, *alarm, flags, &attr);
+ else
+ *alarm = XSyncCreateAlarm (ctx->dpy, flags, &attr);
+}
+
+static void
+on_x_alarm_notify (struct app_context *ctx, XSyncAlarmNotifyEvent *ev)
+{
+ if (ev->alarm == ctx->idle_alarm_inactive)
+ {
+ // Our own lock doesn't matter, we have to check it ourselves
+ if (ctx->system_bus && ctx->insomnia_fd == -1)
+ suspend (ctx);
+
+ XSyncValue one, minus_one;
+ XSyncIntToValue (&one, 1);
+
+ Bool overflow;
+ XSyncValueSubtract (&minus_one, ev->counter_value, one, &overflow);
+
+ // Set an alarm for IDLETIME <= current_idletime - 1
+ set_idle_alarm (ctx, &ctx->idle_alarm_active,
+ XSyncNegativeComparison, minus_one);
+ }
+ else if (ev->alarm == ctx->idle_alarm_active)
+ // XXX: even though it doesn't seem to run during the time the system
+ // is suspended, I haven't found any place where it is specified
+ set_idle_alarm (ctx, &ctx->idle_alarm_inactive,
+ XSyncPositiveComparison, ctx->idle_timeout);
+}
+
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
static void
command_queue_start (struct app_context *ctx)
{
@@ -2386,12 +2492,6 @@ go_insomniac (struct app_context *ctx)
static const char *why = "";
static const char *mode = "block";
- if (!ctx->system_bus)
- {
- ctx->insomnia_info = xstrdup_printf ("%s: %s", "Insomnia", "no DBus");
- return;
- }
-
DBusMessage *msg = dbus_message_new_method_call
("org.freedesktop.login1", "/org/freedesktop/login1",
"org.freedesktop.login1.Manager", "Inhibit");
@@ -2441,7 +2541,7 @@ on_insomnia (struct app_context *ctx, int arg)
xclose (ctx->insomnia_fd);
ctx->insomnia_fd = -1;
}
- else
+ else if (ctx->system_bus)
go_insomniac (ctx);
refresh_status (ctx);
@@ -2556,6 +2656,8 @@ on_xkb_event (struct app_context *ctx, XkbEvent *ev)
refresh_status (ctx);
}
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
static void
on_x_ready (const struct pollfd *pfd, void *user_data)
{
@@ -2569,14 +2671,28 @@ on_x_ready (const struct pollfd *pfd, void *user_data)
exit_fatal ("XNextEvent returned non-zero");
if (ev.type == KeyPress)
on_x_keypress (ctx, &ev.core);
- if (ev.type == ctx->xkb_base_event_code)
+ else if (ev.type == ctx->xkb_base_event_code)
on_xkb_event (ctx, &ev);
+ else if (ctx->xsync_base_event_code
+ && ev.type == ctx->xsync_base_event_code + XSyncAlarmNotify)
+ on_x_alarm_notify (ctx, (XSyncAlarmNotifyEvent *) &ev);
}
}
static void
-grab_keys (struct app_context *ctx)
+init_xlib_events (struct app_context *ctx)
{
+ unsigned long n;
+ const char *sleep_timer = str_map_find (&ctx->config, "sleep_timer");
+ if (sleep_timer && ctx->idle_counter)
+ {
+ if (!xstrtoul (&n, sleep_timer, 10) || !n || n > INT_MAX / 1000)
+ exit_fatal ("invalid value for the sleep timer");
+ XSyncIntToValue (&ctx->idle_timeout, n * 1000);
+ set_idle_alarm (ctx, &ctx->idle_alarm_inactive,
+ XSyncPositiveComparison, ctx->idle_timeout);
+ }
+
unsigned ignored_locks =
LockMask | XkbKeysymToModifiers (ctx->dpy, XK_Num_Lock);
hard_assert (XkbSetIgnoreLockMods
@@ -2751,7 +2867,7 @@ main (int argc, char *argv[])
poller_timer_init_and_set (&ctx.nut_reconnect, &ctx.poller,
on_nut_reconnect, &ctx);
- grab_keys (&ctx);
+ init_xlib_events (&ctx);
if (i3bar)
ctx.backend = backend_i3_new ();
--
cgit v1.2.3-70-g09d2