Add an obprop tool which can print UTF-8 strings properly, for our users.
authorDana Jansens <danakj@orodu.net>
Mon, 14 Dec 2009 20:07:20 +0000 (15:07 -0500)
committerDana Jansens <danakj@orodu.net>
Mon, 14 Dec 2009 20:25:31 +0000 (15:25 -0500)
.gitignore
Makefile.am
tools/obprop/Makefile [new file with mode: 0644]
tools/obprop/obprop.c [new file with mode: 0644]

index 3804330..3ea0363 100644 (file)
@@ -51,6 +51,7 @@ po/remove-potcdate.sed
 render/obrender-3.0.pc
 tools/gnome-panel-control/gnome-panel-control
 tools/gdm-control/gdm-control
+tools/obprop/obprop
 version.h
 .libs
 .deps
index 660b5ef..4b3e0b5 100644 (file)
@@ -32,7 +32,8 @@ lib_LTLIBRARIES = \
 bin_PROGRAMS = \
        openbox/openbox \
        tools/gdm-control/gdm-control \
-       tools/gnome-panel-control/gnome-panel-control
+       tools/gnome-panel-control/gnome-panel-control \
+       tools/obprop/obprop
 
 dist_secretbin_SCRIPTS = \
        tools/xdg-autostart/xdg-autostart
@@ -288,6 +289,17 @@ tools_gnome_panel_control_gnome_panel_control_LDADD = \
 tools_gnome_panel_control_gnome_panel_control_SOURCES = \
        tools/gnome-panel-control/gnome-panel-control.c
 
+## obprop ##
+
+tools_obprop_obprop_CPPFLAGS = \
+       $(GLIB_CFLAGS) \
+       $(X_CFLAGS)
+tools_obprop_obprop_LDADD = \
+       $(GLIB_LIBS) \
+       $(X_LIBS)
+tools_obprop_obprop_SOURCES = \
+       tools/obprop/obprop.c
+
 ## gdm-control ##
 
 tools_gdm_control_gdm_control_CPPFLAGS = \
