Skip to content

poll based timeout + handle SIGTERM for a clean exit #83

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ MANDIR ?= $(PREFIX)/man/man1
INSTALL_PROGRAM ?= install -s
INSTALL_DATA ?= install

LIBS ?= x11 xfixes xi xext
LIBS ?= x11 xfixes xi
INCLUDES?= `pkg-config --cflags $(LIBS)`
LDFLAGS += `pkg-config --libs $(LIBS)`

Expand Down
129 changes: 64 additions & 65 deletions xbanish.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,18 @@
*/

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/extensions/sync.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h>
Expand All @@ -35,10 +38,10 @@ void show_cursor(void);
void snoop_root(void);
int snoop_xinput(Window);
void snoop_legacy(Window);
void set_alarm(XSyncAlarm *, XSyncTestType);
void usage(char *);
int swallow_error(Display *, XErrorEvent *);
int parse_geometry(const char *s);
void sighandler(int);

/* xinput event type ids to be filled in later */
static int button_press_type = -1;
Expand All @@ -51,10 +54,10 @@ static long last_device_change = -1;

static Display *dpy;
static int hiding = 0, legacy = 0, always_hide = 0, ignore_scroll = 0;
static unsigned timeout = 0;
static time_t timeout = 0;
static time_t timeout_at = 0;
static unsigned char ignored;
static XSyncCounter idler_counter = 0;
static XSyncAlarm idle_alarm = None;
static int sigpipe[2];

static int debug = 0;
#define DPRINTF(x) { if (debug) { printf x; } };
Expand All @@ -77,11 +80,7 @@ main(int argc, char *argv[])
{
int ch, i;
XEvent e;
XSyncAlarmNotifyEvent *alarm_e;
XGenericEventCookie *cookie;
XSyncSystemCounter *counters;
int sync_event, error;
int major, minor, ncounters;

struct mod_lookup {
char *name;
Expand Down Expand Up @@ -149,6 +148,15 @@ main(int argc, char *argv[])
if (!(dpy = XOpenDisplay(NULL)))
errx(1, "can't open display %s", XDisplayName(NULL));

if (pipe(sigpipe) < 0)
err(1, "pipe");
if (fcntl(sigpipe[1], F_SETFD, O_NONBLOCK) < 0)
err(1, "fcntl");
struct sigaction sa = { .sa_handler = sighandler };
sigemptyset(&sa.sa_mask);
if (sigaction(SIGTERM, &sa, NULL) < 0)
err(1, "sigaction");

#ifdef __OpenBSD__
if (pledge("stdio", NULL) == -1)
err(1, "pledge");
Expand All @@ -161,27 +169,43 @@ main(int argc, char *argv[])
if (always_hide)
hide_cursor();

/* required setup for the xsync alarms used by timeout */
if (timeout) {
if (XSyncQueryExtension(dpy, &sync_event, &error) != True)
errx(1, "no sync extension available");
for (;;) {
struct pollfd pfd[2] = {0};
int pending = XPending(dpy) > 0;
if (!pending) {
int poll_timeout = -1;
pfd[0].fd = ConnectionNumber(dpy);
pfd[0].events = POLLIN;
pfd[1].fd = sigpipe[0];
pfd[1].events = POLLIN;
if (timeout && !hiding) {
poll_timeout = (timeout_at - time(NULL)) * 1000;
if (poll_timeout < 0)
poll_timeout = 0;
}
DPRINTF(("poll_timeout: %d\n", poll_timeout));
pending = poll(pfd, 2, poll_timeout) > 0 &&
(pfd[0].revents & POLLIN);
}

XSyncInitialize(dpy, &major, &minor);
if (pfd[1].revents & POLLIN) {
DPRINTF(("received signal, exiting...\n"));
if (hiding)
show_cursor();
break;
}

counters = XSyncListSystemCounters(dpy, &ncounters);
for (i = 0; i < ncounters; i++) {
if (!strcmp(counters[i].name, "IDLETIME")) {
idler_counter = counters[i].counter;
break;
}
if (timeout && !hiding && time(NULL) >= timeout_at) {
DPRINTF(("timeout reached, hiding cursor\n"));
hide_cursor();
continue;
}
XSyncFreeSystemCounterList(counters);

if (!idler_counter)
errx(1, "no idle counter");
}
if (!pending) {
DPRINTF(("no xevent pending, continuing\n"));
continue;
}

for (;;) {
cookie = &e.xcookie;
XNextEvent(dpy, &e);

Expand Down Expand Up @@ -280,19 +304,8 @@ main(int argc, char *argv[])
break;

default:
if (!timeout ||
e.type != (sync_event + XSyncAlarmNotify)) {
DPRINTF(("unknown event type %d\n", e.type));
break;
}

alarm_e = (XSyncAlarmNotifyEvent *)&e;
if (alarm_e->alarm == idle_alarm) {
DPRINTF(("idle counter reached %dms, hiding "
"cursor\n",
XSyncValueLow32(alarm_e->counter_value)));
hide_cursor();
}
DPRINTF(("unknown event type %d\n", e.type));
break;
}
}
}
Expand Down Expand Up @@ -380,8 +393,8 @@ show_cursor(void)
(hiding ? "" : "already ")));

if (timeout) {
DPRINTF(("(re)setting timeout of %us\n", timeout));
set_alarm(&idle_alarm, XSyncPositiveComparison);
DPRINTF(("(re)setting timeout of %llds\n", (long long)timeout));
timeout_at = time(NULL) + timeout;
}

if (!hiding)
Expand Down Expand Up @@ -560,30 +573,6 @@ snoop_legacy(Window win)
XFree(kids); /* hide yo kids */
}

void
set_alarm(XSyncAlarm *alarm, XSyncTestType test)
{
XSyncAlarmAttributes attr;
XSyncValue value;
unsigned int flags;

XSyncQueryCounter(dpy, idler_counter, &value);

attr.trigger.counter = idler_counter;
attr.trigger.test_type = test;
attr.trigger.value_type = XSyncRelative;
XSyncIntsToValue(&attr.trigger.wait_value, timeout * 1000,
(unsigned long)(timeout * 1000) >> 32);
XSyncIntToValue(&attr.delta, 0);

flags = XSyncCACounter | XSyncCATestType | XSyncCAValue | XSyncCADelta;

if (*alarm)
XSyncDestroyAlarm(dpy, *alarm);

*alarm = XSyncCreateAlarm(dpy, flags, &attr);
}

void
usage(char *progname)
{
Expand Down Expand Up @@ -621,3 +610,13 @@ parse_geometry(const char *s)
}
return 0;
}

void
sighandler(int signum)
{
char dummy = 1;
int saved_errno = errno;
ssize_t res = write(sigpipe[1], &dummy, 1);
(void)res;
errno = saved_errno;
}