From 30b313f77a4a3986e36e2a22d5ce65eaab3b7a0f Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sun, 7 Dec 2014 14:09:35 +0100 Subject: [PATCH] Implement icon_pixmap and icon_mask from WM_HINTS (FS#1297) Fun fact: ICCCM specifies that icon_pixmap must have depth 1. Xterm uses a pixmap with depth 24. Yay... As such, I don't have any test for the depth == 1 case and will just assume that it does the right thing. If it doesn't, I bet no one will notice anyway. Signed-off-by: Uli Schlachter --- draw.c | 25 ++++++++++++++++--- objects/client.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ objects/client.h | 5 +++- property.c | 14 +++++++++++ 4 files changed, 104 insertions(+), 5 deletions(-) diff --git a/draw.c b/draw.c index 2990633e6..7e5d762d1 100644 --- a/draw.c +++ b/draw.c @@ -188,6 +188,18 @@ draw_surface_from_pixbuf(GdkPixbuf *buf) return surface; } +static void +get_surface_size(cairo_surface_t *surface, int *width, int *height) +{ + double x1, y1, x2, y2; + cairo_t *cr = cairo_create(surface); + + cairo_clip_extents(cr, &x1, &y1, &x2, &y2); + cairo_destroy(cr); + *width = x2 - x1; + *height = y2 - y1; +} + /** Duplicate the specified image surface. * \param surface The surface to copy * \return A pointer to a new cairo image surface. @@ -195,10 +207,15 @@ draw_surface_from_pixbuf(GdkPixbuf *buf) cairo_surface_t * draw_dup_image_surface(cairo_surface_t *surface) { - cairo_surface_t *res = cairo_image_surface_create( - cairo_image_surface_get_format(surface), - cairo_image_surface_get_width(surface), - cairo_image_surface_get_height(surface)); + cairo_surface_t *res; + int width, height; + + get_surface_size(surface, &width, &height); +#if CAIRO_VERSION_MAJOR == 1 && CAIRO_VERSION_MINOR > 12 + res = cairo_surface_create_similar_image(surface, CAIRO_FORMAT_ARGB32, width, height); +#else + res = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); +#endif cairo_t *cr = cairo_create(res); cairo_set_source_surface(cr, surface, 0, 0); diff --git a/objects/client.c b/objects/client.c index 81b853d0f..715e75f49 100644 --- a/objects/client.c +++ b/objects/client.c @@ -1308,6 +1308,71 @@ client_set_icon(client_t *c, cairo_surface_t *s) lua_pop(L, 1); } +/** Set a client icon. + * \param c The client to change. + * \param icon A bitmap containing the icon. + * \param mask A mask for the bitmap (optional) + */ +void +client_set_icon_from_pixmaps(client_t *c, xcb_pixmap_t icon, xcb_pixmap_t mask) +{ + xcb_get_geometry_cookie_t geom_icon_c, geom_mask_c; + xcb_get_geometry_reply_t *geom_icon_r, *geom_mask_r = NULL; + cairo_surface_t *s_icon, *result; + + geom_icon_c = xcb_get_geometry_unchecked(globalconf.connection, icon); + if (mask) + geom_mask_c = xcb_get_geometry_unchecked(globalconf.connection, mask); + geom_icon_r = xcb_get_geometry_reply(globalconf.connection, geom_icon_c, NULL); + if (mask) + geom_mask_r = xcb_get_geometry_reply(globalconf.connection, geom_mask_c, NULL); + + if (!geom_icon_r || (mask && !geom_mask_r)) + goto out; + if ((geom_icon_r->depth != 1 && geom_icon_r->depth != globalconf.screen->root_depth) + || (geom_mask_r && geom_mask_r->depth != 1)) + { + warn("Got pixmaps with depth (%d, %d) while processing icon, but only depth 1 and %d are allowed", + geom_icon_r->depth, geom_mask_r ? geom_mask_r->depth : 0, globalconf.screen->root_depth); + goto out; + } + + if (geom_icon_r->depth == 1) + s_icon = cairo_xcb_surface_create_for_bitmap(globalconf.connection, + globalconf.screen, icon, geom_icon_r->width, geom_icon_r->height); + else + s_icon = cairo_xcb_surface_create(globalconf.connection, icon, globalconf.default_visual, + geom_icon_r->width, geom_icon_r->height); + result = s_icon; + + if (mask) + { + cairo_surface_t *s_mask; + cairo_t *cr; + + result = cairo_surface_create_similar(s_icon, CAIRO_CONTENT_COLOR_ALPHA, geom_icon_r->width, geom_icon_r->height); + s_mask = cairo_xcb_surface_create_for_bitmap(globalconf.connection, + globalconf.screen, mask, geom_icon_r->width, geom_icon_r->height); + cr = cairo_create(result); + + cairo_set_source_surface(cr, s_icon, 0, 0); + cairo_mask_surface(cr, s_mask, 0, 0); + cairo_surface_destroy(s_mask); + cairo_destroy(cr); + } + + client_set_icon(c, result); + + cairo_surface_destroy(result); + if (result != s_icon) + cairo_surface_destroy(s_icon); + +out: + p_delete(&geom_icon_r); + p_delete(&geom_mask_r); +} + + /** Kill a client. * \param L The Lua VM state. * diff --git a/objects/client.h b/objects/client.h index ad2aa702a..fba25595f 100644 --- a/objects/client.h +++ b/objects/client.h @@ -101,6 +101,8 @@ struct client_t key_array_t keys; /** Icon */ cairo_surface_t *icon; + /** True if we ever got an icon from _NET_WM_ICON */ + bool have_ewmh_icon; /** Size hints */ xcb_size_hints_t size_hints; /** The visualtype that c->window uses */ @@ -164,7 +166,8 @@ void client_set_transient_for(lua_State *L, int, client_t *); void client_set_name(lua_State *L, int, char *); void client_set_alt_name(lua_State *L, int, char *); void client_set_group_window(lua_State *, int, xcb_window_t); -void client_set_icon(client_t *c, cairo_surface_t *s); +void client_set_icon(client_t *, cairo_surface_t *); +void client_set_icon_from_pixmaps(client_t *, xcb_pixmap_t, xcb_pixmap_t); void client_set_skip_taskbar(lua_State *, int, bool); void client_focus(client_t *); void client_focus_update(client_t *); diff --git a/property.c b/property.c index 177cc92e0..c866c317f 100644 --- a/property.c +++ b/property.c @@ -206,6 +206,19 @@ property_update_wm_hints(client_t *c, xcb_get_property_cookie_t cookie) if(wmh.flags & XCB_ICCCM_WM_HINT_WINDOW_GROUP) client_set_group_window(L, -1, wmh.window_group); + if(!c->have_ewmh_icon) + { + if(wmh.flags & XCB_ICCCM_WM_HINT_ICON_PIXMAP) + { + if(wmh.flags & XCB_ICCCM_WM_HINT_ICON_MASK) + client_set_icon_from_pixmaps(c, wmh.icon_pixmap, wmh.icon_mask); + else + client_set_icon_from_pixmaps(c, wmh.icon_pixmap, XCB_NONE); + } + else + client_set_icon(c, NULL); + } + lua_pop(L, 1); } @@ -263,6 +276,7 @@ property_update_net_wm_icon(client_t *c, xcb_get_property_cookie_t cookie) if(!surface) return; + c->have_ewmh_icon = true; client_set_icon(c, surface); cairo_surface_destroy(surface); }