diff options
author | Alan Coopersmith <alan.coopersmith@oracle.com> | 2010-06-29 17:56:07 -0700 |
---|---|---|
committer | Alan Coopersmith <alan.coopersmith@oracle.com> | 2010-06-29 20:52:09 -0700 |
commit | e807538b47902e622651f97a41cd2338cd0df883 (patch) | |
tree | f77f5285bf465472e7b4c7c0f013e277ecc99b6a | |
parent | cef5af0c464bfba0d2438cbe90f753f7e68c09ba (diff) |
Handle non-latin-1 window names
Uses _NET_WM_NAME to get UTF-8 encoding, iconv to convert to current locale
Warns that COMPOUND_TEXT WM_NAMEs aren't supported if _NET_WM_NAME isn't set
Adds local atom caching code to dsimple.c and uses it in all three *.c
Signed-off-by: Alan Coopersmith <alan.coopersmith@oracle.com>
-rw-r--r-- | clientwin.c | 33 | ||||
-rw-r--r-- | dsimple.c | 196 | ||||
-rw-r--r-- | dsimple.h | 10 | ||||
-rw-r--r-- | xwininfo.c | 165 |
4 files changed, 326 insertions, 78 deletions
diff --git a/clientwin.c b/clientwin.c index fe6bd18..2b1de04 100644 --- a/clientwin.c +++ b/clientwin.c @@ -26,9 +26,9 @@ #include <string.h> #include "clientwin.h" +#include "dsimple.h" static xcb_atom_t atom_wm_state = XCB_ATOM_NONE; -typedef enum { False = 0, True } Bool; /* * Check if window has given property @@ -139,9 +139,7 @@ Find_Client_In_Children(xcb_connection_t * dpy, xcb_window_t win) static xcb_window_t * Find_Roots(xcb_connection_t * dpy, xcb_window_t root, unsigned int *num) { - xcb_atom_t atom = XCB_ATOM_NONE; - xcb_intern_atom_cookie_t atom_cookie; - xcb_intern_atom_reply_t *atom_reply; + xcb_atom_t atom_virtual_root; xcb_get_property_cookie_t prop_cookie; xcb_get_property_reply_t *prop_reply; @@ -150,18 +148,12 @@ Find_Roots(xcb_connection_t * dpy, xcb_window_t root, unsigned int *num) *num = 0; - atom_cookie = xcb_intern_atom (dpy, False, strlen("_NET_VIRTUAL_ROOTS"), - "_NET_VIRTUAL_ROOTS"); - atom_reply = xcb_intern_atom_reply (dpy, atom_cookie, NULL); - if (atom_reply) { - atom = atom_reply->atom; - free (atom_reply); - } - if (!atom) + atom_virtual_root = Get_Atom (dpy, "_NET_VIRTUAL_ROOTS"); + if (atom_virtual_root == XCB_ATOM_NONE) return NULL; - prop_cookie = xcb_get_property (dpy, False, root, atom, XCB_ATOM_WINDOW, - 0, 0x7fffffff); + prop_cookie = xcb_get_property (dpy, False, root, atom_virtual_root, + XCB_ATOM_WINDOW, 0, 0x7fffffff); prop_reply = xcb_get_property_reply (dpy, prop_cookie, NULL); if (!prop_reply) return NULL; @@ -235,17 +227,8 @@ Find_Client(xcb_connection_t * dpy, xcb_window_t root, xcb_window_t subwin) free (roots); if (atom_wm_state == XCB_ATOM_NONE) { - xcb_intern_atom_cookie_t atom_cookie; - xcb_intern_atom_reply_t *atom_reply; - - atom_cookie = xcb_intern_atom (dpy, False, - strlen("WM_STATE"), "WM_STATE"); - atom_reply = xcb_intern_atom_reply (dpy, atom_cookie, NULL); - if (atom_reply) { - atom_wm_state = atom_reply->atom; - free (atom_reply); - } - if (!atom_wm_state) + atom_wm_state = Get_Atom(dpy, "WM_STATE"); + if (atom_wm_state == XCB_ATOM_NONE) return subwin; } @@ -230,6 +230,7 @@ xcb_window_t Select_Window(xcb_connection_t *dpy, */ struct wininfo_cookies { + xcb_get_property_cookie_t get_net_wm_name; xcb_get_property_cookie_t get_wm_name; xcb_query_tree_cookie_t query_tree; }; @@ -240,6 +241,13 @@ struct wininfo_cookies { XCB_GET_PROPERTY_TYPE_ANY, 0, BUFSIZ) #endif +static xcb_atom_t atom_net_wm_name, atom_utf8_string; + +# define xcb_get_net_wm_name(Dpy, Win) \ + xcb_get_property (Dpy, False, Win, atom_net_wm_name, \ + atom_utf8_string, 0, BUFSIZ) + + static xcb_window_t recursive_Window_With_Name ( xcb_connection_t *dpy, @@ -254,43 +262,71 @@ recursive_Window_With_Name ( xcb_generic_error_t *err; xcb_query_tree_reply_t *tree; struct wininfo_cookies *child_cookies; + xcb_get_property_reply_t *prop; -#ifdef USE_XCB_ICCCM - xcb_get_text_property_reply_t prop; + if (cookies->get_net_wm_name.sequence) { + prop = xcb_get_property_reply (dpy, cookies->get_net_wm_name, &err); - if (xcb_get_wm_name_reply (dpy, cookies->get_wm_name, &prop, &err)) { - /* can't use strcmp, since prop.name is not null terminated */ - if (strncmp (prop.name, name, prop.name_len) == 0) { - w = window; + if (prop) { + if (prop->type == atom_utf8_string) { + const char *prop_name = xcb_get_property_value (prop); + int prop_name_len = xcb_get_property_value_length (prop); + + /* can't use strcmp, since prop.name is not null terminated */ + if (strncmp (prop_name, name, prop_name_len) == 0) { + w = window; + } + } + free (prop); + } else if (err) { + if (err->response_type == 0) + Print_X_Error (dpy, err); + return 0; } + } - xcb_get_text_property_reply_wipe (&prop); + if (w) { + xcb_discard_reply (dpy, cookies->get_wm_name.sequence); + } else { +#ifdef USE_XCB_ICCCM + xcb_get_text_property_reply_t nameprop; + + if (xcb_get_wm_name_reply (dpy, cookies->get_wm_name, + &nameprop, &err)) { + /* can't use strcmp, since nameprop.name is not null terminated */ + if (strncmp (nameprop.name, name, nameprop.name_len) == 0) { + w = window; + } + + xcb_get_text_property_reply_wipe (&nameprop); + } #else - xcb_get_property_reply_t *prop - = xcb_get_property_reply (dpy, cookies->get_wm_name, &err); + prop = xcb_get_property_reply (dpy, cookies->get_wm_name, &err); - if (prop) { - if (prop->type == XCB_ATOM_STRING) { - const char *prop_name = xcb_get_property_value (prop); - int prop_name_len = xcb_get_property_value_length (prop); + if (prop) { + if (prop->type == XCB_ATOM_STRING) { + const char *prop_name = xcb_get_property_value (prop); + int prop_name_len = xcb_get_property_value_length (prop); - /* can't use strcmp, since prop.name is not null terminated */ - if (strncmp (prop_name, name, prop_name_len) == 0) { - w = window; + /* can't use strcmp, since prop.name is not null terminated */ + if (strncmp (prop_name, name, prop_name_len) == 0) { + w = window; + } } + free (prop); } - free (prop); #endif - - if (w) - { - xcb_discard_reply (dpy, cookies->query_tree.sequence); - return w; + else if (err) { + if (err->response_type == 0) + Print_X_Error (dpy, err); + return 0; } - } else if (err) { - if (err->response_type == 0) - Print_X_Error (dpy, err); - return 0; + } + + if (w) + { + xcb_discard_reply (dpy, cookies->query_tree.sequence); + return w; } tree = xcb_query_tree_reply (dpy, cookies->query_tree, &err); @@ -308,6 +344,9 @@ recursive_Window_With_Name ( Fatal_Error("Failed to allocate memory in recursive_Window_With_Name"); for (i = 0; i < nchildren; i++) { + if (atom_net_wm_name && atom_utf8_string) + child_cookies[i].get_net_wm_name = + xcb_get_net_wm_name (dpy, children[i]); child_cookies[i].get_wm_name = xcb_get_wm_name (dpy, children[i]); child_cookies[i].query_tree = xcb_query_tree (dpy, children[i]); } @@ -324,6 +363,9 @@ recursive_Window_With_Name ( { /* clean up remaining replies */ for (/* keep previous i */; i < nchildren; i++) { + if (child_cookies[i].get_net_wm_name.sequence) + xcb_discard_reply (dpy, + child_cookies[i].get_net_wm_name.sequence); xcb_discard_reply (dpy, child_cookies[i].get_wm_name.sequence); xcb_discard_reply (dpy, child_cookies[i].query_tree.sequence); } @@ -342,8 +384,14 @@ Window_With_Name ( { struct wininfo_cookies cookies; + atom_net_wm_name = Get_Atom (dpy, "_NET_WM_NAME"); + atom_utf8_string = Get_Atom (dpy, "UTF8_STRING"); + + if (atom_net_wm_name && atom_utf8_string) + cookies.get_net_wm_name = xcb_get_net_wm_name (dpy, top); cookies.get_wm_name = xcb_get_wm_name (dpy, top); cookies.query_tree = xcb_query_tree (dpy, top); + xcb_flush (dpy); return recursive_Window_With_Name(dpy, top, &cookies, name); } @@ -485,3 +533,99 @@ Print_X_Error ( fprintf (stderr, " Request serial number: %d\n", err->full_sequence); } + +/* + * Cache for atom lookups in either direction + */ +struct atom_cache_entry { + xcb_atom_t atom; + const char *name; + xcb_intern_atom_cookie_t intern_atom; + struct atom_cache_entry *next; +}; + +static struct atom_cache_entry *atom_cache; + +/* + * Send a request to the server for an atom by name + * Does not create the atom if it is not already present + */ +struct atom_cache_entry *Intern_Atom (xcb_connection_t * dpy, const char *name) +{ + struct atom_cache_entry *a; + + for (a = atom_cache ; a != NULL ; a = a->next) { + if (strcmp (a->name, name) == 0) + return a; /* already requested or found */ + } + + a = calloc(1, sizeof(struct atom_cache_entry)); + if (a != NULL) { + a->name = name; + a->intern_atom = xcb_intern_atom (dpy, False, strlen (name), (name)); + a->next = atom_cache; + atom_cache = a; + } + return a; +} + +/* Get an atom by name when it is needed. */ +xcb_atom_t Get_Atom (xcb_connection_t * dpy, const char *name) +{ + struct atom_cache_entry *a = Intern_Atom (dpy, name); + + if (a == NULL) + return XCB_ATOM_NONE; + + if (a->atom == XCB_ATOM_NONE) { + xcb_intern_atom_reply_t *reply; + + reply = xcb_intern_atom_reply(dpy, a->intern_atom, NULL); + if (reply) { + a->atom = reply->atom; + free (reply); + } else { + a->atom = -1; + } + } + if (a->atom == -1) /* internal error */ + return XCB_ATOM_NONE; + + return a->atom; +} + +/* Get the name for an atom when it is needed. */ +const char *Get_Atom_Name (xcb_connection_t * dpy, xcb_atom_t atom) +{ + struct atom_cache_entry *a; + + for (a = atom_cache ; a != NULL ; a = a->next) { + if (a->atom == atom) + return a->name; /* already requested or found */ + } + + a = calloc(1, sizeof(struct atom_cache_entry)); + if (a != NULL) { + xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name (dpy, atom); + xcb_get_atom_name_reply_t *reply + = xcb_get_atom_name_reply (dpy, cookie, NULL); + + a->atom = atom; + if (reply) { + int len = xcb_get_atom_name_name_length (reply); + char *name = malloc(len + 1); + if (name) { + memcpy (name, xcb_get_atom_name_name (reply), len); + name[len] = '\0'; + a->name = name; + } + free (reply); + } + + a->next = atom_cache; + atom_cache = a; + + return a->name; + } + return NULL; +} @@ -51,9 +51,13 @@ const char *Get_Display_Name (const char *displayname); void Setup_Display_And_Screen (const char *displayname, xcb_connection_t **dpy, xcb_screen_t **screen); -xcb_window_t Select_Window(xcb_connection_t *, const xcb_screen_t *, int); -xcb_window_t Window_With_Name(xcb_connection_t *, xcb_window_t, const char *); +xcb_window_t Select_Window (xcb_connection_t *, const xcb_screen_t *, int); +xcb_window_t Window_With_Name (xcb_connection_t *, xcb_window_t, const char *); -void Fatal_Error(char *, ...) _X_NORETURN _X_ATTRIBUTE_PRINTF(1, 2); +void Fatal_Error (char *, ...) _X_NORETURN _X_ATTRIBUTE_PRINTF(1, 2); void Print_X_Error (xcb_connection_t *, xcb_generic_error_t *); + +struct atom_cache_entry *Intern_Atom (xcb_connection_t *, const char *); +xcb_atom_t Get_Atom (xcb_connection_t *, const char *); +const char *Get_Atom_Name (xcb_connection_t *, xcb_atom_t); @@ -76,6 +76,9 @@ of the copyright holder. #include <stdlib.h> #include <string.h> #include <locale.h> +#include <langinfo.h> +#include <iconv.h> +#include <errno.h> /* Include routines to handle parsing defaults */ #include "dsimple.h" @@ -178,12 +181,18 @@ enum { xcb_get_wm_size_hints(Dpy, Win, XCB_ATOM_WM_NORMAL_HINTS) #endif +/* Possibly in xcb-emwh in the future? */ +static xcb_atom_t atom_net_wm_name, atom_utf8_string; +static xcb_get_property_cookie_t get_net_wm_name (xcb_connection_t *, + xcb_window_t); + /* Information we keep track of for each window to allow prefetching/reusing */ struct wininfo { xcb_window_t window; /* cookies for requests we've sent */ xcb_get_geometry_cookie_t geometry_cookie; + xcb_get_property_cookie_t net_wm_name_cookie; xcb_get_property_cookie_t wm_name_cookie; xcb_get_property_cookie_t wm_class_cookie; xcb_translate_coordinates_cookie_t trans_coords_cookie; @@ -222,6 +231,10 @@ static void wininfo_wipe (struct wininfo *); static const char *window_id_format = "0x%lx"; +static const char *user_encoding; +static iconv_t iconv_from_utf8; +static void print_utf8 (const char *, char *, size_t, const char *); + static xcb_connection_t *dpy; static xcb_screen_t *screen; static xcb_generic_error_t *err; @@ -407,6 +420,7 @@ main (int argc, char **argv) if (!setlocale (LC_ALL, "")) fprintf (stderr, "%s: can not set locale properly\n", program_name); + user_encoding = nl_langinfo (CODESET); memset (w, 0, sizeof(struct wininfo)); @@ -493,6 +507,10 @@ main (int argc, char **argv) Setup_Display_And_Screen (display_name, &dpy, &screen); + /* preload atoms we may need later */ + Intern_Atom (dpy, "_NET_WM_NAME"); + Intern_Atom (dpy, "UTF8_STRING"); + /* initialize scaling data */ scale_init(screen); @@ -510,6 +528,8 @@ main (int argc, char **argv) "xwininfo: Please select the window about which you\n" " would like information by clicking the\n" " mouse in that window.\n"); + Intern_Atom (dpy, "_NET_VIRTUAL_ROOTS"); + Intern_Atom (dpy, "WM_STATE"); window = Select_Window (dpy, screen, !frame); } @@ -541,6 +561,7 @@ main (int argc, char **argv) /* Send requests to prefetch data we'll need */ w->window = window; + w->net_wm_name_cookie = get_net_wm_name (dpy, window); w->wm_name_cookie = xcb_get_wm_name (dpy, window); if (children || tree) w->tree_cookie = xcb_query_tree (dpy, window); @@ -581,6 +602,9 @@ main (int argc, char **argv) wininfo_wipe (w); xcb_disconnect (dpy); + if (iconv_from_utf8 && (iconv_from_utf8 != (iconv_t) -1)) { + iconv_close (iconv_from_utf8); + } exit (0); } @@ -694,13 +718,13 @@ static void Display_Window_Id (struct wininfo *w, Bool newline_wanted) { #ifdef USE_XCB_ICCCM - xcb_get_text_property_reply_t prop; -#else - xcb_get_property_reply_t *prop; + xcb_get_text_property_reply_t wmn_reply; #endif - uint8_t got_reply; - const char *wm_name; - int wm_name_len; + xcb_get_property_reply_t *prop; + uint8_t got_reply = False; + const char *wm_name = NULL; + unsigned int wm_name_len = 0; + xcb_atom_t wm_name_encoding = XCB_NONE; printf (window_id_format, w->window); /* print id # in hex/dec */ @@ -711,34 +735,54 @@ Display_Window_Id (struct wininfo *w, Bool newline_wanted) printf (" (the root window)"); } /* Get window name if any */ -#ifdef USE_XCB_ICCCM - got_reply = xcb_get_wm_name_reply (dpy, w->wm_name_cookie, - &prop, NULL); - if (got_reply) { - wm_name = prop.name; - wm_name_len = prop.name_len; - } -#else - prop = xcb_get_property_reply (dpy, w->wm_name_cookie, NULL); - if (prop && (prop->type == XCB_ATOM_STRING)) { + prop = xcb_get_property_reply (dpy, w->net_wm_name_cookie, NULL); + if (prop && (prop->type != XCB_NONE)) { wm_name = xcb_get_property_value (prop); - wm_name_len = xcb_get_property_value_length (prop); + wm_name_len = xcb_get_property_value_length (prop); + wm_name_encoding = prop->type; got_reply = True; - } else { - got_reply = False; } + + if (!got_reply) { /* No _NET_WM_NAME, check WM_NAME */ +#ifdef USE_XCB_ICCCM + got_reply = xcb_get_wm_name_reply (dpy, w->wm_name_cookie, + &wmn_reply, NULL); + if (got_reply) { + wm_name = wmn_reply.name; + wm_name_len = wmn_reply.name_len; + wm_name_encoding = wmn_reply.encoding; + } +#else + prop = xcb_get_property_reply (dpy, w->wm_name_cookie, NULL); + if (prop && (prop->type != XCB_NONE)) { + wm_name = xcb_get_property_value (prop); + wm_name_len = xcb_get_property_value_length (prop); + wm_name_encoding = prop->type; + got_reply = True; + } #endif + } if (!got_reply || wm_name_len == 0) { printf (" (has no name)"); } else { - printf (" \""); - /* XXX: need to handle encoding */ - printf ("%.*s", wm_name_len, wm_name); - printf ("\""); + if (wm_name_encoding == XCB_ATOM_STRING) { + printf (" \"%.*s\"", wm_name_len, wm_name); + } else if (wm_name_encoding == atom_utf8_string) { + print_utf8 (" \"", (char *) wm_name, wm_name_len, "\""); + } else { + /* Encodings we don't support, including COMPOUND_TEXT */ + const char *enc_name = Get_Atom_Name (dpy, wm_name_encoding); + if (enc_name) { + printf (" (name in unsupported encoding %s)", enc_name); + } else { + printf (" (name in unsupported encoding ATOM 0x%x)", + wm_name_encoding); + } + } } #ifdef USE_XCB_ICCCM if (got_reply) - xcb_get_text_property_reply_wipe (&prop); + xcb_get_text_property_reply_wipe (&wmn_reply); #else free (prop); #endif @@ -1166,8 +1210,10 @@ display_tree_info_1 (struct wininfo *w, int recurse, int level) if (level == 0) { struct wininfo rw, pw; rw.window = tree->root; + rw.net_wm_name_cookie = get_net_wm_name (dpy, rw.window); rw.wm_name_cookie = xcb_get_wm_name (dpy, rw.window); pw.window = tree->parent; + pw.net_wm_name_cookie = get_net_wm_name (dpy, pw.window); pw.wm_name_cookie = xcb_get_wm_name (dpy, pw.window); xcb_flush (dpy); @@ -1199,6 +1245,7 @@ display_tree_info_1 (struct wininfo *w, int recurse, int level) struct wininfo *cw = &children[i]; cw->window = child_list[i]; + cw->net_wm_name_cookie = get_net_wm_name (dpy, child_list[i]); cw->wm_name_cookie = xcb_get_wm_name (dpy, child_list[i]); cw->wm_class_cookie = xcb_get_wm_class (dpy, child_list[i]); cw->geometry_cookie = xcb_get_geometry (dpy, child_list[i]); @@ -1536,6 +1583,7 @@ Display_WM_Info (struct wininfo *w) if (flags & XCB_WM_HINT_ICON_WINDOW) { struct wininfo iw; iw.window = wmhints.icon_window; + iw.net_wm_name_cookie = get_net_wm_name (dpy, iw.window); iw.wm_name_cookie = xcb_get_wm_name (dpy, iw.window); printf (" Icon window id: "); @@ -1559,3 +1607,72 @@ wininfo_wipe (struct wininfo *w) free (w->win_attributes); free (w->normal_hints); } + +/* Gets UTF-8 encoded EMWH property _NET_WM_NAME for a window */ +static xcb_get_property_cookie_t +get_net_wm_name (xcb_connection_t *dpy, xcb_window_t win) +{ + if (!atom_net_wm_name) + atom_net_wm_name = Get_Atom (dpy, "_NET_WM_NAME"); + + if (!atom_utf8_string) + atom_utf8_string = Get_Atom (dpy, "UTF8_STRING"); + + if (atom_net_wm_name && atom_utf8_string) + return xcb_get_property (dpy, False, win, atom_net_wm_name, + atom_utf8_string, 0, BUFSIZ); + else { + xcb_get_property_cookie_t dummy = { 0 }; + return dummy; + } +} + +/* + * Converts a UTF-8 encoded string to the current locale encoding, + * if possible, and prints it, with prefix before and suffix after. + * Length of the string is specified in bytes, or -1 for going until '\0' + */ +static void +print_utf8 (const char *prefix, char *u8str, size_t length, const char *suffix) +{ + char convbuf[BUFSIZ]; + char *inp = u8str; + size_t inlen = length; + int convres; + + if (inlen < 0) { + inlen = strlen (inp); + } + + if (!iconv_from_utf8) { + iconv_from_utf8 = iconv_open (user_encoding, "UTF-8"); + } + + if (iconv_from_utf8 != (iconv_t) -1) { + Bool done = True; + + printf ("%s", prefix); + do { + char *outp = convbuf; + size_t outlen = sizeof(convbuf); + + convres = iconv (iconv_from_utf8, &inp, &inlen, &outp, &outlen); + + if ((convres == -1) && (errno == E2BIG)) { + done = False; + convres = 0; + } + + if (convres == 0) { + fwrite (convbuf, 1, sizeof(convbuf) - outlen, stdout); + } else { + printf (" (failure in conversion from UTF8_STRING to %s)", + user_encoding); + } + } while (!done); + printf ("%s", suffix); + } else { + printf (" (can't load iconv conversion for UTF8_STRING to %s)", + user_encoding); + } +} |