commit cb5e051aec61c8910f1a8335320b6dd435005c1d from: Onana Onana Xavier Manuel date: Tue Sep 9 01:33:01 2025 UTC Le commit 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 + +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 +#include +#include +#include + +#include +#include +#include + +#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 + +#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 + +#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 + +#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 +#include +#include +#include +#include + +#include + +/* 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(); +}