-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 Hi list,
attached are the shape patches I've been working on. The first patch adds a function for converting an image_t into a 1bit pixmap. Since there are only two colors available on these, I have to somehow convert the image. Currently the code checks if the average of the RGB channels is above or below 50% intensity. Does someone have a better idea for this conversion? (I need to do something like this, because else the anti-aliasing which e.g. image:draw_circle() does would have even uglier effects). The second patch then adds the necessary hooks and magic for setting a wibox' clipping and bounding shape from lua. I bet this needs to be documented better, but since I don't really know where, I'll do it here: - - Everything which isn't in the bounding shape is transparent (clipping shape is ignored here). - - Everything which is in the bounding, but isn't in the clipping shape is filled with the border color and is said to be the wibox / window's border. - - Everything which is in the bounding *and* the clipping shape is the window's content. Only in this area is the wibox' content visible. To make matters even more complicated, the clipping shape's (0, 0) (upper left corner) is in the window's (0, 0) (upper left corner), but this is *NOT* true for the bounding shape. The bounding shape's (0, 0) is placed at the window's (-border, -border). Without this, one couldn't do a border on the left side of a wibox. Apropos: With the shape extension, one can't extend any of the shapes beyond their default value where the default shape is the area if you wouldn't be using the shape extension. Now the third patch: This adds awful.wibox.rounded_corners(). This function needs to be called every time the wibox is resized or else the old shape will still be used which can have ugly effects. This code only supports rounding all of a wibox' corners because I am lazy today. Feel free to code that yourself if you need it now or ask me to do it and you might get a patch tomorrow. ;) The fourth and last patch isn't meant to be merged, but this is some stuff which I still had in my git tree for testing. Use it as an example or something. ;) The result of adding these patches to latest master is also available at: git://git.znc.in/psychon/awesome.git wibox_shape_for_submit (The wibox_shape branch is my working branch, you can look at it if you want to see the dirty history of this code) jd: I'm quite unsure if this is merge-worthy. Feel free to comment on this stuff, but don't feel obliged to merge this. You could rather help me improving this first and making it more usable. Cheers, Uli - -- "Do you know that books smell like nutmeg or some spice from a foreign land?" -- Faber in Fahrenheit 451 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iQEcBAEBCAAGBQJKQLsvAAoJECLkKOvLj8sG/C8H/A7MJ+dfmDxG7YM5d3wAHnQe bt/e6jWMp3pxFaWAhGe9HpCR+zzZlnLn292KxL/K+pm5bm62gLSejucYm4MlF58N ZQLyjXb9wHs51eZKakyM2zHkOgB7k/sjKVCiHAxU4HUwhBYm3Sy3i3AvhzehKJwl GFJ1+2YjG4Dq5T7Yp93Ik8x5tMC/xT7HCU3b8MPDjw1O85v4tOcMGJIuzgOuWBe+ RLtUoHWbjK9ms4x4SnAkmZHUiiKIQQYlj5JYFTIIuNMjFfhcdjooM4ByLMXaBMkq AOJarJoMPaiCUVOGeEht4LXtTSkYvHvX0jXwBnSROqhqYwu6H6EBq7D+ajg9LFo= =gRgn -----END PGP SIGNATURE-----
>From 9350c5fe0e2fa62b31549d1f21219efb08d33e79 Mon Sep 17 00:00:00 2001 From: Uli Schlachter <psyc...@znc.in> Date: Thu, 18 Jun 2009 14:34:14 +0200 Subject: [PATCH 1/4] Add image_to_1bit_pixmap() This function converts an image_t into a 1bit pixmap. Signed-off-by: Uli Schlachter <psyc...@znc.in> --- image.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ image.h | 2 + 2 files changed, 74 insertions(+), 0 deletions(-) diff --git a/image.c b/image.c index a71df33..2114579 100644 --- a/image.c +++ b/image.c @@ -21,6 +21,8 @@ #include "structs.h" #include "common/tokenize.h" +#include "common/xutil.h" +#include <xcb/xcb_image.h> DO_LUA_TOSTRING(image_t, image, "image"); @@ -138,6 +140,76 @@ image_getdata(image_t *image) image->isupdated = true; return image->data; + +} + +static void +image_draw_to_1bit_ximage(image_t *image, xcb_image_t *img) +{ + uint32_t *data; + int size, x, y, width, height; + + imlib_context_set_image(image->image); + + data = imlib_image_get_data_for_reading_only(); + + width = imlib_image_get_width(); + height = imlib_image_get_height(); + size = width * height; + + for (y = 0; y < height; y++) + for (x = 0; x < width; x++) + { + int i, pixel, tmp; + + i = y * width + x; + + // Sum up all color components ignoring alpha + tmp = (data[i] >> 16) & 0xff; + tmp += (data[i] >> 8) & 0xff; + tmp += data[i] & 0xff; + + if (tmp / 3 < 127) + pixel = 0; + else + pixel = 1; + + xcb_image_put_pixel(img, x, y, pixel); + } +} + +// Convert an image to a 1bit pixmap +xcb_pixmap_t +image_to_1bit_pixmap(image_t *image, xcb_drawable_t d) +{ + xcb_pixmap_t pixmap; + xcb_gcontext_t gc; + xcb_image_t *img; + uint16_t width, height; + + width = image_getwidth(image); + height = image_getheight(image); + + /* Prepare the pixmap and gc */ + pixmap = xcb_generate_id(globalconf.connection); + xcb_create_pixmap(globalconf.connection, 1, pixmap, d, width, height); + + gc = xcb_generate_id(globalconf.connection); + xcb_create_gc(globalconf.connection, gc, pixmap, 0, NULL); + + /* Prepare the image */ + img = xcb_image_create_native(globalconf.connection, width, height, + XCB_IMAGE_FORMAT_XY_BITMAP, 1, NULL, 0, NULL); + image_draw_to_1bit_ximage(image, img); + + /* Paint the image to the pixmap */ + xcb_image_put(globalconf.connection, pixmap, gc, img, 0, 0, 0); + + xcb_free_gc(globalconf.connection, gc); + + xcb_image_destroy(img); + + return pixmap; } /** Create a new image from ARGB32 data. diff --git a/image.h b/image.h index a9acd93..1df544c 100644 --- a/image.h +++ b/image.h @@ -46,5 +46,7 @@ uint8_t * image_getdata(image_t *); int image_getwidth(image_t *); int image_getheight(image_t *); +xcb_pixmap_t image_to_1bit_pixmap(image_t *, xcb_drawable_t); + #endif // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80 -- 1.6.3.1
>From 63e06e21db16d35dc5baf9e9d899c8538f3e4992 Mon Sep 17 00:00:00 2001 From: Uli Schlachter <psyc...@znc.in> Date: Tue, 23 Jun 2009 12:55:50 +0200 Subject: [PATCH 2/4] Add a lua api for setting a wibox' shape When the SHAPE extension is not available, this code prints a harmless warn() on stderr. Signed-off-by: Uli Schlachter <psyc...@znc.in> --- awesomeConfig.cmake | 1 + common/tokenize.gperf | 2 + swindow.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ swindow.h | 9 +++++++ wibox.c | 23 +++++++++++++++++++ wibox.h | 2 + 6 files changed, 96 insertions(+), 0 deletions(-) diff --git a/awesomeConfig.cmake b/awesomeConfig.cmake index ea72c9e..16b0cdb 100644 --- a/awesomeConfig.cmake +++ b/awesomeConfig.cmake @@ -140,6 +140,7 @@ pkg_check_modules(AWESOME_REQUIRED REQUIRED xcb-randr xcb-xtest xcb-xinerama + xcb-shape xcb-event>=0.3.4 xcb-aux>=0.3.0 xcb-atom>=0.3.0 diff --git a/common/tokenize.gperf b/common/tokenize.gperf index 8713fdd..afe36af 100644 --- a/common/tokenize.gperf +++ b/common/tokenize.gperf @@ -89,6 +89,8 @@ role screen selected session +shape_bounding +shape_clip Shift size_hints size_hints_honor diff --git a/swindow.c b/swindow.c index 061d680..2bb4a12 100644 --- a/swindow.c +++ b/swindow.c @@ -22,12 +22,69 @@ #include <math.h> #include <xcb/xcb.h> +#include <xcb/shape.h> #include "structs.h" #include "swindow.h" #include "draw.h" #include "common/xutil.h" +static int +have_shape(void) +{ + const xcb_query_extension_reply_t *reply; + + reply = xcb_get_extension_data(globalconf.connection, &xcb_shape_id); + if (!reply || !reply->present) + return 0; + + /* We don't need a specific version of SHAPE, no version check required */ + return 1; +} + +static void +do_update_shape(xcb_window_t win, xcb_shape_kind_t kind, image_t *image, int offset) +{ + xcb_pixmap_t shape; + + if(!image) + { + /* Reset the shape */ + shape = XCB_NONE; + } else { + shape = image_to_1bit_pixmap(image, win); + } + + xcb_shape_mask(globalconf.connection, XCB_SHAPE_SO_SET, kind, + win, offset, offset, shape); + + if (shape != XCB_NONE) + xcb_free_pixmap(globalconf.connection, shape); +} + +/** Update the window's shape. + * \param sw The simplw window whose shape should be updated. + */ +void +simplewindow_update_shape(simple_window_t *sw) +{ + if(sw->window == XCB_NONE) + return; + + if(!have_shape()) + { + static bool warned = false; + if (!warned) + warn("The X server doesn't have the SHAPE extension; " + "can't change window's shape"); + warned = true; + return; + } + + do_update_shape(sw->window, XCB_SHAPE_SK_CLIP, sw->shape.clip, 0); + do_update_shape(sw->window, XCB_SHAPE_SK_BOUNDING, sw->shape.bounding, -sw->border.width); +} + static void simplewindow_draw_context_update(simple_window_t *sw, xcb_screen_t *s) { @@ -126,6 +183,8 @@ simplewindow_init(simple_window_t *sw, /* The default GC is just a newly created associated to the root window */ sw->gc = xcb_generate_id(globalconf.connection); xcb_create_gc(globalconf.connection, sw->gc, s->root, gc_mask, gc_values); + + simplewindow_update_shape(sw); } /** Destroy all resources of a simple window. diff --git a/swindow.h b/swindow.h index 316e5e2..a158e39 100644 --- a/swindow.h +++ b/swindow.h @@ -55,6 +55,14 @@ typedef struct simple_window_t orientation_t orientation; /** Opacity */ double opacity; + /** The window's shape */ + struct + { + /** The window's content */ + image_t *clip; + /** The window's content and border */ + image_t *bounding; + } shape; } simple_window_t; void simplewindow_init(simple_window_t *s, @@ -71,6 +79,7 @@ void simplewindow_border_width_set(simple_window_t *, uint32_t); void simplewindow_border_color_set(simple_window_t *, const xcolor_t *); void simplewindow_orientation_set(simple_window_t *, orientation_t); void simplewindow_cursor_set(simple_window_t *, xcb_cursor_t); +void simplewindow_update_shape(simple_window_t *); /** Refresh the window content by copying its pixmap data to its window. * \param sw The simple window to refresh. diff --git a/wibox.c b/wibox.c index eb092b2..af32422 100644 --- a/wibox.c +++ b/wibox.c @@ -326,8 +326,15 @@ void wibox_refresh(void) { foreach(w, globalconf.wiboxes) + { + if((*w)->need_shape_update) + { + simplewindow_update_shape(&(*w)->sw); + (*w)->need_shape_update = false; + } if((*w)->need_update) wibox_draw(*w); + } foreach(_c, globalconf.clients) { @@ -581,6 +588,8 @@ luaA_wibox_invalidate_byitem(lua_State *L, const void *item) * \lfield opacity The opacity of the wibox, between 0 and 1. * \lfield mouse_enter A function to execute when the mouse enter the widget. * \lfield mouse_leave A function to execute when the mouse leave the widget. + * \lfield shape_clip Image describing the window's content shape. + * \lfield shape_bounding Image describing the window's border shape. */ static int luaA_wibox_index(lua_State *L) @@ -665,6 +674,10 @@ luaA_wibox_index(lua_State *L) else return 0; return 1; + case A_TK_SHAPE_BOUNDING: + image_push(L, wibox->sw.shape.bounding); + case A_TK_SHAPE_CLIP: + image_push(L, wibox->sw.shape.clip); default: return 0; } @@ -872,6 +885,16 @@ luaA_wibox_newindex(lua_State *L) case A_TK_MOUSE_LEAVE: luaA_registerfct(L, 3, &wibox->mouse_leave); return 0; + case A_TK_SHAPE_BOUNDING: + image_unref(L, wibox->sw.shape.bounding); + wibox->sw.shape.bounding = image_ref(L, 3); + wibox->need_shape_update = true; + break; + case A_TK_SHAPE_CLIP: + image_unref(L, wibox->sw.shape.clip); + wibox->sw.shape.clip = image_ref(L, 3); + wibox->need_shape_update = true; + break; default: switch(wibox->type) { diff --git a/wibox.h b/wibox.h index 5f548d0..583a193 100644 --- a/wibox.h +++ b/wibox.h @@ -59,6 +59,8 @@ struct wibox_t luaA_ref mouse_enter, mouse_leave; /** Need update */ bool need_update; + /** Need shape update */ + bool need_shape_update; /** Cursor */ char *cursor; /** Background image */ -- 1.6.3.1
>From c05875412bf63b9d80516de0107fc63e3d3dec2a Mon Sep 17 00:00:00 2001 From: Uli Schlachter <psyc...@znc.in> Date: Tue, 23 Jun 2009 12:42:36 +0200 Subject: [PATCH 3/4] Add an awful functions for rounded corners This uses hexadecimal colors, because named colors require a round trip to the X server and are thus slower. :( Signed-off-by: Uli Schlachter <psyc...@znc.in> --- lib/awful/wibox.lua.in | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 61 insertions(+), 0 deletions(-) diff --git a/lib/awful/wibox.lua.in b/lib/awful/wibox.lua.in index c419a5d..01e041e 100644 --- a/lib/awful/wibox.lua.in +++ b/lib/awful/wibox.lua.in @@ -15,6 +15,7 @@ local setmetatable = setmetatable local ipairs = ipairs local table = table local type = type +local image = image local hooks = require("awful.hooks") --- Wibox module for awful. @@ -272,6 +273,66 @@ function new(arg) return w end +local function do_rounded_corners(width, height, corner) + local img = image.argb32(width, height, nil) + + -- Completly transparent + img:draw_rectangle(0, 0, width, height, true, "#ffffff") + + -- [[ This is the "layout" of the wibox: + -- ---------------------- + -- /| |\ + -- /4| 1 |5\ + -- |--------------------------| + -- | 2 | + -- |--------------------------| + -- \6| 3 |7/ + -- \| |/ + -- ---------------------- + -- ]] + + -- Show the "content" of the wibox + -- 1 + img:draw_rectangle(corner, 0, width - corner * 2, corner, true, "#000000") + -- 2 + img:draw_rectangle(0, corner, width, height - corner * 2, true, "#000000") + -- 3 + img:draw_rectangle(corner, height - corner, width - corner * 2, corner, true, "#000000") + + -- These are the 'real' rounded corners + -- 4 + img:draw_circle(corner, corner, corner, corner, true, "#000000") + -- 5 + img:draw_circle(width - 1 - corner, corner, corner, corner, true, "#000000") + -- 6 + img:draw_circle(corner, height - 1 - corner, corner, corner, true, "#000000") + -- 7 + img:draw_circle(width - 1 - corner, height - 1 - corner, corner, corner, true, "#000000") + + return img +end + +--- Add rounded corners to a wibox +-- @param wibox The wibox. +-- @param corner_size The size in pixel of the rounded corners. +function rounded_corners(wibox, corner_size) + local border = wibox.border_width + local geometry = wibox:geometry() + local width = geometry.width + local height = geometry.height + + -- Corners can't be larger than half the wibox' space + if width / 2 < corner_size then + corner_size = width / 2 + end + if height / 2 < corner_size then + corner_size = height / 2 + end + + wibox.shape_clip = do_rounded_corners(width, height, corner_size) + wibox.shape_bounding = do_rounded_corners(width + border * 2, height + border * 2, corner_size + border) +end + local function update_wiboxes_position(obj, prop) if (type(obj) == "wibox" and (prop == nil -- 1.6.3.1
>From c94889fa64cc0ff204fc0157265267a6c588d6ec Mon Sep 17 00:00:00 2001 From: Uli Schlachter <psyc...@znc.in> Date: Tue, 23 Jun 2009 12:55:50 +0200 Subject: [PATCH 4/4] Config test Signed-off-by: Uli Schlachter <psyc...@znc.in> --- awesomerc.lua.in | 21 +++++++++++++++++++++ 1 files changed, 21 insertions(+), 0 deletions(-) diff --git a/awesomerc.lua.in b/awesomerc.lua.in index 4783c2f..9aaed45 100644 --- a/awesomerc.lua.in +++ b/awesomerc.lua.in @@ -179,8 +179,29 @@ for s = 1, screen.count() do mytextbox, mylayoutbox[s], s == 1 and mysystray or nil } + awful.wibox.rounded_corners(mywibox[s], 5) end +w = wibox({ position = "floating", x = 20, y = 30, width = 30, height = 30, border_width = 5, border_color = "blue"}) +w.ontop = true +i = image.argb32(30, 30, nil) +i:draw_rectangle(0, 0, 30, 30, true, "white") +i:draw_circle(15, 15, 14, 14, true, "black") +w.shape_clip = i +k = image.argb32(40, 40, nil) +k:draw_rectangle(0, 0, 40, 40, true, "white") +k:draw_circle(20, 20, 19, 19, true, "black") +w.shape_bounding = k +local t = widget({type = "textbox"}) +t.text = " Test\n Test" +w.widgets = { t } +--w.shape_clip = image("/tmp/shape.png") +--w.shape_bounding = image("/tmp/shape.png") +w.screen = 1 -- }}} +w = wibox({ x = 100, y = 60, width = 150, height = 150, border_width = 5, border_color = "blue"}) +w.screen = 1 +w.ontop = true +awful.wibox.rounded_corners(w, 30) -- {{{ Mouse bindings root.buttons(awful.util.table.join( -- 1.6.3.1