chiark / gitweb /
826085cd7c0c05e87d65c1386271802a637e4e7f
[xtoys] / xatom.c
1 /* -*-c-*-
2  *
3  * Set and fetch X atom properties
4  *
5  * (c) 1999 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the Edgeware X tools collection.
11  *
12  * X tools is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * X tools is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with X tools; if not, write to the Free Software Foundation,
24  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25  */
26
27 /*----- Header files ------------------------------------------------------*/
28
29 #include <errno.h>
30 #include <signal.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include <sys/wait.h>
36
37 #include <X11/cursorfont.h>
38 #include <X11/Xatom.h>
39 #include <X11/Xlib.h>
40 #include <X11/Xutil.h>
41
42 #include <mLib/mdwopt.h>
43 #include <mLib/quis.h>
44 #include <mLib/alloc.h>
45 #include <mLib/report.h>
46
47 #include "libxatom.h"
48
49 /*----- Static variables --------------------------------------------------*/
50
51 static Display *display = 0;
52 static Window window = None;
53
54 /*----- Command implementations -------------------------------------------*/
55
56 static void help(char **);
57 static int c_help(int argc, char **argv)
58   { help(argv + 1); return (0); }
59
60 static int c_get(int argc, char **argv)
61 {
62   Atom p, a;
63   char *name;
64
65   if (argc != 2)
66     die(EXIT_FAILURE, "Usage: get PROPERTY");
67   if ((p = XInternAtom(display, argv[1], True)) == None ||
68       (a = xatom_get(display, window, p)) == None)
69     return (0);
70   name = XGetAtomName(display, a);
71   puts(name);
72   return (0);
73 }
74
75 static int c_set(int argc, char **argv)
76 {
77   Atom p, a;
78
79   if (argc != 3)
80     die(EXIT_FAILURE, "Usage: set PROPERTY VALUE");
81   p = XInternAtom(display, argv[1], False);
82   a = XInternAtom(display, argv[2], False);
83   xatom_set(display, window, p, a);
84   return (0);
85 }
86
87 static int c_delete(int argc, char **argv)
88 {
89   Atom p;
90
91   if (argc != 2)
92     die(EXIT_FAILURE, "Usage: delete PROPERTY");
93   if ((p = XInternAtom(display, argv[1], True)) == None)
94     return (0);
95   xatom_delete(display, window, p);
96   return (0);
97 }
98
99 static int c_wait(int argc, char **argv)
100 {
101   Atom p, a, *aa = 0;
102   int n;
103   char *name;
104
105   if (argc < 2)
106     die(EXIT_FAILURE, "Usage: wait PROPERTY [VALUE...]");
107
108   p = XInternAtom(display, argv[1], False);
109   n = argc - 2;
110   if (n) {
111     aa = xmalloc(n * sizeof(Atom));
112     XInternAtoms(display, argv + 2, n, False, aa);
113   }
114
115   a = xatom_wait(display, window, p, aa, n);
116   if (n != 1) {
117     name = XGetAtomName(display, a);
118     puts(name);
119     XFree(name);
120   }
121   if (aa)
122     xfree(aa);
123   return (0);
124 }
125
126 /*----- Utilities ---------------------------------------------------------*/
127
128 /* --- @choosewindow@ --- *
129  *
130  * Arguments:   ---
131  *
132  * Returns:     An X window id.
133  *
134  * Use:         Allows the user to select a window using the mouse.
135  */
136
137 static Window choosewindow(void)
138 {
139   Cursor cross = XCreateFontCursor(display, XC_crosshair);
140   XEvent event;
141
142   XGrabPointer(display,
143                DefaultRootWindow(display),
144                False,
145                ButtonPressMask,
146                GrabModeAsync,
147                GrabModeAsync,
148                None,
149                cross,
150                CurrentTime);
151
152   for (;;) {
153     XNextEvent(display, &event);
154     switch (event.type) {
155       case ButtonPress:
156         switch (event.xbutton.button) {
157           case 3:
158             XUngrabPointer(display, event.xbutton.time);
159             die(EXIT_FAILURE, "aborted window selection");
160             break;
161           case 1:
162             window = event.xbutton.subwindow;
163             if (window == None)
164               window = event.xbutton.window;
165             XUngrabPointer(display, event.xbutton.time);
166             return (window);
167         }
168         break;
169     }
170   }
171 }
172
173 /* --- @autoreap@ --- *
174  *
175  * Arguments:   ---
176  *
177  * Returns:     ---
178  *
179  * Use:         Causes child processes to be reaped as reports of their
180  *              demises come in.  Their exit statuses are simply discarded.
181  *
182  *              This program needs to reap child processes even though it
183  *              didn't create them and doesn't know what to do with their
184  *              statuses because it's often used in shell scripts of the form
185  *
186  *                      ... start lots of stuff ...
187  *                      exec xatom wait GODOT ARRIVED
188  */
189
190 static void reap(int sig)
191   { int e = errno; while (waitpid(-1, 0, WNOHANG) > 0) ; errno = e; }
192
193 static void autoreap(void)
194 {
195   struct sigaction sa;
196   sigset_t ss, oss;
197
198   sa.sa_handler = reap;
199   sigemptyset(&sa.sa_mask);
200   sigaddset(&sa.sa_mask, SIGCHLD);
201   sa.sa_flags = SA_NOCLDSTOP;
202 #ifdef SA_RESTART
203   sa.sa_flags |= SA_RESTART;
204 #endif
205   sigaction(SIGCHLD, &sa, 0);
206
207   sigemptyset(&ss);
208   sigaddset(&ss, SIGCHLD);
209   sigprocmask(SIG_BLOCK, &ss, &oss);
210   reap(SIGCHLD);
211   sigprocmask(SIG_SETMASK, &oss, 0);
212 }
213
214 /*----- Command dispatch --------------------------------------------------*/
215
216 static const struct cmd {
217   const char *name;
218   int (*cmd)(int, char **);
219   const char *usage;
220   const char *help;
221 } cmds[] = {
222   { "help", c_help, "help [COMMANDS...]" },
223   { "get", c_get, "get PROPERTY" },
224   { "set", c_set, "set PROPERTY VALUE" },
225   { "delete", c_delete, "delete PROPERTY" },
226   { "wait", c_wait, "wait PROPERTY [VALUE...]" },
227   { 0 }
228 };
229
230 const struct cmd *findcmd(const char *name)
231 {
232   const struct cmd *c, *chosen = 0;
233   size_t sz = strlen(name);
234
235   for (c = cmds; c->name; c++) {
236     if (strncmp(name, c->name, sz) == 0) {
237       if (c->name[sz] == 0) {
238         chosen = c;
239         break;
240       } else if (chosen)
241         die(EXIT_FAILURE, "ambiguous command name `%s'", name);
242       else
243         chosen = c;
244     }
245   }
246   if (!chosen)
247     die(EXIT_FAILURE, "unknown command name `%s'", name);
248   return (chosen);
249 }
250
251 /*----- Help and version information --------------------------------------*/
252
253 static void version(void)
254   { pquis(stdout, "$ version " VERSION "\n"); }
255
256 static void usage(FILE *fp)
257   { pquis(fp, "Usage: $ [-d DISPLAY] SUBCOMMAND [ARGUMENTS...]\n"); }
258
259 static void help(char **av)
260 {
261   const struct cmd *c;
262
263   version(); putchar('\n');
264   if (!*av) {
265     usage(stdout);
266     fputs("\n\
267 Sets, retrieves and waits for properties on an X window.\n\
268 \n\
269 Global command-line options:\n\
270 \n\
271 -h, --help [COMMAND]            Display this help, or help on COMMAND.\n\
272 -v, --version                   Display program's version number.\n\
273 -u, --usage                     Display short usage summary.\n\
274 \n\
275 -d, --display=DISPLAY           Connect to X DISPLAY.\n\
276 -w, --window=WINDOW             Use properties on WINDOW instead of root.\n\
277 \n\
278 The following subcommands are understood:\n\n",
279           stdout);
280     for (c = cmds; c->name; c++)
281       printf("%s\n", c->usage);
282   } else {
283     while (*av) {
284       c = findcmd(*av++);
285       printf("Usage: %s [-OPTIONS] %s\n", QUIS, c->usage);
286       if (c->help) {
287         putchar('\n');
288         pquis(stdout, c->help);
289       }
290       if (*av) putchar('\n');
291     }
292   }
293 }
294
295 /*----- Main program ------------------------------------------------------*/
296
297 int main(int argc, char *argv[])
298 {
299   const char *dpy = 0;
300   const char *win = 0;
301
302   unsigned f = 0;
303 #define F_BOGUS 1u
304
305   ego(argv[0]);
306
307   /* --- Parse arguments --- */
308
309   for (;;) {
310     static struct option opt[] = {
311       { "help",         0,                      0,      'h' },
312       { "usage",        0,                      0,      'u' },
313       { "version",      0,                      0,      'v' },
314       { "display",      OPTF_ARGREQ,            0,      'd' },
315       { "window",       OPTF_ARGREQ,            0,      'w' },
316       { 0,              0,                      0,      0 }
317     };
318
319     int i = mdwopt(argc, argv, "+huvd:w:", opt, 0, 0, 0);
320     if (i < 0) break;
321     switch (i) {
322       case 'h': help(argv + optind); exit(0);
323       case 'u': usage(stdout); exit(0);
324       case 'v': version(); exit(0);
325       case 'd': dpy = optarg; break;
326       case 'w': win = optarg; break;
327       default: f |= F_BOGUS; break;
328     }
329   }
330   if ((f & F_BOGUS) || optind >= argc) {
331     usage(stderr);
332     exit(EXIT_FAILURE);
333   }
334
335   /* --- Initialize --- */
336
337   autoreap();
338   if ((display = XOpenDisplay(dpy)) == 0)
339     die(EXIT_FAILURE, "couldn't open display");
340
341   /* --- Select a target window --- */
342
343   if (!win)
344     window = DefaultRootWindow(display);
345   else if (strcmp(win, "choose") == 0)
346     window = choosewindow();
347   else
348     window = (Window)strtoul(win, 0, 0);
349
350   /* --- Dispatch the command --- */
351
352   argc -= optind;
353   argv += optind;
354   optind = 0;
355   return (findcmd(argv[0])->cmd(argc, argv));
356
357 #undef F_BOGUS
358 }
359
360 /*----- That's all, folks -------------------------------------------------*/