Release 1.6.0.
[xtoys] / xatom.c
CommitLineData
01a2fe8e
MW
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
51static Display *display = 0;
52static Window window = None;
53
54/*----- Command implementations -------------------------------------------*/
55
56static void help(char **);
57static int c_help(int argc, char **argv)
58 { help(argv + 1); return (0); }
59
60static 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
75static 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
87static 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
99static 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
137static 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
190static void reap(int sig)
191 { int e = errno; while (waitpid(-1, 0, WNOHANG) > 0) ; errno = e; }
192
193static 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
216static 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
230const 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) {
c1c9f4d2
MW
238 chosen = c;
239 break;
01a2fe8e 240 } else if (chosen)
c1c9f4d2 241 die(EXIT_FAILURE, "ambiguous command name `%s'", name);
01a2fe8e 242 else
c1c9f4d2 243 chosen = c;
01a2fe8e
MW
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
253static void version(void)
254 { pquis(stdout, "$ version " VERSION "\n"); }
255
256static void usage(FILE *fp)
257 { pquis(fp, "Usage: $ [-d DISPLAY] SUBCOMMAND [ARGUMENTS...]\n"); }
258
259static void help(char **av)
260{
261 const struct cmd *c;
262
263 version(); putchar('\n');
264 if (!*av) {
265 usage(stdout);
266 fputs("\n\
267Sets, retrieves and waits for properties on an X window.\n\
268\n\
269Global 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\
278The 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
297int 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[] = {
c1c9f4d2
MW
311 { "help", 0, 0, 'h' },
312 { "usage", 0, 0, 'u' },
313 { "version", 0, 0, 'v' },
314 { "display", OPTF_ARGREQ, 0, 'd' },
01a2fe8e
MW
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 -------------------------------------------------*/