advance while parsing a hex input in obxprop
[dana/openbox.git] / tools / obxprop / obxprop.c
1 #include <X11/Xlib.h>
2 #include <X11/cursorfont.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include <assert.h>
7 #include <glib.h>
8
9 gint fail(const gchar *s) {
10     if (s)
11         fprintf(stderr, "%s\n", s);
12     else
13         fprintf
14             (stderr,
15              "Usage: obxprop [OPTIONS] [--] [PROPERTIES ...]\n\n"
16              "Options:\n"
17              "    --help              Display this help and exit\n"
18              "    --display DISPLAY   Connect to this X display\n"
19              "    --id ID             Show the properties for this window\n"
20              "    --root              Show the properties for the root window\n");
21     return 1;
22 }
23
24 gint parse_hex(gchar *s) {
25     gint result = 0;
26     while (*s) {
27         gint add;
28         if (*s >= '0' && *s <='9')
29             add = *s-'0';
30         else if (*s >= 'A' && *s <='F')
31             add = *s-'A';
32         else if (*s >= 'a' && *s <='f')
33             add = *s-'a';
34         else
35             break;
36
37         result *= 16;
38         result += add;
39         ++s;
40     }
41     return result;
42 }
43
44 Window find_client(Display *d, Window win)
45 {
46     Window r, *children;
47     guint n, i;
48     Atom state = XInternAtom(d, "WM_STATE", True);
49     Atom ret_type;
50     gint ret_format, res;
51     gulong ret_items, ret_bytesleft, *xdata;
52
53     XQueryTree(d, win, &r, &r, &children, &n);
54     for (i = 0; i < n; ++i) {
55         Window w = find_client(d, children[i]);
56         if (w) return w;
57     }
58
59     // try me
60     res = XGetWindowProperty(d, win, state, 0, 1,
61                        False, state, &ret_type, &ret_format,
62                        &ret_items, &ret_bytesleft,
63                        (unsigned char**) &xdata);
64     XFree(xdata);
65     if (res != Success || ret_type == None || ret_items < 1)
66         return None;
67     return win; // found it!
68 }
69
70 static gboolean get_all(Display *d, Window win, Atom prop,
71                         Atom *type, gint *size,
72                         guchar **data, guint *num)
73 {
74     gboolean ret = FALSE;
75     gint res;
76     guchar *xdata = NULL;
77     gulong ret_items, bytes_left;
78
79     res = XGetWindowProperty(d, win, prop, 0l, G_MAXLONG,
80                              FALSE, AnyPropertyType, type, size,
81                              &ret_items, &bytes_left, &xdata);
82     if (res == Success) {
83         if (ret_items > 0) {
84             guint i;
85
86             *data = g_malloc(ret_items * (*size / 8));
87             for (i = 0; i < ret_items; ++i)
88                 switch (*size) {
89                 case 8:
90                     (*data)[i] = xdata[i];
91                     break;
92                 case 16:
93                     ((guint16*)*data)[i] = ((gushort*)xdata)[i];
94                     break;
95                 case 32:
96                     ((guint32*)*data)[i] = ((gulong*)xdata)[i];
97                     break;
98                 default:
99                     g_assert_not_reached(); /* unhandled size */
100                 }
101             *num = ret_items;
102             ret = TRUE;
103         }
104         XFree(xdata);
105     }
106     return ret;
107 }
108
109 gchar *append_string(gchar *before, gchar *after, gboolean quote)
110 {
111     gchar *tmp;
112     const gchar *q = quote ? "\"" : "";
113     if (before)
114         tmp = g_strdup_printf("%s, %s%s%s", before, q, after, q);
115     else
116         tmp = g_strdup_printf("%s%s%s", q, after, q);
117     g_free(before);
118     return tmp;
119 }
120
121 gchar *append_int(gchar *before, guint after)
122 {
123     gchar *tmp;
124     if (before)
125         tmp = g_strdup_printf("%s, %u", before, after);
126     else
127         tmp = g_strdup_printf("%u", after);
128     g_free(before);
129     return tmp;
130 }
131
132 gchar* read_strings(gchar *val, guint n, gboolean utf8)
133 {
134     GSList *strs = NULL, *it;
135     gchar *ret, *p;
136     guint i;
137
138     p = val;
139     while (p < val + n) {
140         strs = g_slist_append(strs, g_strndup(p, n - (p - val)));
141         p += strlen(p) + 1; /* next string */
142     }
143
144     ret = NULL;
145     for (i = 0, it = strs; it; ++i, it = g_slist_next(it)) {
146         char *data;
147
148         if (utf8) {
149             if (g_utf8_validate(it->data, -1, NULL))
150                 data = g_strdup(it->data);
151             else
152                 data = g_strdup("");
153         }
154         else
155             data = g_locale_to_utf8(it->data, -1, NULL, NULL, NULL);
156
157         ret = append_string(ret, data, TRUE);
158         g_free(data);
159     }
160
161     while (strs) {
162         g_free(strs->data);
163         strs = g_slist_delete_link(strs, strs);
164     }
165     return ret;
166 }
167
168 gchar* read_atoms(Display *d, guchar *val, guint n)
169 {
170     gchar *ret;
171     guint i;
172
173     ret = NULL;
174     for (i = 0; i < n; ++i)
175         ret = append_string(ret, XGetAtomName(d, ((guint32*)val)[i]), FALSE);
176     return ret;
177 }
178
179 gchar* read_numbers(guchar *val, guint n, guint size)
180 {
181     gchar *ret;
182     guint i;
183
184     ret = NULL;
185     for (i = 0; i < n; ++i)
186         switch (size) {
187         case 8:
188             ret = append_int(ret, ((guint8*)val)[i]);
189             break;
190         case 16:
191             ret = append_int(ret, ((guint16*)val)[i]);
192             break;
193         case 32:
194             ret = append_int(ret, ((guint32*)val)[i]);
195             break;
196         default:
197             g_assert_not_reached(); /* unhandled size */
198         }
199
200     return ret;
201 }
202
203 gboolean read_prop(Display *d, Window w, Atom prop, const gchar **type, gchar **val)
204 {
205     guchar *ret;
206     guint nret;
207     gint size;
208     Atom ret_type;
209
210     ret = NULL;
211     if (get_all(d, w, prop, &ret_type, &size, &ret, &nret)) {
212         *type = XGetAtomName(d, ret_type);
213
214         if (strcmp(*type, "STRING") == 0)
215             *val = read_strings((gchar*)ret, nret, FALSE);
216         else if (strcmp(*type, "UTF8_STRING") == 0)
217             *val = read_strings((gchar*)ret, nret, TRUE);
218         else if (strcmp(*type, "ATOM") == 0) {
219             g_assert(size == 32);
220             *val = read_atoms(d, ret, nret);
221         }
222        else
223             *val = read_numbers(ret, nret, size);
224
225         g_free(ret);
226         return TRUE;
227     }
228     return FALSE;
229 }
230
231 void show_properties(Display *d, Window w, int argc, char **argv)
232 {
233     Atom* props;
234     int i, n;
235
236     props = XListProperties(d, w, &n);
237
238     for (i = 0; i < n; ++i) {
239         const char *type;
240         char *name, *val;
241
242         name = XGetAtomName(d, props[i]);
243
244         if (read_prop(d, w, props[i], &type, &val)) {
245             int found = 1;
246             if (argc) {
247                 int i;
248
249                 found = 0;
250                 for (i = 0; i < argc; i++)
251                     if (!strcmp(name, argv[i])) {
252                         found = 1;
253                         break;
254                     }
255             }
256             if (found)
257                 g_print("%s(%s) = %s\n", name, type, val);
258             g_free(val);
259         }
260
261         XFree(name);
262     }
263
264     XFree(props);
265 }
266
267 int main(int argc, char **argv)
268 {
269     Display *d;
270     Window id, userid = None;
271     int i;
272     char *dname = NULL;
273     gboolean root = FALSE;
274
275     for (i = 1; i < argc; ++i) {
276         if (!strcmp(argv[i], "--help")) {
277             return fail(NULL);
278         }
279         else if (!strcmp(argv[i], "--root"))
280             root = TRUE;
281         else if (!strcmp(argv[i], "--id")) {
282             if (++i == argc)
283                 return fail(NULL);
284             if (argv[i][0] == '0' && argv[i][1] == 'x') {
285                 /* hex */
286                 userid = parse_hex(argv[i]+2);
287             }
288             else {
289                 /* decimal */
290                 userid = atoi(argv[i]);
291             }
292             if (!userid)
293                 return fail("Unable to parse argument to --id.");
294         }
295         else if (!strcmp(argv[i], "--display")) {
296             if (++i == argc)
297                 return fail(NULL);
298             dname = argv[i];
299         }
300         else if (*argv[i] != '-')
301             break;
302         else if (!strcmp(argv[i], "--")) {
303             i++;
304             break;
305         }
306         else
307             return fail(NULL);
308     }
309
310     d = XOpenDisplay(dname);
311     if (!d) {
312         return fail("Unable to find an X display. "
313                     "Ensure you have permission to connect to the display.");
314     }
315
316     if (root)
317         userid = RootWindow(d, DefaultScreen(d));
318
319     if (userid == None) {
320         int j;
321         j = XGrabPointer(d, RootWindow(d, DefaultScreen(d)),
322                          False, ButtonPressMask,
323                          GrabModeAsync, GrabModeAsync,
324                          None, XCreateFontCursor(d, XC_crosshair),
325                          CurrentTime);
326         if (j != GrabSuccess)
327             return fail("Unable to grab the pointer device");
328         while (1) {
329             XEvent ev;
330
331             XNextEvent(d, &ev);
332             if (ev.type == ButtonPress) {
333                 XUngrabPointer(d, CurrentTime);
334                 userid = ev.xbutton.subwindow;
335                 break;
336             }
337         }
338         id = find_client(d, userid);
339     }
340     else
341         id = userid; /* they picked this one */
342
343     if (id == None)
344         return fail("Unable to find window with the requested ID");
345
346     show_properties(d, id, argc - i, &argv[i]);
347     
348     XCloseDisplay(d);
349
350     return 0;
351 }