Merge branch 'master' of git://git.openbox.org/dana/openbox
[mikachu/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'+10;
32         else if (*s >= 'a' && *s <='f')
33             add = *s-'a'+10;
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         }
102         *num = ret_items;
103         ret = TRUE;
104         XFree(xdata);
105     }
106     return ret;
107 }
108
109 GString *append_string(GString *before, gchar *after, gboolean quote)
110 {
111     const gchar *q = quote ? "\"" : "";
112     if (before)
113         g_string_append_printf(before, ", %s%s%s", q, after, q);
114     else
115         g_string_append_printf(before = g_string_new(NULL), "%s%s%s", q, after, q);
116     return before;
117 }
118
119 GString *append_int(GString *before, guint after)
120 {
121     if (before)
122         g_string_append_printf(before, ", %u", after);
123     else
124         g_string_append_printf(before = g_string_new(NULL), "%u", after);
125     return before;
126 }
127
128 gchar* read_strings(gchar *val, guint n, gboolean utf8)
129 {
130     GSList *strs = NULL, *it;
131     GString *ret;
132     gchar *p;
133     guint i;
134
135     p = val;
136     while (p < val + n) {
137         strs = g_slist_append(strs, g_strndup(p, n - (p - val)));
138         p += strlen(p) + 1; /* next string */
139     }
140
141     ret = NULL;
142     for (i = 0, it = strs; it; ++i, it = g_slist_next(it)) {
143         char *data;
144
145         if (utf8) {
146             if (g_utf8_validate(it->data, -1, NULL))
147                 data = g_strdup(it->data);
148             else
149                 data = g_strdup("");
150         }
151         else
152             data = g_locale_to_utf8(it->data, -1, NULL, NULL, NULL);
153
154         ret = append_string(ret, data, TRUE);
155         g_free(data);
156     }
157
158     while (strs) {
159         g_free(strs->data);
160         strs = g_slist_delete_link(strs, strs);
161     }
162     if (ret)
163         return g_string_free(ret, FALSE);
164     return NULL;
165 }
166
167 gchar* read_atoms(Display *d, guchar *val, guint n)
168 {
169     GString *ret;
170     guint i;
171
172     ret = NULL;
173     for (i = 0; i < n; ++i)
174         ret = append_string(ret, XGetAtomName(d, ((guint32*)val)[i]), FALSE);
175     if (ret)
176         return g_string_free(ret, FALSE);
177     return NULL;
178 }
179
180 gchar* read_numbers(guchar *val, guint n, guint size)
181 {
182     GString *ret;
183     guint i;
184
185     ret = NULL;
186     for (i = 0; i < n; ++i)
187         switch (size) {
188         case 8:
189             ret = append_int(ret, ((guint8*)val)[i]);
190             break;
191         case 16:
192             ret = append_int(ret, ((guint16*)val)[i]);
193             break;
194         case 32:
195             ret = append_int(ret, ((guint32*)val)[i]);
196             break;
197         default:
198             g_assert_not_reached(); /* unhandled size */
199         }
200
201     if (ret)
202         return g_string_free(ret, FALSE);
203     return NULL;
204 }
205
206 gboolean read_prop(Display *d, Window w, Atom prop, const gchar **type, gchar **val)
207 {
208     guchar *ret;
209     guint nret;
210     gint size;
211     Atom ret_type;
212
213     ret = NULL;
214     if (get_all(d, w, prop, &ret_type, &size, &ret, &nret)) {
215         *type = XGetAtomName(d, ret_type);
216
217         if (strcmp(*type, "STRING") == 0)
218             *val = read_strings((gchar*)ret, nret, FALSE);
219         else if (strcmp(*type, "UTF8_STRING") == 0)
220             *val = read_strings((gchar*)ret, nret, TRUE);
221         else if (strcmp(*type, "ATOM") == 0) {
222             g_assert(size == 32);
223             *val = read_atoms(d, ret, nret);
224         }
225        else
226             *val = read_numbers(ret, nret, size);
227
228         g_free(ret);
229         return TRUE;
230     }
231     return FALSE;
232 }
233
234 void show_properties(Display *d, Window w, int argc, char **argv)
235 {
236     Atom* props;
237     int i, n;
238
239     props = XListProperties(d, w, &n);
240
241     for (i = 0; i < n; ++i) {
242         const char *type;
243         char *name, *val;
244
245         name = XGetAtomName(d, props[i]);
246
247         if (read_prop(d, w, props[i], &type, &val)) {
248             int found = 1;
249             if (argc) {
250                 int i;
251
252                 found = 0;
253                 for (i = 0; i < argc; i++)
254                     if (!strcmp(name, argv[i])) {
255                         found = 1;
256                         break;
257                     }
258             }
259             if (found)
260                 g_print("%s(%s) = %s\n", name, type, (val ? val : ""));
261             g_free(val);
262         }
263
264         XFree(name);
265     }
266
267     XFree(props);
268 }
269
270 int main(int argc, char **argv)
271 {
272     Display *d;
273     Window id, userid = None;
274     int i;
275     char *dname = NULL;
276     gboolean root = FALSE;
277
278     for (i = 1; i < argc; ++i) {
279         if (!strcmp(argv[i], "--help")) {
280             return fail(NULL);
281         }
282         else if (!strcmp(argv[i], "--root"))
283             root = TRUE;
284         else if (!strcmp(argv[i], "--id")) {
285             if (++i == argc)
286                 return fail(NULL);
287             if (argv[i][0] == '0' && argv[i][1] == 'x') {
288                 /* hex */
289                 userid = parse_hex(argv[i]+2);
290             }
291             else {
292                 /* decimal */
293                 userid = atoi(argv[i]);
294             }
295             if (!userid)
296                 return fail("Unable to parse argument to --id.");
297         }
298         else if (!strcmp(argv[i], "--display")) {
299             if (++i == argc)
300                 return fail(NULL);
301             dname = argv[i];
302         }
303         else if (*argv[i] != '-')
304             break;
305         else if (!strcmp(argv[i], "--")) {
306             i++;
307             break;
308         }
309         else
310             return fail(NULL);
311     }
312
313     d = XOpenDisplay(dname);
314     if (!d) {
315         return fail("Unable to find an X display. "
316                     "Ensure you have permission to connect to the display.");
317     }
318
319     if (root)
320         userid = RootWindow(d, DefaultScreen(d));
321
322     if (userid == None) {
323         int j;
324         j = XGrabPointer(d, RootWindow(d, DefaultScreen(d)),
325                          False, ButtonPressMask,
326                          GrabModeAsync, GrabModeAsync,
327                          None, XCreateFontCursor(d, XC_crosshair),
328                          CurrentTime);
329         if (j != GrabSuccess)
330             return fail("Unable to grab the pointer device");
331         while (1) {
332             XEvent ev;
333
334             XNextEvent(d, &ev);
335             if (ev.type == ButtonPress) {
336                 XUngrabPointer(d, CurrentTime);
337                 userid = ev.xbutton.subwindow;
338                 break;
339             }
340         }
341         id = find_client(d, userid);
342     }
343     else
344         id = userid; /* they picked this one */
345
346     if (id == None)
347         return fail("Unable to find window with the requested ID");
348
349     show_properties(d, id, argc - i, &argv[i]);
350     
351     XCloseDisplay(d);
352
353     return 0;
354 }