diff --git a/src/bin/xmoji/emojibutton.c b/src/bin/xmoji/emojibutton.c index 8134976..96e5fd8 100644 --- a/src/bin/xmoji/emojibutton.c +++ b/src/bin/xmoji/emojibutton.c @@ -2,6 +2,7 @@ #include "flowgrid.h" #include "flyout.h" +#include "shape.h" #include "textlabel.h" #include "textrenderer.h" #include "unistr.h" @@ -24,23 +25,18 @@ struct EmojiButton Object base; FlowGrid *flowgrid; Flyout *flyout; - xcb_pixmap_t pixmap; - xcb_render_picture_t pic; + Shape *triangle; xcb_render_picture_t pen; Color pencolor; + Size trianglesize; int selected; }; static void destroy(void *obj) { EmojiButton *self = obj; - if (self->pen) - { - xcb_connection_t *c = X11Adapter_connection(); - xcb_render_free_picture(c, self->pen); - xcb_render_free_picture(c, self->pic); - xcb_free_pixmap(c, self->pixmap); - } + if (self->pen) xcb_render_free_picture(X11Adapter_connection(), self->pen); + Shape_destroy(self->triangle); free(self); } @@ -85,62 +81,70 @@ static void renderCallback(void *ctx, TextRenderer *renderer) } } -static void prerender(EmojiButton *self, Size newSize) +static xcb_render_picture_t renderTriangle(xcb_render_picture_t ownerpic, + const void *data) { - if (!self->flyout) return; - + const Size *sz = data; xcb_connection_t *c = X11Adapter_connection(); - xcb_render_picture_t picture = Widget_picture(self); + xcb_screen_t *s = X11Adapter_screen(); - if (!self->pen) - { - xcb_pixmap_t p = xcb_generate_id(c); - CHECK(xcb_create_pixmap(c, 24, p, X11Adapter_screen()->root, 1, 1), - "Cannot create pen pixmap for 0x%x", (unsigned)picture); - self->pen = xcb_generate_id(c); - uint32_t repeat = XCB_RENDER_REPEAT_NORMAL; - CHECK(xcb_render_create_picture(c, self->pen, p, - X11Adapter_rgbformat(), XCB_RENDER_CP_REPEAT, &repeat), - "Cannot create pen for 0x%x", (unsigned)picture); - Color fill = 0xffffffff; - xcb_rectangle_t rect = {0, 0, 1, 1}; - CHECK(xcb_render_fill_rectangles(c, XCB_RENDER_PICT_OP_OVER, - self->pen, Color_xcb(fill), 1, &rect), - "Canot colorize pen for 0x%x", (unsigned)picture); - xcb_free_pixmap(c, p); - } - if (self->pic) - { - xcb_render_free_picture(c, self->pic); - xcb_free_pixmap(c, self->pixmap); - } - self->pixmap = xcb_generate_id(c); - CHECK(xcb_create_pixmap(c, 8, self->pixmap, - X11Adapter_screen()->root, - newSize.width, newSize.height), - "Cannot create triangle pixmap for 0x%x", - (unsigned)picture); - self->pic = xcb_generate_id(c); - CHECK(xcb_render_create_picture(c, self->pic, self->pixmap, - X11Adapter_alphaformat(), 0, 0), - "Cannot create triangle picture for 0x%x", - (unsigned)picture); - Color clear = 0; - xcb_rectangle_t rect = { 0, 0, newSize.width, newSize.height }; + xcb_pixmap_t tmp = xcb_generate_id(c); + CHECK(xcb_create_pixmap(c, 8, tmp, s->root, 1, 1), + "Cannot create triangle pen pixmap for 0x%x", (unsigned)ownerpic); + xcb_render_picture_t pen = xcb_generate_id(c); + uint32_t repeat = XCB_RENDER_REPEAT_NORMAL; + CHECK(xcb_render_create_picture(c, pen, tmp, X11Adapter_alphaformat(), + XCB_RENDER_CP_REPEAT, &repeat), + "Cannot create triangle pen for 0x%x", (unsigned)ownerpic); + xcb_free_pixmap(c, tmp); + Color color = 0xffffffff; + xcb_rectangle_t rect = {0, 0, 1, 1}; + CHECK(xcb_render_fill_rectangles(c, XCB_RENDER_PICT_OP_OVER, + pen, Color_xcb(color), 1, &rect), + "Cannot colorize triangle pen for 0x%x", (unsigned)ownerpic); + + tmp = xcb_generate_id(c); + CHECK(xcb_create_pixmap(c, 8, tmp, s->root, sz->width, sz->height), + "Cannot create triangle pixmap for 0x%x", (unsigned)ownerpic); + xcb_render_picture_t pic = xcb_generate_id(c); + CHECK(xcb_render_create_picture(c, pic, tmp, X11Adapter_alphaformat(), + 0, 0), + "Cannot create triangle picture for 0x%x", (unsigned)ownerpic); + xcb_free_pixmap(c, tmp); + color = 0; + rect.width = sz->width; + rect.height = sz->height; CHECK(xcb_render_fill_rectangles(c, XCB_RENDER_PICT_OP_SRC, - self->pic, Color_xcb(clear), 1, &rect), - "Cannot clear triangle picture for 0x%x", - (unsigned)picture); - uint32_t x = newSize.width << 16; - uint32_t y = newSize.height << 16; + pic, Color_xcb(color), 1, &rect), + "Cannot clear triangle picture for 0x%x", (unsigned)ownerpic); + uint32_t x = sz->width << 16; + uint32_t y = sz->height << 16; xcb_render_triangle_t triangle = { - { x - (newSize.width << 14), y }, + { x - (sz->width << 14), y }, { x, y }, - { x, y - (newSize.height << 14) } + { x, y - (sz->height << 14) } }; CHECK(xcb_render_triangles(c, XCB_RENDER_PICT_OP_OVER, - self->pen, self->pic, 0, 0, 0, 1, &triangle), - "Cannot render fly-out indicator for 0x%x", (unsigned)picture); + pen, pic, 0, 0, 0, 1, &triangle), + "Cannot render triangle for 0x%x", (unsigned)ownerpic); + xcb_render_free_picture(c, pen); + + return pic; +} + +static void prerender(EmojiButton *self, Size newSize) +{ + if (self->flyout && newSize.width && newSize.height + && newSize.width != self->trianglesize.width + && newSize.height != self->trianglesize.height) + { + self->trianglesize = newSize; + Shape *triangle = Shape_create(renderTriangle, + sizeof self->trianglesize, &self->trianglesize); + if (self->triangle) Shape_destroy(self->triangle); + Shape_render(triangle, Widget_picture(self)); + self->triangle = triangle; + } } static int draw(void *obj, xcb_render_picture_t picture) @@ -151,6 +155,19 @@ static int draw(void *obj, xcb_render_picture_t picture) if (rc == 0 && picture && self->flyout) { xcb_connection_t *c = X11Adapter_connection(); + if (!self->pen) + { + xcb_pixmap_t tmp = xcb_generate_id(c); + CHECK(xcb_create_pixmap(c, 24, tmp, + X11Adapter_screen()->root, 1, 1), + "Cannot create pen pixmap for 0x%x", (unsigned)picture); + self->pen = xcb_generate_id(c); + uint32_t repeat = XCB_RENDER_REPEAT_NORMAL; + CHECK(xcb_render_create_picture(c, self->pen, tmp, + X11Adapter_rgbformat(), XCB_RENDER_CP_REPEAT, &repeat), + "Cannot create pen for 0x%x", (unsigned)picture); + xcb_free_pixmap(c, tmp); + } Rect geom = Widget_geometry(self); if (!self->pencolor) prerender(self, geom.size); Color pencolor = Widget_color(self, COLOR_ACTIVE); @@ -163,7 +180,7 @@ static int draw(void *obj, xcb_render_picture_t picture) "Canot colorize pen for 0x%x", (unsigned)picture); } CHECK(xcb_render_composite(c, XCB_RENDER_PICT_OP_OVER, self->pen, - self->pic, picture, 0, 0, 0, 0, + Shape_picture(self->triangle), picture, 0, 0, 0, 0, geom.pos.x, geom.pos.y, geom.size.width, geom.size.height), "Cannot composite fly-out indicator on 0x%x", (unsigned)picture); @@ -210,7 +227,7 @@ static void sizeChanged(void *receiver, void *sender, void *args) EmojiButton *self = receiver; SizeChangedEventArgs *ea = args; - if (ea->newSize.width && ea->newSize.height) prerender(self, ea->newSize); + prerender(self, ea->newSize); } EmojiButton *EmojiButton_createBase(void *derived, @@ -220,10 +237,10 @@ EmojiButton *EmojiButton_createBase(void *derived, CREATEBASE(Button, name, parent); self->flowgrid = 0; self->flyout = 0; - self->pixmap = 0; - self->pic = 0; + self->triangle = 0; self->pen = 0; self->pencolor = 0; + self->trianglesize = (Size){0, 0}; self->selected = 0; Button_setBorderWidth(self, 0); diff --git a/src/bin/xmoji/shape.c b/src/bin/xmoji/shape.c new file mode 100644 index 0000000..e5b685f --- /dev/null +++ b/src/bin/xmoji/shape.c @@ -0,0 +1,70 @@ +#include "shape.h" + +#include "x11adapter.h" + +#include +#include +#include + +struct Shape +{ + ShapeRenderer renderer; + const void *data; + size_t datasz; + xcb_render_picture_t picture; + unsigned refcnt; +}; + +static PSC_List *shapes; + +Shape *Shape_create(ShapeRenderer renderer, size_t datasz, const void *data) +{ + if (!shapes) shapes = PSC_List_create(); + for (size_t i = 0; i < PSC_List_size(shapes); ++i) + { + Shape *shape = PSC_List_at(shapes, i); + if (shape->renderer == renderer && shape->datasz == datasz + && !memcmp(shape->data, data, datasz)) + { + ++shape->refcnt; + return shape; + } + } + Shape *self = PSC_malloc(sizeof *self); + self->renderer = renderer; + self->data = data; + self->datasz = datasz; + self->picture = 0; + self->refcnt = 1; + PSC_List_append(shapes, self, 0); + return self; +} + +void Shape_render(Shape *self, xcb_render_picture_t ownerpic) +{ + if (self->picture) return; + self->picture = self->renderer(ownerpic, self->data); +} + +xcb_render_picture_t Shape_picture(const Shape *self) +{ + return self->picture; +} + +void Shape_destroy(Shape *self) +{ + if (!self) return; + if (--self->refcnt) return; + PSC_List_remove(shapes, self); + if (!PSC_List_size(shapes)) + { + PSC_List_destroy(shapes); + shapes = 0; + } + if (self->picture) + { + xcb_render_free_picture(X11Adapter_connection(), self->picture); + } + free(self); +} + diff --git a/src/bin/xmoji/shape.h b/src/bin/xmoji/shape.h new file mode 100644 index 0000000..ae037d3 --- /dev/null +++ b/src/bin/xmoji/shape.h @@ -0,0 +1,18 @@ +#ifndef XMOJI_SHAPE_H +#define XMOJI_SHAPE_H + +#include +#include + +C_CLASS_DECL(Shape); + +typedef xcb_render_picture_t (*ShapeRenderer)( + xcb_render_picture_t ownerpic, const void *data); + +Shape *Shape_create(ShapeRenderer renderer, + size_t datasz, const void *data) ATTR_RETNONNULL; +void Shape_render(Shape *self, xcb_render_picture_t ownerpic) CMETHOD; +xcb_render_picture_t Shape_picture(const Shape *self) CMETHOD ATTR_PURE; +void Shape_destroy(Shape *self); + +#endif diff --git a/src/bin/xmoji/xmoji.mk b/src/bin/xmoji/xmoji.mk index 0846397..1a83140 100644 --- a/src/bin/xmoji/xmoji.mk +++ b/src/bin/xmoji/xmoji.mk @@ -21,6 +21,7 @@ xmoji_MODULES= button \ object \ pixmap \ scrollbox \ + shape \ surface \ tabbox \ textbox \