Commit Diff


commit - /dev/null
commit + cb5e051aec61c8910f1a8335320b6dd435005c1d
blob - /dev/null
blob + 6ac7bd351e23a9d3c253521d1db27fec0488bf12 (mode 644)
--- /dev/null
+++ LICENSE
@@ -0,0 +1,13 @@
+Copyright (c) 2025 Onana Onana Xavier Manuel <xavier@onana.net>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED AS IS AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
blob - /dev/null
blob + 2224ddef2b331222a6301be020522753cdebd389 (mode 644)
--- /dev/null
+++ Makefile
@@ -0,0 +1,26 @@
+CFLAGS = -fPIC -std=c99
+
+PREFIX=/usr/local
+
+INCS=-I ./ -I /usr/X11R6/include -I /usr/X11R6/include/freetype2
+
+LIBS=-L /usr/X11R6/lib -lxcb -lxcb-render -lxcb-render-util -lxcb-image -lfreetype -lm -lfontconfig
+
+SRCS= window.c layout.c group.c context.c shape.c event.c label.c image.c
+
+OBJS=${SRCS:.c=.o}
+
+
+all: mui
+
+.c.o:
+	${CC} -c ${CFLAGS} ${INCS} $< -o $@
+
+
+mui: ${OBJS}
+	${CC} -shared -Wl,-soname,libmui.so -o libmui.so ${OBJS} ${LIBS}
+
+clean:
+	rm -rf ${OBJS} libmui.so
+
+.PHONY: all mui clean fonts uninstall
blob - /dev/null
blob + c5e9a12c1ea1174ea41c329c59240654e68ae840 (mode 644)
--- /dev/null
+++ README
@@ -0,0 +1 @@
+mui stands for minimal UI
blob - /dev/null
blob + 2fde96a624cd67966672ed3bfcb09cb4d96e552c (mode 644)
--- /dev/null
+++ TODO
@@ -0,0 +1,47 @@
+Event targets
+
+Debug macro
+
+Complete man `mui.1` for demos
+
+Complete man `mui.3`
+
+Nested groups
+
+Groups styles (Scroll, VStack, HStack, Grid)
+
+Group clipping
+
+Font faces (color, size, style)
+
+Minimum layout dimensions
+
+Multiline support text rendering
+
+Man page
+
+Fix image rendering and text rendering on FreeBSD and NetBSD (DONE)
+
+Fix issue when text render with noise when having a small len (DONE?)
+
+Cleanup functions aka delete functions (DONE)
+
+Fix alignment to update when added to group (DONE)
+
+New flag 'MUI_SCALE_X' and 'MUI_SCALE_Y' which will be actual scale of
+the layout while 'MUI_EXPAND_X' and 'MUI_EXPAND_Y' will be the actual
+width and height. (DONE)
+
+Proper scale matrix for xcb_render (DONE)
+
+Layout anchoring (DONE)
+
+Change shape color after adding to group (DONE)
+
+Events (KeyPress and KeyRelease) (DONE)
+
+Fix issue when shape changes color but isn't visible until update (DONE)
+
+Image rendering (DONE)
+
+Text rendering (the hardest) (DONE)
blob - /dev/null
blob + c6cd2ed28ae61171708a3e79ed1c71dc0464f649 (mode 755)
--- /dev/null
+++ cleanup.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# Run this script before pushing
+
+make clean
+
+for folder in demo/*; do
+    [ -d $folder ] && [ -f $folder/Makefile ] && make -C $folder clean
+done
blob - /dev/null
blob + 98401c97a40dd5fb6e56f4dbb3e0840c44a1750d (mode 644)
--- /dev/null
+++ context.c
@@ -0,0 +1,782 @@
+#include "mui.h"
+
+#include <xcb/xcb.h>
+#include <xcb/render.h>
+#include <xcb/xcb_image.h>
+#include <xcb/xcb_renderutil.h>
+
+#include <ft2build.h>
+#include <freetype/freetype.h>
+#include <fontconfig/fontconfig.h>
+
+#define MAX_FONTS       100
+#define MAX_UINT16      65536
+
+struct ctx_node {
+    void *ptr;
+    uint16_t type;
+    xcb_render_picture_t picture;
+    xcb_render_transform_t transform;
+};
+
+struct ctx_font {
+    char   *path;
+    int     height;
+    int     widths[128]; /* Latin Characters */
+    xcb_render_glyphset_t gs;
+};
+
+struct {
+    xcb_connection_t       *conn;
+    const xcb_setup_t      *setup;
+    xcb_screen_t           *screen;
+
+    uint32_t                fmt_normal;
+    uint32_t                fmt_alpha8;
+    uint32_t                fmt_argb32;
+
+    struct ctx_font        *fonts[MAX_FONTS];
+    FT_Library              ft_library;
+
+    /* 
+     * Linked list used to keep track on existing
+     * contexes for operations such as distributing
+     * events among windows
+     */
+    LIST_HEAD(, mui_ctx)    ctx_head;
+} xcb;
+
+
+struct mui_ctx {
+    xcb_window_t                window;
+    xcb_gcontext_t              gc;
+    xcb_pixmap_t                pixmap;
+    xcb_render_picture_t        picture;
+    xcb_intern_atom_reply_t    *wm_delete_window;
+
+    struct                  mui_window *mw;
+    LIST_ENTRY(mui_ctx)     entries;
+};
+
+void
+ctx_create_bg(struct mui_ctx *ctx)
+{
+    uint32_t mask;
+    uint16_t width;
+    uint16_t height;
+    uint32_t values[2];
+    struct mui_group *group;
+    struct mui_layout *layout;
+
+    if (ctx->pixmap) xcb_free_pixmap(xcb.conn, ctx->pixmap);
+    if (ctx->picture) xcb_render_free_picture(xcb.conn, ctx->picture);
+
+    group = mui_get_window_group(ctx->mw);
+    layout = mui_get_group_layout(group);
+
+    width = mui_get_layout_width(layout);
+    height = mui_get_layout_height(layout);
+
+    ctx->pixmap = xcb_generate_id(xcb.conn);
+    ctx->picture = xcb_generate_id(xcb.conn);
+
+    xcb_create_pixmap(xcb.conn, xcb.screen->root_depth, ctx->pixmap, xcb.screen->root,
+        width, height);
+
+    mask = XCB_RENDER_CP_POLY_MODE | XCB_RENDER_CP_POLY_EDGE;
+    values[0] = XCB_RENDER_POLY_MODE_IMPRECISE;
+    values[1] = XCB_RENDER_POLY_EDGE_SMOOTH;
+
+    xcb_render_create_picture(xcb.conn, ctx->picture, ctx->pixmap, xcb.fmt_normal, mask,
+        values);
+}
+
+void
+ctx_load_formats(void)
+{
+    xcb_render_query_pict_formats_cookie_t cookie =
+        xcb_render_query_pict_formats_unchecked(xcb.conn);
+
+    xcb_render_query_pict_formats_reply_t *reply =
+        xcb_render_query_pict_formats_reply(xcb.conn, cookie, NULL);
+
+    xcb.fmt_normal = xcb_render_util_find_visual_format(reply, 
+        xcb.screen->root_visual)->format;
+
+    xcb.fmt_argb32 = xcb_render_util_find_standard_format(reply, 
+        XCB_PICT_STANDARD_ARGB_32)->id;
+
+    xcb.fmt_alpha8 = xcb_render_util_find_standard_format(reply, 
+        XCB_PICT_STANDARD_A_8)->id;
+
+}
+
+static void
+ctx_load_glyph(struct ctx_font *font, FT_Face face, int charcode)
+{
+    int gindx;
+    uint32_t gid;
+    FT_Bitmap *bitmap;
+    xcb_render_glyphinfo_t ginfo;
+
+    FT_Select_Charmap(face, ft_encoding_unicode);
+    gindx = FT_Get_Char_Index(face, charcode);
+
+    FT_Load_Glyph(face, gindx, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT);
+
+    bitmap = &face->glyph->bitmap;
+    ginfo.x = -face->glyph->bitmap_left;
+    ginfo.y =  face->glyph->bitmap_top;
+    ginfo.width = bitmap->width;
+    ginfo.height = bitmap->rows;
+    ginfo.x_off = face->glyph->advance.x / 64;
+    ginfo.y_off = face->glyph->advance.y / 64;
+
+    gid = charcode;
+
+    int y;
+    int stride = (ginfo.width+3)&~3;
+    uint8_t tmp_bitmap[stride * ginfo.height];
+
+    for (y = 0; y < ginfo.height; y++)
+        memcpy(tmp_bitmap + y * stride, bitmap->buffer + y * ginfo.width, ginfo.width);
+
+    int height = face->glyph->metrics.height / 64;
+    font->height = (font->height < height) ? height : font->height;
+    font->widths[charcode] = face->glyph->advance.x / 64;
+
+    xcb_render_add_glyphs(xcb.conn, font->gs, 1, &gid, &ginfo, stride * ginfo.height, tmp_bitmap);
+}
+
+struct ctx_font*
+ctx_load_font()
+{
+    FT_Face     face;
+    FcChar8    *fpath;
+    FcConfig   *config;
+    FcPattern  *pattern;
+    FcPattern  *matched;
+    FcResult    result;
+    struct      ctx_font *font;
+    int         font_size;
+
+    if (FT_Init_FreeType(&xcb.ft_library)) {
+        exit(1);
+    }
+
+    font = calloc(1, sizeof(*font));
+    config = FcInitLoadConfigAndFonts();
+
+    if (!config) {
+        printf("Failed to load font config and fonts\n");
+        exit(1);
+    }
+
+    pattern = FcPatternCreate();
+    FcPatternAddString(pattern, FC_FAMILY, (const FcChar8 *)"sans-serif");
+
+    FcDefaultSubstitute(pattern);
+    matched = FcFontMatch(config, pattern, &result);
+
+    if (!matched)
+        exit(1);
+
+    if (FcPatternGetString(matched, FC_FILE, 0, &fpath) == FcResultMatch) {
+
+    }
+
+    font_size = 16;
+    font->gs = xcb_generate_id(xcb.conn);
+    xcb_render_create_glyph_set(xcb.conn, font->gs, xcb.fmt_alpha8);
+    printf("%s\n", fpath);
+
+    if (FT_New_Face(xcb.ft_library, fpath, 0, &face)) {
+        exit(1);
+    }
+
+    FT_Set_Char_Size(face, 0, font_size * 64, 90, 90);
+
+    for (int n = 32; n < 128; n++)
+        ctx_load_glyph(font, face, n);
+
+    FT_Done_Face(face);
+
+    return font;
+}
+
+struct mui_group*
+ctx_get_node_group(struct ctx_node *node)
+{
+    switch (node->type) {
+        case MUI_SHAPE:
+            return mui_get_shape_group(node->ptr);
+        case MUI_LABEL:
+            return mui_get_label_group(node->ptr);
+        case MUI_IMAGE:
+            return mui_get_image_group(node->ptr);
+    }
+    return NULL;
+}
+
+
+struct mui_ctx*
+mui_create_ctx(struct mui_window *mw)
+{
+    uint32_t mask;
+    uint32_t values[2];
+    uint16_t width;
+    uint16_t height;
+    struct mui_ctx *ctx;
+    struct mui_group *group;
+    struct mui_layout *layout;
+    xcb_intern_atom_reply_t *reply;
+    xcb_intern_atom_cookie_t cookie1, cookie2;
+
+    if (xcb.conn == NULL) {
+        xcb.conn = xcb_connect(NULL, NULL);
+
+        xcb.setup = xcb_get_setup(xcb.conn);
+        xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb.setup);
+        xcb.screen = iter.data;
+
+        ctx_load_formats();
+        xcb.fonts[0] = ctx_load_font();
+        LIST_INIT(&xcb.ctx_head);
+    }
+
+
+    ctx = calloc(1, sizeof(*ctx));
+    ctx->mw = mw;
+
+    mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
+    values[0] = xcb.screen->white_pixel;
+    values[1] = XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_POINTER_MOTION |
+                XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE |
+                XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | 
+                XCB_EVENT_MASK_STRUCTURE_NOTIFY;
+
+    group = mui_get_window_group(mw);
+    layout = mui_get_group_layout(group);
+    width = mui_get_layout_width(layout);
+    height = mui_get_layout_height(layout);
+    ctx->window = xcb_generate_id(xcb.conn);
+
+    xcb_create_window(xcb.conn,
+                      XCB_COPY_FROM_PARENT,
+                      ctx->window,
+                      xcb.screen->root,
+                      0, 0,
+                      width,
+                      height,
+                      0,
+                      XCB_WINDOW_CLASS_INPUT_OUTPUT,
+                      xcb.screen->root_visual,
+                      mask,
+                      values
+    );
+
+    mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
+    values[0] = 0xcbcbcb | 0xff000000;
+    values[1] = 0;
+
+    const xcb_change_window_attributes_value_list_t cw = { 0 };
+    xcb_change_window_attributes(xcb.conn, ctx->window, XCB_CW_BACK_PIXMAP, &cw);
+
+    ctx->gc = xcb_generate_id(xcb.conn);
+    xcb_create_gc(xcb.conn, ctx->gc, ctx->window, mask, values);
+
+    ctx_create_bg(ctx);
+
+    cookie1 = xcb_intern_atom(xcb.conn, 1, 12, "WM_PROTOCOLS");
+    cookie2 = xcb_intern_atom(xcb.conn, 1, 16, "WM_DELETE_WINDOW");
+
+    reply = xcb_intern_atom_reply(xcb.conn, cookie1, 0);
+    ctx->wm_delete_window = xcb_intern_atom_reply(xcb.conn, cookie2, 0);
+
+    xcb_change_property(xcb.conn, XCB_PROP_MODE_REPLACE, ctx->window, reply->atom,
+        4, 32, 1, &(ctx->wm_delete_window->atom));
+    xcb_change_property(xcb.conn, XCB_PROP_MODE_REPLACE, ctx->window,
+        XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8, strlen(mui_get_window_name(ctx->mw)),
+        mui_get_window_name(ctx->mw));
+
+
+    xcb_map_window(xcb.conn, ctx->window);
+    xcb_flush(xcb.conn);
+
+    LIST_INSERT_HEAD(&xcb.ctx_head, ctx, entries);
+    return ctx;
+}
+
+void
+mui_delete_ctx(struct mui_ctx *ctx)
+{
+
+}
+
+xcb_render_picture_t
+ctx_create_shape(struct ctx_node *node)
+{
+    uint16_t width;
+    uint16_t height;
+    xcb_rectangle_t rect;
+    xcb_pixmap_t xcb_pixmap;
+    xcb_render_picture_t xcb_picture;
+    xcb_render_color_t xcb_color;
+
+    struct mui_shape *shape;
+    struct mui_layout *layout;
+    struct mui_color color;
+
+    shape = (struct mui_shape *)node->ptr;
+    layout = mui_get_shape_layout(shape);
+    color = mui_get_shape_color(shape);
+
+    width = mui_get_layout_width(layout);
+    height = mui_get_layout_height(layout);
+
+    xcb_color.red   = color.r * 0xffff;
+    xcb_color.green = color.g * 0xffff;
+    xcb_color.blue  = color.b * 0xffff;
+    xcb_color.alpha = color.a * 0xffff;
+
+    rect.x = 0;
+    rect.y = 0;
+    rect.width = width;
+    rect.height = height;
+
+    xcb_pixmap = xcb_generate_id(xcb.conn);
+    xcb_picture = xcb_generate_id(xcb.conn);
+
+    xcb_create_pixmap(xcb.conn, 32, xcb_pixmap, xcb.screen->root, width, height);
+    xcb_render_create_picture(xcb.conn, xcb_picture, xcb_pixmap, xcb.fmt_argb32, 0, NULL);
+    xcb_render_fill_rectangles(xcb.conn, XCB_RENDER_PICT_OP_OVER, xcb_picture, xcb_color, 1, &rect);
+    xcb_free_pixmap(xcb.conn, xcb_pixmap);
+    return xcb_picture;
+}
+
+
+/*
+ * This creates a picture that contains glyphs that will be then
+ * composited to the context picture
+ */
+
+struct gheader {
+    uint8_t     count;
+    uint8_t     pad[3];
+    uint16_t    dx;
+    uint16_t    dy;
+};
+
+xcb_render_picture_t
+ctx_create_label(struct ctx_node *node)
+{
+    uint32_t mask;
+    uint32_t value;
+    uint16_t width;
+    uint16_t height;
+    xcb_pixmap_t xcb_pixmap;
+    xcb_render_color_t color;
+    xcb_rectangle_t rectangle;
+    xcb_render_picture_t xcb_picture;
+    struct mui_label *label;
+    struct mui_layout *layout;
+
+    label = (struct mui_label *)node->ptr;
+    layout = mui_get_label_layout(label);
+
+    width = mui_get_layout_width(layout);
+    height = mui_get_layout_height(layout) * 1.5;
+
+    rectangle.x = 0;
+    rectangle.y = 0;
+    rectangle.width = width;
+    rectangle.height = height;
+
+    color.red   = 0;
+    color.green = 0;
+    color.blue  = 0;
+    color.alpha = 0;
+
+    xcb_pixmap = xcb_generate_id(xcb.conn);
+    xcb_picture = xcb_generate_id(xcb.conn);
+
+    xcb_create_pixmap(xcb.conn, 32, xcb_pixmap, xcb.screen->root, width, height);
+    xcb_render_create_picture(xcb.conn, xcb_picture, xcb_pixmap, xcb.fmt_argb32, 0, NULL);
+    xcb_render_fill_rectangles(xcb.conn, XCB_RENDER_PICT_OP_OVER, xcb_picture, color, 1, &rectangle);
+
+    /*
+     * Actual drawing the text
+     */
+    xcb_pixmap_t            gpixmap;
+    xcb_render_picture_t    gpicture;
+    struct gheader          header;
+    uint32_t               *data;
+    char                   *text;
+
+    header.count = mui_get_label_len(label);
+    header.pad[0] = 0;
+    header.pad[1] = 0;
+    header.pad[2] = 0;
+    header.dx = 0;
+    header.dy = xcb.fonts[0]->height;
+
+    width = 1;
+    height = 1;
+    rectangle.x = 0;
+    rectangle.y = 0;
+    rectangle.width = width;
+    rectangle.height = height;
+
+    color.red = 0;
+    color.green = 0;
+    color.blue = 0;
+    color.alpha = 0xffff;
+
+    mask = XCB_RENDER_CP_REPEAT;
+    value = XCB_RENDER_REPEAT_NORMAL;
+
+    gpixmap = xcb_generate_id(xcb.conn);
+    gpicture = xcb_generate_id(xcb.conn);
+
+    xcb_create_pixmap(xcb.conn, 32, gpixmap, xcb.screen->root, width, height);
+    xcb_render_create_picture(xcb.conn, gpicture, gpixmap, xcb.fmt_argb32, mask, &value);
+    xcb_render_fill_rectangles(xcb.conn, XCB_RENDER_PICT_OP_OVER, gpicture, color, 1, &rectangle);
+
+
+    void *line;
+    line = NULL;
+
+    header.count = mui_get_label_len(label);
+
+    text = mui_get_label_text(label);
+    data = malloc(sizeof(uint32_t) * header.count + sizeof(header));
+
+    for (int i = 0; i < header.count; i++)
+        data[i + sizeof(header) / sizeof(uint32_t)] = text[i];
+
+    memcpy(data, &header, sizeof(header));
+
+    xcb_render_composite_glyphs_32(xcb.conn, XCB_RENDER_PICT_OP_OVER, gpicture, xcb_picture, 0,
+        xcb.fonts[0]->gs, 0, 0, sizeof(header) + header.count * sizeof(uint32_t), (uint8_t *)data);
+
+    free(data);
+
+    xcb_render_free_picture(xcb.conn, gpicture);
+    xcb_free_pixmap(xcb.conn, gpixmap);
+    xcb_free_pixmap(xcb.conn, xcb_pixmap);
+
+    return xcb_picture;
+}
+
+xcb_render_picture_t
+ctx_create_image(struct ctx_node *node)
+{
+    uint8_t                *data;
+    uint16_t                width;
+    uint16_t                height;
+    xcb_image_t            *xcb_image;
+    xcb_pixmap_t            xcb_pixmap;
+    xcb_gcontext_t          gc;
+    xcb_render_picture_t    xcb_picture;
+    struct                  mui_layout *layout;
+    struct                  mui_group *group;
+
+    data = (uint8_t *)mui_get_image_data(node->ptr);
+    group = mui_get_image_group(node->ptr);
+    layout = mui_get_image_layout(node->ptr);
+    width = mui_get_layout_width(layout);
+    height = mui_get_layout_height(layout);
+
+    xcb_pixmap = xcb_generate_id(xcb.conn);
+    xcb_picture = xcb_generate_id(xcb.conn);
+
+    xcb_create_pixmap(xcb.conn, 32, xcb_pixmap, xcb.screen->root, width, height);
+    xcb_image = xcb_image_create_native(xcb.conn, width, height, XCB_IMAGE_FORMAT_Z_PIXMAP,
+        32, NULL, 0, data);
+
+    xcb_render_create_picture(xcb.conn, xcb_picture, xcb_pixmap, xcb.fmt_argb32, 0, NULL);
+
+    gc = xcb_generate_id(xcb.conn);
+    xcb_create_gc(xcb.conn, gc, xcb_pixmap, 0, NULL);
+    xcb_image_put(xcb.conn, xcb_pixmap, gc, xcb_image, 0, 0, 0);
+
+    xcb_free_gc(xcb.conn, gc);
+    xcb_image_destroy(xcb_image);
+    xcb_free_pixmap(xcb.conn, xcb_pixmap);
+
+    return xcb_picture;
+}
+
+void*
+mui_ctx_create_node(struct mui_ctx *ctx, uint16_t type, void *data)
+{
+    struct ctx_node *node;
+
+    node = malloc(sizeof(*node));
+    node->type = type;
+    node->ptr = data;
+
+    switch (type) {
+        case MUI_SHAPE:
+            node->picture = ctx_create_shape(node);
+            mui_ctx_transform_node(ctx, node);
+            break;
+
+        case MUI_LABEL:
+            node->picture = ctx_create_label(node);
+            mui_ctx_transform_node(ctx, node);
+            break;
+
+        case MUI_IMAGE:
+            node->picture = ctx_create_image(node);
+            mui_ctx_transform_node(ctx, node);
+            break;
+    }
+    return node;
+}
+
+void
+mui_ctx_delete_node(void *node)
+{
+    struct ctx_node *ctx_node; 
+
+    ctx_node = (struct ctx_node *)node;
+    xcb_render_free_picture(xcb.conn, ctx_node->picture);
+    free(node);
+}
+
+
+void
+ctx_draw(struct mui_ctx *ctx)
+{
+    uint16_t width;
+    uint16_t height;
+    struct mui_group *group;
+    struct mui_layout *layout;
+
+    group = mui_get_window_group(ctx->mw);
+    layout = mui_get_group_layout(group);
+
+    width = mui_get_layout_width(layout);
+    height = mui_get_layout_height(layout);
+
+    xcb_render_fill_rectangles(xcb.conn, XCB_RENDER_PICT_OP_OVER, ctx->picture,
+        (xcb_render_color_t) { 0xcbcb, 0xcbcb, 0xcbcb, 0xffff }, 1, 
+        &(xcb_rectangle_t) { 0, 0, width, height });
+
+    mui_draw_group_nodes(group);
+
+    xcb_copy_area(xcb.conn, ctx->pixmap, ctx->window, ctx->gc, 0, 0, 0, 0, width, height);
+    xcb_flush(xcb.conn);
+}
+
+void
+mui_ctx_transform_node(struct mui_ctx *ctx, void *node)
+{
+    float *matrix;
+    struct ctx_node *ctx_node; 
+    struct mui_layout *layout;
+
+    ctx_node = (struct ctx_node *)node;
+
+    switch (ctx_node->type) {
+        case MUI_SHAPE:
+            layout = mui_get_shape_layout(ctx_node->ptr);
+            break;
+
+        case MUI_LABEL:
+            layout = mui_get_label_layout(ctx_node->ptr);
+            break;
+
+        case MUI_IMAGE:
+            layout = mui_get_image_layout(ctx_node->ptr);
+            break;
+    }
+
+    matrix = mui_get_layout_matrix(layout);
+
+    ctx_node->transform.matrix11 = MAX_UINT16 * matrix[0];
+    ctx_node->transform.matrix12 = MAX_UINT16 * matrix[1];
+    ctx_node->transform.matrix13 = MAX_UINT16 * matrix[2];
+    ctx_node->transform.matrix21 = MAX_UINT16 * matrix[3];
+    ctx_node->transform.matrix22 = MAX_UINT16 * matrix[4];
+    ctx_node->transform.matrix23 = MAX_UINT16 * matrix[5];
+    ctx_node->transform.matrix31 = MAX_UINT16 * matrix[6];
+    ctx_node->transform.matrix32 = MAX_UINT16 * matrix[7];
+    ctx_node->transform.matrix33 = MAX_UINT16 * matrix[8];
+
+    free(matrix);
+    ctx_draw(ctx);
+}
+
+
+void
+mui_ctx_draw_node(struct mui_ctx *ctx, void *node)
+{
+    if (node == NULL)
+        return;
+
+    uint16_t width;
+    uint16_t height;
+    struct mui_group *group;
+    struct mui_layout *layout;
+    struct ctx_node *ctx_node;
+
+    ctx_node = (struct ctx_node *)node;
+    group = ctx_get_node_group(ctx_node);
+    layout = mui_get_group_layout(group);
+    width = mui_get_layout_width(layout);
+    height = mui_get_layout_height(layout);
+
+    xcb_render_set_picture_transform(xcb.conn, ctx_node->picture, ctx_node->transform);
+
+    xcb_render_composite(xcb.conn, XCB_RENDER_PICT_OP_OVER, ctx_node->picture,
+        0, ctx->picture, 0, 0, 0, 0, 0, 0, width, height);
+}
+
+struct mui_ctx*
+ctx_find_from_window(xcb_window_t xcb_window)
+{
+    struct mui_ctx *np;
+    LIST_FOREACH(np, &xcb.ctx_head, entries) {
+        if (np->window == xcb_window)
+            return np;
+    }
+    return NULL;
+}
+
+int
+mui_ctx_get_glyph_width(char glyph, int font_id)
+{
+    return xcb.fonts[font_id]->widths[glyph];
+}
+
+int
+mui_ctx_get_font_height(int font_id)
+{
+    return xcb.fonts[font_id]->height;
+}
+
+void
+mui_ctx_process_event()
+{
+    xcb_generic_event_t            *event;
+    xcb_expose_event_t             *expose;
+    xcb_client_message_event_t     *client_message;
+    xcb_button_press_event_t       *button_press;
+    xcb_button_release_event_t     *button_release;
+    xcb_motion_notify_event_t      *motion_notify;
+    xcb_key_press_event_t          *key_press;
+    xcb_key_release_event_t        *key_release;
+    xcb_configure_notify_event_t   *configure_notify;
+
+    struct mui_ctx                 *ctx;
+    struct mui_group               *group;
+    struct mui_layout              *layout;
+    struct mui_event_handler       *handler;
+
+    while ((event = xcb_poll_for_event(xcb.conn))) {
+        switch (event->response_type & ~0x80) {
+            case XCB_EXPOSE:
+                xcb_flush(xcb.conn);
+                break;
+
+            case XCB_CLIENT_MESSAGE:
+                client_message = (xcb_client_message_event_t *)event;
+                ctx = ctx_find_from_window(client_message->window);
+                handler = mui_get_window_event_handler(ctx->mw);
+
+                if (client_message->data.data32[0] == ctx->wm_delete_window->atom)
+                    mui_push_event(handler, &(struct mui_event) { .type = MUI_EVENT_QUIT });
+                break;
+
+            case XCB_BUTTON_PRESS:
+                button_press = (xcb_button_press_event_t *)event;
+                ctx = ctx_find_from_window(button_press->event);
+                handler = mui_get_window_event_handler(ctx->mw);
+
+                mui_push_event(handler, &(struct mui_event) { .type = MUI_EVENT_MOUSE_PRESS,
+                    .mouse_press = {
+                        .button = button_press->detail,
+                        .x = button_press->event_x,
+                        .y = button_press->event_y
+                    }
+                });
+                break;
+
+            case XCB_BUTTON_RELEASE:
+                button_release = (xcb_button_release_event_t *)event;
+                ctx = ctx_find_from_window(button_release->event);
+                handler = mui_get_window_event_handler(ctx->mw);
+
+                mui_push_event(handler, &(struct mui_event) { .type = MUI_EVENT_MOUSE_RELEASE,
+                    .mouse_release = {
+                        .button = button_release->detail,
+                        .x = button_release->event_x,
+                        .y = button_release->event_y
+                    }
+                });
+                break;
+
+            case XCB_MOTION_NOTIFY:
+                motion_notify = (xcb_motion_notify_event_t *)event;
+                ctx = ctx_find_from_window(motion_notify->event);
+                handler = mui_get_window_event_handler(ctx->mw);
+
+                mui_push_event(handler, &(struct mui_event) { .type = MUI_EVENT_MOUSE_MOTION,
+                    .mouse_motion = {
+                        .x = motion_notify->event_x,
+                        .y = motion_notify->event_y
+                    }
+                });
+                break;
+
+            case XCB_KEY_PRESS:
+                key_press = (xcb_key_press_event_t *)event;
+                ctx = ctx_find_from_window(key_press->event);
+                handler = mui_get_window_event_handler(ctx->mw);
+
+
+                mui_push_event(handler, &(struct mui_event) { .type = MUI_EVENT_KEY_PRESS,
+                    .key_press = {
+                        .key = key_press->detail,
+                    }
+                });
+                break;
+
+            case XCB_KEY_RELEASE:
+                key_release = (xcb_key_release_event_t *)event;
+                ctx = ctx_find_from_window(key_release->event);
+                handler = mui_get_window_event_handler(ctx->mw);
+
+                mui_push_event(handler, &(struct mui_event) { .type = MUI_EVENT_KEY_RELEASE,
+                    .key_release = {
+                        .key = key_release->detail
+                    }
+                });
+                break;
+
+            case XCB_CONFIGURE_NOTIFY:
+                configure_notify = (xcb_configure_notify_event_t *)event;
+                ctx = ctx_find_from_window(configure_notify->window);
+                group = mui_get_window_group(ctx->mw);
+                layout = mui_get_group_layout(group);
+
+                /*
+                 * Resize the window layout here then proceed
+                 * to redraw the contex with the new values
+                 */
+                if (mui_get_layout_width(layout) != configure_notify->width ||
+                    mui_get_layout_height(layout) != configure_notify->height) {
+
+                    mui_resize_layout(layout, configure_notify->width, configure_notify->height);
+                    ctx_create_bg(ctx);
+
+                }
+
+                ctx_draw(ctx);
+                break;
+        }
+        free(event);
+    }
+}
blob - /dev/null
blob + 5b823fd26ce57b28b0e2b525da790bab22b41c92 (mode 644)
Binary files /dev/null and demo/common/fish.raw differ
blob - /dev/null
blob + b38610dd68f45e3855a0b7a1c0ed8de241ebb0d2 (mode 644)
Binary files /dev/null and demo/common/flag.raw differ
blob - /dev/null
blob + 3ff5ea8b0ea946d3b5df94cfb36ae72da49356b3 (mode 644)
--- /dev/null
+++ demo/image/Makefile
@@ -0,0 +1,11 @@
+SRCS=main.c
+LIBDIR=../..
+INCS= -I ${LIBDIR}
+LIBS=-Wl,-rpath=${LIBDIR} -L ${LIBDIR} -lmui
+
+all:
+	make MUI_DEBUG=1 -C ${LIBDIR}
+	${CC} ${SRCS} -o bin ${INCS} ${LIBS}
+
+clean:
+	rm -rf bin bin.core
blob - /dev/null
blob + 9f1c515482dc202e4a3c6a404e7d50de332466f0 (mode 644)
--- /dev/null
+++ demo/image/main.c
@@ -0,0 +1,71 @@
+#include <unistd.h>
+
+#include "mui.h"
+
+static struct {
+    struct mui_window  *mw;
+    struct mui_group   *group;
+    struct mui_layout  *layout;
+    struct mui_event_handler *handler;
+} window;
+
+char*
+load_image_data(char *fpath)
+{
+    int c;
+    FILE *fp;
+    long fsize;
+    char *buffer;
+
+    c = 0;
+    fp = fopen(fpath, "rb");
+    buffer = malloc(200 * 200 * 4);
+
+    fseek(fp, 0, SEEK_END);
+    fsize = ftell(fp);
+    fseek(fp, 0, SEEK_SET);
+
+    for (int i = 0; i < (200 * 200 * 4) && c != EOF; i++) {
+        c = fgetc(fp);
+        buffer[i] = c;
+    }
+    return buffer;
+}
+
+int
+main(void)
+{
+    char *data;
+    struct mui_image *image;
+    struct mui_label *label;
+    struct mui_layout *layout;
+    struct mui_layout *layint;
+
+    data = load_image_data("../common/fish.raw");
+    image = mui_create_image(data, 200, 200);
+    label = mui_create_label("The Fish", 8);
+    layout = mui_get_image_layout(image);
+    layint = mui_get_label_layout(label);
+
+    window.mw = mui_create_window("Puffy", 320, 240);
+    window.group = mui_get_window_group(window.mw);
+    window.handler = mui_get_window_event_handler(window.mw);
+
+
+    mui_set_layout_flags(layout, MUI_ALIGN_CENTER_X | MUI_ALIGN_CENTER_Y |
+        MUI_ANCHOR_CENTER_X | MUI_ANCHOR_CENTER_Y);
+    mui_set_layout_flags(layint, MUI_ALIGN_RIGHT | MUI_ALIGN_BOTTOM | MUI_ANCHOR_RIGHT 
+        | MUI_ANCHOR_BOTTOM);
+    mui_group_add(window.group, MUI_IMAGE, image);
+    mui_group_add(window.group, MUI_LABEL, label);
+
+    while ((mui_pop_event(window.handler)).type != MUI_EVENT_QUIT) {
+        mui_update();
+    }
+
+    mui_delete_image(image);
+    mui_delete_label(label);
+    mui_delete_window(window.mw);
+
+    return 0;
+}
blob - /dev/null
blob + 3ff5ea8b0ea946d3b5df94cfb36ae72da49356b3 (mode 644)
--- /dev/null
+++ demo/shape/Makefile
@@ -0,0 +1,11 @@
+SRCS=main.c
+LIBDIR=../..
+INCS= -I ${LIBDIR}
+LIBS=-Wl,-rpath=${LIBDIR} -L ${LIBDIR} -lmui
+
+all:
+	make MUI_DEBUG=1 -C ${LIBDIR}
+	${CC} ${SRCS} -o bin ${INCS} ${LIBS}
+
+clean:
+	rm -rf bin bin.core
blob - /dev/null
blob + f1e0f61e2989ee60003ac6436ae3fa4f6dcf12be (mode 644)
--- /dev/null
+++ demo/shape/main.c
@@ -0,0 +1,65 @@
+/*********************************************************/
+/*
+ * This demo shows some layout transformation in response
+ * to the window being resized
+ */
+/*********************************************************/
+
+#include <unistd.h>
+
+#include "mui.h"
+
+static struct {
+    struct mui_window *mw;
+    struct mui_group  *group;
+    struct mui_layout *layout;
+    struct mui_event_handler *handler;
+} window;
+
+int
+main(void)
+{
+    struct mui_event        event;
+    struct mui_shape       *shape[3];
+    struct mui_layout      *slayout[3];
+
+
+    shape[0] = mui_create_rectangle(120, 30);
+    shape[1] = mui_create_rectangle(120, 30);
+    shape[2] = mui_create_rectangle(100, 100);
+
+    slayout[0] = mui_get_shape_layout(shape[0]);
+    slayout[1] = mui_get_shape_layout(shape[1]);
+    slayout[2] = mui_get_shape_layout(shape[2]);
+
+    mui_set_layout_flags(slayout[0], MUI_SCALE_X);
+
+    mui_set_layout_flags(slayout[1], MUI_SCALE_X | MUI_ANCHOR_RIGHT | 
+    MUI_ANCHOR_BOTTOM | MUI_ALIGN_RIGHT | MUI_ALIGN_BOTTOM);
+
+    mui_set_layout_flags(slayout[2], MUI_KEEP_RATIO | MUI_ANCHOR_CENTER_X | 
+    MUI_ANCHOR_CENTER_Y | MUI_ALIGN_CENTER_X | MUI_ALIGN_CENTER_Y);
+
+
+    window.mw = mui_create_window("Alignment", 320, 240);
+    window.group = mui_get_window_group(window.mw);
+    window.layout = mui_get_group_layout(window.group);
+    window.handler = mui_get_window_event_handler(window.mw);
+
+    mui_group_add(window.group, MUI_SHAPE, shape[2]);
+    mui_group_add(window.group, MUI_SHAPE, shape[1]);
+    mui_group_add(window.group, MUI_SHAPE, shape[0]);
+
+    while ((event = mui_pop_event(window.handler)).type != MUI_EVENT_QUIT) {
+        mui_rotate_layout(slayout[2], 0.01);
+        mui_update();
+        usleep(1000);
+    }
+
+    mui_delete_shape(shape[2]);
+    mui_delete_shape(shape[1]);
+    mui_delete_shape(shape[0]);
+    mui_delete_window(window.mw);
+
+    return 0;
+}
blob - /dev/null
blob + 3ff5ea8b0ea946d3b5df94cfb36ae72da49356b3 (mode 644)
--- /dev/null
+++ demo/text/Makefile
@@ -0,0 +1,11 @@
+SRCS=main.c
+LIBDIR=../..
+INCS= -I ${LIBDIR}
+LIBS=-Wl,-rpath=${LIBDIR} -L ${LIBDIR} -lmui
+
+all:
+	make MUI_DEBUG=1 -C ${LIBDIR}
+	${CC} ${SRCS} -o bin ${INCS} ${LIBS}
+
+clean:
+	rm -rf bin bin.core
blob - /dev/null
blob + 54dd7ff5de77607479a727ae6c1491c06daab714 (mode 644)
--- /dev/null
+++ demo/text/main.c
@@ -0,0 +1,45 @@
+#include <unistd.h>
+
+#include "mui.h"
+
+static struct {
+    struct mui_window *mw;
+    struct mui_group *group;
+    struct mui_layout *layout;
+    struct mui_event_handler *handler;
+} window;
+
+
+char notice[] = "OpenBSD - The secure OS";
+
+int
+main(void)
+{
+    int                 count;
+    struct mui_event    event;
+    struct mui_label   *label;
+    struct mui_layout  *layout;
+
+    window.mw = mui_create_window("Text", 320, 240);
+    window.group = mui_get_window_group(window.mw);
+    window.layout = mui_get_group_layout(window.group);
+    window.handler = mui_get_window_event_handler(window.mw);
+
+    label = mui_create_label(notice, strlen(notice));
+    layout = mui_get_label_layout(label);
+
+    mui_set_layout_flags(layout, MUI_ALIGN_CENTER_X | MUI_ALIGN_CENTER_Y |
+        MUI_ANCHOR_CENTER_X | MUI_ANCHOR_CENTER_Y | MUI_KEEP_RATIO);
+
+    mui_group_add(window.group, MUI_LABEL, label);
+
+    while ((event = mui_pop_event(window.handler)).type != MUI_EVENT_QUIT) {
+        mui_update();
+        usleep(1000);
+    }
+
+    mui_delete_label(label);
+    mui_delete_window(window.mw);
+
+    return 0;
+}
blob - /dev/null
blob + 0bb4a83133f20cb4b6e2c7fea8df799d4a47cc52 (mode 644)
--- /dev/null
+++ event.c
@@ -0,0 +1,67 @@
+#include "mui.h"
+
+struct mui_event_handler {
+    uint8_t         count;
+    TAILQ_HEAD(ev_list, mui_event) head;
+};
+
+
+struct mui_event_handler*
+mui_create_event_handler()
+{
+    struct mui_event_handler *handler;
+
+    handler = calloc(1, sizeof(*handler));
+    TAILQ_INIT(&handler->head);
+    handler->count = 0;
+
+    return handler;
+}
+
+void
+mui_push_event(struct mui_event_handler *handler, struct mui_event *event)
+{
+    struct mui_event *current;
+
+    if (handler->count == MUI_EVENT_MAX) {
+        struct mui_event *tail;
+
+        tail = TAILQ_LAST(&handler->head, ev_list);
+        TAILQ_REMOVE(&handler->head, tail, entries);
+        handler->count--;
+        free(tail);
+    }
+
+    current = calloc(1, sizeof(*current));
+    memcpy(current, event, sizeof(struct mui_event));
+    TAILQ_INSERT_HEAD(&handler->head, current, entries);
+
+    handler->count++;
+}
+
+struct mui_event
+mui_pop_event(struct mui_event_handler *handler)
+{
+    struct mui_event  event;
+    struct mui_event *head;
+
+    event.type = MUI_EVENT_NONE;
+
+    if (handler->count == 0)
+        return event;
+
+    head = TAILQ_FIRST(&handler->head);
+
+    memcpy(&event, head, sizeof(event));
+    TAILQ_REMOVE(&handler->head, head, entries);
+    free(head);
+
+    handler->count--;
+    return event;
+}
+
+void
+mui_event_set_target(struct mui_window *mw, struct mui_event *event)
+{
+    struct mui_event_handler *handler;
+}
blob - /dev/null
blob + 90108fc2a236a4cf13c5c1d62990b01aae977805 (mode 644)
--- /dev/null
+++ group.c
@@ -0,0 +1,275 @@
+#include "mui.h"
+
+struct member {
+    unsigned int type;
+    void *ctx_node;
+    void *ptr;
+
+    TAILQ_ENTRY(member) entries;
+};
+
+struct mui_group {
+    struct          mui_ctx *ctx;
+    struct          mui_layout *layout;
+
+    TAILQ_HEAD(mem_head, member) head;
+};
+
+
+struct mui_group*
+mui_create_group()
+{
+    struct mui_group *group;
+
+    group = malloc(sizeof(*group));
+    group->layout = mui_create_layout(MUI_GROUP, group);
+    group->ctx = NULL;
+
+    TAILQ_INIT(&group->head);
+
+    return group;
+}
+
+void
+mui_delete_group(struct mui_group *group)
+{
+    struct member *np;
+
+    TAILQ_FOREACH(np, &group->head, entries) {
+        mui_group_remove(group, np->type, np->ptr);
+    }
+}
+
+void
+mui_set_group_ctx(struct mui_group *group, struct mui_ctx *ctx)
+{
+    struct member *np;
+
+    TAILQ_FOREACH(np, &group->head, entries) {
+        mui_ctx_delete_node(np->ctx_node);
+
+        if (np->type == MUI_GROUP) {
+            mui_set_group_ctx(group, ctx);
+            continue;
+        }
+        np->ctx_node = mui_ctx_create_node(ctx, np->type, np->ptr);
+    }
+
+    group->ctx = ctx;
+}
+
+struct mui_layout*
+mui_get_group_layout(struct mui_group *group)
+{
+    return group->layout;
+}
+
+
+void
+mui_group_add(struct mui_group *group, uint16_t type, void *data)
+{
+    struct mui_label *d_label;
+    struct mui_shape *d_shape;
+    struct mui_image *d_image;
+    struct mui_group *d_group;
+
+    struct member *member;
+    struct mui_group *current_group;
+
+    switch (type) {
+        case MUI_SHAPE:
+            d_shape = (struct mui_shape *)data;
+            current_group = mui_get_shape_group(d_shape);
+
+            if (current_group != group) {
+                mui_set_shape_group(d_shape, group);
+                return;
+            }
+            break;
+
+        case MUI_LABEL:
+            d_label = (struct mui_label *)data;
+            current_group = mui_get_label_group(d_label);
+
+            if (current_group != group) {
+                mui_set_label_group(d_label, group);
+                return;
+            }
+            break;
+
+        case MUI_IMAGE:
+            d_image = (struct mui_image *)data;
+            current_group = mui_get_image_group(d_image);
+
+            if (current_group != group) {
+                mui_set_image_group(d_image, group);
+                return;
+            }
+            break;
+
+        case MUI_GROUP:
+            d_group = (struct mui_group *)data;
+
+            /* Don't add a group within itself */
+            if (d_group == group)
+                return;
+
+            mui_set_group_ctx(d_group, group->ctx);
+            break;
+
+        default:
+            break;
+    }
+
+    if (mui_in_group(group, type, data)) {
+        printf("Already in group!\n");
+        return;
+    }
+
+
+    member = malloc(sizeof(*member));
+    member->type = type;
+    member->ptr = data;
+    member->ctx_node = (group->ctx && type != MUI_GROUP) ? 
+        mui_ctx_create_node(group->ctx, type, data) : NULL;
+
+    TAILQ_INSERT_TAIL(&group->head, member, entries);
+}
+
+
+void
+mui_group_remove(struct mui_group *group, uint16_t type, void *data)
+{
+    struct mui_label *d_label;
+    struct mui_shape *d_shape;
+    struct mui_image *d_image;
+    struct mui_group *d_group;
+
+    struct member *member;
+    struct mui_group *current_group;
+
+    switch (type) {
+        case MUI_SHAPE:
+            d_shape = (struct mui_shape *)data;
+            current_group = mui_get_shape_group(d_shape);
+
+            if (current_group == group) {
+                mui_set_shape_group(d_shape, NULL);
+                return;
+            }
+            break;
+
+        case MUI_LABEL:
+            d_label = (struct mui_label *)data;
+            current_group = mui_get_label_group(d_label);
+
+            if (current_group == group) {
+                mui_set_label_group(d_label, NULL);
+                return;
+            }
+
+        case MUI_IMAGE:
+            d_image = (struct mui_image *)data;
+            current_group = mui_get_image_group(d_image);
+
+            if (current_group == group) {
+                mui_set_image_group(d_image, NULL);
+                return;
+            }
+
+        default:
+            break;
+    }
+
+
+    struct member *np;
+
+    TAILQ_FOREACH(np, &group->head, entries) {
+        if (np->ptr == data) {
+            TAILQ_REMOVE(&group->head, np, entries);
+            return;
+        }
+    }
+}
+
+void*
+mui_in_group(struct mui_group *group, uint16_t type, void *data)
+{
+    struct member *np;
+
+    TAILQ_FOREACH(np, &group->head, entries) {
+        if (np->ptr == data) return np;
+    }
+    return NULL;
+}
+
+void
+mui_draw_group_nodes(struct mui_group *group)
+{
+    if (group->ctx == NULL)
+        return;
+
+    struct member *member;
+
+    TAILQ_FOREACH(member, &group->head, entries) {
+        if (member->type == MUI_GROUP) {
+            mui_draw_group_nodes(member->ptr);
+            continue;
+        }
+        mui_ctx_draw_node(group->ctx, member->ctx_node);
+    }
+}
+
+
+void*
+mui_get_group_next_member(struct mui_group *group, void *ptr)
+{
+    if (ptr == NULL)
+        return TAILQ_FIRST(&group->head);
+
+    struct member *curr;
+    struct member *next;
+
+    curr = (struct member *)ptr;
+    next = TAILQ_NEXT(curr, entries);
+
+    return (curr == next) ? NULL : next;
+}
+
+uint16_t
+mui_get_group_member_type(void *ptr)
+{
+    return ((struct member*)ptr)->type;
+}
+
+void*
+mui_get_group_member_data(void *ptr)
+{
+    return ((struct member*)ptr)->ptr;
+}
+
+void
+mui_update_group_member(struct mui_group *group, void *ptr)
+{
+    mui_ctx_transform_node(group->ctx, ((struct member *)ptr)->ctx_node);
+}
+
+void
+mui_update_group_member_node(struct mui_group *group, void *ptr)
+{
+    struct member *member;
+
+    member = (struct member*)ptr;
+    mui_ctx_delete_node(member->ctx_node);
+
+    /*
+     * If ctx_node is not set to NULL, mui_ctx_draw_node will fail
+     * with a Segmentation Fault as it will try to draw a new node
+     * before it is assigned
+     */
+    member->ctx_node = NULL;
+    member->ctx_node = mui_ctx_create_node(group->ctx, member->type, member->ptr);
+
+    /* Force to use this function to live update */
+    mui_ctx_transform_node(group->ctx, member->ctx_node);
+}
blob - /dev/null
blob + 885188fd016951a9134caa650aec5b47ad23d5bb (mode 644)
--- /dev/null
+++ image.c
@@ -0,0 +1,121 @@
+#include "mui.h"
+
+struct mui_image {
+    char       *data;
+    uint8_t     fmt;
+    uint16_t    width;
+    uint16_t    height;
+    struct      mui_group *group;
+    struct      mui_layout *layout;
+};
+
+uint32_t
+u8_to_u32(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
+{
+    uint8_t rgba[4];
+    uint32_t result;
+
+    rgba[0] = (float) r / 255 * a;
+    rgba[1] = (float) g / 255 * a;
+    rgba[2] = (float) b / 255 *a;
+
+    result = (rgba[0] << 24) + (rgba[1] << 16) + (rgba[2] << 8) + a;
+    return result;
+}
+
+/*
+ * This is necessary as xcb_xrender images stores them in BGRA format
+ */
+void
+rgba_to_bgra(char *data, uint16_t width, uint16_t height)
+{
+    int start;
+    uint32_t rgba;
+    int r, g, b, a;
+
+    for (int i = 0; i < width * height; i++) {
+        start = i * 4;
+        rgba = u8_to_u32(data[start + 0],
+                         data[start + 1],
+                         data[start + 2],
+                         data[start + 3]);
+
+        r = (rgba & 0xff000000) >> 24;
+        g = (rgba & 0x00ff0000) >> 16;
+        b = (rgba & 0x0000ff00) >> 8;
+
+        data[start + 0] = b;
+        data[start + 1] = g;
+        data[start + 2] = r;
+        data[start + 3] = data[start + 3]; /* um... */
+    }
+}
+
+
+struct mui_image*
+mui_create_image(char *data, uint16_t width, uint16_t height)
+{
+    struct mui_image *image;
+
+    image = calloc(1, sizeof(*image));
+    image->width = width;
+    image->height = height;
+    image->fmt    = 4;
+    image->layout = mui_create_layout(MUI_IMAGE, image);
+
+    image->data = malloc(width * height * image->fmt);
+    memcpy(image->data, data, width * height * image->fmt);
+    mui_resize_layout(image->layout, width, height);
+
+    rgba_to_bgra(image->data, width, height);
+
+    return image;
+}
+
+void
+mui_delete_image(struct mui_image *image)
+{
+    if (image->group)
+        mui_group_remove(image->group, MUI_IMAGE, image);
+
+    mui_delete_layout(image->layout);
+    free(image->data);
+    free(image);
+}
+
+struct mui_group*
+mui_get_image_group(struct mui_image *image)
+{
+    return image->group;
+}
+
+struct mui_layout*
+mui_get_image_layout(struct mui_image *image)
+{
+    return image->layout;
+}
+
+char*
+mui_get_image_data(struct mui_image *image)
+{
+    return image->data;
+}
+
+void
+mui_set_image_group(struct mui_image *image, struct mui_group *group)
+{
+    struct mui_group   *current;
+    struct mui_layout  *layout;
+
+    current = image->group;
+    image->group = group;
+
+    if (current != NULL)
+        mui_group_remove(current, MUI_IMAGE, image);
+
+    if (group) {
+        layout = mui_get_group_layout(group);
+        mui_group_add(group, MUI_IMAGE, image);
+        mui_resize_layout(layout, mui_get_layout_width(layout), mui_get_layout_height(layout));
+    }
+}
blob - /dev/null
blob + 3170b3ca351bb8f97c1fd0b9d812ca430198e11b (mode 644)
--- /dev/null
+++ label.c
@@ -0,0 +1,147 @@
+#include "mui.h"
+
+struct mui_label {
+    int         len;
+    char       *text;
+    int         flags;
+    int         font_id;
+    struct      mui_group *group;
+    struct      mui_layout *layout;
+};
+
+struct mui_label*
+mui_create_label(char *text, int len)
+{
+    struct mui_label *label;
+
+    label = calloc(1, sizeof(*label));
+    label->text = malloc(len + 1);
+    memcpy(label->text, text, len);
+    label->text[len] = '\0';
+    label->len = len;
+    label->font_id = 0;
+
+    label->group = NULL;
+    label->layout = mui_create_layout(MUI_LABEL, label);
+
+    return label;
+}
+
+void
+mui_delete_label(struct mui_label *label)
+{
+    if (label->group)
+        mui_group_remove(label->group, MUI_LABEL, label);
+
+    mui_delete_layout(label->layout);
+    free(label->text);
+    free(label);
+}
+
+struct mui_group*
+mui_get_label_group(struct mui_label *label)
+{
+    return label->group;
+}
+
+struct mui_layout*
+mui_get_label_layout(struct mui_label *label)
+{
+    return label->layout;
+}
+
+int
+mui_get_label_font_id(struct mui_label *label)
+{
+    return label->font_id;
+}
+
+int
+mui_get_label_len(struct mui_label *label)
+{
+    return label->len;
+}
+
+char*
+mui_get_label_text(struct mui_label *label)
+{
+    return label->text;
+}
+
+void
+mui_get_label_lines(struct mui_label *label, int *line_count, int *rwidth, int *rheight)
+{
+    char      **lines;
+    uint16_t    width;
+    uint16_t    lwidth;
+    int         lindex;
+    uint16_t    swidth;
+    int         sindex;
+    char       *line;
+    int         len;
+    struct      mui_layout *layout;
+
+    *line_count = 1;
+    layout = mui_get_label_layout(label);
+    width = mui_get_layout_width(layout);
+
+    *rwidth = *rheight = 0;
+
+    for (int i = 0; i < label->len; i++) {
+
+        lindex = i;
+        lwidth = 0;
+
+        for (; lwidth < width && i < label->len && label->text[i] != '\n'; i++) {
+            if (label->text[i] == ' ') {
+                swidth = lwidth;
+                sindex = i;
+            }
+        }
+
+        if (lwidth > width) {
+            lwidth = swidth;
+            i = sindex;
+        }
+
+        len = i - lindex;
+        line = malloc(len + 1);
+        memcpy(line, label->text + lindex, len);
+        line[len] = '\0';
+
+        *rwidth = (*rwidth > lwidth) ? *rwidth : lwidth;
+        *rheight += mui_ctx_get_font_height(label->font_id);
+
+        free(line);
+
+    }
+}
+
+void
+mui_set_label_group(struct mui_label *label, struct mui_group *group)
+{
+    uint16_t            width;
+    uint16_t            height;
+    struct mui_group   *current;
+    struct mui_layout  *layout;
+
+    current = label->group;
+    label->group = group;
+    width = 0;
+
+    if (current != NULL)
+        mui_group_remove(current, MUI_LABEL, label);
+
+    if (group) {
+        layout = mui_get_group_layout(group);
+
+        /* Initial dimensions when group changes */
+        for (int i = 0; i < label->len; i++)
+            width += mui_ctx_get_glyph_width(label->text[i], label->font_id);
+        height = mui_ctx_get_font_height(label->font_id);
+        mui_resize_layout(label->layout, width, height);
+
+        mui_group_add(group, MUI_LABEL, label);
+        mui_resize_layout(layout, mui_get_layout_width(layout), mui_get_layout_height(layout));
+    }
+}
blob - /dev/null
blob + 0c08902f9ccd746d7c575b6b0492f8a71e309c01 (mode 644)
--- /dev/null
+++ layout.c
@@ -0,0 +1,388 @@
+#include "mui.h"
+
+struct mui_layout {
+    uint16_t        width;
+    uint16_t        height;
+
+    uint32_t        flags;
+    uint8_t         type;
+    void           *ptr;
+
+    /* Pairs of translation, scale 
+       and rotation */
+    float           x;
+    float           y;
+    float           scale_x;
+    float           scale_y;
+    float           align_x;
+    float           align_y;
+    float           rotation;
+};
+
+
+void
+matrix_identity(float *matrix)
+{
+    matrix[0] = 1.0; matrix[1] = 0.0; matrix[2] = 0.0;
+    matrix[3] = 0.0; matrix[4] = 1.0; matrix[5] = 0.0;
+    matrix[6] = 0.0; matrix[7] = 0.0; matrix[8] = 1.0;
+}
+
+/*
+ * Multiply a 3x3 matrix by a 3x3 matrix and push the data in the
+ * src pointer
+ */
+void
+matrix_multiply(float *src, float *scale)
+{
+    float dst[9];
+
+    dst[0] = src[0] * scale[0] + src[1] * scale[3] + src[2] * scale[6];
+    dst[1] = src[0] * scale[1] + src[1] * scale[4] + src[2] * scale[7];
+    dst[2] = src[0] * scale[2] + src[1] * scale[5] + src[2] * scale[8];
+
+    dst[3] = src[3] * scale[0] + src[4] * scale[3] + src[5] * scale[6];
+    dst[4] = src[3] * scale[1] + src[4] * scale[4] + src[5] * scale[7];
+    dst[5] = src[3] * scale[2] + src[4] * scale[5] + src[5] * scale[8];
+
+    dst[6] = src[6] * scale[0] + src[7] * scale[3] + src[8] * scale[6];
+    dst[7] = src[6] * scale[1] + src[7] * scale[4] + src[8] * scale[7];
+    dst[8] = src[6] * scale[2] + src[7] * scale[5] + src[8] * scale[8];
+
+    memcpy(src, dst, sizeof(dst));
+}
+
+void
+matrix_translate(float *matrix, float x, float y)
+{
+    matrix[2] += x;
+    matrix[5] += y;
+}
+
+void
+matrix_rotate(float *matrix, float r)
+{
+    float m[]=  { cos(r), sin(r), 0,
+                 -sin(r), cos(r), 0,
+                    0,      0,    1};
+
+    matrix_multiply(matrix, m);
+}
+
+void
+matrix_scale(float *matrix, float sx, float sy)
+{
+    float scale[] = { sx, 0, 0, 0, sy, 0, 0, 0, 1};
+    matrix_multiply(matrix, scale);
+}
+
+struct mui_group*
+get_layout_group(struct mui_layout *layout)
+{
+    switch (layout->type) {
+        case MUI_IMAGE:
+            return mui_get_image_group(layout->ptr);
+        case MUI_LABEL:
+            return mui_get_label_group(layout->ptr);
+        case MUI_SHAPE:
+            return mui_get_shape_group(layout->ptr);
+        default:
+            return NULL;
+    }
+}
+
+
+struct mui_layout*
+mui_create_layout(uint8_t type, void *ptr)
+{
+    struct mui_layout *layout;
+
+    layout = calloc(1, sizeof(*layout));
+
+    layout->flags = 0;
+    layout->width = 0;
+    layout->height = 0;
+    layout->type = type;
+    layout->ptr = ptr;
+
+    layout->x = 0.0;
+    layout->y = 0.0;
+    layout->scale_x = 1.0;
+    layout->scale_y = 1.0;
+    layout->rotation = 0.0;
+
+    return layout;
+}
+
+void
+mui_delete_layout(struct mui_layout *layout)
+{
+    free(layout);
+}
+
+void
+resize_group_layout(struct mui_layout *layout, uint16_t width, uint16_t height)
+{
+    void *ptr;
+    uint16_t type;
+    float scale_x, scale_y;
+    float align_x, align_y;
+    uint16_t owidth, oheight;
+    struct mui_group *group;
+
+    struct mui_label *label;
+    struct mui_shape *shape;
+    struct mui_image *image;
+    struct mui_layout *m_layout;
+
+    ptr = NULL;
+    group = (struct mui_group *)layout->ptr;
+
+    owidth = layout->width;
+    oheight = layout->height;
+
+    layout->width = width;
+    layout->height = height;
+
+    while ((ptr = mui_get_group_next_member(group, ptr))) {
+        m_layout = NULL;
+        type = mui_get_group_member_type(ptr);
+
+        switch (type) {
+            case MUI_SHAPE:
+                shape = mui_get_group_member_data(ptr);
+                m_layout = mui_get_shape_layout(shape);
+
+                break;
+
+            case MUI_LABEL:
+                label = mui_get_group_member_data(ptr);
+                m_layout = mui_get_label_layout(label);
+                break;
+
+            case MUI_IMAGE:
+                image = mui_get_group_member_data(ptr);
+                m_layout = mui_get_image_layout(image);
+
+            default:
+                break;
+        }
+
+        if (!m_layout) continue;
+
+        scale_x = (m_layout->width * m_layout->scale_x + (width - owidth)) /
+                  (m_layout->width * m_layout->scale_x);
+
+        scale_y = (m_layout->height * m_layout->scale_y + (height - oheight)) /
+                  (float) (m_layout->height * m_layout->scale_y);
+
+
+        align_x = align_y = 0;
+
+        if (m_layout->flags & MUI_ALIGN_BOTTOM)
+            align_y = layout->height;
+
+        if (m_layout->flags & MUI_ALIGN_RIGHT)
+            align_x = layout->width;
+
+        if (m_layout->flags & MUI_ALIGN_CENTER_X)
+            align_x = layout->width / 2.0;
+
+        if (m_layout->flags & MUI_ALIGN_CENTER_Y)
+            align_y = layout->height / 2.0;
+
+        m_layout->align_x = align_x;
+        m_layout->align_y = align_y;
+
+        mui_scale_layout(m_layout, scale_x, scale_y);
+
+    }
+}
+
+void
+mui_resize_layout(struct mui_layout *layout, uint16_t width, uint16_t height)
+{
+    switch (layout->type) {
+        case MUI_GROUP:
+            resize_group_layout(layout, width, height);
+            break;
+
+        case MUI_SHAPE:
+            layout->width = width;
+            layout->height = height;
+            break;
+
+        case MUI_LABEL:
+            layout->width = width;
+            layout->height = height;
+            break;
+
+        case MUI_IMAGE:
+            layout->width = width;
+            layout->height = height;
+            break;
+    }
+}
+
+void
+mui_scale_layout(struct mui_layout *layout, float scale_x, float scale_y)
+{
+    struct mui_group *l_group;
+    float l_scale_x, l_scale_y;
+
+    l_group = get_layout_group(layout);
+
+    l_scale_x = layout->scale_x;
+    l_scale_y = layout->scale_y;
+
+    if (layout->flags & MUI_SCALE_X)
+        l_scale_x = layout->scale_x * scale_x;
+
+    if (layout->flags & MUI_SCALE_Y)
+        l_scale_y = layout->scale_y * scale_y;
+
+    if (layout->flags & MUI_KEEP_RATIO) {
+        l_scale_x = layout->scale_y * scale_y;
+        l_scale_y = layout->scale_y * scale_y;
+    }
+
+    layout->scale_x = l_scale_x;
+    layout->scale_y = l_scale_y;
+
+    if (l_group) mui_update_group_member(l_group, mui_in_group(l_group, layout->type, layout->ptr));
+}
+
+void
+mui_rotate_layout(struct mui_layout *layout, float rot)
+{
+    struct mui_group *l_group;
+
+    layout->rotation += rot;
+    l_group = get_layout_group(layout);
+
+    if (l_group) mui_update_group_member(l_group, mui_in_group(l_group, layout->type, layout->ptr));
+}
+
+void
+mui_translate_layout(struct mui_layout *layout, float dx, float dy)
+{
+    struct mui_group *l_group;
+
+    layout->x += dx;
+    layout->y += dy;
+    l_group = get_layout_group(layout);
+
+    if (l_group) mui_update_group_member(l_group, mui_in_group(l_group, layout->type, layout->ptr));
+}
+
+uint16_t
+mui_get_layout_width(struct mui_layout *layout)
+{
+    return layout->width;
+}
+
+uint16_t
+mui_get_layout_height(struct mui_layout *layout)
+{
+    return layout->height;
+}
+
+void
+mui_set_layout_flags(struct mui_layout *layout, uint32_t flags)
+{
+    layout->flags = flags;
+}
+
+uint32_t
+mui_get_layout_flags(struct mui_layout *layout)
+{
+    return layout->flags;
+}
+
+/*
+ * VERY important function for XCB as it sets up a matrix for
+ * transforming the image to match the current layout that
+ * works with XCB.
+ *
+ * Very weird scaling operations in this function
+ */
+
+float*
+mui_get_layout_matrix(struct mui_layout *layout)
+{
+    float *matrix;
+    float  dx, dy;
+    float  m_align1[9];
+    float  m_align2[9];
+
+    dy = dx = 0;
+
+    matrix_identity(m_align1);
+    matrix_identity(m_align2);
+
+    if (layout->flags & MUI_ANCHOR_RIGHT)
+        dx = layout->width * layout->scale_x;
+
+    if (layout->flags & MUI_ANCHOR_CENTER_X)
+        dx = layout->width / 2.0 * layout->scale_x;
+
+    if (layout->flags & MUI_ANCHOR_CENTER_Y)
+        dy = layout->height / 2.0 * layout->scale_y;
+
+    if (layout->flags & MUI_ANCHOR_BOTTOM)
+        dy = layout->height * layout->scale_y;
+
+    matrix = calloc(9, sizeof(*matrix));
+    matrix_identity(matrix);
+
+    /*
+     * In an ideal world the matrix should be set to zero. However,
+     * as we are using XCB, it needs to be infinity
+     */
+    if (layout->scale_x < 0.0 || layout->scale_y < 0.0) {
+        matrix[0] = 1 / 0.0;
+        matrix[4] = 1 / 0.0;
+        return matrix;
+    }
+
+    m_align1[2] = -layout->x - layout->align_x;
+    m_align1[5] = -layout->y - layout->align_y;
+    m_align2[2] = -m_align1[2];
+    m_align2[5] = -m_align1[5];
+
+    matrix_scale(matrix, 1 / layout->scale_x, 1 / layout->scale_y);
+    matrix_multiply(matrix, m_align2);
+    matrix_rotate(matrix, layout->rotation);
+    matrix_multiply(matrix, m_align1);
+    matrix_translate(matrix, (-layout->x + dx - layout->align_x) / layout->scale_x,
+                    (-layout->y + dy - layout->align_y) / layout->scale_y);
+
+    return matrix;
+}
+
+float
+mui_get_layout_rotation(struct mui_layout *layout)
+{
+    return layout->rotation;
+}
+
+void
+mui_print_layout_matrix(float *matrix)
+{
+    printf("Matrix:\n%.2f, %.2f, %.2f\n%.2f, %.2f, %.2f\n%.2f, %.2f, %.2f\n",
+        matrix[0], matrix[1], matrix[2],
+        matrix[3], matrix[4], matrix[5],
+        matrix[6], matrix[7], matrix[8]);
+}
+
+float
+mui_get_layout_scale_x(struct mui_layout *layout)
+{
+    return layout->scale_x;
+}
+
+float
+mui_get_layout_scale_y(struct mui_layout *layout)
+{
+    return layout->scale_y;
+}
blob - /dev/null
blob + 13b3cc23fd96597022a53dc61d2f6b811e1915f7 (mode 644)
--- /dev/null
+++ mui.7
@@ -0,0 +1,41 @@
+.Dd $Mdocdate: September 07 2025 $
+.Dt MUI 7
+.Os
+.Sh NAME
+.Nm mui
+.Nd General information
+.Sh DESCRIPTION
+mui is a library with very few modules to focuse on maintainability
+and simplicity. It is made of drawables, groups, layouts and a 
+context tied to the used backend.
+.Sh DRAWABLES
+Drawables are instances that can be displayed on a window. There are
+3 types of drawables: images, shapes and labels.
+.sp
+.Sh GROUPS
+Groups are the only container of mui which can contain drawables or
+other groups. They define how drawables should be organized.
+.sp
+.Sh LAYOUTS
+Layouts are instances that are attached to groups and drawables. They
+define the transformations that occure in them.
+.sp
+.Sh CONTEXES
+Contexes are a way of communication between mui and the backend
+currently beeing used. This provides a layer of abstraction so
+the developer can create a GUI in a simple maner. This implementation
+of mui uses XCB to create a context.
+.sp
+The context can be reimplemented to be based on OpenGL or other graphics
+backend by editing the source code.
+.sp
+.Sh BASICS
+A mui program needs a display to render its drawable but not to create
+them. A general application follows these steps: create a window, get
+the window group, add instances and update mui in a loop.
+.sp
+.Sh SEE ALSO
+queue(3), mui(1)
+.Sh AUTHORS
+Onana Onana Xavier Manuel 
+.Em xavier@onana.net
blob - /dev/null
blob + 31472cc2453fa7899fee5ba80af4de7efa9e60ec (mode 644)
--- /dev/null
+++ mui.h
@@ -0,0 +1,336 @@
+/***********************************************************/
+/* MUI LIBRARY
+ *
+ * This file can be used as a reference to understand the
+ * library API
+ */
+/***********************************************************/
+
+#ifndef _MUI_H_
+#define _MUI_H_
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <sys/queue.h>
+
+/* BASIC TYPES */
+
+struct mui_label;
+
+struct mui_shape;
+
+struct mui_image;
+
+struct mui_group;
+
+struct mui_color { float r, g, b, a; };
+
+/* The layout type is used by all the basic types in mui */
+
+enum mui_layout_flags {
+    MUI_SCALE_X = 1,
+    MUI_SCALE_Y = 2,
+    MUI_KEEP_RATIO = 4,
+    MUI_ANCHOR_LEFT = 8,
+    MUI_ANCHOR_RIGHT = 16,
+    MUI_ANCHOR_TOP = 32,
+    MUI_ANCHOR_BOTTOM = 64,
+    MUI_ANCHOR_CENTER_X = 128,
+    MUI_ANCHOR_CENTER_Y = 256,
+    MUI_ALIGN_RIGHT = 512,
+    MUI_ALIGN_BOTTOM = 1024,
+    MUI_ALIGN_CENTER_X = 2048,
+    MUI_ALIGN_CENTER_Y = 4096,
+    MUI_EXPAND_X = 8192,
+    MUI_EXPAND_Y = 16384
+};
+
+struct mui_layout;
+
+struct mui_window;
+
+struct mui_ctx;
+
+#define MUI_EVENT_MAX           32
+
+enum {
+    MUI_EVENT_NONE,
+    MUI_EVENT_QUIT,
+    MUI_EVENT_RESIZE,
+    MUI_EVENT_KEY_PRESS,
+    MUI_EVENT_KEY_RELEASE,
+    MUI_EVENT_MOUSE_PRESS,
+    MUI_EVENT_MOUSE_MOTION,
+    MUI_EVENT_MOUSE_RELEASE
+};
+
+struct mui_event_key_press { int key; };
+
+struct mui_event_key_release { int key; };
+
+struct mui_event_button_press { int key; };
+
+struct mui_event_button_release { int key; };
+
+struct mui_event_mouse_press { int button, x, y; };
+
+struct mui_event_mouse_release { int button, x, y; };
+
+struct mui_event_mouse_motion { int x, y; };
+
+struct mui_event {
+    uint8_t  type;
+
+    struct mui_event_key_press          key_press;
+    struct mui_event_key_release        key_release;
+    struct mui_event_mouse_press        mouse_press;
+    struct mui_event_mouse_motion       mouse_motion;
+    struct mui_event_mouse_release      mouse_release;
+    TAILQ_ENTRY(mui_event) entries;
+
+    struct { uint8_t id; void *ptr; } target;
+};
+
+
+struct mui_event_handler;
+
+struct mui_event_handler           *mui_create_event_handler(void);
+
+void                                mui_push_event(struct mui_event_handler *handler, struct mui_event *event);
+
+struct mui_event                    mui_pop_event(struct mui_event_handler *handler);
+
+/*
+ * This function is very important and should be placed
+ * in the main loop of you GUI program
+ */
+void                                mui_update(void);
+
+/* SHAPE FUNCTIONS */
+
+struct mui_shape                   *mui_create_rectangle(uint16_t width, uint16_t height);
+
+void                                mui_delete_shape(struct mui_shape *shape);
+
+struct mui_group                   *mui_get_shape_group(struct mui_shape *shape);
+
+struct mui_layout                  *mui_get_shape_layout(struct mui_shape *shape);
+
+struct mui_color                    mui_get_shape_color(struct mui_shape *shape);
+
+void                                mui_set_shape_group(struct mui_shape *shape, struct mui_group *group);
+
+void                                mui_set_shape_color(struct mui_shape *shape, float r, float g, float b, float a);
+
+
+/* LABEL FUNCTIONS */
+
+struct mui_label                   *mui_create_label(char *text, int len);
+
+void                                mui_delete_label(struct mui_label *label);
+
+struct mui_group                   *mui_get_label_group(struct mui_label *label);
+
+struct mui_layout                  *mui_get_label_layout(struct mui_label *label);
+
+int                                 mui_get_label_font_id(struct mui_label *label);
+
+char                               *mui_get_label_text(struct mui_label *label);
+
+int                                 mui_get_label_len(struct mui_label *label);
+
+/*
+ * Returns the line count, width and height of a label
+ * based on its parent.
+ */
+void                                mui_get_label_lines(struct mui_label *label, int *line_count, int *rwidth, int *rheight);
+
+void                                mui_set_label_flags(struct mui_label *label, int flags);
+
+void                                mui_set_label_group(struct mui_label *label, struct mui_group *group);
+
+/* IMAGE FUNCTIONS */
+
+struct mui_image                   *mui_create_image(char *data, uint16_t width, uint16_t height);
+
+void                                mui_delete_image(struct mui_image *image);
+
+struct mui_group                   *mui_get_image_group(struct mui_image *image);
+
+struct mui_layout                  *mui_get_image_layout(struct mui_image *image);
+
+char                               *mui_get_image_data(struct mui_image *image);
+
+void                                mui_set_image_group(struct mui_image *image, struct mui_group *group);
+
+
+/* WINDOW FUNCTIONS */
+
+struct mui_window                  *mui_create_window(const char *name, uint16_t width, uint16_t height);
+
+void                                mui_delete_window(struct mui_window *window);
+
+struct mui_group                   *mui_get_window_group(struct mui_window *window);
+
+char                               *mui_get_window_name(struct mui_window *window);
+
+struct mui_event_handler           *mui_get_window_event_handler(struct mui_window *mw);
+
+
+/* LAYOUT FUNCTIONS */
+
+struct mui_layout                  *mui_create_layout(uint8_t type, void *ptr);
+
+void                                mui_delete_layout(struct mui_layout *layout);
+
+void                                mui_resize_layout(struct mui_layout *layout, uint16_t width, uint16_t height);
+
+void                                mui_scale_layout(struct mui_layout *layout, float scale_x, float scale_y);
+
+void                                mui_rotate_layout(struct mui_layout *layout, float rot);
+
+void                                mui_translate_layout(struct mui_layout *layout, float dx, float dy);
+
+void                                mui_set_layout_flags(struct mui_layout *layout, uint32_t flags);
+
+uint32_t                            mui_get_layout_flags(struct mui_layout *layout);
+
+uint16_t                            mui_get_layout_width(struct mui_layout *layout);
+
+uint16_t                            mui_get_layout_height(struct mui_layout *layout);
+
+float                              *mui_get_layout_transform(struct mui_layout *layout);
+
+float                              *mui_get_layout_matrix(struct mui_layout *layout);
+
+float                               mui_get_layout_rotation(struct mui_layout *layout);
+
+float                               mui_get_layout_scale_x(struct mui_layout *layout);
+
+float                               mui_get_layout_scale_y(struct mui_layout *layout);
+
+/*
+ * This function should be removed later and replaced with a
+ * debugging marcro
+ */
+void                                mui_print_layout_matrix(float *matrix);
+
+
+/* GROUP FUNCTIONS */
+
+struct mui_group                   *mui_create_group(void);
+
+void                                mui_draw_group_nodes(struct mui_group *group);
+
+void                                mui_set_group_ctx(struct mui_group *group, struct mui_ctx *ctx);
+
+void                                mui_delete_group(struct mui_group *group);
+
+struct mui_layout                  *mui_get_group_layout(struct mui_group *group);
+
+void                                mui_group_add(struct mui_group *group, uint16_t type, void *data);
+
+void                                mui_group_remove(struct mui_group *group, uint16_t type, void *data);
+
+void                               *mui_in_group(struct mui_group *group, uint16_t type, void *data);
+
+void                                mui_update_group_member(struct mui_group *group, void *ptr);
+
+void                                mui_update_group_member_node(struct mui_group *group, void *ptr);
+
+/*
+ * Returns the next member in a group. If NULL is passed as `ptr`, the
+ * first element of the group will be returned
+ */
+void                               *mui_get_group_next_member(struct mui_group *group, void *ptr);
+
+uint16_t                            mui_get_group_member_type(void *ptr);
+
+void                               *mui_get_group_member_data(void *ptr);
+
+
+
+/* CONTEXT FUNCTIONS (for XCB) 
+ *
+ * These functions can be used but it is not recommended. They
+ * are more dedicated to the library.
+*/
+
+struct mui_ctx                     *mui_create_ctx(struct mui_window *mw);
+
+void                                mui_delete_ctx(struct mui_ctx *ctx);
+
+void                               *mui_ctx_create_node(struct mui_ctx *ctx, uint16_t type, void *data);
+
+void                                mui_ctx_draw_node(struct mui_ctx *ctx, void *node);
+
+void                                mui_ctx_delete_node(void *node);
+
+void                                mui_ctx_transform_node(struct mui_ctx *ctx, void *node);
+
+void                                mui_ctx_process_event(void);
+
+int                                 mui_ctx_get_glyph_width(char glyph, int font_id);
+
+int                                 mui_ctx_get_font_height(int font_id);
+
+
+/* TYPES */
+
+#define MUI_UNDEF                   0
+#define MUI_SHAPE                   1
+#define MUI_LABEL                   2
+#define MUI_IMAGE                   3
+#define MUI_GROUP                   4
+
+/* LABEL FLAGS */
+
+#define MUI_TEXT_WRAPPED            1
+#define MUI_TEXT_MULTILINE          2
+
+/* KEY CODES
+ *
+ * This is a list of keys based on an US keyboard on OpenBSD. It will be modified
+ * later when my knowledge gets better upon this section of XCB
+ */
+
+#define MUI_KEY_Q           24
+#define MUI_KEY_W           25
+#define MUI_KEY_E           26
+#define MUI_KEY_R           27
+#define MUI_KEY_T           28
+#define MUI_KEY_Y           29
+#define MUI_KEY_U           30
+#define MUI_KEY_I           31
+#define MUI_KEY_O           32
+#define MUI_KEY_P           33
+#define MUI_KEY_A           38
+#define MUI_KEY_S           39
+#define MUI_KEY_D           40
+#define MUI_KEY_F           41
+#define MUI_KEY_G           42
+#define MUI_KEY_H           43
+#define MUI_KEY_J           44
+#define MUI_KEY_K           45
+#define MUI_KEY_L           46
+#define MUI_KEY_Z           52
+#define MUI_KEY_X           53
+#define MUI_KEY_C           54
+#define MUI_KEY_V           55
+#define MUI_KEY_B           56
+#define MUI_KEY_N           57
+#define MUI_KEY_M           58
+
+
+/* DEBUGGING FUNCTIONS 
+ *
+ * These macros are used when defining MUI_DEBUG while
+ * compiling. They can help pinpoint an error.
+*/
+
+
+#endif
blob - /dev/null
blob + 63b4038912507151fd9d32e04563f3aebfa06b2b (mode 644)
--- /dev/null
+++ shape.c
@@ -0,0 +1,87 @@
+#include "mui.h"
+
+struct mui_shape {
+    uint8_t     type;
+    struct      mui_color   color;
+    struct      mui_group  *group;
+    struct      mui_layout *layout;
+};
+
+
+struct mui_shape*
+mui_create_rectangle(uint16_t width, uint16_t height)
+{
+    struct mui_shape *shape;
+
+    shape = malloc(sizeof(*shape));
+    shape->group = NULL;
+    shape->layout = mui_create_layout(MUI_SHAPE, shape);
+    shape->color.r = 0.5;
+    shape->color.g = 0.5;
+    shape->color.b = 0.5;
+    shape->color.a = 1.0;
+    mui_resize_layout(shape->layout, width, height);
+
+    return shape;
+}
+
+void
+mui_delete_shape(struct mui_shape *shape)
+{
+    if (shape->group)
+        mui_group_remove(shape->group, MUI_SHAPE, shape);
+
+    mui_delete_layout(shape->layout);
+    free(shape);
+}
+
+struct mui_group*
+mui_get_shape_group(struct mui_shape *shape)
+{
+    return shape->group;
+}
+
+struct mui_layout*
+mui_get_shape_layout(struct mui_shape *shape)
+{
+    return shape->layout;
+}
+
+struct mui_color
+mui_get_shape_color(struct mui_shape *shape)
+{
+    return shape->color;
+}
+
+void
+mui_set_shape_color(struct mui_shape *shape, float r, float g, float b, float a)
+{
+    shape->color.r = r;
+    shape->color.g = g;
+    shape->color.b = b;
+    shape->color.a = a;
+
+    if (shape->group) {
+        mui_update_group_member_node(shape->group, mui_in_group(shape->group, MUI_SHAPE, shape));
+    }
+}
+
+void
+mui_set_shape_group(struct mui_shape *shape, struct mui_group *group)
+{
+    struct mui_group *current;
+    struct mui_layout *layout;
+
+    current = shape->group;
+    shape->group = group;
+
+    if (current != NULL)
+        mui_group_remove(current, MUI_SHAPE, shape);
+
+    if (group) { 
+        layout = mui_get_group_layout(group);
+        mui_group_add(group, MUI_SHAPE, shape);
+
+        mui_resize_layout(layout, mui_get_layout_width(layout), mui_get_layout_height(layout));
+    }
+}
blob - /dev/null
blob + 5e964f11bdc556ab55781c50157a885f462f5f94 (mode 644)
--- /dev/null
+++ window.c
@@ -0,0 +1,64 @@
+#include "mui.h"
+
+struct mui_window {
+    struct      mui_event_handler *handler;
+    struct      mui_group *group;
+    struct      mui_ctx *ctx;
+    char       *name;
+};
+
+
+
+struct mui_window*
+mui_create_window(const char *name, uint16_t width, uint16_t height)
+{
+    struct mui_window *mw;
+    struct mui_layout *layout;
+
+    mw = malloc(sizeof(*mw));
+    mw->group = mui_create_group();
+    layout = mui_get_group_layout(mw->group);
+    mui_resize_layout(layout, width, height);
+    mui_set_layout_flags(layout, MUI_EXPAND_X | MUI_EXPAND_Y);
+    mw->handler = mui_create_event_handler();
+
+    mw->name = malloc(strlen(name)+1);
+    memcpy(mw->name, name, strlen(name));
+    mw->name[strlen(name)] = '\0';
+
+    mw->ctx = mui_create_ctx(mw);
+    mui_set_group_ctx(mw->group, mw->ctx);
+
+    return mw;
+}
+
+void
+mui_delete_window(struct mui_window *mw)
+{
+
+}
+
+
+struct mui_group*
+mui_get_window_group(struct mui_window *mw)
+{
+    return mw->group;
+}
+
+char*
+mui_get_window_name(struct mui_window *mw)
+{
+    return mw->name;
+}
+
+struct mui_event_handler*
+mui_get_window_event_handler(struct mui_window *mw)
+{
+    return mw->handler;
+}
+
+void
+mui_update()
+{
+    mui_ctx_process_event();
+}