From dd7acf73e9698717b82ab86ecec7c0d8bd76ff01 Mon Sep 17 00:00:00 2001 From: Michael Stapelberg Date: Tue, 13 Apr 2010 17:46:54 +0200 Subject: [PATCH] re-add support for legacy window titles (WM_NAME) --- include/data.h | 18 +++++++++--- include/handlers.h | 13 ++------- include/window.h | 1 + src/handlers.c | 68 ++++++---------------------------------------- src/manage.c | 1 + src/nc.c | 5 ++++ src/window.c | 64 +++++++++++++++++++++++++++++++++++++++---- src/x.c | 38 +++++++++++++++++--------- 8 files changed, 116 insertions(+), 92 deletions(-) diff --git a/include/data.h b/include/data.h index 154c0828..565dbaac 100644 --- a/include/data.h +++ b/include/data.h @@ -247,11 +247,21 @@ struct xoutput { struct Window { xcb_window_t id; - const char *class_class; - const char *class_instance; - const char *name_ucs2; - const char *name_utf8; + char *class_class; + char *class_instance; + + /** The name of the window as it will be passod to X11 (in UCS2 if the + * application supports _NET_WM_NAME, in COMPOUND_TEXT otherwise). */ + char *name_x; + + /** The name of the window as used in JSON (in UTF-8 if the application + * supports _NET_WM_NAME, in COMPOUND_TEXT otherwise) */ + char *name_json; + + /** The length of the name in glyphs (not bytes) */ int name_len; + + /** Whether the application used _NET_WM_NAME */ bool uses_net_wm_name; }; diff --git a/include/handlers.h b/include/handlers.h index ecfa6a53..f4aaf582 100644 --- a/include/handlers.h +++ b/include/handlers.h @@ -115,17 +115,9 @@ int handle_destroy_notify_event(void *data, xcb_connection_t *conn, int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop); -#if 0 /** - * We handle legacy window names (titles) which are in COMPOUND_TEXT - * encoding. However, we just pass them along, so when containing non-ASCII - * characters, those will be rendering incorrectly. In order to correctly - * render unicode window titles in i3, an application has to set _NET_WM_NAME, - * which is in UTF-8 encoding. - * - * On every update, a message is put out to the user, so he may improve the - * situation and update applications which display filenames in their title to - * correctly use _NET_WM_NAME and therefore support unicode. + * Handles legacy window name updates (WM_NAME), see also src/window.c, + * window_update_name_legacy(). * */ int handle_windowname_change_legacy(void *data, xcb_connection_t *conn, @@ -133,6 +125,7 @@ int handle_windowname_change_legacy(void *data, xcb_connection_t *conn, xcb_atom_t atom, xcb_get_property_reply_t *prop); +#if 0 /** * Store the window classes for jumping to them later. * diff --git a/include/window.h b/include/window.h index d2b3e9dc..a126a36c 100644 --- a/include/window.h +++ b/include/window.h @@ -3,5 +3,6 @@ void window_update_class(i3Window *win, xcb_get_property_reply_t *prop); void window_update_name(i3Window *win, xcb_get_property_reply_t *prop); +void window_update_name_legacy(i3Window *win, xcb_get_property_reply_t *prop); #endif diff --git a/src/handlers.c b/src/handlers.c index 2f89afe9..b5609540 100644 --- a/src/handlers.c +++ b/src/handlers.c @@ -582,74 +582,24 @@ int handle_windowname_change(void *data, xcb_connection_t *conn, uint8_t state, return 1; } -#if 0 + /* - * We handle legacy window names (titles) which are in COMPOUND_TEXT encoding. However, we - * just pass them along, so when containing non-ASCII characters, those will be rendering - * incorrectly. In order to correctly render unicode window titles in i3, an application - * has to set _NET_WM_NAME, which is in UTF-8 encoding. - * - * On every update, a message is put out to the user, so he may improve the situation and - * update applications which display filenames in their title to correctly use - * _NET_WM_NAME and therefore support unicode. + * Handles legacy window name updates (WM_NAME), see also src/window.c, + * window_update_name_legacy(). * */ int handle_windowname_change_legacy(void *data, xcb_connection_t *conn, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { - if (prop == NULL || xcb_get_property_value_length(prop) == 0) { - DLOG("prop == NULL\n"); - return 1; - } - Client *client = table_get(&by_child, window); - if (client == NULL) - return 1; - - /* Client capable of _NET_WM_NAME, ignore legacy name changes */ - if (client->uses_net_wm_name) - return 1; - - /* Save the old pointer to make the update atomic */ - char *new_name; - if (asprintf(&new_name, "%.*s", xcb_get_property_value_length(prop), (char*)xcb_get_property_value(prop)) == -1) { - perror("Could not get old name"); - DLOG("Could not get old name\n"); - return 1; - } - /* Convert it to UCS-2 here for not having to convert it later every time we want to pass it to X */ - LOG("WM_NAME changed to \"%s\"\n", new_name); - - /* Check if they are the same and don’t update if so. */ - if (client->name != NULL && - strlen(new_name) == strlen(client->name) && - strcmp(client->name, new_name) == 0) { - free(new_name); - return 1; - } - - LOG("Using legacy window title. Note that in order to get Unicode window titles in i3, " - "the application has to set _NET_WM_NAME which is in UTF-8 encoding.\n"); - - char *old_name = client->name; - client->name = new_name; - client->name_len = -1; - - if (old_name != NULL) - free(old_name); + Con *con; + if ((con = con_by_window_id(window)) == NULL || con->window == NULL) + return 1; - /* If the client is a dock window, we don’t need to render anything */ - if (client->dock) - return 1; + window_update_name_legacy(con->window, prop); - if (client->container != NULL && - (client->container->mode == MODE_STACK || - client->container->mode == MODE_TABBED)) - render_container(conn, client->container); - else decorate_window(conn, client, client->frame, client->titlegc, 0, 0); - xcb_flush(conn); + x_push_changes(croot); - return 1; + return 1; } -#endif /* * Updates the client’s WM_CLASS property diff --git a/src/manage.c b/src/manage.c index 7ffcd61e..49f10599 100644 --- a/src/manage.c +++ b/src/manage.c @@ -129,6 +129,7 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki /* update as much information as possible so far (some replies may be NULL) */ window_update_class(cwindow, xcb_get_property_reply(conn, class_cookie, NULL)); + window_update_name_legacy(cwindow, xcb_get_property_reply(conn, title_cookie, NULL)); window_update_name(cwindow, xcb_get_property_reply(conn, utf8_title_cookie, NULL)); Con *nc; diff --git a/src/nc.c b/src/nc.c index 7b2b126a..e1b8a409 100644 --- a/src/nc.c +++ b/src/nc.c @@ -347,8 +347,13 @@ int main(int argc, char *argv[]) { GET_ATOM(_NET_ACTIVE_WINDOW); GET_ATOM(_NET_WORKAREA); + /* Watch _NET_WM_NAME (title of the window encoded in UTF-8) */ xcb_property_set_handler(&prophs, atoms[_NET_WM_NAME], 128, handle_windowname_change, NULL); + /* Watch WM_NAME (title of the window encoded in COMPOUND_TEXT) */ + xcb_watch_wm_name(&prophs, 128, handle_windowname_change_legacy, NULL); + + keysyms = xcb_key_symbols_alloc(conn); xcb_get_numlock_mask(conn); diff --git a/src/window.c b/src/window.c index ac6f45a0..93812b3f 100644 --- a/src/window.c +++ b/src/window.c @@ -7,6 +7,11 @@ */ #include "all.h" +/* + * Updates the WM_CLASS (consisting of the class and instance) for the + * given window. + * + */ void window_update_class(i3Window *win, xcb_get_property_reply_t *prop) { if (prop == NULL || xcb_get_property_value_length(prop) == 0) { DLOG("empty property, not updating\n"); @@ -23,24 +28,71 @@ void window_update_class(i3Window *win, xcb_get_property_reply_t *prop) { win->class_instance = strdup(new_class); if ((strlen(new_class) + 1) < xcb_get_property_value_length(prop)) - win->class_class = strdup(new_class + strlen(new_class) + 1); + win->class_class = strdup(new_class + strlen(new_class) + 1); else win->class_class = NULL; LOG("WM_CLASS changed to %s (instance), %s (class)\n", win->class_instance, win->class_class); } +/* + * Updates the name by using _NET_WM_NAME (encoded in UTF-8) for the given + * window. Further updates using window_update_name_legacy will be ignored. + * + */ void window_update_name(i3Window *win, xcb_get_property_reply_t *prop) { if (prop == NULL || xcb_get_property_value_length(prop) == 0) { DLOG("_NET_WM_NAME not specified, not changing\n"); - return 1; + return; } /* Save the old pointer to make the update atomic */ - int new_len; - asprintf(&win->name_utf8, "%.*s", xcb_get_property_value_length(prop), (char*)xcb_get_property_value(prop)); + char *new_name; + if (asprintf(&new_name, "%.*s", xcb_get_property_value_length(prop), + (char*)xcb_get_property_value(prop)) == -1) { + perror("asprintf()"); + DLOG("Could not get window name\n"); + } /* Convert it to UCS-2 here for not having to convert it later every time we want to pass it to X */ - win->name_ucs2 = convert_utf8_to_ucs2(win->name_utf8, &win->name_len); - LOG("_NET_WM_NAME changed to \"%s\"\n", win->name_utf8); + FREE(win->name_x); + FREE(win->name_json); + win->name_json = new_name; + win->name_x = convert_utf8_to_ucs2(win->name_json, &win->name_len); + LOG("_NET_WM_NAME changed to \"%s\"\n", win->name_json); win->uses_net_wm_name = true; } + +/* + * Updates the name by using WM_NAME (encoded in COMPOUND_TEXT). We do not + * touch what the client sends us but pass it to xcb_image_text_8. To get + * proper unicode rendering, the application has to use _NET_WM_NAME (see + * window_update_name()). + * + */ +void window_update_name_legacy(i3Window *win, xcb_get_property_reply_t *prop) { + if (prop == NULL || xcb_get_property_value_length(prop) == 0) { + DLOG("prop == NULL\n"); + return; + } + + /* ignore update when the window is known to already have a UTF-8 name */ + if (win->uses_net_wm_name) + return; + + char *new_name; + if (asprintf(&new_name, "%.*s", xcb_get_property_value_length(prop), + (char*)xcb_get_property_value(prop)) == -1) { + perror("asprintf()"); + DLOG("Could not get legacy window name\n"); + return; + } + + LOG("Using legacy window title. Note that in order to get Unicode window " + "titles in i3, the application has to set _NET_WM_NAME (UTF-8)\n"); + + FREE(win->name_x); + FREE(win->name_json); + win->name_x = new_name; + win->name_json = strdup(new_name); + win->name_len = strlen(new_name); +} diff --git a/src/x.c b/src/x.c index ee0bd07d..347b8f76 100644 --- a/src/x.c +++ b/src/x.c @@ -153,28 +153,40 @@ void x_draw_decoration(Con *con) { xcb_rectangle_t drect = { con->deco_rect.x, con->deco_rect.y, con->deco_rect.width, con->deco_rect.height }; xcb_poly_fill_rectangle(conn, parent->frame, parent->gc, 1, &drect); - if (con->window == NULL) { + if (con->window == NULL) return; - } - if (con->window->name_ucs2 == NULL) { + i3Window *win = con->window; + + if (win->name_x == NULL) { LOG("not rendering decoration, not yet known\n"); return; } - LOG("should render text %s onto %p / %s\n", con->window->name_utf8, parent, parent->name); + LOG("should render text %s onto %p / %s\n", win->name_json, parent, parent->name); xcb_change_gc_single(conn, parent->gc, XCB_GC_FOREGROUND, get_colorpixel("#FFFFFF")); - xcb_image_text_16( - conn, - con->window->name_len, - parent->frame, - parent->gc, - con->deco_rect.x, - con->deco_rect.y + 14, - (xcb_char2b_t*)con->window->name_ucs2 - ); + if (win->uses_net_wm_name) + xcb_image_text_16( + conn, + win->name_len, + parent->frame, + parent->gc, + con->deco_rect.x, + con->deco_rect.y + 14, /* TODO: hardcoded */ + (xcb_char2b_t*)win->name_x + ); + else + xcb_image_text_8( + conn, + win->name_len, + parent->frame, + parent->gc, + con->deco_rect.x, + con->deco_rect.y + 14, /* TODO: hardcoded */ + win->name_x + ); } /* -- 2.39.2