Finish C-side support for window shapes (FS#1051)

A drawin's and a client's bounding and clip shape can now be queried and is
returned as a cairo surface.  Also, a client window's shape (e.g. xeyes setting
its own shape) can be queried via c.shape_client_bounding and
c.shape_client_clip.  All of these properties now emit signals when changed.

(This also silently fixes a bug in luaA_drawin_set_shape_bounding() which forgot
to include the drawin's border in its size calculation)

Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
Uli Schlachter 2014-01-03 16:51:16 +01:00
parent 5cd8c7e540
commit 56c5797905
8 changed files with 238 additions and 12 deletions

View File

@ -460,9 +460,13 @@ main(int argc, char **argv)
xcb_prefetch_maximum_request_length(globalconf.connection);
/* check for xtest extension */
const xcb_query_extension_reply_t *xtest_query;
xtest_query = xcb_get_extension_data(globalconf.connection, &xcb_test_id);
globalconf.have_xtest = xtest_query->present;
const xcb_query_extension_reply_t *query;
query = xcb_get_extension_data(globalconf.connection, &xcb_test_id);
globalconf.have_xtest = query->present;
/* check for shape extension */
query = xcb_get_extension_data(globalconf.connection, &xcb_shape_id);
globalconf.have_shape = query->present;
/* Allocate the key symbols */
globalconf.keysyms = xcb_key_symbols_alloc(globalconf.connection);

30
event.c
View File

@ -21,6 +21,7 @@
#include <xcb/xcb.h>
#include <xcb/randr.h>
#include <xcb/shape.h>
#include <xcb/xcb_atom.h>
#include <xcb/xcb_icccm.h>
#include <xcb/xcb_event.h>
@ -686,6 +687,24 @@ event_handle_randr_screen_change_notify(xcb_randr_screen_change_notify_event_t *
awesome_restart();
}
/** The shape notify event handler.
* \param ev The event.
*/
static void
event_handle_shape_notify(xcb_shape_notify_event_t *ev)
{
client_t *c = client_getbywin(ev->affected_window);
if (c)
{
luaA_object_push(globalconf.L, c);
if (ev->shape_kind == XCB_SHAPE_SK_BOUNDING)
luaA_object_emit_signal(globalconf.L, -1, "property::shape_client_bounding", 0);
if (ev->shape_kind == XCB_SHAPE_SK_CLIP)
luaA_object_emit_signal(globalconf.L, -1, "property::shape_client_clip", 0);
lua_pop(globalconf.L, 1);
}
}
/** The client message event handler.
* \param ev The event.
*/
@ -829,6 +848,7 @@ void event_handle(xcb_generic_event_t *event)
}
static uint8_t randr_screen_change_notify = 0;
static uint8_t shape_notify = 0;
if(randr_screen_change_notify == 0)
{
@ -838,9 +858,19 @@ void event_handle(xcb_generic_event_t *event)
if(randr_query->present)
randr_screen_change_notify = randr_query->first_event + XCB_RANDR_SCREEN_CHANGE_NOTIFY;
}
if(shape_notify == 0)
{
/* check for shape extension */
const xcb_query_extension_reply_t *shape_query;
shape_query = xcb_get_extension_data(globalconf.connection, &xcb_shape_id);
if(shape_query->present)
shape_notify = shape_query->first_event + XCB_SHAPE_NOTIFY;
}
if (response_type == randr_screen_change_notify)
event_handle_randr_screen_change_notify((void *) event);
if (response_type == shape_notify)
event_handle_shape_notify((void *) event);
}
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -83,6 +83,8 @@ typedef struct
uint16_t numlockmask, shiftlockmask, capslockmask, modeswitchmask;
/** Check for XTest extension */
bool have_xtest;
/** Check for SHAPE extension */
bool have_shape;
/** Clients list */
client_array_t clients;
/** Embedded windows */

View File

@ -41,8 +41,10 @@ module("client")
-- @field sticky Set the client sticky, i.e. available on all tags.
-- @field modal Indicate if the client is modal.
-- @field focusable True if the client can receive the input focus.
-- @field shape_bounding The client's bounding shape as a (native) cairo surface.
-- @field shape_clip The client's clip shape as a (native) cairo surface.
-- @field shape_bounding The client's bounding shape as set by awesome as a (native) cairo surface.
-- @field shape_clip The client's clip shape as set by awesome as a (native) cairo surface.
-- @field shape_client_bounding The client's bounding shape as set by the program as a (native) cairo surface.
-- @field shape_client_clip The client's clip shape as set by the program as a (native) cairo surface.
-- @class table
-- @name client

