X-Git-Url: https://git.distorted.org.uk/~mdw/xtoys/blobdiff_plain/47747dbe04f28a3f2d4748780a088f8309fc2990..HEAD:/xatom.c diff --git a/xatom.c b/xatom.c index b5cdaa9..0e486bb 100644 --- a/xatom.c +++ b/xatom.c @@ -1,13 +1,11 @@ /* -*-c-*- * - * $Id: xatom.c,v 1.2 2004/04/08 01:36:29 mdw Exp $ - * * Set and fetch X atom properties * * (c) 1999 Straylight/Edgeware */ -/*----- Licensing notice --------------------------------------------------* +/*----- Licensing notice --------------------------------------------------* * * This file is part of the Edgeware X tools collection. * @@ -15,12 +13,12 @@ * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * X tools 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 the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with X tools; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. @@ -28,78 +26,333 @@ /*----- Header files ------------------------------------------------------*/ +#include +#include #include #include #include -#include +#include + +#include #include -#include -/*----- Main code ---------------------------------------------------------*/ +#include +#include +#include +#include + +#include "libxatom.h" + +/*----- Static variables --------------------------------------------------*/ + +static Display *dpy = 0; +static Window window = None; + +/*----- Command implementations -------------------------------------------*/ + +static void help(char **); +static int c_help(int argc, char **argv) + { help(argv + 1); return (0); } + +static int c_get(int argc, char **argv) +{ + Atom p, a; + char *name; + + if (argc != 2) + die(EXIT_FAILURE, "Usage: get PROPERTY"); + if ((p = XInternAtom(dpy, argv[1], True)) == None || + (a = xatom_get(dpy, window, p)) == None) + return (0); + name = XGetAtomName(dpy, a); + puts(name); + return (0); +} + +static int c_set(int argc, char **argv) +{ + Atom p, a; + + if (argc != 3) + die(EXIT_FAILURE, "Usage: set PROPERTY VALUE"); + p = XInternAtom(dpy, argv[1], False); + a = XInternAtom(dpy, argv[2], False); + xatom_set(dpy, window, p, a); + return (0); +} -/* --- @xatom_set@ --- * +static int c_delete(int argc, char **argv) +{ + Atom p; + + if (argc != 2) + die(EXIT_FAILURE, "Usage: delete PROPERTY"); + if ((p = XInternAtom(dpy, argv[1], True)) == None) + return (0); + xatom_delete(dpy, window, p); + return (0); +} + +static int c_wait(int argc, char **argv) +{ + Atom p, a, *aa = 0; + int n; + char *name; + + if (argc < 2) + die(EXIT_FAILURE, "Usage: wait PROPERTY [VALUE...]"); + + p = XInternAtom(dpy, argv[1], False); + n = argc - 2; + if (n) { + aa = xmalloc(n * sizeof(Atom)); + XInternAtoms(dpy, argv + 2, n, False, aa); + } + + a = xatom_wait(dpy, window, p, aa, n); + if (n != 1) { + name = XGetAtomName(dpy, a); + puts(name); + XFree(name); + } + if (aa) + xfree(aa); + return (0); +} + +/*----- Utilities ---------------------------------------------------------*/ + +/* --- @choosewindow@ --- * * - * Arguments: @Display *d@ = pointer to display - * @Window w@ = window to set - * @Atom p@ = property to set - * @Atom a@ = atom property value + * Arguments: --- * - * Returns: --- + * Returns: An X window id. * - * Use: Sets an atom property on a particular window. + * Use: Allows the user to select a window using the mouse. */ -void xatom_set(Display *d, Window w, Atom p, Atom a) +static Window choosewindow(void) { - XChangeProperty(d, w, p, XA_ATOM, 32, PropModeReplace, - (unsigned char *)&a, 1); + Cursor cross = XCreateFontCursor(dpy, XC_crosshair); + XEvent event; + + XGrabPointer(dpy, + DefaultRootWindow(dpy), + False, + ButtonPressMask, + GrabModeAsync, + GrabModeAsync, + None, + cross, + CurrentTime); + + for (;;) { + XNextEvent(dpy, &event); + switch (event.type) { + case ButtonPress: + switch (event.xbutton.button) { + case 3: + XUngrabPointer(dpy, event.xbutton.time); + die(EXIT_FAILURE, "aborted window selection"); + break; + case 1: + window = event.xbutton.subwindow; + if (window == None) + window = event.xbutton.window; + XUngrabPointer(dpy, event.xbutton.time); + return (window); + } + break; + } + } } -/* --- @xatom_get@ --- * +/* --- @autoreap@ --- * + * + * Arguments: --- + * + * Returns: --- * - * Arguments: @Display *d@ = pointer to display - * @Window w@ = window to set - * @Atom p@ = property to read + * Use: Causes child processes to be reaped as reports of their + * demises come in. Their exit statuses are simply discarded. * - * Returns: Atom which is the value of the property. + * This program needs to reap child processes even though it + * didn't create them and doesn't know what to do with their + * statuses because it's often used in shell scripts of the form * - * Use: Reads an atom property from a particular window. The value - * @None@ is returned if there is no atom value. + * ... start lots of stuff ... + * exec xatom wait GODOT ARRIVED */ -Atom xatom_get(Display *d, Window w, Atom p) +static void reap(int sig) + { int e = errno; while (waitpid(-1, 0, WNOHANG) > 0) ; errno = e; } + +static void autoreap(void) +{ + struct sigaction sa; + sigset_t ss, oss; + + sa.sa_handler = reap; + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGCHLD); + sa.sa_flags = SA_NOCLDSTOP; +#ifdef SA_RESTART + sa.sa_flags |= SA_RESTART; +#endif + sigaction(SIGCHLD, &sa, 0); + + sigemptyset(&ss); + sigaddset(&ss, SIGCHLD); + sigprocmask(SIG_BLOCK, &ss, &oss); + reap(SIGCHLD); + sigprocmask(SIG_SETMASK, &oss, 0); +} + +/*----- Command dispatch --------------------------------------------------*/ + +static const struct cmd { + const char *name; + int (*cmd)(int, char **); + const char *usage; + const char *help; +} cmds[] = { + { "help", c_help, "help [COMMANDS...]" }, + { "get", c_get, "get PROPERTY" }, + { "set", c_set, "set PROPERTY VALUE" }, + { "delete", c_delete, "delete PROPERTY" }, + { "wait", c_wait, "wait PROPERTY [VALUE...]" }, + { 0 } +}; + +const struct cmd *findcmd(const char *name) { - Atom type, v; - unsigned long n, left; - int fmt; - unsigned char *buf; - - /* --- Fetch the property value --- */ - - if (XGetWindowProperty(d, w, p, /* Display, window, property */ - 0, 64, /* Offset, length (both in words) */ - False, /* Delete after return? */ - XA_ATOM, /* Data format type */ - &type, &fmt, /* Actual type and format */ - &n, &left, /* Amount read, and bytes left */ - &buf) /* Where to put the buffer */ - != Success || - type != XA_ATOM || - n < 1 || fmt < 32) - return (None); - - /* --- OK, get the atom and return --- * - * - * This assumes that atoms are 32-bit things. This may not be the case. - * That's a right pain, actually. It looks as if Xlib is trying to do the - * right thing, so I'll go with that rather than trying to do anything - * clever. This is actually a bit of a poor interface. - */ - - v = *(Atom *)buf; - XFree(buf); - return (v); + const struct cmd *c, *chosen = 0; + size_t sz = strlen(name); + + for (c = cmds; c->name; c++) { + if (strncmp(name, c->name, sz) == 0) { + if (c->name[sz] == 0) { + chosen = c; + break; + } else if (chosen) + die(EXIT_FAILURE, "ambiguous command name `%s'", name); + else + chosen = c; + } + } + if (!chosen) + die(EXIT_FAILURE, "unknown command name `%s'", name); + return (chosen); +} + +/*----- Help and version information --------------------------------------*/ + +static void version(void) + { pquis(stdout, "$ version " VERSION "\n"); } + +static void usage(FILE *fp) + { pquis(fp, "Usage: $ [-d DISPLAY] SUBCOMMAND [ARGUMENTS...]\n"); } + +static void help(char **av) +{ + const struct cmd *c; + + version(); putchar('\n'); + if (!*av) { + usage(stdout); + fputs("\n\ +Sets, retrieves and waits for properties on an X window.\n\ +\n\ +Global command-line options:\n\ +\n\ +-h, --help [COMMAND] Display this help, or help on COMMAND.\n\ +-v, --version Display program's version number.\n\ +-u, --usage Display short usage summary.\n\ +\n\ +-d, --display=DISPLAY Connect to X DISPLAY.\n\ +-w, --window=WINDOW Use properties on WINDOW instead of root.\n\ +\n\ +The following subcommands are understood:\n\n", + stdout); + for (c = cmds; c->name; c++) + printf("%s\n", c->usage); + } else { + while (*av) { + c = findcmd(*av++); + printf("Usage: %s [-OPTIONS] %s\n", QUIS, c->usage); + if (c->help) { + putchar('\n'); + pquis(stdout, c->help); + } + if (*av) putchar('\n'); + } + } +} + +/*----- Main program ------------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ + const char *display = 0; + const char *win = 0; + + unsigned f = 0; +#define F_BOGUS 1u + + ego(argv[0]); + + /* --- Parse arguments --- */ + + for (;;) { + static struct option opt[] = { + { "help", 0, 0, 'h' }, + { "usage", 0, 0, 'u' }, + { "version", 0, 0, 'v' }, + { "display", OPTF_ARGREQ, 0, 'd' }, + { "window", OPTF_ARGREQ, 0, 'w' }, + { 0, 0, 0, 0 } + }; + + int i = mdwopt(argc, argv, "+huvd:w:", opt, 0, 0, 0); + if (i < 0) break; + switch (i) { + case 'h': help(argv + optind); exit(0); + case 'u': usage(stdout); exit(0); + case 'v': version(); exit(0); + case 'd': display = optarg; break; + case 'w': win = optarg; break; + default: f |= F_BOGUS; break; + } + } + if ((f & F_BOGUS) || optind >= argc) { + usage(stderr); + exit(EXIT_FAILURE); + } + + /* --- Initialize --- */ + + autoreap(); + if ((dpy = XOpenDisplay(display)) == 0) + die(EXIT_FAILURE, "couldn't open display"); + + /* --- Select a target window --- */ + + if (!win) + window = DefaultRootWindow(dpy); + else if (strcmp(win, "choose") == 0) + window = choosewindow(); + else + window = (Window)strtoul(win, 0, 0); + + /* --- Dispatch the command --- */ + + argc -= optind; + argv += optind; + optind = 0; + return (findcmd(argv[0])->cmd(argc, argv)); + +#undef F_BOGUS } /*----- That's all, folks -------------------------------------------------*/