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