diff options
-rw-r--r-- | app/xidle/Makefile | 15 | ||||
-rw-r--r-- | app/xidle/xidle.1 | 143 | ||||
-rw-r--r-- | app/xidle/xidle.c | 434 |
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 */ +} |