diff --git a/awesome.c b/awesome.c index d643378ef..aae5062d8 100644 --- a/awesome.c +++ b/awesome.c @@ -543,6 +543,9 @@ main(int argc, char **argv) /* We have no clue where the input focus is right now */ globalconf.focus.need_update = true; + /* set the default preferred icon size */ + globalconf.preferred_icon_size = 0; + /* XLib sucks */ XkbIgnoreExtension(True); diff --git a/ewmh.c b/ewmh.c index 97a9a120d..94142b8a7 100755 --- a/ewmh.c +++ b/ewmh.c @@ -662,27 +662,45 @@ ewmh_window_icon_get_unchecked(xcb_window_t w) } static cairo_surface_t * -ewmh_window_icon_from_reply(xcb_get_property_reply_t *r) +ewmh_window_icon_from_reply(xcb_get_property_reply_t *r, uint32_t preferred_size) { - uint32_t *data; - uint64_t len; + uint32_t *data, *end, *found_data = 0; + uint32_t found_size = 0; if(!r || r->type != XCB_ATOM_CARDINAL || r->format != 32 || r->length < 2) return 0; data = (uint32_t *) xcb_get_property_value(r); - if (!data) - return 0; + if (!data) return 0; - /* Check that the property is as long as it should be, handling integer - * overflow. times always - * fits into an uint64_t and thus this multiplication cannot overflow. + end = data + r->length; + + /* Goes over the icon data and picks the icon that best matches the size preference. + * In case the size match is not exact, picks the closest bigger size if present, + * closest smaller size otherwise. */ - len = data[0] * (uint64_t) data[1]; - if (!data[0] || !data[1] || len > r->length - 2) - return 0; + while (data + 1 < end) { + /* check whether the data size specified by width and height fits into the array we got */ + uint64_t data_size = (uint64_t) data[0] * data[1]; + if (data_size > (uint64_t) (end - data - 2)) break; - return draw_surface_from_data(data[0], data[1], data + 2); + /* use the greater of the two dimensions to match against the preferred size */ + uint32_t size = MAX(data[0], data[1]); + /* pick the icon if it's a better match than the one we already have */ + if (data[0] && data[1] && ( + (found_size < preferred_size && size > found_size) || + (found_size > preferred_size && size >= preferred_size && size < found_size))) + { + found_data = data; + found_size = size; + } + + data += data_size + 2; + } + + if (!found_data) return 0; + + return draw_surface_from_data(found_data[0], found_data[1], found_data + 2); } /** Get NET_WM_ICON. @@ -690,10 +708,10 @@ ewmh_window_icon_from_reply(xcb_get_property_reply_t *r) * \return The number of elements on stack. */ cairo_surface_t * -ewmh_window_icon_get_reply(xcb_get_property_cookie_t cookie) +ewmh_window_icon_get_reply(xcb_get_property_cookie_t cookie, uint32_t preferred_size) { xcb_get_property_reply_t *r = xcb_get_property_reply(globalconf.connection, cookie, NULL); - cairo_surface_t *surface = ewmh_window_icon_from_reply(r); + cairo_surface_t *surface = ewmh_window_icon_from_reply(r, preferred_size); p_delete(&r); return surface; } diff --git a/ewmh.h b/ewmh.h index 420162159..6473687e5 100644 --- a/ewmh.h +++ b/ewmh.h @@ -41,7 +41,7 @@ void ewmh_process_client_strut(client_t *); void ewmh_update_strut(xcb_window_t, strut_t *); void ewmh_update_window_type(xcb_window_t window, uint32_t type); xcb_get_property_cookie_t ewmh_window_icon_get_unchecked(xcb_window_t); -cairo_surface_t *ewmh_window_icon_get_reply(xcb_get_property_cookie_t); +cairo_surface_t *ewmh_window_icon_get_reply(xcb_get_property_cookie_t, uint32_t preferred_size); #endif // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/globalconf.h b/globalconf.h index 344cfe5f4..523474ceb 100644 --- a/globalconf.h +++ b/globalconf.h @@ -167,6 +167,8 @@ typedef struct struct xkb_context *xkb_ctx; /* xkb state of dead keys on keyboard */ struct xkb_state *xkb_state; + /** The preferred size of client icons for this screen */ + uint32_t preferred_icon_size; } awesome_t; extern awesome_t globalconf; diff --git a/luaa.c b/luaa.c index a5a2cf5db..446cc7ea9 100644 --- a/luaa.c +++ b/luaa.c @@ -162,6 +162,21 @@ luaA_load_image(lua_State *L) return 1; } +/** Set the preferred size for client icons. + * + * The closest equal or bigger size is picked if present, otherwise the closest + * smaller size is picked. The default is 0 pixels, ie. the smallest icon. + * + * @param size The size of the icons in pixels. + * @function set_preferred_icon_size + */ +static int +luaA_set_preferred_icon_size(lua_State *L) +{ + globalconf.preferred_icon_size = luaL_checknumber(L, 1); + return 0; +} + /** UTF-8 aware string length computing. * \param L The Lua VM state. * \return The number of elements pushed on stack. @@ -407,6 +422,7 @@ luaA_init(xdgHandle* xdg) { "emit_signal", luaA_awesome_emit_signal }, { "systray", luaA_systray }, { "load_image", luaA_load_image }, + { "set_preferred_icon_size", luaA_set_preferred_icon_size }, { "register_xproperty", luaA_register_xproperty }, { "set_xproperty", luaA_set_xproperty }, { "get_xproperty", luaA_get_xproperty }, diff --git a/property.c b/property.c index 542a9c2b9..8069884ca 100644 --- a/property.c +++ b/property.c @@ -273,7 +273,7 @@ property_get_net_wm_icon(client_t *c) void property_update_net_wm_icon(client_t *c, xcb_get_property_cookie_t cookie) { - cairo_surface_t *surface = ewmh_window_icon_get_reply(cookie); + cairo_surface_t *surface = ewmh_window_icon_get_reply(cookie, globalconf.preferred_icon_size); if(!surface) return;