/* -*-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.
*
* 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 <errno.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <X11/Xatom.h>
+#include <sys/wait.h>
+
+#include <X11/cursorfont.h>
#include <X11/Xlib.h>
-#include <X11/Xutil.h>
-/*----- Main code ---------------------------------------------------------*/
+#include <mLib/mdwopt.h>
+#include <mLib/quis.h>
+#include <mLib/alloc.h>
+#include <mLib/report.h>
+
+#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 -------------------------------------------------*/