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 <psychon@znc.in>
This commit is contained in:
Uli Schlachter 2014-12-07 14:09:35 +01:00
parent b9ea9255e9
commit 30b313f77a
4 changed files with 104 additions and 5 deletions

25
draw.c
View File

@ -188,6 +188,18 @@ draw_surface_from_pixbuf(GdkPixbuf *buf)
return surface; 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. /** Duplicate the specified image surface.
* \param surface The surface to copy * \param surface The surface to copy
* \return A pointer to a new cairo image surface. * \return A pointer to a new cairo image surface.
@ -195,10 +207,15 @@ draw_surface_from_pixbuf(GdkPixbuf *buf)
cairo_surface_t * cairo_surface_t *
draw_dup_image_surface(cairo_surface_t *surface) draw_dup_image_surface(cairo_surface_t *surface)
{ {
cairo_surface_t *res = cairo_image_surface_create( cairo_surface_t *res;
cairo_image_surface_get_format(surface), int width, height;
cairo_image_surface_get_width(surface),
cairo_image_surface_get_height(surface)); 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_t *cr = cairo_create(res);
cairo_set_source_surface(cr, surface, 0, 0); cairo_set_source_surface(cr, surface, 0, 0);

View File

@ -1308,6 +1308,71 @@ client_set_icon(client_t *c, cairo_surface_t *s)
lua_pop(L, 1); 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. /** Kill a client.
* \param L The Lua VM state. * \param L The Lua VM state.
* *

View File

@ -101,6 +101,8 @@ struct client_t
key_array_t keys; key_array_t keys;
/** Icon */ /** Icon */
cairo_surface_t *icon; cairo_surface_t *icon;
/** True if we ever got an icon from _NET_WM_ICON */
bool have_ewmh_icon;
/** Size hints */ /** Size hints */
xcb_size_hints_t size_hints; xcb_size_hints_t size_hints;
/** The visualtype that c->window uses */ /** 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_name(lua_State *L, int, char *);
void client_set_alt_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_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_set_skip_taskbar(lua_State *, int, bool);
void client_focus(client_t *); void client_focus(client_t *);
void client_focus_update(client_t *); void client_focus_update(client_t *);

View File

@ -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) if(wmh.flags & XCB_ICCCM_WM_HINT_WINDOW_GROUP)
client_set_group_window(L, -1, wmh.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); lua_pop(L, 1);
} }
@ -263,6 +276,7 @@ property_update_net_wm_icon(client_t *c, xcb_get_property_cookie_t cookie)
if(!surface) if(!surface)
return; return;
c->have_ewmh_icon = true;
client_set_icon(c, surface); client_set_icon(c, surface);
cairo_surface_destroy(surface); cairo_surface_destroy(surface);
} }