diff --git a/tools/obprop/Makefile b/tools/obprop/Makefile
new file mode 100644 (file)
index 0000000..cfc4653
--- /dev/null
@@ -0,0 +1,4 @@
+all clean install:
+       $(MAKE) -C ../.. -$(MAKEFLAGS) $@
+
+.PHONY: all clean install
diff --git a/tools/obprop/obprop.c b/tools/obprop/obprop.c
new file mode 100644 (file)
index 0000000..e288d7c
--- /dev/null
@@ -0,0 +1,320 @@
+#include <X11/Xlib.h>
+#include <X11/cursorfont.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include <glib.h>
+
+gint fail(const gchar *s) {
+    if (s)
+        fprintf(stderr, "%s\n", s);
+    else
+        fprintf
+            (stderr,
+             "Usage: obprop [OPTIONS]\n\n"
+             "Options:\n"
+             "    --help              Display this help and exit\n"
+             "    --display DISPLAY   Connect to this X display\n"
+             "    --id ID             Show the properties for this window\n");
+    return 1;
+}
+
+gint parse_hex(gchar *s) {
+    gint result = 0;
+    while (*s) {
+        gint add;
+        if (*s >= '0' && *s <='9')
+            add = *s-'0';
+        else if (*s >= 'A' && *s <='F')
+            add = *s-'A';
+        else if (*s >= 'a' && *s <='f')
+            add = *s-'a';
+        else
+            break;
+
+        result *= 16;
+        result += add;
+    }
+    return result;
+}
+
+Window find_client(Display *d, Window win)
+{
+    Window r, *children;
+    guint n, i;
+    Atom state = XInternAtom(d, "WM_STATE", True);
+    Atom ret_type;
+    gint ret_format, res;
+    gulong ret_items, ret_bytesleft, *xdata;
+
+    XQueryTree(d, win, &r, &r, &children, &n);
+    for (i = 0; i < n; ++i) {
+        Window w = find_client(d, children[i]);
+        if (w) return w;
+    }
+
+    // try me
+    res = XGetWindowProperty(d, win, state, 0, 1,
+                       False, state, &ret_type, &ret_format,
+                       &ret_items, &ret_bytesleft,
+                       (unsigned char**) &xdata);
+    XFree(xdata);
+    if (res != Success || ret_type == None || ret_items < 1)
+        return None;
+    return win; // found it!
+}
+
+static gboolean get_all(Display *d, Window win, Atom prop,
+                        Atom *type, gint *size,
+                        guchar **data, guint *num)
+{
+    gboolean ret = FALSE;
+    gint res;
+    guchar *xdata = NULL;
+    gulong ret_items, bytes_left;
+
+    res = XGetWindowProperty(d, win, prop, 0l, G_MAXLONG,
+                             FALSE, AnyPropertyType, type, size,
+                             &ret_items, &bytes_left, &xdata);
+    if (res == Success) {
+        if (ret_items > 0) {
+            guint i;
+
+            *data = g_malloc(ret_items * (*size / 8));
+            for (i = 0; i < ret_items; ++i)
+                switch (*size) {
+                case 8:
+                    (*data)[i] = xdata[i];
+                    break;
+                case 16:
+                    ((guint16*)*data)[i] = ((gushort*)xdata)[i];
+                    break;
+                case 32:
+                    ((guint32*)*data)[i] = ((gulong*)xdata)[i];
+                    break;
+                default:
+                    g_assert_not_reached(); /* unhandled size */
+                }
+            *num = ret_items;
+            ret = TRUE;
+        }
+        XFree(xdata);
+    }
+    return ret;
+}
+
+gchar *append_string(gchar *before, gchar *after, gboolean quote)
+{
+    gchar *tmp;
+    const gchar *q = quote ? "\"" : "";
+    if (before)
+        tmp = g_strdup_printf("%s, %s%s%s", before, q, after, q);
+    else
+        tmp = g_strdup_printf("%s%s%s", q, after, q);
+    g_free(before);
+    return tmp;
+}
+
+gchar *append_int(gchar *before, guint after)
+{
+    gchar *tmp;
+    if (before)
+        tmp = g_strdup_printf("%s, %u", before, after);
+    else
+        tmp = g_strdup_printf("%u", after);
+    g_free(before);
+    return tmp;
+}
+
+gchar* read_strings(gchar *val, guint n, gboolean utf8)
+{
+    GSList *strs = NULL, *it;
+    gchar *ret, *p;
+    guint i;
+
+    p = val;
+    while (p < val + n) {
+        strs = g_slist_append(strs, g_strndup(p, n - (p - val)));
+        p += strlen(p) + 1; /* next string */
+    }
+
+    ret = NULL;
+    for (i = 0, it = strs; it; ++i, it = g_slist_next(it)) {
+        char *data;
+
+        if (utf8) {
+            if (g_utf8_validate(it->data, -1, NULL))
+                data = g_strdup(it->data);
+            else
+                data = g_strdup("");
+        }
+        else
+            data = g_locale_to_utf8(it->data, -1, NULL, NULL, NULL);
+
+        ret = append_string(ret, data, TRUE);
+        g_free(data);
+    }
+
+    while (strs) {
+        g_free(strs->data);
+        strs = g_slist_delete_link(strs, strs);
+    }
+    return ret;
+}
+
+gchar* read_atoms(Display *d, guchar *val, guint n)
+{
+    gchar *ret;
+    guint i;
+
+    ret = NULL;
+    for (i = 0; i < n; ++i)
+        ret = append_string(ret, XGetAtomName(d, ((guint32*)val)[i]), FALSE);
+    return ret;
+}
+
+gchar* read_numbers(guchar *val, guint n, guint size)
+{
+    gchar *ret;
+    guint i;
+
+    ret = NULL;
+    for (i = 0; i < n; ++i)
+        switch (size) {
+        case 8:
+            ret = append_int(ret, ((guint8*)val)[i]);
+            break;
+        case 16:
+            ret = append_int(ret, ((guint16*)val)[i]);
+            break;
+        case 32:
+            ret = append_int(ret, ((guint32*)val)[i]);
+            break;
+        default:
+            g_assert_not_reached(); /* unhandled size */
+        }
+
+    return ret;
+}
+
+gboolean read_prop(Display *d, Window w, Atom prop, const gchar **type, gchar **val)
+{
+    guchar *ret;
+    guint nret;
+    gint size;
+    Atom ret_type;
+
+    ret = NULL;
+    if (get_all(d, w, prop, &ret_type, &size, &ret, &nret)) {
+        *type = XGetAtomName(d, ret_type);
+
+        if (strcmp(*type, "STRING") == 0)
+            *val = read_strings((gchar*)ret, nret, FALSE);
+        else if (strcmp(*type, "UTF8_STRING") == 0)
+            *val = read_strings((gchar*)ret, nret, TRUE);
+        else if (strcmp(*type, "ATOM") == 0) {
+            g_assert(size == 32);
+            *val = read_atoms(d, ret, nret);
+        }
+       else
+            *val = read_numbers(ret, nret, size);
+
+        g_free(ret);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+void show_properties(Display *d, Window w)
+{
+    Atom* props;
+    int i, n;
+
+    props = XListProperties(d, w, &n);
+
+    for (i = 0; i < n; ++i) {
+        const char *type;
+        char *name, *val;
+
+        name = XGetAtomName(d, props[i]);
+
+        if (read_prop(d, w, props[i], &type, &val)) {
+            g_print("%s(%s) = %s\n", name, type, val);
+            g_free(val);
+        }
+
+        XFree(name);
+    }
+
+    XFree(props);
+}
+
+int main(int argc, char **argv)
+{
+    Display *d;
+    Window id, userid = None;
+    int i;
+    char *dname = NULL;
+
+    for (i = 1; i < argc; ++i) {
+        if (!strcmp(argv[i], "--help")) {
+            return fail(0);
+        }
+        else if (!strcmp(argv[i], "--id")) {
+            if (++i == argc)
+                return fail(0);
+            if (argv[i][0] == '0' && argv[i][1] == 'x') {
+                /* hex */
+                userid = parse_hex(argv[i]+2);
+            }
+            else {
+                /* decimal */
+                userid = atoi(argv[i]);
+            }
+            break;
+        }
+        else if (!strcmp(argv[i], "--display")) {
+            if (++i == argc)
+                return fail(0);
+            dname = argv[i];
+        }
+    }
+
+    d = XOpenDisplay(dname);
+    if (!d) {
+        return fail("Unable to find an X display. "
+                    "Ensure you have permission to connect to the display.");
+    }
+
+    if (userid == None) {
+        i = XGrabPointer(d, RootWindow(d, DefaultScreen(d)),
+                         False, ButtonPressMask,
+                         GrabModeAsync, GrabModeAsync,
+                         None, XCreateFontCursor(d, XC_crosshair),
+                         CurrentTime);
+        if (i != GrabSuccess)
+            return fail("Unable to grab the pointer device");
+        while (1) {
+            XEvent ev;
+
+            XNextEvent(d, &ev);
+            if (ev.type == ButtonPress) {
+                XUngrabPointer(d, CurrentTime);
+                userid = ev.xbutton.subwindow;
+                break;
+            }
+        }
+    }
+
+    id = find_client(d, userid);
+
+    if (id == None)
+        return fail("Unable to find window with the requested ID");
+
+    show_properties(d, id);
+    
+    XCloseDisplay(d);
+
+    return 0;
+}