From 01a2fe8e4dc87fdb256d95504482acf0fb06b67b Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Sun, 23 Mar 2008 16:06:36 +0000 Subject: [PATCH] xatom: Overhaul xwait/xtell into a single program with subcommands. We can also turn xshutdown into a simple shell script at the same time. --- Makefile.am | 11 ++ debian/control | 1 + debian/xtoys.install | 2 + libxatom.c | 152 ++++++++++++++++++++++ libxatom.h | 106 +++++++++++++++ xatom.1 | 186 ++++++++++++++++++++++++++ xatom.c | 360 +++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 818 insertions(+) create mode 100644 libxatom.c create mode 100644 libxatom.h create mode 100644 xatom.1 create mode 100644 xatom.c diff --git a/Makefile.am b/Makefile.am index 01885b1..14d29ed 100644 --- a/Makefile.am +++ b/Makefile.am @@ -44,6 +44,17 @@ LDADD = -lX11 $(X_LIBS) ## xscsize bin_PROGRAMS += xscsize dist_man_MANS += xscsize.1 +xscsize_SOURCES = + +xscsize_SOURCES += xscsize.c + +## xatom +bin_PROGRAMS += xatom +dist_man_MANS += xatom.1 +xatom_SOURCES = + +xatom_SOURCES += xatom.c +xatom_SOURCES += libxatom.h libxatom.c ###-------------------------------------------------------------------------- ### Debian. diff --git a/debian/control b/debian/control index 5a1c73f..db4fd1b 100644 --- a/debian/control +++ b/debian/control @@ -11,3 +11,4 @@ Depends: ${shlibs:Depends} Description: A collection of small X11 tools We have: xscsize -- reports the display size as shell variables + xatom -- inspect properties on windows, and wait for changes diff --git a/debian/xtoys.install b/debian/xtoys.install index 8c49cf3..595c141 100644 --- a/debian/xtoys.install +++ b/debian/xtoys.install @@ -1,2 +1,4 @@ +debian/tmp/usr/bin/xatom +debian/tmp/usr/share/man/man1/xatom.1 debian/tmp/usr/bin/xscsize debian/tmp/usr/share/man/man1/xscsize.1 diff --git a/libxatom.c b/libxatom.c new file mode 100644 index 0000000..3bca18f --- /dev/null +++ b/libxatom.c @@ -0,0 +1,152 @@ +/* -*-c-*- + * + * Messing with X atom properties + * + * (c) 2007 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the Edgeware X tools collection. + * + * X tools is free software; you can redistribute it and/or modify + * 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. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include +#include + +#include "libxatom.h" + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @xatom_set@ --- * + * + * Arguments: @Display *d@ = pointer to display + * @Window w@ = window to set + * @Atom p@ = property to set + * @Atom a@ = atom property value + * + * Returns: --- + * + * Use: Sets an atom property on a particular window. + */ + +void xatom_set(Display *d, Window w, Atom p, Atom a) +{ + XChangeProperty(d, w, p, XA_ATOM, 32, PropModeReplace, + (unsigned char *)&a, 1); + XSync(d, False); +} + +/* --- @xatom_get@ --- * + * + * Arguments: @Display *d@ = pointer to display + * @Window w@ = window to set + * @Atom p@ = property to read + * + * Returns: Atom which is the value of the property. + * + * Use: Reads an atom property from a particular window. The value + * @None@ is returned if there is no atom value. + */ + +Atom xatom_get(Display *d, Window w, Atom p) +{ + 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); +} + +/* --- @xatom_delete@ --- * + * + * Arguments: @Display *d@ = pointer to display + * @Window w@ = window containing atom + * @Atom p@ = property to delete + * + * Returns: --- + * + * Use: Removes a property from a window. + */ + +void xatom_delete(Display *d, Window w, Atom p) +{ + XDeleteProperty(d, w, p); + XSync(d, False); +} + +/* --- @xatom_wait@ --- * + * + * Arguments: @Display *d@ = pointer to display + * @Window w@ = window to watch + * @Atom p@ = property to fetch + * @const Atom *aa@ = pointer to vector of atoms + * @size_t n@ = numer of atoms in vector + * + * Returns: The matching atom. + * + * Use: Waits for the given property on the window to match one of + * the @aa[i]@. + */ + +Atom xatom_wait(Display *d, Window w, Atom p, const Atom *aa, size_t n) +{ + Atom a; + size_t i; + XEvent event; + + XSelectInput(d, w, PropertyChangeMask); + for (;;) { + a = xatom_get(d, w, p); + if (a != None) { + if (!n) return (a); + for (i = 0; i < n; i++) + if (a == aa[i]) return (a); + } + do XNextEvent(d, &event); while (event.type != PropertyNotify); + } +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/libxatom.h b/libxatom.h new file mode 100644 index 0000000..200276b --- /dev/null +++ b/libxatom.h @@ -0,0 +1,106 @@ +/* -*-c-*- + * + * Messing with X atom properties + * + * (c) 2007 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the Edgeware X tools collection. + * + * X tools is free software; you can redistribute it and/or modify + * 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. + */ + +#ifndef LIBXATOM_H +#define LIBXATOM_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @xatom_set@ --- * + * + * Arguments: @Display *d@ = pointer to display + * @Window w@ = window to set + * @Atom p@ = property to set + * @Atom a@ = atom property value + * + * Returns: --- + * + * Use: Sets an atom property on a particular window. + */ + +extern void xatom_set(Display */*d*/, Window /*w*/, Atom /*p*/, Atom /*a*/); + +/* --- @xatom_get@ --- * + * + * Arguments: @Display *d@ = pointer to display + * @Window w@ = window to set + * @Atom p@ = property to read + * + * Returns: Atom which is the value of the property. + * + * Use: Reads an atom property from a particular window. The value + * @None@ is returned if there is no atom value. + */ + +extern Atom xatom_get(Display */*d*/, Window /*w*/, Atom /*p*/); + +/* --- @xatom_delete@ --- * + * + * Arguments: @Display *d@ = pointer to display + * @Window w@ = window containing atom + * @Atom p@ = property to delete + * + * Returns: --- + * + * Use: Removes a property from a window. + */ + +extern void xatom_delete(Display */*d*/, Window /*w*/, Atom /*p*/); + +/* --- @xatom_wait@ --- * + * + * Arguments: @Display *d@ = pointer to display + * @Window w@ = window to watch + * @Atom p@ = property to fetch + * @const Atom *aa@ = pointer to vector of atoms + * @size_t n@ = numer of atoms in vector + * + * Returns: The matching atom. + * + * Use: Waits for the given property on the window to match one of + * the @aa[i]@. + */ + +extern Atom xatom_wait(Display */*d*/, Window /*w*/, Atom /*p*/, + const Atom */*aa*/, size_t /*n*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/xatom.1 b/xatom.1 new file mode 100644 index 0000000..59d7ae8 --- /dev/null +++ b/xatom.1 @@ -0,0 +1,186 @@ +.\" -*-nroff-*- +.TH xatom 1 "13 September 2007" "Straylight/Edgeware" "xtoys" +.SH NAME +xatom \- manipulate properties on X windows +.SH SYNOPSIS +.B xatom +.RB [ \- d +.IR display ] +.RB [ \- w +.IR window ] +.I command +.PP +where +.I command +is one of: +.PP +.B help +.RI [ command ...] +.br +.B get +.I property +.br +.B set +.I property +.I value +.br +.B delete +.I property +.br +.B wait +.I property +.RI [ value ...] +.SH DESCRIPTION +The +.B xatom +program manipulates properties of a simple kind on X windows. It deals +only with those properties whose value is a single X atom. It is +capable of setting, reading, and deleting properties, and also waiting +until a particular property is set, maybe to one of a number of given +values. +.PP +The program provides a number of subcommands, by which the various +operations may be carried out. +.SS "Global options" +Before the command name, +.I "global options" +may be given. The following global options are supported. +.TP +.BR "\-h, \-\-help " [\fIcommand\fP...] +Writes a brief summary of +.BR xatom 's +various operations to standard output, and returns a successful exit +status. With command names, gives help on those commands. +.TP +.B "\-v, \-\-version" +Writes the program's version number to standard output, and returns a +successful exit status. +.TP +.B "\-u, \-\-usage" +Writes a very terse command line summary to standard output, and returns +a successful exit status. +.TP +.BI "\-d, \-\-display=" display +Connect to the X server named by +.I display +rather than the default, which is to consult the +.B DISPLAY +environment variable. +.TP +.BI "\-w, \-\-window=" window +Manipulate properties on the window whose ID is +.I window +rather than the default, which is to use the root window of the screen +named by +.IR display . +If +.I window +is the string +.B choose +then the window can be chosen interactively: the +.B xatom +program will grab the pointer and wait for a button-1 click over a +window. A button-3 click causes +.B xatom +to quit (and return an unsuccessful exit status). +.SH "COMMAND REFERENCE" +.SS "help" +The +.B help +command behaves exactly as the +.B \-\-help +option. With no arguments, it shows an overview of +.BR xatom 's +options; with arguments, it described the named subcommands. +.SS "get" +The +.B get +command retrieves the named +.I property +from the specified window, and reports its value on standard output. +.PP +If the property wasn't found, or its value wasn't a single atom, nothing +is written, but a successful exit status is still returned. If a value +was found, it is written and followed by a newline: therefore a client +can distinguish an empty value from no value at all. +.SS "set" +The +.B set +command sets the named +.I property +to have the specified +.IR value , +overwriting any existing value. +.SS "delete" +The +.B delete +command removes the named +.I property +from the window. If no such property exists, nothing happens. +.SS "wait" +The +.B wait +command waits on a particular +.IR property . +If any +.IR value s +are specified, then the command waits until the property's value matches +one of the specified +.IR value s: +if it already matches one of them then the command won't wait. +If no +.IR value s +are specified, then the command waits until the property is set to any +value; again, if it is already set, the command returns immediately. +.PP +If exactly one +.I value +is given, the command produces no output; otherwise, it writes the new +value of the property, as for the +.B get +command. +.SH EXAMPLE +The author uses this command at the end of his +.BR .xinitrc +file, to control the duration of his X session. Specifically, he uses +the code +.PP +.RS +.ft B +.nf +xatom set MDW_SHUTDOWN READY +xatom wait MDW_SHUTDOWN SHUTDOWN +xatom delete MDW_SHUTDOWN +.fi +.RE +The script +.B xshutdown +looks like this. +.PP +.RS +.ft B +.nf +#! /bin/sh +set -e +me=$(basename $0) +case "$(xatom get MDW_SHUTDOWN)" in + READY) ;; + *) xmsg -e -t $me "Nobody's waiting for my signal."; exit 1;; +esac +case "$(xmsg -q -t $me \ + "Really shut down this session?" :_Shutdown \~gtk-cancel)" in + _Shutdown) + xatom set MDW_SHUTDOWN SHUTDOWN + ;; +esac +.fi +.RE +.SH BUGS +None currently known. +.SH SEE ALSO +.BR xmsg (1), +.BR XChangeProperty (3x), +.BR XGetWindowProperty (3x), +.BR XDeleteProperty (3x). +.SH AUTHOR +Mark Wooding (mdw@distorted.org.uk). diff --git a/xatom.c b/xatom.c new file mode 100644 index 0000000..826085c --- /dev/null +++ b/xatom.c @@ -0,0 +1,360 @@ +/* -*-c-*- + * + * Set and fetch X atom properties + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the Edgeware X tools collection. + * + * X tools is free software; you can redistribute it and/or modify + * 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. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "libxatom.h" + +/*----- Static variables --------------------------------------------------*/ + +static Display *display = 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(display, argv[1], True)) == None || + (a = xatom_get(display, window, p)) == None) + return (0); + name = XGetAtomName(display, 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(display, argv[1], False); + a = XInternAtom(display, argv[2], False); + xatom_set(display, window, p, a); + return (0); +} + +static int c_delete(int argc, char **argv) +{ + Atom p; + + if (argc != 2) + die(EXIT_FAILURE, "Usage: delete PROPERTY"); + if ((p = XInternAtom(display, argv[1], True)) == None) + return (0); + xatom_delete(display, 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(display, argv[1], False); + n = argc - 2; + if (n) { + aa = xmalloc(n * sizeof(Atom)); + XInternAtoms(display, argv + 2, n, False, aa); + } + + a = xatom_wait(display, window, p, aa, n); + if (n != 1) { + name = XGetAtomName(display, a); + puts(name); + XFree(name); + } + if (aa) + xfree(aa); + return (0); +} + +/*----- Utilities ---------------------------------------------------------*/ + +/* --- @choosewindow@ --- * + * + * Arguments: --- + * + * Returns: An X window id. + * + * Use: Allows the user to select a window using the mouse. + */ + +static Window choosewindow(void) +{ + Cursor cross = XCreateFontCursor(display, XC_crosshair); + XEvent event; + + XGrabPointer(display, + DefaultRootWindow(display), + False, + ButtonPressMask, + GrabModeAsync, + GrabModeAsync, + None, + cross, + CurrentTime); + + for (;;) { + XNextEvent(display, &event); + switch (event.type) { + case ButtonPress: + switch (event.xbutton.button) { + case 3: + XUngrabPointer(display, event.xbutton.time); + die(EXIT_FAILURE, "aborted window selection"); + break; + case 1: + window = event.xbutton.subwindow; + if (window == None) + window = event.xbutton.window; + XUngrabPointer(display, event.xbutton.time); + return (window); + } + break; + } + } +} + +/* --- @autoreap@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Causes child processes to be reaped as reports of their + * demises come in. Their exit statuses are simply discarded. + * + * 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 + * + * ... start lots of stuff ... + * exec xatom wait GODOT ARRIVED + */ + +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) +{ + 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 *dpy = 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': dpy = 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 ((display = XOpenDisplay(dpy)) == 0) + die(EXIT_FAILURE, "couldn't open display"); + + /* --- Select a target window --- */ + + if (!win) + window = DefaultRootWindow(display); + 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 -------------------------------------------------*/ -- 2.11.0