diff -Nurp ../i3-4.12/include/atoms_rest.xmacro ./include/atoms_rest.xmacro --- ../i3-4.12/include/atoms_rest.xmacro 2016-03-06 16:17:18.000000000 +0100 +++ ./include/atoms_rest.xmacro 2016-10-11 23:56:12.406428013 +0200 @@ -1,6 +1,7 @@ xmacro(_NET_WM_USER_TIME) xmacro(_NET_STARTUP_ID) xmacro(_NET_WORKAREA) +xmacro(_NET_WM_ICON) xmacro(WM_PROTOCOLS) xmacro(WM_DELETE_WINDOW) xmacro(UTF8_STRING) diff -Nurp ../i3-4.12/include/data.h ./include/data.h --- ../i3-4.12/include/data.h 2016-03-06 16:17:18.000000000 +0100 +++ ./include/data.h 2016-10-11 23:56:12.407428013 +0200 @@ -426,6 +426,9 @@ struct Window { /* aspect ratio from WM_NORMAL_HINTS (MPlayer uses this for example) */ double aspect_ratio; + + /** Window icon, array of size 16x16 containing the ARGB pixels */ + uint32_t* icon; }; /** diff -Nurp ../i3-4.12/include/libi3.h ./include/libi3.h --- ../i3-4.12/include/libi3.h 2016-03-06 16:17:18.000000000 +0100 +++ ./include/libi3.h 2016-10-11 23:56:12.407428013 +0200 @@ -587,6 +587,11 @@ color_t draw_util_hex_to_color(const cha void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width); /** + * Draw the given image using libi3. + */ +void draw_util_image(unsigned char *pixels, surface_t *surface, int x, int y, int width, int height); + +/** * Draws a filled rectangle. * This function is a convenience wrapper and takes care of flushing the * surface as well as restoring the cairo state. diff -Nurp ../i3-4.12/include/window.h ./include/window.h --- ../i3-4.12/include/window.h 2016-03-06 16:17:18.000000000 +0100 +++ ./include/window.h 2016-10-11 23:56:12.408428013 +0200 @@ -87,3 +87,9 @@ void window_update_hints(i3Window *win, * */ void window_update_motif_hints(i3Window *win, xcb_get_property_reply_t *prop, border_style_t *motif_border_style); + +/** + * Updates the _NET_WM_ICON + * + */ +void window_update_icon(i3Window *win, xcb_get_property_reply_t *prop); diff -Nurp ../i3-4.12/libi3/draw_util.c ./libi3/draw_util.c --- ../i3-4.12/libi3/draw_util.c 2016-03-06 16:17:18.000000000 +0100 +++ ./libi3/draw_util.c 2016-10-11 23:56:12.408428013 +0200 @@ -153,6 +153,39 @@ void draw_util_text(i3String *text, surf #endif } + +/** + * Draw the given image using libi3. + * This function is a convenience wrapper and takes care of flushing the + * surface as well as restoring the cairo state. + * + */ +void draw_util_image(unsigned char *pixels, surface_t *surface, int x, int y, int width, int height) { + RETURN_UNLESS_SURFACE_INITIALIZED(surface); + +#if CAIRO_SUPPORT + cairo_save(surface->cr); + + cairo_surface_t *image; + + image = cairo_image_surface_create_for_data( + pixels, + CAIRO_FORMAT_ARGB32, + width, + height, + width * 4); + + cairo_set_operator(surface->cr, CAIRO_OPERATOR_OVER); + cairo_set_source_surface(surface->cr, image, x, y); + cairo_rectangle(surface->cr, x, y, width, height); + cairo_fill(surface->cr); + + cairo_surface_destroy(image); + + cairo_restore(surface->cr); +#endif +} + /** * Draws a filled rectangle. * This function is a convenience wrapper and takes care of flushing the diff -Nurp ../i3-4.12/src/handlers.c ./src/handlers.c --- ../i3-4.12/src/handlers.c 2016-03-06 16:17:18.000000000 +0100 +++ ./src/handlers.c 2016-10-11 23:56:12.409428013 +0200 @@ -1295,6 +1295,20 @@ static bool handle_strut_partial_change( return true; } +static bool handle_windowicon_change(void *data, xcb_connection_t *conn, uint8_t state, + xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *prop) { + Con *con; + if ((con = con_by_window_id(window)) == NULL || con->window == NULL) + return false; + + window_update_icon(con->window, prop); + + x_push_changes(croot); + + return true; +} + + /* Returns false if the event could not be processed (e.g. the window could not * be found), true otherwise */ typedef bool (*cb_property_handler_t)(void *data, xcb_connection_t *c, uint8_t state, xcb_window_t window, xcb_atom_t atom, xcb_get_property_reply_t *property); @@ -1315,7 +1329,8 @@ static struct property_handler_t propert {0, 128, handle_windowrole_change}, {0, 128, handle_class_change}, {0, UINT_MAX, handle_strut_partial_change}, - {0, UINT_MAX, handle_window_type}}; + {0, UINT_MAX, handle_window_type}, + {0, UINT_MAX, handle_windowicon_change}}; #define NUM_HANDLERS (sizeof(property_handlers) / sizeof(struct property_handler_t)) /* @@ -1336,6 +1351,7 @@ void property_handlers_init(void) { property_handlers[7].atom = XCB_ATOM_WM_CLASS; property_handlers[8].atom = A__NET_WM_STRUT_PARTIAL; property_handlers[9].atom = A__NET_WM_WINDOW_TYPE; + property_handlers[10].atom = A__NET_WM_ICON; } static void property_notify(uint8_t state, xcb_window_t window, xcb_atom_t atom) { diff -Nurp ../i3-4.12/src/manage.c ./src/manage.c --- ../i3-4.12/src/manage.c 2016-03-06 16:17:18.000000000 +0100 +++ ./src/manage.c 2016-10-11 23:56:12.410428013 +0200 @@ -92,6 +92,8 @@ void manage_window(xcb_window_t window, role_cookie, startup_id_cookie, wm_hints_cookie, wm_normal_hints_cookie, motif_wm_hints_cookie, wm_user_time_cookie, wm_desktop_cookie; + xcb_get_property_cookie_t wm_icon_cookie; + geomc = xcb_get_geometry(conn, d); /* Check if the window is mapped (it could be not mapped when intializing and @@ -163,6 +165,7 @@ void manage_window(xcb_window_t window, motif_wm_hints_cookie = GET_PROPERTY(A__MOTIF_WM_HINTS, 5 * sizeof(uint64_t)); wm_user_time_cookie = GET_PROPERTY(A__NET_WM_USER_TIME, UINT32_MAX); wm_desktop_cookie = GET_PROPERTY(A__NET_WM_DESKTOP, UINT32_MAX); + wm_icon_cookie = GET_PROPERTY(A__NET_WM_ICON, UINT32_MAX); DLOG("Managing window 0x%08x\n", window); @@ -176,6 +179,7 @@ void manage_window(xcb_window_t window, window_update_class(cwindow, xcb_get_property_reply(conn, class_cookie, NULL), true); window_update_name_legacy(cwindow, xcb_get_property_reply(conn, title_cookie, NULL), true); window_update_name(cwindow, xcb_get_property_reply(conn, utf8_title_cookie, NULL), true); + window_update_icon(cwindow, xcb_get_property_reply(conn, wm_icon_cookie, NULL)); window_update_leader(cwindow, xcb_get_property_reply(conn, leader_cookie, NULL)); window_update_transient_for(cwindow, xcb_get_property_reply(conn, transient_cookie, NULL)); window_update_strut_partial(cwindow, xcb_get_property_reply(conn, strut_cookie, NULL)); @@ -184,6 +188,8 @@ void manage_window(xcb_window_t window, window_update_hints(cwindow, xcb_get_property_reply(conn, wm_hints_cookie, NULL), &urgency_hint); border_style_t motif_border_style = BS_NORMAL; window_update_motif_hints(cwindow, xcb_get_property_reply(conn, motif_wm_hints_cookie, NULL), &motif_border_style); + + xcb_size_hints_t wm_size_hints; if (!xcb_icccm_get_wm_size_hints_reply(conn, wm_normal_hints_cookie, &wm_size_hints, NULL)) memset(&wm_size_hints, '\0', sizeof(xcb_size_hints_t)); diff -Nurp ../i3-4.12/src/render.c ./src/render.c --- ../i3-4.12/src/render.c 2016-03-06 16:17:18.000000000 +0100 +++ ./src/render.c 2016-10-11 23:56:12.410428013 +0200 @@ -127,6 +127,10 @@ void render_con(Con *con, bool render_fu /* find the height for the decorations */ params.deco_height = render_deco_height(); + /* minimum decoration height to allow icon to fit + * not actuuly required, icon would be cropped otherwise */ + params.deco_height = (params.deco_height < 16) ? 16 : params.deco_height; + /* precalculate the sizes to be able to correct rounding errors */ params.sizes = precalculate_sizes(con, ¶ms); diff -Nurp ../i3-4.12/src/window.c ./src/window.c --- ../i3-4.12/src/window.c 2016-03-06 16:17:18.000000000 +0100 +++ ./src/window.c 2016-10-11 23:56:44.972426651 +0200 @@ -19,6 +19,7 @@ void window_free(i3Window *win) { FREE(win->class_class); FREE(win->class_instance); i3string_free(win->name); + FREE(win->icon); FREE(win->ran_assignments); FREE(win); } @@ -367,3 +368,88 @@ void window_update_motif_hints(i3Window #undef MWM_DECOR_BORDER #undef MWM_DECOR_TITLE } + +/* + * Copy and resize icon if needed + */ +void copy_icon_with_resize(uint32_t *dst, int width, int height, uint32_t* src, int s_width, int s_height) +{ + int i, j; + + if (width == s_width && height == s_height) { + /* easy case, same dimensions, just copy data */ + memcpy(dst, src, width*height*sizeof(uint32_t)); + return; + } + + uint32_t* row = src; + int xstep = s_width / width; + int ystep = s_height / height * s_width; + + for (i=0; i < height; ++i) { + uint32_t* ptr = row; + for(j=0; j < width; ++j) { + *dst++ = *ptr; + ptr+=xstep; + } + row += ystep; + } +} + + +void window_update_icon(i3Window *win, xcb_get_property_reply_t *prop) +{ + uint32_t *data = NULL; + uint64_t len = 0; + + if(!prop || prop->type != XCB_ATOM_CARDINAL || prop->format != 32) { + DLOG("_NET_WM_ICON is not set\n"); + FREE(prop); + return; + } + + uint32_t prop_value_len = xcb_get_property_value_length(prop); + uint32_t *prop_value = (uint32_t *) xcb_get_property_value(prop); + + /* Find the number of icons in the reply. */ + while (prop_value_len > (sizeof(uint32_t) * 2) && prop_value && + prop_value[0] && prop_value[1]) + { + /* Check that the property is as long as it should be (in bytes), + handling integer overflow. "+2" to handle the width and height + fields. */ + const uint64_t crt_len = prop_value[0] * (uint64_t) prop_value[1]; + const uint64_t expected_len = (crt_len + 2) * 4; + + if (expected_len > prop_value_len) { + break; + } + + if (len == 0 || (crt_len >= 16*16 && crt_len < len)) { + len = crt_len; + data = prop_value; + } + if (len == 16*16) { + break; // found 16 pixels icon + } + + /* Find pointer to next icon in the reply. */ + prop_value_len -= expected_len; + prop_value = (uint32_t *) (((uint8_t *) prop_value) + expected_len); + } + + if (!data) { + DLOG("Could not get _NET_WM_ICON\n"); + FREE(prop); + return; + } + + LOG("Got _NET_WM_ICON of size: (%d,%d)\n", data[0], data[1]); + win->name_x_changed = true; // trigger a redraw + + FREE(win->icon); + win->icon = malloc(16 * 16 * sizeof(uint32_t)); + copy_icon_with_resize(win->icon, 16, 16, data + 2, data[0], data[1]); + + FREE(prop); +} diff -Nurp ../i3-4.12/src/x.c ./src/x.c --- ../i3-4.12/src/x.c 2016-03-06 16:17:18.000000000 +0100 +++ ./src/x.c 2016-10-11 23:56:12.412428013 +0200 @@ -571,6 +571,10 @@ void x_draw_decoration(Con *con) { //DLOG("indent_level = %d, indent_mult = %d\n", indent_level, indent_mult); int indent_px = (indent_level * 5) * indent_mult; + if (win->icon) { + indent_px += 18; + } + int mark_width = 0; if (config.show_marks && !TAILQ_EMPTY(&(con->marks_head))) { char *formatted_mark = sstrdup(""); @@ -611,6 +615,22 @@ void x_draw_decoration(Con *con) { if (con->title_format != NULL) I3STRING_FREE(title); + /* Draw the icon */ + if (win->icon) { + uint16_t width = 16; + uint16_t height = 16; + + int icon_offset_y = (con->deco_rect.height - height) / 2; + + draw_util_image( + (unsigned char *)win->icon, + &(parent->frame_buffer), + con->deco_rect.x + indent_px - width, + con->deco_rect.y + icon_offset_y, + width, + height); + } + after_title: x_draw_decoration_after_title(con, p); copy_pixmaps: