-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

Hi,

Garoth (jokingly?) asked for rounded corners on wiboxes. With the help of
plagman I took a look at the "X nonrectangular window shape extension" and came
up with the attached C file. It creates a window and adds some rounded corners
on it. All the code in main() isn't needed for making some of a window's corner
rounded.
Before I start hacking on wibox.c I wanted to ask you guys about comments. The
functions init(), init_arc() and do_rounded_corners() can be easily copy-pasted
into awesome and one can just add a call to do_rounded_corners() when a wibox is
resized plus some lua magic for setting the corner size and which corners are
rounded.

Should I try to do so or are there some issues with the current code that would
have to be sorted out first? (E.g. farhaven suggested to load the mask for the
rounded corners from a file instead of generating it, I dunno...)

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)

iQEcBAEBCAAGBQJKIDiYAAoJECLkKOvLj8sGIKAH/RXlxexYve16lHeGzfgQQjb6
7kXr7ztAJAwVuBGiS7BnJNUnZsDkBi4p3yTVlUBwwYZuKiyOXKLEJuqJ5ac8PFSL
GcdDT/T4PioFGiQc3MsrpdzvZDy9v6cVaCAGumcNL7upxjOM4xGSecWQAAtRebHw
jAn50XzlWqui7fae3/RORZGcj17DiVapu2NQwXWNd3fIohxAm5YtFU/xzUgy8RGs
mHpsjFhRUP99ljdFw0uS3az9Ybxb+caNllhudv+aZsn7BrM25tx8e2yZMHIikPE4
HDnGQ4pdud+5EoqYTVfvhy+b2isOelGiaGiHw6BLmKvbxLsRPFaZkqsMXAtH5Nc=
=lwor
-----END PGP SIGNATURE-----
/* stolen from wikipedia and modified a bit,
 * then again stolen from farhaven.
 * compile like this:
 * gcc -o test test.c -std=c99 $(pkg-config --cflags --libs xcb xcb-shape)
 */

#include <xcb/xcb.h>
#include <xcb/shape.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static int have_xshape = 0;

static void init(xcb_connection_t *c)
{
    const char *name = "SHAPE";
    xcb_query_extension_cookie_t cookie;
    xcb_shape_query_version_cookie_t shape_cookie;
    xcb_shape_query_version_reply_t *shape;

    cookie = xcb_query_extension(c, strlen(name), name);
    if (!xcb_query_extension_reply(c, cookie, NULL)->present) {
        have_xshape = 0;
        return;
    }

    shape_cookie = xcb_shape_query_version_unchecked(c);
    shape = xcb_shape_query_version_reply(c, shape_cookie, NULL);
    printf("XShape version %d.%d\n", shape->major_version, shape->minor_version);
    /* This works with all existing versions of the SHAPE extension, so no
     * version check is required.
     */
    have_xshape = 1;
}

static void init_arc(xcb_arc_t *a, int x, int y, int corner, int angle)
{
    a->x = x;
    a->y = y;
    a->width = corner * 2;
    a->height = corner * 2;
    a->angle1 = angle * 64;
    a->angle2 = 90 * 64;
}

/* In each corner there is an arc which does the actual rounded corner,
 * the rest is covered by three rectangles:
 *    -----------------------------------------
 *   /|                  1                    |\
 *  |-------------------------------------------|
 *  |                                           |
 *  |                    2                      |
 *  |                                           |
 *  |-------------------------------------------|
 *   \|                  3                    |/
 *    -----------------------------------------
 */

