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;
};
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,
xcb_atom_t atom, xcb_get_property_reply_t
*prop);
+#if 0
/**
* Store the window classes for jumping to them later.
*
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
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
/* 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;
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);
*/
#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");
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);
+}
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
+ );
}
/*