View File

@ -440,6 +440,8 @@ client_manage(xcb_window_t w, xcb_get_geometry_reply_t *wgeom, bool startup)
/* Make sure the window is automatically mapped if awesome exits or dies. */
xcb_change_save_set(globalconf.connection, XCB_SET_MODE_INSERT, w);
if (globalconf.have_shape)
xcb_shape_select_input(globalconf.connection, w, 1);
client_t *c = client_new(globalconf.L);
xcb_screen_t *s = globalconf.screen;
@ -1198,6 +1200,8 @@ client_unmanage(client_t *c, bool window_valid)
/* Remove this window from the save set since this shouldn't be made visible
* after a restart anymore. */
xcb_change_save_set(globalconf.connection, XCB_SET_MODE_DELETE, c->window);
if (globalconf.have_shape)
xcb_shape_select_input(globalconf.connection, c->window, 0);
/* Do this last to avoid races with clients. According to ICCCM, clients
* arent allowed to re-use the window until after this. */
@ -1972,7 +1976,39 @@ luaA_client_get_size_hints(lua_State *L, client_t *c)
return 1;
}
/** Set the client's bounding shape.
/** Get the client's child window bounding shape.
* \param L The Lua VM state.
* \param client The client object.
* \return The number of elements pushed on stack.
*/
static int
luaA_client_get_client_shape_bounding(lua_State *L, client_t *c)
{
cairo_surface_t *surf = xwindow_get_shape(c->window, XCB_SHAPE_SK_BOUNDING);
if (!surf)
return 0;
/* lua has to make sure to free the ref or we have a leak */
lua_pushlightuserdata(L, surf);
return 1;
}
/** Get the client's frame window bounding shape.
* \param L The Lua VM state.
* \param client The client object.
* \return The number of elements pushed on stack.
*/
static int
luaA_client_get_shape_bounding(lua_State *L, client_t *c)
{
cairo_surface_t *surf = xwindow_get_shape(c->frame_window, XCB_SHAPE_SK_BOUNDING);
if (!surf)
return 0;
/* lua has to make sure to free the ref or we have a leak */
lua_pushlightuserdata(L, surf);
return 1;
}
/** Set the client's frame window bounding shape.
* \param L The Lua VM state.
* \param client The client object.
* \return The number of elements pushed on stack.
@ -1987,10 +2023,43 @@ luaA_client_set_shape_bounding(lua_State *L, client_t *c)
c->geometry.width + (c->border_width * 2),
c->geometry.height + (c->border_width * 2),
XCB_SHAPE_SK_BOUNDING, surf, -c->border_width);
luaA_object_emit_signal(L, -3, "property::shape_bounding", 0);
return 0;
}
/** Set the client's clip shape.
/** Get the client's child window clip shape.
* \param L The Lua VM state.
* \param client The client object.
* \return The number of elements pushed on stack.
*/
static int
luaA_client_get_client_shape_clip(lua_State *L, client_t *c)
{
cairo_surface_t *surf = xwindow_get_shape(c->window, XCB_SHAPE_SK_CLIP);
if (!surf)
return 0;
/* lua has to make sure to free the ref or we have a leak */
lua_pushlightuserdata(L, surf);
return 1;
}
/** Get the client's frame window clip shape.
* \param L The Lua VM state.
* \param client The client object.
* \return The number of elements pushed on stack.
*/
static int
luaA_client_get_shape_clip(lua_State *L, client_t *c)
{
cairo_surface_t *surf = xwindow_get_shape(c->frame_window, XCB_SHAPE_SK_CLIP);
if (!surf)
return 0;
/* lua has to make sure to free the ref or we have a leak */
lua_pushlightuserdata(L, surf);
return 1;
}
/** Set the client's frame window clip shape.
* \param L The Lua VM state.
* \param client The client object.
* \return The number of elements pushed on stack.
@ -2003,6 +2072,7 @@ luaA_client_set_shape_clip(lua_State *L, client_t *c)
surf = (cairo_surface_t *)lua_touserdata(L, -1);
xwindow_set_shape(c->frame_window, c->geometry.width, c->geometry.height,
XCB_SHAPE_SK_CLIP, surf, 0);
luaA_object_emit_signal(L, -3, "property::shape_clip", 0);
return 0;
}
@ -2226,12 +2296,20 @@ client_class_setup(lua_State *L)
NULL);
luaA_class_add_property(&client_class, "shape_bounding",
(lua_class_propfunc_t) luaA_client_set_shape_bounding,
NULL,
(lua_class_propfunc_t) luaA_client_get_shape_bounding,
(lua_class_propfunc_t) luaA_client_set_shape_bounding);
luaA_class_add_property(&client_class, "shape_clip",
(lua_class_propfunc_t) luaA_client_set_shape_clip,
NULL,
(lua_class_propfunc_t) luaA_client_get_shape_clip,
(lua_class_propfunc_t) luaA_client_set_shape_clip);
luaA_class_add_property(&client_class, "client_shape_bounding",
NULL,
(lua_class_propfunc_t) luaA_client_get_client_shape_bounding,
NULL);
luaA_class_add_property(&client_class, "client_shape_clip",
NULL,
(lua_class_propfunc_t) luaA_client_get_client_shape_clip,
NULL);
signal_add(&client_class.signals, "focus");
signal_add(&client_class.signals, "list");
@ -2263,6 +2341,10 @@ client_class_setup(lua_State *L)
signal_add(&client_class.signals, "property::pid");
signal_add(&client_class.signals, "property::role");
signal_add(&client_class.signals, "property::screen");
signal_add(&client_class.signals, "property::shape_bounding");
signal_add(&client_class.signals, "property::shape_client_bounding");
signal_add(&client_class.signals, "property::shape_client_clip");
signal_add(&client_class.signals, "property::shape_clip");
signal_add(&client_class.signals, "property::size_hints_honor");
signal_add(&client_class.signals, "property::skip_taskbar");
signal_add(&client_class.signals, "property::sticky");

View File

@ -536,6 +536,22 @@ luaA_drawin_get_drawable(lua_State *L, drawin_t *drawin)
return 1;
}
/** Get the drawin's bounding shape.
* \param L The Lua VM state.
* \param drawin The drawin object.
* \return The number of elements pushed on stack.
*/
static int
luaA_drawin_get_shape_bounding(lua_State *L, drawin_t *drawin)
{
cairo_surface_t *surf = xwindow_get_shape(drawin->window, XCB_SHAPE_SK_BOUNDING);
if (!surf)
return 0;
/* lua has to make sure to free the ref or we have a leak */
lua_pushlightuserdata(L, surf);
return 1;
}
/** Set the drawin's bounding shape.
* \param L The Lua VM state.
* \param drawin The drawin object.
@ -547,11 +563,30 @@ luaA_drawin_set_shape_bounding(lua_State *L, drawin_t *drawin)
cairo_surface_t *surf = NULL;
if(!lua_isnil(L, -1))
surf = (cairo_surface_t *)lua_touserdata(L, -1);
xwindow_set_shape(drawin->window, drawin->geometry.width, drawin->geometry.height,
xwindow_set_shape(drawin->window,
drawin->geometry.width + 2*drawin->border_width,
drawin->geometry.height + 2*drawin->border_width,
XCB_SHAPE_SK_BOUNDING, surf, -drawin->border_width);
luaA_object_emit_signal(L, -3, "property::shape_bounding", 0);
return 0;
}
/** Get the drawin's clip shape.
* \param L The Lua VM state.
* \param drawin The drawin object.
* \return The number of elements pushed on stack.
*/
static int
luaA_drawin_get_shape_clip(lua_State *L, drawin_t *drawin)
{
cairo_surface_t *surf = xwindow_get_shape(drawin->window, XCB_SHAPE_SK_CLIP);
if (!surf)
return 0;
/* lua has to make sure to free the ref or we have a leak */
lua_pushlightuserdata(L, surf);
return 1;
}
/** Set the drawin's clip shape.
* \param L The Lua VM state.
* \param drawin The drawin object.
@ -565,6 +600,7 @@ luaA_drawin_set_shape_clip(lua_State *L, drawin_t *drawin)
surf = (cairo_surface_t *)lua_touserdata(L, -1);
xwindow_set_shape(drawin->window, drawin->geometry.width, drawin->geometry.height,
XCB_SHAPE_SK_CLIP, surf, 0);
luaA_object_emit_signal(L, -3, "property::shape_clip", 0);
return 0;
}
@ -630,13 +666,15 @@ drawin_class_setup(lua_State *L)
(lua_class_propfunc_t) luaA_window_set_type);
luaA_class_add_property(&drawin_class, "shape_bounding",
(lua_class_propfunc_t) luaA_drawin_set_shape_bounding,
NULL,
(lua_class_propfunc_t) luaA_drawin_get_shape_bounding,
(lua_class_propfunc_t) luaA_drawin_set_shape_bounding);
luaA_class_add_property(&drawin_class, "shape_clip",
(lua_class_propfunc_t) luaA_drawin_set_shape_clip,
NULL,
(lua_class_propfunc_t) luaA_drawin_get_shape_clip,
(lua_class_propfunc_t) luaA_drawin_set_shape_clip);
signal_add(&client_class.signals, "property::shape_bounding");
signal_add(&client_class.signals, "property::shape_clip");
signal_add(&drawin_class.signals, "property::border_width");
signal_add(&drawin_class.signals, "property::cursor");
signal_add(&drawin_class.signals, "property::height");

View File

@ -258,6 +258,70 @@ xwindow_set_border_color(xcb_window_t w, color_t *color)
xcb_change_window_attributes(globalconf.connection, w, XCB_CW_BORDER_PIXEL, &color->pixel);
}
/** Get one of a window's shapes as a cairo surface */
cairo_surface_t *
xwindow_get_shape(xcb_window_t win, enum xcb_shape_sk_t kind)
{
if (!globalconf.have_shape)
return NULL;
xcb_shape_query_extents_cookie_t ecookie = xcb_shape_query_extents(globalconf.connection, win);
xcb_shape_get_rectangles_cookie_t rcookie = xcb_shape_get_rectangles(globalconf.connection, win, kind);
xcb_shape_query_extents_reply_t *extents = xcb_shape_query_extents_reply(globalconf.connection, ecookie, NULL);
xcb_shape_get_rectangles_reply_t *rects_reply = xcb_shape_get_rectangles_reply(globalconf.connection, rcookie, NULL);
if (!extents || !rects_reply)
{
free(extents);
free(rects_reply);
/* Create a cairo surface in an error state */
return cairo_image_surface_create(CAIRO_FORMAT_INVALID, -1, -1);
}
int16_t x, y;
uint16_t width, height;
bool shaped;
if (kind == XCB_SHAPE_SK_BOUNDING)
{
x = extents->bounding_shape_extents_x;
y = extents->bounding_shape_extents_y;
width = extents->bounding_shape_extents_width;
height = extents->bounding_shape_extents_height;
shaped = extents->bounding_shaped;
} else {
assert(kind == XCB_SHAPE_SK_CLIP);
x = extents->clip_shape_extents_x;
y = extents->clip_shape_extents_y;
width = extents->clip_shape_extents_width;
height = extents->clip_shape_extents_height;
shaped = extents->clip_shaped;
}
if (!shaped)
{
free(extents);
free(rects_reply);
return NULL;
}
cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_A1, width, height);
cairo_t *cr = cairo_create(surface);
int num_rects = xcb_shape_get_rectangles_rectangles_length(rects_reply);
xcb_rectangle_t *rects = xcb_shape_get_rectangles_rectangles(rects_reply);
cairo_surface_set_device_offset(surface, -x, -y);
cairo_set_fill_rule(cr, CAIRO_FILL_RULE_WINDING);
for (int i = 0; i < num_rects; i++)
cairo_rectangle(cr, rects[i].x, rects[i].y, rects[i].width, rects[i].height);
cairo_fill(cr);
cairo_destroy(cr);
free(extents);
free(rects_reply);
return surface;
}
/** Turn a cairo surface into a pixmap with depth 1 */
static xcb_pixmap_t
xwindow_shape_pixmap(int width, int height, cairo_surface_t *surf)
@ -286,6 +350,9 @@ xwindow_shape_pixmap(int width, int height, cairo_surface_t *surf)
void
xwindow_set_shape(xcb_window_t win, int width, int height, enum xcb_shape_sk_t kind, cairo_surface_t *surf, int offset)
{
if (!globalconf.have_shape)
return;
xcb_pixmap_t pixmap = XCB_NONE;
if (surf)
pixmap = xwindow_shape_pixmap(width, height, surf);

View File

@ -40,6 +40,7 @@ void xwindow_grabkeys(xcb_window_t, key_array_t *);
void xwindow_takefocus(xcb_window_t);
void xwindow_set_cursor(xcb_window_t, xcb_cursor_t);
void xwindow_set_border_color(xcb_window_t, color_t *);
cairo_surface_t *xwindow_get_shape(xcb_window_t, enum xcb_shape_sk_t);
void xwindow_set_shape(xcb_window_t, int, int, enum xcb_shape_sk_t, cairo_surface_t *, int);
void xwindow_translate_for_gravity(xcb_gravity_t, int16_t, int16_t, int16_t, int16_t, int16_t *, int16_t *);