summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/xidle/Makefile15
-rw-r--r--app/xidle/xidle.1143
-rw-r--r--app/xidle/xidle.c434
3 files changed, 592 insertions, 0 deletions
diff --git a/app/xidle/Makefile b/app/xidle/Makefile
new file mode 100644
index 000000000..954c2ad28
--- /dev/null
+++ b/app/xidle/Makefile
@@ -0,0 +1,15 @@
+# $OpenBSD: Makefile,v 1.1 2006/11/26 10:57:44 matthieu Exp $
+
+.include <bsd.own.mk>
+X11BASE?= /usr/X11R6
+
+PROG= xidle
+
+CPPFLAGS+= -I${X11BASE}/include
+CFLAGS+= -Wall
+LDADD+= -L${X11BASE}/lib -lXss -lXext -lX11
+
+MANDIR= ${X11BASE}/man/cat
+
+.include <bsd.prog.mk>
+.include <bsd.xorg.mk>
diff --git a/app/xidle/xidle.1 b/app/xidle/xidle.1
new file mode 100644
index 000000000..12f8db426
--- /dev/null
+++ b/app/xidle/xidle.1
@@ -0,0 +1,143 @@
+.\" $OpenBSD: xidle.1,v 1.1 2006/11/26 10:57:44 matthieu Exp $
+.\"
+.\" Copyright (c) 2005 Federico G. Schwindt.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+.\" LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+.\" A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+.\" PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+.\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd June 20, 2005
+.Dt XIDLE 1
+.Os
+.Sh NAME
+.Nm xidle
+.Nd run a program on X inactivity
+.Sh SYNOPSIS
+.Nm xidle
+.Bk -words
+.Op Fl area Ar pixels
+.Op Fl delay Ar secs
+.Op Fl display Ar display
+.Op Fl nw | ne | sw | se
+.Op Fl program Ar path
+.Op Fl timeout Ar secs
+.Ek
+.Sh DESCRIPTION
+.Nm
+uses the
+.Xr XScreenSaver 3
+extension to receive inactivity events when a timeout is specified, running
+a specific program after the elapsed time.
+.Nm
+also monitors the very corner of the given position for pointer activity
+and runs a program if the pointer sits there for more than the specified
+number of seconds.
+This behavior is always present, whether
+.Fl timeout
+is specified or not.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl area Ar pixels
+Specify the size, in pixels, of the corner area.
+The default is 2 pixels.
+.It Fl delay Ar secs
+Specify the number of seconds the pointer has to be in the given position
+before running the program.
+The default is 2 seconds.
+.It Fl display Ar display
+This argument allows you to specify the server to connect to; see
+.Xr X 7 .
+.It Fl nw | ne | sw | se
+Set the position to one of northwest, northeast, southwest, or southeast,
+respectively.
+If no position is specified,
+the default is northwest.
+.It Fl program Ar path
+Specify the full pathname of the program to run on any of the
+aforementioned events.
+Arguments to the program may also be specified, separated by whitespace.
+If
+.Fl program
+is not specified, the default is
+.Xr xlock 1 .
+.It Fl timeout Ar secs
+Set the timeout to the specified number of seconds.
+If
+.Fl timeout
+is not specified,
+.Nm
+won't run any program on inactivity.
+.El
+.Sh RESOURCES
+A few sets of application specific resources are supported and their values
+can be overriden by the command line options:
+.Bl -tag -width Ds
+.It Sy area No (class Sy Area )
+Specify the size, in pixels, of the corner area; see the
+.Fl area
+option.
+.It Sy delay No (class Sy Delay )
+Specify the number of seconds to wait before running the program; see the
+.Fl delay
+option.
+.It Sy position No (class Sy Position )
+Set the position to one of: "nw", "ne", "sw", or "se"; see descriptions of the
+.Fl nw ,
+.Fl ne ,
+.Fl sw ,
+and
+.Fl se
+options.
+.It Sy program No (class Sy Program )
+Specify the full pathname of the program to run; see the
+.Fl program
+option.
+.It Sy timeout No (class Sy Timeout )
+Set the timeout to the specified number of seconds; see the
+.Fl timeout
+option.
+.El
+.Pp
+The
+.Nm
+program resource class is
+.Sy XIdle .
+.Sh EXAMPLES
+Run
+.Xr xlock 1
+using the flying bats mode if no activity is detected in 300 seconds or the
+pointer sits in the southwest corner for more than 5 seconds:
+.Bd -literal -offset indent
+$ xidle -delay 5 -sw -program "/usr/X11R6/bin/xlock -mode bat" \e
+ -timeout 300
+.Ed
+.Sh SEE ALSO
+.Xr xlock 1 ,
+.Xr XScreenSaver 3 ,
+.Xr X 7
+.Sh AUTHORS
+The
+.Nm
+program was written by Federico Schwindt as an xautolock replacement.
+.Sh BUGS
+The maximum number of arguments for
+.Fl program ,
+including the executable file, is currently set to 9.
diff --git a/app/xidle/xidle.c b/app/xidle/xidle.c
new file mode 100644
index 000000000..3c7069257
--- /dev/null
+++ b/app/xidle/xidle.c
@@ -0,0 +1,434 @@
+/* $OpenBSD: xidle.c,v 1.1 2006/11/26 10:57:44 matthieu Exp $ */
+/*
+ * Copyright (c) 2005 Federico G. Schwindt
+ * Copyright (c) 2005 Claudio Castiglia
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+#include <X11/extensions/scrnsaver.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <err.h>
+#include <limits.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef CLASS_NAME
+#define CLASS_NAME "XIdle"
+#endif
+
+#ifndef PATH_PROG
+#define PATH_PROG "/usr/X11R6/bin/xlock"
+#endif
+
+
+enum {
+ north = 0x01,
+ south = 0x02,
+ east = 0x04,
+ west = 0x08
+};
+
+struct xinfo {
+ Display *dpy;
+ Window win;
+ int coord_x;
+ int coord_y;
+
+ int saver_event; /* Only if Xss ext is available */
+
+ int saved_timeout;
+ int saved_interval;
+ int saved_pref_blank;
+ int saved_allow_exp;
+};
+
+struct xinfo x;
+
+static XrmOptionDescRec fopts[] = {
+ { "-display", ".display", XrmoptionSepArg, (caddr_t)NULL },
+};
+
+static XrmOptionDescRec opts[] = {
+ { "-area", ".area", XrmoptionSepArg, (caddr_t)NULL },
+ { "-delay", ".delay", XrmoptionSepArg, (caddr_t)NULL },
+ { "-program", ".program", XrmoptionSepArg, (caddr_t)NULL },
+ { "-timeout", ".timeout", XrmoptionSepArg, (caddr_t)NULL },
+
+ { "-ne", ".position", XrmoptionNoArg, (caddr_t)"ne" },
+ { "-nw", ".position", XrmoptionNoArg, (caddr_t)"nw" },
+ { "-se", ".position", XrmoptionNoArg, (caddr_t)"se" },
+ { "-sw", ".position", XrmoptionNoArg, (caddr_t)"sw" }
+};
+
+extern char *__progname;
+
+void action(struct xinfo *, char **);
+void close_x(struct xinfo *);
+Bool getres(XrmValue *, const XrmDatabase, const char *, const char *);
+void init_x(struct xinfo *, int, int, int);
+void handler(int);
+void parse_opts(int, char **, Display **, int *, int *, int *, int *,
+ char **);
+int str2pos(const char *);
+__dead void usage(void);
+
+
+__dead void
+usage()
+{
+ fprintf(stderr, "Usage:\n%s %s\n", __progname,
+ "[-area pixels] [-delay secs] [-display host:dpy] "
+ "[-ne | -nw | -se | -sw]\n [-program path] [-timeout secs]");
+ exit(1);
+}
+
+
+void
+init_x(struct xinfo *xi, int position, int area, int timeout)
+{
+ XSetWindowAttributes attr;
+ Display *dpy = xi->dpy;
+ int error, event;
+ int screen;
+
+ screen = DefaultScreen(dpy);
+
+ if (position & south)
+ xi->coord_y = DisplayHeight(dpy, screen) - area;
+ if (position & east)
+ xi->coord_x = DisplayWidth(dpy, screen) - area;
+
+ attr.override_redirect = True;
+ xi->win = XCreateWindow(dpy, DefaultRootWindow(dpy),
+ xi->coord_x, xi->coord_y, area, area, 0, 0, InputOnly,
+ CopyFromParent, CWOverrideRedirect, &attr);
+
+ XSelectInput(dpy, xi->win, EnterWindowMask|StructureNotifyMask
+#if 0
+ |VisibilityChangeMask
+#endif
+ );
+ XMapWindow(dpy, xi->win);
+
+ /*
+ * AFAICT, we need the event number for ScreenSaverNotify
+ * _always_ since it's the only way to distinguish whether
+ * we've been obscured by an external locking program or
+ * by another window and react according.
+ */
+ if (XScreenSaverQueryExtension(dpy, &event, &error) == True) {
+ xi->saver_event = event;
+
+ XScreenSaverSelectInput(dpy, DefaultRootWindow(dpy),
+ ScreenSaverNotifyMask);
+ } else
+ warnx("XScreenSaver extension not available.%s",
+ timeout > 0 ? " Timeout disabled." : "");
+
+ if (timeout > 0 && xi->saver_event) {
+ XGetScreenSaver(dpy, &xi->saved_timeout, &xi->saved_interval,
+ &xi->saved_pref_blank, &xi->saved_allow_exp);
+
+ XSetScreenSaver(dpy, timeout, 0, DontPreferBlanking,
+ DontAllowExposures);
+ }
+}
+
+
+void
+close_x(struct xinfo *xi)
+{
+ XSetScreenSaver(xi->dpy, xi->saved_timeout, xi->saved_interval,
+ xi->saved_pref_blank, xi->saved_allow_exp);
+ XDestroyWindow(xi->dpy, xi->win);
+ XCloseDisplay(xi->dpy);
+}
+
+
+void
+action(struct xinfo *xi, char **args)
+{
+ int dumb;
+
+ switch (fork()) {
+ case -1:
+ err(1, "fork");
+ /* NOTREACHED */
+
+ case 0:
+ execv(*args, args);
+ exit(1);
+ /* NOTREACHED */
+
+ default:
+ wait(&dumb);
+ XSync(xi->dpy, True);
+ break;
+ }
+}
+
+
+void
+handler(int sig)
+{
+ XClientMessageEvent ev;
+
+ ev.type = ClientMessage;
+ ev.display = x.dpy;
+ ev.window = x.win;
+ ev.message_type = 0xdead;
+ ev.format = 8;
+ XSendEvent(x.dpy, x.win, False, 0L, (XEvent *)&ev);
+ XFlush(x.dpy);
+}
+
+
+int
+str2pos(const char *src)
+{
+ static struct {
+ char *str;
+ int pos;
+ } s2p[] = {
+ { "ne", north|east },
+ { "nw", north|west },
+ { "se", south|east },
+ { "sw", south|west },
+ { NULL, 0 }
+ }, *s;
+
+ for (s = s2p; s->str != NULL; s++)
+ if (!strcmp(src, s->str))
+ break;
+ return (s->pos);
+}
+
+
+Bool
+getres(XrmValue *value, const XrmDatabase rdb, const char *rname,
+ const char *cname)
+{
+ char fullres[PATH_MAX], fullclass[PATH_MAX], *type;
+
+ snprintf(fullres, sizeof(fullres), "%s.%s", __progname, rname);
+ snprintf(fullclass, sizeof(fullclass), "%s.%s", CLASS_NAME, cname);
+ return (XrmGetResource(rdb, fullres, fullclass, &type, value));
+}
+
+
+void
+parse_opts(int argc, char **argv, Display **dpy, int *area, int *delay,
+ int *timeout, int *position, char **args)
+{
+ char **ap, *program = PATH_PROG;
+ char *display, *p;
+ XrmDatabase tdb, rdb = NULL;
+ XrmValue value;
+
+ XrmInitialize();
+
+ /* Get display to open. */
+ XrmParseCommand(&rdb, fopts, sizeof(fopts) / sizeof(fopts[0]),
+ __progname, &argc, argv);
+
+ display = (getres(&value, rdb, "display", "Display") == True) ?
+ (char *)value.addr : NULL;
+
+ *dpy = XOpenDisplay(display);
+ if (!*dpy) {
+ errx(1, "Unable to open display %s", XDisplayName(display));
+ /* NOTREACHED */
+ }
+
+ /* Get server resources database. */
+ p = XResourceManagerString(*dpy);
+ if (!p) {
+ /* Get screen resources database. */
+ p = XScreenResourceString(ScreenOfDisplay(*dpy,
+ DefaultScreen(*dpy)));
+ }
+
+ if (p) {
+ tdb = XrmGetStringDatabase(p);
+ XrmMergeDatabases(tdb, &rdb);
+ }
+
+ /* Get remaining command line values. */
+ XrmParseCommand(&rdb, opts, sizeof(opts) / sizeof(opts[0]),
+ __progname, &argc, argv);
+ if (argc > 1) {
+ usage();
+ /* NOTREACHED */
+ }
+ if (getres(&value, rdb, "area", "Area")) {
+ *area = strtol((char *)value.addr, &p, 10);
+ if (*p || *area < 1) {
+fail: errx(1, "illegal value -- %s", (char *)value.addr);
+ /* NOTREACHED */
+ }
+ }
+ if (getres(&value, rdb, "delay", "Delay")) {
+ *delay = strtol((char *)value.addr, &p, 10);
+ if (*p || *delay < 0)
+ goto fail;
+ }
+ if (getres(&value, rdb, "position", "Position")) {
+ *position = str2pos((char *)value.addr);
+ if (!*position)
+ goto fail;
+ }
+ if (getres(&value, rdb, "timeout", "Timeout")) {
+ *timeout = strtol((char *)value.addr, &p, 10);
+ if (*p || *timeout < 0)
+ goto fail;
+ }
+ if (getres(&value, rdb, "program", "Program")) {
+ /* Should be the last :) */
+ program = (char *)value.addr;
+ }
+
+ for (ap = args; ap < &args[9] &&
+ (*ap = strsep(&program, " ")) != NULL;) {
+ if (**ap != '\0')
+ ap++;
+ }
+ *ap = NULL;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ char *args[10];
+ int area = 2, delay = 2, timeout = 0;
+ int position = north|west;
+ u_long last_serial = 0;
+
+ bzero(&x, sizeof(struct xinfo));
+
+ parse_opts(argc, argv, &x.dpy, &area, &delay, &timeout,
+ &position, args);
+
+#ifdef DEBUG
+ printf("Area: %d\nDelay: %d\nPosition: %d\nTimeout: %d\n"
+ "Program: \"%s\"\n",
+ area, delay, position, timeout, args[0]);
+#endif
+
+ init_x(&x, position, area, timeout);
+
+ signal(SIGINT, handler);
+ signal(SIGTERM, handler);
+
+ for (;;) {
+ XEvent ev;
+ u_long mask;
+
+ XNextEvent(x.dpy, &ev);
+
+#ifdef DEBUG
+ printf("got event %d\n", ev.type);
+#endif
+
+ switch (ev.type) {
+ case VisibilityNotify:
+ /*
+ * If we got here and the current serial matches
+ * the one from saver_event, we're being obscured
+ * by a locking program. Disable further
+ * screen saver events and raises.
+ */
+ if (ev.xvisibility.serial == last_serial)
+ mask = 0;
+ else
+ mask = ScreenSaverNotifyMask;
+
+ XScreenSaverSelectInput(x.dpy,
+ DefaultRootWindow(x.dpy), mask);
+
+ if (mask)
+ XMapRaised(x.dpy, x.win);
+
+ XSync(x.dpy, True);
+ break;
+
+ case MapNotify:
+ XMapRaised(x.dpy, x.win);
+ break;
+
+ case ClientMessage:
+ if (ev.xclient.message_type != 0xdead)
+ break;
+ close_x(&x);
+ exit(0);
+ /* NOTREACHED */
+
+ case EnterNotify:
+ sleep(delay);
+
+ XQueryPointer(x.dpy, x.win, &ev.xcrossing.root,
+ &ev.xcrossing.window,
+ &ev.xcrossing.x_root, &ev.xcrossing.y_root,
+ &ev.xcrossing.x, &ev.xcrossing.y,
+ &ev.xcrossing.state);
+
+ /* Check it was for real. */
+ if (ev.xcrossing.x_root < x.coord_x ||
+ ev.xcrossing.y_root < x.coord_y ||
+ ev.xcrossing.x_root > x.coord_x + area ||
+ ev.xcrossing.y_root > x.coord_y + area)
+ break;
+ /* FALLTHROUGH */
+
+ default:
+ if (ev.type != EnterNotify &&
+ ev.type != x.saver_event)
+ break;
+
+ if (ev.type == x.saver_event) {
+ XScreenSaverNotifyEvent *se =
+ (XScreenSaverNotifyEvent *)&ev;
+
+ /* Take note of the serial for this event. */
+ last_serial = se->serial;
+
+ /*
+ * Was for real or due to terminal
+ * switching or a locking program?
+ */
+ if (se->forced != False)
+ break;
+ }
+ action(&x, args);
+ break;
+ }
+ }
+
+ /* NOTREACHED */
+}