static void do_rounded_corners(xcb_connection_t *c, xcb_window_t w, xcb_screen_t *s, int corner, int width, int height,
                int top_left, int top_right, int bottom_left, int bottom_right)
{
    if (!have_xshape)
        return;
    /* Make sure the corner size is a sane value, anything bigger than half the
     * window will break
     */
    if (corner > width / 2)
            corner = width / 2;
    if (corner > height / 2)
            corner = height / 2;
    xcb_rectangle_t full_window = { 0, 0, width, height };
    xcb_arc_t arcs[4];
    int num_arcs = 0;
    xcb_rectangle_t      rects[] = {
            { corner, 0,               width - corner * 2, corner }, /* 1 Top area */
            { 0, corner,               width, height - corner * 2 }, /* 2 Middle */
            { corner, height - corner, width - corner * 2, corner }  /* 3 bottom area */
    };
    xcb_pixmap_t         p;
    xcb_gcontext_t       g;
    uint32_t             mask;
    uint32_t             values[1];
    int num_rects = sizeof(rects) / sizeof(rects[0]);

    /* Set up the stuff we intend to draw (= have visible) */
    if (top_left)
        init_arc(&arcs[num_arcs++], 0, 0, corner, 90);
    else {
        /* Extend rectangle 1 to cover this corner */
        rects[0].width += rects[0].x;
        rects[0].x = 0;
    }
    if (top_right)
        init_arc(&arcs[num_arcs++], width - corner * 2, 0, corner, 0);
    else {
        /* Rect 1 got to cover this area then */
        rects[0].width += corner;
    }
    if (bottom_left)
        init_arc(&arcs[num_arcs++], 0, height - corner * 2, corner, 180);
    else {
        /* Rect 3's duty to cover this */
        rects[2].width += rects[2].x;
        rects[2].x = 0;
    }
    if (bottom_right)
        init_arc(&arcs[num_arcs++], width - corner * 2, height - corner * 2, corner, 270);
    else
        /* Rect 3 also got to cover this corner */
        rects[2].width += corner;

    p = xcb_generate_id(c);
    xcb_create_pixmap(c, 1, p, w, width, height);

    /* First "hide" the whole window */
    g = xcb_generate_id(c);
    values[0] = s->black_pixel;
    mask = XCB_GC_FOREGROUND;
    xcb_create_gc(c, g, p, mask, values);

    xcb_poly_fill_rectangle(c, p, g, 1, &full_window);

    /* Then draw the stuff we want to have visible */
    values[0] = s->white_pixel;
    xcb_change_gc(c, g, mask, values);

    xcb_poly_fill_rectangle(c, p, g, num_rects, rects);
    if (num_arcs > 0)
        xcb_poly_fill_arc(c, p, g, num_arcs, arcs);

    xcb_shape_mask(c, XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, w, 0, 0, p);

    xcb_free_gc(c, g);
    xcb_free_pixmap(c, p);
}

static void do_corners(xcb_connection_t *c, xcb_window_t w, xcb_screen_t *s, int width, int height)
{
    do_rounded_corners(c, w, s, 30, width, height, 1, 1, 1, 1);
}

int main(int argc, char **argv)
{
    xcb_connection_t    *c;
    xcb_screen_t        *s;
    xcb_window_t         w;
    xcb_generic_event_t *e;
    xcb_configure_notify_event_t *conf;
    uint32_t             mask;
    uint32_t             values[2];

    c = xcb_connect(NULL,NULL);
    if (xcb_connection_has_error(c))
    {
        printf("Cannot open display\n");
        exit(1);
    }

    init(c);

    s = xcb_setup_roots_iterator( xcb_get_setup(c) ).data;

    mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
    values[0] = s->white_pixel;
    values[1] = XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_STRUCTURE_NOTIFY;

    w = xcb_generate_id(c);
    xcb_create_window(c, s->root_depth, w, s->root,
                        10, 10, 200, 100, 1,
                        XCB_WINDOW_CLASS_INPUT_OUTPUT, s->root_visual,
                        mask, values);
    do_corners(c, w, s, 200, 100);

    xcb_map_window(c, w);

    xcb_flush(c);

    printf("Done with setup\n");

    while (e = xcb_wait_for_event(c))
    {
        switch (e->response_type & ~0x80)
        {
            case XCB_CONFIGURE_NOTIFY:
                /* We only care about resized, but meh, I'm lazy */
                conf = (xcb_configure_notify_event_t*) e;
                do_corners(c, w, s, conf->width, conf->height);
                xcb_flush(c);
                break;
        }
        free(e);
    }

    xcb_disconnect(c);

    return 0;
}

Reply via email to