commit ad1030b41065a5fd42324100b9689f43a9a84b15 from: xavier date: Thu Jul 23 15:27:02 2026 UTC Import commit - /dev/null commit + ad1030b41065a5fd42324100b9689f43a9a84b15 blob - /dev/null blob + 20d4bcef726a4cbf96ffbb605738e9ba4a899377 (mode 644) --- /dev/null +++ LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2026 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 + 526401143b87409307e1d991f80cabac3e6b3de6 (mode 644) --- /dev/null +++ Makefile @@ -0,0 +1,38 @@ +CC= cc +LIB= libaui +PREFIX= /usr/local + +SRCS= button.c \ + canvas.c \ + driver.c \ + frame.c \ + font.c \ + label.c \ + layout.c \ + ui.c \ + xcb.c \ + widget.c \ + window.c +CFLAGS= -fPIC -O0 -std=c99 -Werror +OBJS= ${SRCS:.c=.o} +INCS= -I /usr/X11R6/include -I /usr/X11R6/include/freetype2 +HDR= aui.h +LDADD= -L /usr/X11R6/lib -lxcb -lxcb-render -lfontconfig -lfreetype + + +all: ${LIB} + + +${LIB}: ${OBJS} + ${INSTALL} -m 755 ${HDR} ${PREFIX}/include + ${CC} ${OBJS} -shared -o ${PREFIX}/lib/${LIB}.so ${LDADD} + ar rc ${PREFIX}/lib/${LIB}.a ${OBJS} + +.c.o: + ${CC} -c -fPIC $< -o $@ ${INCS} ${CFLAGS} + +clean: + rm -rf ${PREFIX}/lib/${LIB}.so ${PREFIX}/include/${HDR} ${PREFIX}/lib/${LIB}.a ${OBJS} + make -C examples clean + +.PHONY: examples blob - /dev/null blob + 0106a7c1cc9ef90a3965a6743bf1fa20259b7b28 (mode 644) --- /dev/null +++ README @@ -0,0 +1,6 @@ +libaui - A User Interface Library + +Minimal, portable and simple. This library is inspired by Tk. +The goal of this library is not to be competing with popular +UI toolkits but rather offer a simple codebase that can be +used for studying. blob - /dev/null blob + 23726f477429dbcce18c8e990451b9de4f169f21 (mode 644) --- /dev/null +++ aui.h @@ -0,0 +1,165 @@ +#ifndef __AUI_H__ +#define __AUI_H__ + +#define AUI_WIDGET(widget) ((struct aui_widget *) widget) + +struct aui_color { + unsigned char red; + unsigned char green; + unsigned char blue; + unsigned char alpha; +}; + +struct aui_placepar { + int x, y; + float relx; + float rely; + unsigned int width; + unsigned int height; + float relwidth; + float relheight; +#define AUI_ANCHOR_NW 0 +#define AUI_ANCHOR_N 1 +#define AUI_ANCHOR_E 2 +#define AUI_ANCHOR_SE 3 +#define AUI_ANCHOR_S 4 +#define AUI_ANCHOR_SW 5 +#define AUI_ANCHOR_W 6 +#define AUI_ANCHOR_NE 7 +#define AUI_ANCHOR_CENTER 8 + unsigned char anchor; +}; + +struct aui_packpar { + unsigned char anchor; +#define AUI_FILL_NONE 0 +#define AUI_FILL_X 1 +#define AUI_FILL_Y 2 +#define AUI_FILL_BOTH 3 + unsigned char fill; +#define AUI_SIDE_TOP 0 +#define AUI_SIDE_RIGHT 1 +#define AUI_SIDE_LEFT 2 +#define AUI_SIDE_BOTTOM 3 + unsigned char side; + unsigned char expand; +}; + +struct aui_gridpar { + unsigned int column; + unsigned int row; + unsigned int columnspan; + unsigned int rowspan; + unsigned int ipadx; /* Internal padding X */ + unsigned int ipady; /* Internal padding Y */ + unsigned int padx; + unsigned int pady; +}; + +struct aui_rowpar { + unsigned int minsize; + unsigned char weight; /* Minimum is 1 */ + unsigned char uniform; + unsigned int pad; +}; + +struct aui_columnpar { + unsigned int minsize; + unsigned char weight; /* Minimum is 1 */ + unsigned char uniform; + unsigned int pad; +}; + +struct aui_widget; +struct aui_window; +struct aui_frame; +struct aui_canvas; /* rendering free of rules */ +struct aui_button; +struct aui_label; + +struct aui_window_config { + unsigned int width; + unsigned int height; + const char *title; /* Not allocated by libaui */ +}; + +struct aui_button_config { + struct aui_color foreground; + const char *text; /* Not allocated by libaui */ + void (*command) (struct aui_button *, void *args); + void *args; +}; + +struct aui_frame_config { + struct aui_color background; +}; + +struct aui_canvas_config { + struct aui_color background; +}; + +struct aui_label_config { + char *text; + struct aui_color foreground; + struct aui_color bg; +}; + +void aui_run(void); +void aui_destroy(struct aui_widget *); +struct aui_window *aui_window_new(struct aui_window_config *); +struct aui_button *aui_button_new(struct aui_widget *, struct aui_button_config *); +int aui_button_config(struct aui_widget *, struct aui_button_config *); +struct aui_frame *aui_frame_new(struct aui_widget *, struct aui_frame_config *); +int aui_frame_config(struct aui_widget *, struct aui_frame_config *); +struct aui_canvas *aui_canvas_new(struct aui_widget *, struct aui_canvas_config *); +int aui_canvas_config(struct aui_widget *, struct aui_canvas_config *); +struct aui_label *aui_label_new(struct aui_widget *, struct aui_label_config *); +int aui_label_config(struct aui_widget *, struct aui_label_config *); +int aui_place(struct aui_widget *, struct aui_placepar *); +int aui_pack(struct aui_widget *, struct aui_packpar *); +int aui_grid(struct aui_widget *, struct aui_gridpar *); +int aui_getplacepar(struct aui_widget *, struct aui_placepar *); +int aui_getgridpar(struct aui_widget *, struct aui_gridpar *); +int aui_getpackpar(struct aui_widget *, struct aui_packpar *); + +/* + * Grid container operations + */ +int aui_rowconfigure(struct aui_widget *, struct aui_rowpar *); +int aui_columnconfigure(struct aui_widget *, struct aui_columnpar *); + +#define AUI_CANVAS_RECTANGLE 0 +#define AUI_CANVAS_ARC 1 +#define AUI_CANVAS_IMAGE 2 +#define AUI_CANVAS_BITMAP 3 +#define AUI_CANVAS_OVAL 4 +#define AUI_CANVAS_POLYGON 5 +#define AUI_CANVAS_LINE 6 +#define AUI_CANVAS_TEXT 7 +#define AUI_CANVAS_WINDOW 8 + +struct aui_canvas_item; + +struct aui_canvas_line { + int x1, y1; + int x2, y2; +}; + +struct aui_canvas_rectangle { + int x, y; + struct aui_color color; + unsigned int width, height; +}; + +struct aui_canvas_item_config { + union { + struct aui_canvas_line line; + struct aui_canvas_rectangle rectangle; + }; +}; + +struct aui_canvas_item *aui_canvas_create(struct aui_canvas *, unsigned char, + struct aui_canvas_item_config *); +void aui_canvas_delete(struct aui_canvas *, struct aui_canvas_item *); + +#endif blob - /dev/null blob + 27247026aac62a520238719eea0fa73522bf1522 (mode 644) --- /dev/null +++ aui_run.3 @@ -0,0 +1,109 @@ +.Dd $Mdocdate: July 14 2026$ +.Dt AUI_RUN 3 +.Os +.Sh NAME +.Nm aui_run , +.Nm aui_destroy , +.Nm aui_window_new , +.Nm aui_button_new , +.Nm aui_button_config , +.Nm aui_frame_new , +.Nm aui_frame_config , +.Nm aui_label_new , +.Nm aui_label_config , +.Nm aui_canvas_new , +.Nm aui_canvas_config , +.Nm aui_place , +.Nm aui_pack , +.Nm aui_grid , +.Nm aui_getplacepar , +.Nm aui_getgridpar , +.Nm aui_getpackpar +.Sh SYNOPSIS +.In aui.h +.Ft void +.Fn aui_run "void" +.Ft void +.Fn aui_destroy "struct aui_widget *" +.Ft struct aui_window* +.Fn aui_window_new "struct aui_window_config * config" +.Ft struct aui_button* +.Fn aui_button_new "struct aui_widget * parent" "struct aui_button_config * config" +.Ft int +.Fn aui_button_config "struct aui_widget * widget" "struct aui_button_config * config" +.Ft struct aui_label* +.Fn aui_label_new "struct aui_widget * parent" "struct aui_label_config * config" +.Ft int +.Fn aui_label_config "struct aui_widget * widget" "struct aui_label_config * config" +.Ft struct aui_frame* +.Fn aui_frame_new "struct aui_widget * parent" "struct aui_frame_config * config" +.Ft int +.Fn aui_frame_config "struct aui_widget * widget" "struct aui_frame_config * config" +.Ft struct aui_canvas* +.Fn aui_canvas_new "struct aui_widget * widget" "struct aui_canvas_config * config" +.Ft int +.Fn aui_canvas_config "struct aui_widget * parent" "struct aui_canvas_config * config" +.Ft int +.Fn aui_place "struct aui_widget * widget" "struct aui_placepar * par" +.Ft int +.Fn aui_grid "struct aui_widget * widget" "struct aui_gridpar * par" +.Ft int +.Fn aui_pack "struct aui_widget * widget" "struct aui_packpar * par" +.Ft int +.Fn aui_getplacepar "struct aui_widget * widget" +.Ft int +.Fn aui_getgridpar "struct aui_widget * widget" +.Ft int +.Fn aui_getpackpar "struct aui_widget * widget" +.Sh DESCRIPTION +Assides for windows, every widget must take a container in order to be +created. The containers can be a window or a frame. Once a widget is +created it can be mapped using place, grid or pack. +.Pp +Canvases are also containers although they cannot take widgets as childrens. +.Sh EXAMPLES +Creating a basic window is done with the following. +.Bd -literal -offset indent +struct aui_window *aw = aui_window_new(NULL); +.Ed +.Pp +Afterwards the mainloop can be started using: +.Bd -literal -offset indent +aui_run(); +.Ed +.Pp +As for buttons they can be created using the following: +.Bd -literal -offset indent +struct aui_button *btn = aui_button_new(AUI_WIDGET(aw)); +.Ed +.Pp +The AUI_WIDGET macro must be used in order to pass widgets so the +.Nm aui +library can understand. In the example above aw can be switched for a +container like frame. Here is an example. +.Pp +.Bd -literal -offset indent +struct aui_frame *frame = aui_frame_new(AUI_WIDGET(aw), NULL); +struct aui_button *btn = aui_button_new(AUI_WIDGET(frame), NULL); +.Ed +.Pp +Adding widgets to containers does not make them directly visible. You must +first pack, place or grid them for them to be mapped. +.Pp +.Bd -literal -offset indent +aui_pack(AUI_WIDGET(btn), NULL); +.Ed +.Pp +It is important to keep in mind that operations for +.Nm aui +must be done before +.Nm aui_run +in a single threaded program. +.Sh RETURN VALUES +For all these function, if the output is -1 or NULL it means an error. Otherwise, +the function was successful. +.Sh AUTHORS +The +.Nm aui +library was written by +.An Onana Onana Xavier Manuel . blob - /dev/null blob + 36661b64d6d1cac811398d1ce8ebc2207b00cdc3 (mode 644) --- /dev/null +++ button.c @@ -0,0 +1,148 @@ +#include +#include +#include + +#include "driver.h" +#include "widget.h" + +static void button_mouse_hover(struct aui_widget *, uint16_t, uint16_t); +static void button_mouse_unhover(struct aui_widget *); +static void button_mouse_press(struct aui_widget *, uint16_t, uint16_t, uint8_t); +static void button_mouse_release(struct aui_widget *, uint16_t, uint16_t, uint8_t); +static void button_set_geometry(struct aui_widget *, struct aui_geometry *); +static void button_free(struct aui_widget *); +static struct aui_geometry button_get_min_size(struct aui_widget *); + +static struct widget_ops button_ops = { + button_mouse_hover, + button_mouse_unhover, + button_mouse_press, + button_mouse_release, + button_set_geometry, /* set geometry */ + button_free, + button_get_min_size, +}; + +struct aui_color color[] = { + { 0xff, 0, 0, 0xff }, /* Borders */ + { 0, 0, 0, 0xff }, /* Normal */ + { 0x1b, 0x1b, 0x1b, 0xff }, /* Hover */ + { 0x3b, 0x3b, 0x3b, 0xff }, /* Press */ +}; + +static struct aui_button_config default_config = { + .text = NULL, + .foreground = { 0xff, 0, 0, 0xff } +}; + +struct aui_button* +aui_button_new(struct aui_widget *parent, struct aui_button_config *config) +{ + struct aui_button *button = calloc(1, sizeof(struct aui_button)); + struct aui_widget *widget = &button->widget; + widget_init(&button->widget, WIDGET_TYPE_BUTTON, &button_ops); + + /* + * Creating the rectangle primitive + */ + widget_add(parent, widget); + button->config = (config == NULL) ? default_config : *config; + + widget->primitives.count = 3; + widget->primitives.list = calloc(widget->primitives.count, sizeof(struct primitive*)); + widget->primitives.list[0] = driver->ops->create_rectangle(); + widget->primitives.list[1] = driver->ops->create_rectangle(); + widget->primitives.list[2] = driver->ops->create_text(); + + driver->ops->set_rectangle_color(widget->window, widget->primitives.list[0], + &button->config.foreground); + driver->ops->set_rectangle_color(widget->window, widget->primitives.list[1], &color[1]); + driver->ops->set_text(widget->primitives.list[2], button->config.text, 0, 0); + driver->ops->set_text_color(widget->primitives.list[2], &button->config.foreground); + + return button; +} + +void +button_free(struct aui_widget *widget) +{ + struct aui_button *button = (struct aui_button *)widget; + widget_remove(widget->parent, widget); + + driver->ops->delete_rectangle(widget->primitives.list[0]); + driver->ops->delete_rectangle(widget->primitives.list[1]); + + widget->window->draw_flag = 1; + + free(button); +} + +static void +button_mouse_hover(struct aui_widget *widget, uint16_t x, uint16_t y) +{ + struct aui_button *btn = (struct aui_button *)widget; + if (btn->pressed == 0) { + driver->ops->set_rectangle_color(widget->window, widget->primitives.list[1], &color[2]); + } else { + driver->ops->set_rectangle_color(widget->window, widget->primitives.list[1], &color[3]); + } +} + +static void +button_mouse_unhover(struct aui_widget *widget) +{ + driver->ops->set_rectangle_color(widget->window, widget->primitives.list[1], &color[1]); +} + +static void +button_mouse_press(struct aui_widget *widget, uint16_t x, uint16_t y, uint8_t button) +{ + struct aui_button *btn = (struct aui_button *)widget; + + btn->pressed = 1; + driver->ops->set_rectangle_color(widget->window, widget->primitives.list[1], &color[3]); +} + +static void +button_mouse_release(struct aui_widget *widget, uint16_t x, uint16_t y, uint8_t button) +{ + struct aui_button *btn = (struct aui_button *)widget; + btn->pressed = 0; + + driver->ops->set_rectangle_color(widget->window, widget->primitives.list[1], &color[1]); + + if (btn->config.command && widget_collides_with_point(widget, x, y)) + btn->config.command(btn, btn->config.args); +} + +static void +button_set_geometry(struct aui_widget *widget, struct aui_geometry *geom) +{ + struct aui_button *button = (struct aui_button *)widget; + struct aui_geometry foregroundeom = { 0 }; + struct aui_geometry tgeom = driver->ops->get_text_geometry(widget->primitives.list[2]); + int tx = geom->x + geom->width / 2 - tgeom.width / 2; + int ty = geom->y + geom->height / 2 + tgeom.height / 2; + + foregroundeom.x = geom->x + 1; + foregroundeom.y = geom->y + 1; + foregroundeom.width = geom->width - 2; + foregroundeom.height = geom->height - 2; + + memcpy(&widget->geom, geom, sizeof(struct aui_geometry)); + driver->ops->set_rectangle_geometry(widget->window, widget->primitives.list[0], geom); + driver->ops->set_rectangle_geometry(widget->window, widget->primitives.list[1], &foregroundeom); + driver->ops->set_text(widget->primitives.list[2], button->config.text, tx, ty); +} + +static struct aui_geometry +button_get_min_size(struct aui_widget *widget) +{ + struct aui_geometry geom = driver->ops->get_text_geometry(widget->primitives.list[2]); + struct aui_button *button = (struct aui_button *)widget; + + geom.width = (geom.width < 30) ? geom.width = 30 : geom.width + 30; + geom.height = (button->config.text == NULL) ? 12 + 20 : geom.height + 20; + + return geom; +} blob - /dev/null blob + d3bbeda2bba3d8e751a603f30dc66edd518f168a (mode 644) --- /dev/null +++ canvas.c @@ -0,0 +1,159 @@ +/* + * Canvases do not store pixels but rather the operations taking + * place within them. This makes resizing easier. + */ + +#include +#include +#include + +#include "driver.h" +#include "widget.h" + +static void canvas_mouse_hover(struct aui_widget *, uint16_t, uint16_t); +static void canvas_mouse_unhover(struct aui_widget *); +static void canvas_mouse_press(struct aui_widget *, uint16_t, uint16_t, uint8_t); +static void canvas_mouse_release(struct aui_widget *, uint16_t, uint16_t, uint8_t); +static void canvas_set_geometry(struct aui_widget *, struct aui_geometry *); +static void canvas_free(struct aui_widget *); +static struct aui_geometry canvas_get_min_size(struct aui_widget *); + +#define DEFAULT_W 300 +#define DEFAULT_H 200 + +static struct widget_ops canvas_ops = { + canvas_mouse_hover, + canvas_mouse_unhover, + canvas_mouse_press, + canvas_mouse_release, + canvas_set_geometry, /* set geometry */ + canvas_free, + canvas_get_min_size, +}; + +static struct aui_color colors[] = { + { 0xff, 0xff, 0xff, 0xff }, +}; + +struct aui_canvas* +aui_canvas_new(struct aui_widget *parent, struct aui_canvas_config *config) +{ + struct aui_canvas *canvas = calloc(1, sizeof(struct aui_canvas)); + struct aui_widget *widget = (struct aui_widget *)canvas; + + widget_init(widget, WIDGET_TYPE_CANVAS, &canvas_ops); + + widget_add(parent, widget); + widget->primitives.count = 1; + widget->primitives.list = calloc(widget->primitives.count, sizeof(struct primitive *)); + widget->primitives.list[0] = driver->ops->create_rectangle(); + + driver->ops->set_rectangle_color(widget->window, widget->primitives.list[0], &colors[0]); + + TAILQ_INIT(&canvas->queue); + + return canvas; +} + +struct aui_canvas_item* +aui_canvas_create(struct aui_canvas *canvas, unsigned char type, struct aui_canvas_item_config *cfg) +{ + struct aui_canvas_item *i = calloc(1, sizeof(struct aui_canvas_item)); + struct aui_widget *widget = (struct aui_widget *)canvas; + + if (i == NULL) + return NULL; + + i->config = *cfg; + + switch (type) { + case AUI_CANVAS_RECTANGLE: + i->primitives.count = 1; + i->primitives.list = calloc(widget->primitives.count, sizeof(struct primitives *)); + i->primitives.list[0] = driver->ops->create_rectangle(); + + driver->ops->set_rectangle_color(widget->window, i->primitives.list[0], + &i->config.rectangle.color); + break; + default: + fprintf(stderr, "libaui: uknwon canvas item type\n"); + free(i); + return NULL; + } + + TAILQ_INSERT_TAIL(&canvas->queue, i, entries); + + return i; +} + +static void +canvas_mouse_hover(struct aui_widget *widget, uint16_t dx, uint16_t dy) +{ + +} + +static void +canvas_mouse_unhover(struct aui_widget *widget) +{ + +} + +static void +canvas_mouse_press(struct aui_widget *widget, uint16_t x, uint16_t y, uint8_t button) +{ + +} + +static void +canvas_mouse_release(struct aui_widget *widget, uint16_t x, uint16_t y, uint8_t button) +{ + +} + +static void +canvas_set_geometry(struct aui_widget *widget, struct aui_geometry *geom) +{ + struct aui_canvas *canvas = (struct aui_canvas *)widget; + struct aui_canvas_item *item; + + memcpy(&widget->geom, geom, sizeof(struct aui_geometry)); + driver->ops->set_rectangle_geometry(widget->window, widget->primitives.list[0], geom); + + TAILQ_FOREACH(item, &canvas->queue, entries) { + struct primitive *prim; + + switch (item->type) { + case AUI_CANVAS_RECTANGLE: { + struct aui_geometry g = { + geom->x + item->config.rectangle.x, + geom->y + item->config.rectangle.y, + item->config.rectangle.width, + item->config.rectangle.height + }; + + prim = item->primitives.list[0]; + driver->ops->set_rectangle_geometry( + widget->window, + prim, + &g + ); + break; + } + default: + break; + } + } +} + +static void +canvas_free(struct aui_widget *widget) +{ + +} + +static struct aui_geometry +canvas_get_min_size(struct aui_widget *widget) +{ + struct aui_geometry geom = { .x= 0, .y = 0, .width = DEFAULT_W, .height = DEFAULT_H }; + return geom; +} blob - /dev/null blob + 541800459ec99ef1b349cc301b1093997f0e234b (mode 644) --- /dev/null +++ driver.c @@ -0,0 +1,18 @@ +#include +#include + +#include "driver.h" + +int +driver_open(enum driver_type type) +{ + switch (type) { + case DRIVER_TYPE_X11: + xcb_driver_open(); + break; + default: + fprintf(stderr, "libaui: attempt to open unknown driver\n"); + return -1; + } + return 0; +} blob - /dev/null blob + 0ee22df660d00d2889e0e6f995024c9d0a56132c (mode 644) --- /dev/null +++ driver.h @@ -0,0 +1,43 @@ +#ifndef __DRIVER_H__ +#define __DRIVER_H__ + +#include "aui.h" +#include "widget.h" + +enum driver_type { + DRIVER_TYPE_X11 +}; + +struct aui_dri { + struct dri_ops *ops; +}; + +struct dri_ops { + struct aui_window *(*create_window)(struct aui_window_config*); + void (*delete_window)(struct aui_window *); + void (*handle_events)(void); + void (*resize_window)(struct aui_window *); + void (*render_background)(struct aui_window *); + void (*render_foreground)(struct aui_window *); + + struct primitive *(*create_rectangle)(void); + void (*delete_rectangle) (struct primitive *); + void (*render_rectangle) (struct aui_window *, struct primitive *); + void (*set_rectangle_color) (struct aui_window *, struct primitive *, struct aui_color *); + void (*set_rectangle_geometry) (struct aui_window *, struct primitive *, struct aui_geometry *); + + struct primitive *(*create_text) (void); + void (*set_text) (struct primitive *, const char *, int16_t, int16_t); + void (*set_text_color) (struct primitive *, struct aui_color *); + void (*render_text) (struct aui_window *, struct primitive *); + struct aui_geometry (*get_text_geometry) (struct primitive *); + + struct aui_canvas *(*create_canvas)(); +}; + +extern struct aui_dri *driver; /* Associated with xcb backend */ + +int driver_open(enum driver_type); +int xcb_driver_open(void); + +#endif blob - /dev/null blob + e2600bc25354e3a91a8706bb61f91a67849dd1cd (mode 644) --- /dev/null +++ event.h @@ -0,0 +1,66 @@ +#ifndef __AUI_EVENT_H__ +#define __AUI_EVENT_H__ + +#include "openbsd-queue.h" + +/* + * These aren't accessible in aui.h because there is no + * need for events. While using the library, you should + * handle interactions through callback functions + * + * These are used internaly. + */ + +enum aui_event_type { + AUI_EVENT_QUIT, + AUI_EVENT_RESIZE, + AUI_EVENT_KEY_PRESS, + AUI_EVENT_MOUSE_PRESS, + AUI_EVENT_MOUSE_RELEASE, + AUI_EVENT_MOUSE_MOTION, +}; + +TAILQ_HEAD(aui_event_queue, aui_event); +struct aui_event { + int type; + struct aui_window *aw; + TAILQ_ENTRY(aui_event) entries; +}; + +struct aui_event_quit { + struct aui_event event; +}; + +struct aui_event_key_press { + struct aui_event event; + unsigned int key; +}; + +struct aui_event_resize { + struct aui_event event; + uint16_t old_w, old_h; + uint16_t new_w, new_h; +}; + +struct aui_event_mouse_motion { + struct aui_event event; + uint16_t x, y; +}; + +struct aui_event_mouse_press { + struct aui_event event; + uint16_t x, y; + uint8_t button; +}; + +struct aui_event_mouse_release { + struct aui_event event; + uint16_t x, y; + uint8_t button; +}; + +extern struct aui_event_queue ev_queue; + +void aui_add_event(struct aui_event *); + +#endif blob - /dev/null blob + 1afaa1f144e8a23216d36f361fcfe57234537502 (mode 644) --- /dev/null +++ examples/Makefile @@ -0,0 +1,16 @@ +ACALC_BIN= acalc/acalc +ACALC_SRC= acalc/acalc.c + +INCS= -I /usr/local/include +LIBS= -L /usr/local/lib -laui +CFLAGS= -O0 + +all: acalc + +acalc: + ${CC} ${ACALC_SRC} -o ${ACALC_BIN} ${CFLAGS} ${LIBS} ${INCS} + +clean: + rm -rf ${ACALC_BIN} ${ACALC_BIN}.core + +.PHONY: acalc blob - /dev/null blob + 14979f10387d8311e504cbbba898861cd690550d (mode 644) --- /dev/null +++ examples/acalc/acalc.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2026 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. + */ + +#include +#include +#include +#include + +#include + +#define DGT_MAX 9 + +struct equation { + struct aui_label_config config; + struct aui_label *label; + char *display; + int queue[2]; + int qindex; + struct binfo *op; +}; + +struct acalc { + struct equation equation; + struct aui_window *aw; +}; + +enum button_class { + CLASS_DGT, + CLASS_DIV, + CLASS_ADD, + CLASS_SUB, + CLASS_MUL, + CLASS_EQU, + CLASS_CLR, +}; + +struct binfo { + enum button_class class; + struct aui_button_config config; + struct aui_gridpar par; +}; + +static struct acalc app; + +static const struct aui_color theme[] = { + { 255, 255, 255, 255 }, +}; + +static struct binfo binfo[] = { + { CLASS_CLR, { .text = "C", .foreground = theme[0] }, { .row = 0, .columnspan = 3 } }, + { CLASS_DIV, { .text = "/", .foreground = theme[0] }, { .row = 0, .column = 3 } }, + + { CLASS_DGT, { .text = "7", .foreground = theme[0] }, { .row = 1, .column = 0 } }, + { CLASS_DGT, { .text = "8", .foreground = theme[0] }, { .row = 1, .column = 1 } }, + { CLASS_DGT, { .text = "9", .foreground = theme[0] }, { .row = 1, .column = 2 } }, + { CLASS_MUL, { .text = "*", .foreground = theme[0] }, { .row = 1, .column = 3 } }, + + { CLASS_DGT, { .text = "4", .foreground = theme[0] }, { .row = 2, .column = 0 } }, + { CLASS_DGT, { .text = "5", .foreground = theme[0] }, { .row = 2, .column = 1 } }, + { CLASS_DGT, { .text = "6", .foreground = theme[0] }, { .row = 2, .column = 2 } }, + { CLASS_SUB, { .text = "-", .foreground = theme[0] }, { .row = 2, .column = 3 } }, + + { CLASS_DGT, { .text = "1", .foreground = theme[0] }, { .row = 3, .column = 0 } }, + { CLASS_DGT, { .text = "2", .foreground = theme[0] }, { .row = 3, .column = 1 } }, + { CLASS_DGT, { .text = "3", .foreground = theme[0] }, { .row = 3, .column = 2 } }, + { CLASS_ADD, { .text = "+", .foreground = theme[0] }, { .row = 3, .column = 3 } }, + + { CLASS_DGT, { .text = "0", .foreground = theme[0] }, { .row = 4, .columnspan = 3 } }, + { CLASS_EQU, { .text = "=", .foreground = theme[0] }, { .row = 4, .column = 3 } }, +}; + +void +display_init(struct aui_frame *frame) +{ + app.equation.display = malloc(DGT_MAX + 1); + memset(app.equation.display, 0, DGT_MAX); + + app.equation.config = (struct aui_label_config) { + .text = "What's up?", + .foreground = { 0, 0, 0, 255 }, + }; + + app.equation.label = aui_label_new(AUI_WIDGET(frame), &app.equation.config); + aui_pack(AUI_WIDGET(app.equation.label), NULL); + app.equation.op = NULL; +} + +void +display_refresh() +{ + app.equation.config.text = app.equation.display; + aui_label_config(AUI_WIDGET(app.equation.label), &app.equation.config); +} + +void +display_empty() +{ + memset(app.equation.display, 0, DGT_MAX); +} + +void +display_warn(const char *msg) +{ + display_empty(); + strncpy(app.equation.display, msg, strlen(msg)); + display_refresh(); + display_empty(); +} + +void +display_result(int res) +{ + display_empty(); + snprintf(app.equation.display, DGT_MAX, "%d", res); + display_refresh(); + display_empty(); +} + +void +equation_add() +{ + const char *errstr; + long long res; + + if (strlen(app.equation.display) == 0) + return; + + res = strtonum(app.equation.display, 0, INT_MAX, &errstr); + if (errstr != NULL) { + fprintf(stderr, "acalc: invalid equation number added!\n"); + exit(1); + } + + app.equation.queue[app.equation.qindex++] = (int)res; +} + +void +equation_solve() +{ + long long res; + + if (app.equation.qindex < 2 || app.equation.op == NULL) + return; + + switch (app.equation.op->class) { + case CLASS_ADD: + res = app.equation.queue[0] + app.equation.queue[1]; + if (res > 999999999 || res < 0) { + display_warn("TOO BIG!"); + goto clear; + } + display_result(res); + break; + case CLASS_SUB: + res = app.equation.queue[0] - app.equation.queue[1]; + if (res < 0 || res > 999999999) { + display_warn("TOO SMALL!"); + goto clear; + } + display_result(res); + break; + case CLASS_MUL: + res = app.equation.queue[0] * app.equation.queue[1]; + if (res > 999999999 || res < 0) { + display_warn("TOO BIG!"); + goto clear; + } + display_result(res); + break; + case CLASS_DIV: + if (app.equation.queue[1] == 0) { + display_warn("NaN!"); + goto clear; + } + res = app.equation.queue[0] / app.equation.queue[1]; + if (res < 0 || res > 999999999) { + display_warn("TOO SMALL!"); + goto clear; + } + display_result(res); + break; + default: + break; + } + app.equation.queue[0] = res; + app.equation.qindex = 1; + return; + +clear: + app.equation.qindex = 0; + display_empty(); +} + +void +write_dgt(const char *dgt) +{ + const char *errstr; + long long num; + int index; + + num = strtonum(dgt, 0, 9, &errstr); + if (errstr != NULL) { + fprintf(stderr, "acalc: out of bounds\n"); + exit(1); + } + + index = strlen(app.equation.display); + if (index == DGT_MAX) + return; + + app.equation.display[index++] = dgt[0]; + app.equation.display[index] = '\0'; + display_refresh(); +} + +static void +button_cmd(struct aui_button *b, void *args) +{ + struct binfo *info = (struct binfo *)args; + + switch (info->class) { + case CLASS_DGT: + write_dgt(info->config.text); + break; + case CLASS_ADD: + case CLASS_SUB: + case CLASS_MUL: + case CLASS_DIV: + equation_add(); + equation_solve(); + app.equation.op = info; + display_empty(); + break; + case CLASS_CLR: + display_empty(); + display_refresh(); + break; + case CLASS_EQU: + if (app.equation.op) { + equation_add(); + equation_solve(); + app.equation.qindex = 0; + } + break; + } +} + +int +main() +{ + struct aui_frame *display_frame; + struct aui_frame *buttons_frame; + size_t bcount = sizeof(binfo) / sizeof(*binfo); + + { + struct aui_window_config config = { .title = "acalc", .width = 120, .height = 188 }; + app.aw = aui_window_new(&config); + } + + { + struct aui_frame_config config = { .background = { 255, 255, 255, 255 } }; + display_frame = aui_frame_new(AUI_WIDGET(app.aw), &config); + } + + { + struct aui_frame_config config = { .background = { 0, 0, 0, 255 } }; + buttons_frame = aui_frame_new(AUI_WIDGET(app.aw), &config); + } + + struct aui_placepar place[] = { + { .relwidth = 1.0, .relheight = 0.2 }, + { .rely = 0.2, .relwidth = 1.0, .relheight = 0.8 } + }; + + aui_place(AUI_WIDGET(display_frame), &place[0]); + aui_place(AUI_WIDGET(buttons_frame), &place[1]); + display_init(display_frame); + + for (int i = 0; i < bcount; i++) { + struct binfo info = binfo[i]; + + info.config.command = &button_cmd; + info.config.args = &binfo[i]; + + struct aui_button *btn = aui_button_new(AUI_WIDGET(buttons_frame), &info.config); + aui_grid(AUI_WIDGET(btn), &binfo[i].par); + } + + aui_run(); + return 0; +} blob - /dev/null blob + 1f102d48dd4007452f43c9b190d4d1735611fc12 (mode 644) --- /dev/null +++ font.c @@ -0,0 +1,111 @@ +#include +#include +#include FT_FREETYPE_H + +#include "font.h" + +struct _font { + FcConfig *fc_config; + FT_Library ft_library; + char *base; + unsigned int size; +}; + +static struct _font font; + +static char *find_font_fc(const char *); + +int +font_init() +{ + font.fc_config = FcInitLoadConfigAndFonts(); + if (font.fc_config == NULL) { + fprintf(stderr, "libaui: failed to load fontconfig\n"); + return -1; + } + + font.size = 12; + font.base = find_font_fc("sans-serif"); + + if (FT_Init_FreeType(&font.ft_library) != 0) { + fprintf(stderr, "libaui: FT_Init_FreeType failed!\n"); + return -1; + } + + return 0; +} + +int +font_load_glyphs(struct glyph *gptr, unsigned int dt) +{ + FT_Face face; + + if (FT_New_Face(font.ft_library, font.base, 0, &face) != 0 ) { + fprintf(stderr, "libaui: failed to load new face\n"); + return -1; + } + + if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0) { + fprintf(stderr, "libaui: FT_SelectCharmap failed\n"); + return -1; + } + + FT_Set_Pixel_Sizes(face, 0, font.size); + + for (int i = 0; i < dt; i++) { + struct glyph *g = &gptr[i]; + if (FT_Load_Char(face, g->charcode, FT_LOAD_RENDER) != 0) { + fprintf(stderr, "libaui: FT_Load_Char failed\n"); + return -1; + } + + FT_Bitmap bmp = face->glyph->bitmap; + + g->x = -face->glyph->bitmap_left; + g->y = face->glyph->bitmap_top; + g->height = bmp.rows; + g->width = bmp.width; + g->x_offset = face->glyph->advance.x >> 6; /* 26.6 format to pixels */ + g->y_offset = face->glyph->metrics.horiBearingY >> 6; + g->stride = (g->width + 3) &~3; + g->bitmap = calloc(g->stride * g->height, sizeof(*g->bitmap)); + + for (int y = 0; y < g->height; y++) + memcpy(g->bitmap + y * g->stride, bmp.buffer + y * g->width, g->width); + } + + FT_Done_Face(face); + return 0; +} + +static char* +find_font_fc(const char *name) +{ + FcPattern *pattern; + FcPattern *match; + FcResult result; + FcChar8 *file; + char *path; + + pattern = FcNameParse((FcChar8 *)name); + if (pattern == NULL) + return NULL; + + if (FcConfigSubstitute(font.fc_config, pattern, FcMatchPattern) == FcFalse) { + fprintf(stderr, "libaui: FcConfigSubstitute failed!\n"); + return NULL; + } + + FcConfigSetDefaultSubstitute(font.fc_config, pattern); + match = FcFontMatch(font.fc_config, pattern, &result); + + if (FcPatternGetString(match, FC_FILE, 0, &file) == FcResultTypeMismatch) { + fprintf(stderr, "libaui: FcPatternGetString\n"); + return NULL; + } + + path = strdup((char *)file); + FcPatternDestroy(match); + FcPatternDestroy(pattern); + return path; +} blob - /dev/null blob + 93f82183920b9cdbff84b43d81c664782946d2af (mode 644) --- /dev/null +++ font.h @@ -0,0 +1,17 @@ +#ifndef FONT_H + +struct glyph { + unsigned int charcode; + int x, y; + unsigned int width; + unsigned int height; + int x_offset; + int y_offset; + unsigned int stride; + unsigned char *bitmap; +}; + +int font_init(void); +int font_load_glyphs(struct glyph *, unsigned int); + +#endif blob - /dev/null blob + a68537cc8a39f45621290298693e63dcd7c0b066 (mode 644) --- /dev/null +++ frame.c @@ -0,0 +1,144 @@ +#include +#include +#include + +#include "driver.h" +#include "widget.h" + +static void frame_mouse_hover(struct aui_widget *, uint16_t, uint16_t); +static void frame_mouse_unhover(struct aui_widget *); +static void frame_mouse_press(struct aui_widget *, uint16_t, uint16_t, uint8_t); +static void frame_mouse_release(struct aui_widget *, uint16_t, uint16_t, uint8_t); +static void frame_set_geometry(struct aui_widget *, struct aui_geometry *); +static void frame_free(struct aui_widget *); +static struct aui_geometry frame_get_min_size(struct aui_widget *); + +static struct widget_ops frame_ops = { + frame_mouse_hover, + frame_mouse_unhover, + frame_mouse_press, + frame_mouse_release, + frame_set_geometry, + frame_free, + frame_get_min_size, +}; + +static struct aui_frame_config default_config = { + .background = { 0x77, 0x77, 0x77, 0xff }, +}; + +struct aui_frame* +aui_frame_new(struct aui_widget *parent, struct aui_frame_config *config) +{ + struct aui_frame *frame = calloc(1, sizeof(struct aui_frame)); + struct aui_widget *widget = (struct aui_widget *)frame; + struct aui_container *con = (struct aui_container *)frame; + + widget_init(widget, WIDGET_TYPE_FRAME, &frame_ops); + + widget_add(parent, widget); + con->map_count = 0; + + widget->primitives.count = 1; + widget->primitives.list = calloc(widget->primitives.count, sizeof(struct primitive *)); + widget->primitives.list[0] = driver->ops->create_rectangle(); + + aui_frame_config(widget, config); + + return frame; +} + +int +aui_frame_config(struct aui_widget *widget, struct aui_frame_config *config) +{ + struct aui_frame *frame = (struct aui_frame *)widget; + + frame->config = (config == NULL) ? default_config : *config; + driver->ops->set_rectangle_color(widget->window, widget->primitives.list[0], + &frame->config.background); + + return 0; +} + +static void +frame_mouse_hover(struct aui_widget *widget, uint16_t x, uint16_t y) +{ + struct aui_container *con = (struct aui_container *)widget; + struct aui_widget *tohover = NULL; + struct aui_widget *child; + + TAILQ_FOREACH(child, &widget->queue, entries) { + if (child->mapped && widget_collides_with_point(child, x, y)) + tohover = child; + } + + if (tohover != con->focus.hover && con->focus.hover != NULL) + con->focus.hover->ops->mouse_unhover(con->focus.hover); + + con->focus.hover = tohover; + + if (tohover != NULL) + tohover->ops->mouse_hover(tohover, x, y); +} + +static void +frame_mouse_unhover(struct aui_widget *widget) +{ + struct aui_container *con = (struct aui_container *)widget; + + if (con->focus.hover) { + con->focus.hover->ops->mouse_unhover(con->focus.hover); + con->focus.hover = NULL; + } +} + +static void +frame_mouse_press(struct aui_widget *widget, uint16_t x, uint16_t y, uint8_t button) +{ + struct aui_container *con = (struct aui_container *)widget; + struct aui_widget *topress = NULL; + struct aui_widget *child; + + TAILQ_FOREACH(child, &widget->queue, entries) { + if (child->mapped && widget_collides_with_point(child, x, y)) + topress = child; + } + + if (topress != con->focus.press && con->focus.press != NULL) + con->focus.press->ops->mouse_release(con->focus.press, x, y, button); + + con->focus.press = topress; + + if (topress != NULL) + topress->ops->mouse_press(topress, x, y, button); +} + +static void +frame_mouse_release(struct aui_widget *widget, uint16_t x, uint16_t y, uint8_t button) +{ + struct aui_container *con = (struct aui_container *)widget; + + if (con->focus.press != NULL) { + con->focus.press->ops->mouse_release(con->focus.press, x, y, button); + con->focus.press = NULL; + } +} + +static void +frame_set_geometry(struct aui_widget *widget, struct aui_geometry *geom) +{ + memcpy(&widget->geom, geom, sizeof(struct aui_geometry)); + driver->ops->set_rectangle_geometry(widget->window, widget->primitives.list[0], geom); +} + +static void +frame_free(struct aui_widget *widget) +{ + +} + +static struct aui_geometry frame_get_min_size(struct aui_widget *widget) +{ + struct aui_geometry geom = { 0, 0, 120, 120 }; + return geom; +} blob - /dev/null blob + ebeb86585fd96a64565a5fc6881784a9a9726eb3 (mode 644) --- /dev/null +++ label.c @@ -0,0 +1,124 @@ +#include +#include +#include + +#include "driver.h" +#include "widget.h" + +static void label_mouse_hover(struct aui_widget *, uint16_t, uint16_t); +static void label_mouse_unhover(struct aui_widget *); +static void label_mouse_press(struct aui_widget *, uint16_t, uint16_t, uint8_t); +static void label_mouse_release(struct aui_widget *, uint16_t, uint16_t, uint8_t); +static void label_set_geometry(struct aui_widget *, struct aui_geometry *); +static void label_free(struct aui_widget *); +static struct aui_geometry label_get_min_size(struct aui_widget *); + +static struct widget_ops label_ops = { + label_mouse_hover, + label_mouse_unhover, + label_mouse_press, + label_mouse_release, + label_set_geometry, /* set geometry */ + label_free, + label_get_min_size, +}; + +struct aui_label_config default_config = { + .text = "Hello AUI", + .foreground = { 0, 0, 0, 255 }, +}; + +struct aui_label* +aui_label_new(struct aui_widget *parent, struct aui_label_config *config) +{ + struct aui_label *label = calloc(1, sizeof(struct aui_label)); + struct aui_widget *widget = &label->widget; + + widget_init(widget, WIDGET_TYPE_LABEL, &label_ops); + widget_add(parent, widget); + + widget->primitives.count = 1; + widget->primitives.list = calloc(widget->primitives.count, sizeof(struct primitive*)); + + widget->primitives.list[0] = driver->ops->create_text(); + aui_label_config(widget, config); + + return label; +} + +int +aui_label_config(struct aui_widget *widget, struct aui_label_config *config) +{ + struct aui_label *label = (struct aui_label *)widget; + struct aui_geometry geom = widget->geom; + + if (label->config.text) { + free(label->config.text); + label->config.text = NULL; + } + + config = (config == NULL) ? &default_config : config; + label->config = *config; + + if (config->text) + label->config.text = strdup(config->text); + + driver->ops->set_text(widget->primitives.list[0], label->config.text, 0, 0); + driver->ops->set_text_color(widget->primitives.list[0], &label->config.foreground); + label_set_geometry(widget, &geom); + + layout_organize(widget->parent); + return 0; +} + +static void +label_mouse_hover(struct aui_widget *widget, uint16_t dx, uint16_t dy) +{ + +} + +static void +label_mouse_unhover(struct aui_widget *widget) +{ + +} + +static void +label_mouse_press(struct aui_widget *widget, uint16_t x, uint16_t y, uint8_t button) +{ + +} + +static void +label_mouse_release(struct aui_widget *widget, uint16_t x, uint16_t y, uint8_t button) +{ + +} + +static void +label_set_geometry(struct aui_widget *widget, struct aui_geometry *geom) +{ + struct aui_geometry tgeom = driver->ops->get_text_geometry(widget->primitives.list[0]); + struct aui_label *label = (struct aui_label *)widget; + + driver->ops->set_text( + widget->primitives.list[0], + label->config.text, + geom->x + geom->width / 2 - tgeom.width / 2 , + geom->y + geom->height + ); + widget->geom = *geom; +} + +static void +label_free(struct aui_widget *widget) +{ + +} + +static struct aui_geometry +label_get_min_size(struct aui_widget *widget) +{ + struct aui_geometry geom = driver->ops->get_text_geometry(widget->primitives.list[0]); + return geom; +} blob - /dev/null blob + b831bfede94fa9091f932ac551253007c170c259 (mode 644) --- /dev/null +++ layout.c @@ -0,0 +1,728 @@ +#include +#include +#include + +#include "aui.h" +#include "widget.h" + +static struct aui_placepar default_place = { + .x = 0, + .y = 0, + .relx = 0, + .rely = 0, + .width = 0, + .height = 0, + .relwidth = 0, + .relheight = 0, + .anchor = AUI_ANCHOR_NW, +}; + +static struct aui_packpar default_pack = { + .anchor = AUI_ANCHOR_NW, + .fill = AUI_FILL_NONE, + .side = AUI_SIDE_TOP, + .expand = 0, +}; + +static struct aui_gridpar default_grid = { + .column = 0, + .row = 0, + .columnspan = 1, + .rowspan = 1, + .ipadx = 0, + .ipady = 0, + .padx = 0, + .pady = 0 +}; + +static struct aui_rowpar default_rowconfigure = { + .minsize = 0, + .weight = 0, + .uniform = 0, + .pad = 0, +}; + +static struct aui_columnpar default_columnconfigure = { + .minsize = 0, + .weight = 0, + .uniform = 0, + .pad = 0, +}; + +static int +can_add(struct aui_widget *widget, enum aui_layout_type type) +{ + struct aui_widget *parent = widget->parent; + + if (parent->type != WIDGET_TYPE_WINDOW && parent->type != WIDGET_TYPE_CANVAS && + parent->type != WIDGET_TYPE_FRAME) { + fprintf(stderr, "libaui: cannot map widget in non-container\n"); + return -1; + } + + struct aui_container *con = (struct aui_container *)parent; + + if ((con->layout_type != type) && (con->layout_type != AUI_LAYOUT_NONE)) { + fprintf(stderr, "libaui: cannot map as mismatching type / non-empty container\n"); + return -1; + } + + return 0; +} + +void +place(struct aui_widget *widget) +{ + struct aui_widget *child; + + TAILQ_FOREACH(child, &widget->queue, entries) { + if (child->mapped == 0) + continue; + + struct aui_placepar *par = &child->placepar; + int dx = 0, dy = 0; + + struct aui_geometry geom = { + .x = widget->geom.x + child->placepar.x + widget->geom.width * child->placepar.relx, + .y = widget->geom.y + child->placepar.y + widget->geom.height * child->placepar.rely, + .width = child->placepar.width + child->placepar.relwidth * widget->geom.width, + .height = child->placepar.height + child->placepar.relheight * widget->geom.height }; + + switch (par->anchor) { + case AUI_ANCHOR_NW: + dx = 0; + dy = 0; + break; + case AUI_ANCHOR_NE: + dx = -geom.width; + dy = 0; + break; + case AUI_ANCHOR_N: + dx = -geom.width / 2; + dy = 0; + break; + case AUI_ANCHOR_W: + dx = 0; + dy = -geom.height / 2; + break; + case AUI_ANCHOR_E: + dx = -geom.width; + dy = -geom.height /2; + break; + case AUI_ANCHOR_SE: + dx = -geom.width; + dy = -geom.height; + break; + case AUI_ANCHOR_SW: + dx = 0; + dy = -geom.height; + break; + case AUI_ANCHOR_S: + dx = -geom.width / 2; + dy = -geom.height; + break; + case AUI_ANCHOR_CENTER: + dx = -geom.width / 2; + dy = -geom.height / 2; + break; + default: + break; + } + + geom.x += dx; + geom.y += dy; + child->ops->set_geometry(child, &geom); + + switch (child->type) { + case WIDGET_TYPE_FRAME: + layout_organize(child); + break; + default: + break; + } + } +} + +void +grid(struct aui_widget *widget) +{ + struct aui_container *con = (struct aui_container *)widget; + uint8_t *rmap = con->row_maps; + uint8_t *cmap = con->col_maps; + struct aui_widget *child; + + unsigned int *rsize = con->row_sizes; + unsigned int *csize = con->col_sizes; + + memset(con->row_maps, 0, sizeof(*con->row_maps) * con->grid_size[1]); + memset(con->col_maps, 0, sizeof(*con->col_maps) * con->grid_size[0]); + + memset(con->row_sizes, 0, sizeof(*con->row_sizes) * con->grid_size[1]); + memset(con->col_sizes, 0, sizeof(*con->col_sizes) * con->grid_size[0]); + + TAILQ_FOREACH(child, &widget->queue, entries) { + if (child->mapped == 0) + continue; + + struct aui_geometry min = child->ops->get_min_size(child); + + rmap[child->gridpar.row] = 1; + cmap[child->gridpar.column] = 1; + + rsize[child->gridpar.row] = (rsize[child->gridpar.row] < min.height) ? + min.height : rsize[child->gridpar.row]; + + csize[child->gridpar.column] = (csize[child->gridpar.column] < min.width) ? + min.width : csize[child->gridpar.column]; + } + + unsigned int *rpos = con->row_pos; + unsigned int *cpos = con->col_pos; + + unsigned int rowdt = 0; + unsigned int coldt = 0; + + for (int r = 0; r < con->grid_size[1]; r++) { + rpos[r] = rowdt; + rowdt = (rmap[r] == 1) ? rowdt + rsize[r] : rowdt; + } + + for (int c = 0; c < con->grid_size[0]; c++) { + cpos[c] = coldt; + coldt = (cmap[c] == 1) ? coldt + csize[c] : coldt; + } + + TAILQ_FOREACH(child, &widget->queue, entries) { + if (child->mapped == 0) + continue; + + struct aui_geometry min = child->ops->get_min_size(child); + struct aui_geometry geom = { 0 }; + struct aui_gridpar *gpar = &child->gridpar; + + int cspan = (gpar->column + gpar->columnspan) <= con->grid_size[0] ? + gpar->columnspan - 1: con->grid_size[0] - gpar->column - 1; + + int rspan = (gpar->row + gpar->rowspan) <= con->grid_size[1] ? + gpar->rowspan - 1: con->grid_size[1] - gpar->row - 1; + + + int dx = (csize[child->gridpar.column] - min.width); + int dy = (rsize[child->gridpar.row] - min.height); + + int width = min.width; + int height = min.height; + + for (int i = gpar->column; i < gpar->column + cspan; i++) + width += csize[i]; + + for (int i = gpar->row; i < gpar->row + rspan; i++) + height += rsize[i]; + + geom.x = widget->geom.x + cpos[child->gridpar.column] + dx / 2; + geom.y = widget->geom.y + rpos[child->gridpar.row] + dy / 2; + geom.width = width; + geom.height = height; + + child->ops->set_geometry(child, &geom); + } + + TAILQ_FOREACH(child, &widget->queue, entries) { + switch (child->type) { + case WIDGET_TYPE_FRAME: + layout_organize(child); + break; + default: + break; + } + } +} + +void +pack(struct aui_widget *widget) +{ + struct aui_container *con = (struct aui_container *)widget; + struct aui_widget *child; + struct aui_geometry space = widget->geom; + unsigned int hexpand = 0; + unsigned int vexpand = 0; + unsigned int req_w = 0; + unsigned int req_h = 0; + int extra_w = 0; + int extra_h = 0; + int hextra = 0; + int vextra = 0; + + /* + * Compute requested and extra space. hextra and vextra are only set + * if there is no top/bottom widgets succeeding it. This guarantees + * a proper request size. + */ + TAILQ_FOREACH(child, &widget->queue, entries) { + if (child->mapped == 0) + continue; + + struct aui_geometry cgeom = child->ops->get_min_size(child); + switch (child->packpar.side) { + case AUI_SIDE_TOP: + case AUI_SIDE_BOTTOM: + vexpand = (child->packpar.expand) ? vexpand + 1 : vexpand; + if (vextra) vextra = 0; + req_h += cgeom.height; + hextra = (cgeom.width > hextra) ? cgeom.width : hextra; + break; + case AUI_SIDE_LEFT: + case AUI_SIDE_RIGHT: + hexpand = (child->packpar.expand) ? hexpand + 1 : hexpand; + if (hextra) hextra = 0; + vextra = (cgeom.height > vextra) ? cgeom.height : vextra; + req_w += cgeom.width; + break; + default: + break; + } + } + + req_w += hextra; + req_h += vextra; + + if (req_w > space.width) + req_w = space.width; + + if (req_h > space.height) + req_h = space.height; + + extra_w = space.width - req_w; + extra_h = space.height - req_h; + + /* + * Making sure each widget consume space before expension + * for their area. + */ + TAILQ_FOREACH(child, &widget->queue, entries) { + if (child->mapped == 0) + continue; + + struct aui_geometry min = child->ops->get_min_size(child); + + switch (child->packpar.side) { + case AUI_SIDE_TOP: + child->packarea = (struct aui_geometry) { + .x = space.x, + .y = space.y, + .width = space.width, + .height = min.height, + }; + + if (child->packarea.height > space.height) { + child->packarea.height = space.height; + } + + space.y += child->packarea.height; + space.height -= child->packarea.height; + break; + case AUI_SIDE_BOTTOM: + child->packarea = (struct aui_geometry) { + .x = space.x, + .y = space.y, + .width = space.width, + .height = min.height + }; + + if (child->packarea.height > space.height) { + child->packarea.height = space.height; + } + + child->packarea.y = space.y + space.height - child->packarea.height; + + space.height -= child->packarea.height; + break; + case AUI_SIDE_LEFT: + child->packarea = (struct aui_geometry) { + .x = space.x, + .y = space.y, + .width = min.width, + .height = space.height + }; + + if (child->packarea.width > space.width) { + child->packarea.width = space.width; + } + + space.x += child->packarea.width; + space.width -= child->packarea.width; + break; + case AUI_SIDE_RIGHT: + child->packarea = (struct aui_geometry) { + .x = space.x, + .y = space.y, + .width = min.width, + .height = space.height + }; + + if (child->packarea.width > space.width) { + child->packarea.width = space.width; + } + + child->packarea.x = space.x + space.width - child->packarea.width; + + space.width -= child->packarea.width; + break; + default: + break; + } + } + + int top = 0; + int bottom = 0; + int right = 0; + int left = 0; + + /* + * Consuming the remaining space if expanded + */ + TAILQ_FOREACH(child, &widget->queue, entries) { + if (child->mapped == 0) + continue; + + if (space.width == 0 && space.height == 0) + break; + + struct aui_geometry *area = &child->packarea; + + switch (child->packpar.side) { + case AUI_SIDE_TOP: + area->x += left; + area->y += top; + area->width -= left; + + area->width -= right; + + if (child->packpar.expand == 1) { + if (vexpand) { + top += extra_h / vexpand; + area->height += extra_h / vexpand; + extra_h -= extra_h / vexpand; + vexpand--; + } + } + break; + + case AUI_SIDE_BOTTOM: + area->x += left; + area->width -= left; + + area->width -= right; + if (child->packpar.expand == 1) { + if (vexpand) { + bottom += extra_h / vexpand; + area->height += extra_h / vexpand; + area->y -= extra_h / vexpand; + extra_h -= extra_h / vexpand; + vexpand--; + } + } + break; + case AUI_SIDE_LEFT: + area->y += top; + area->x += left; + area->height -= top; + area->height -= bottom; + + if (child->packpar.expand == 1) { + if (hexpand) { + left += extra_w / hexpand; + area->width += extra_w / hexpand; + extra_w -= extra_w / hexpand; + hexpand--; + } + } + break; + case AUI_SIDE_RIGHT: + area->y += top; + area->height -= top; + area->height -= bottom; + + if (child->packpar.expand == 1) { + if (hexpand) { + right += extra_w / hexpand; + area->x -= extra_w / hexpand; + area->width += extra_w / hexpand; + extra_w -= extra_w / hexpand; + hexpand--; + } + } + break; + default: + break; + } + } + + /* + * Setting geometry + */ + TAILQ_FOREACH(child, &widget->queue, entries) { + if (child->mapped == 0) + continue; + + struct aui_geometry min = child->ops->get_min_size(child); + struct aui_geometry *area = &child->packarea; + + switch (child->packpar.fill) { + case AUI_FILL_NONE: + child->geom.width = (min.width > area->width) ? area->width : min.width; + child->geom.height = (min.height > area->height) ? area->height : min.height; + break; + case AUI_FILL_X: + child->geom.width = area->width; + child->geom.height = (min.height > area->height) ? area->height : min.height; + break; + case AUI_FILL_Y: + child->geom.width = (min.width > area->width) ? area->width : min.width; + child->geom.height = area->height; + break; + case AUI_FILL_BOTH: + child->geom.width = area->width; + child->geom.height = area->height; + break; + default: + break; + } + + switch (child->packpar.anchor) { + case AUI_ANCHOR_NW: + child->geom.x = area->x; + child->geom.y = area->y; + break; + case AUI_ANCHOR_N: + child->geom.x = area->x + area->width / 2 - child->geom.width / 2; + child->geom.y = area->y; + break; + case AUI_ANCHOR_NE: + child->geom.x = area->x + area->width - child->geom.width; + child->geom.y = area->y; + break; + case AUI_ANCHOR_CENTER: + child->geom.x = area->x + area->width / 2 - child->geom.width / 2; + child->geom.y = area->y + area->height / 2 - child->geom.height / 2; + break; + case AUI_ANCHOR_SE: + child->geom.x = area->x + area->width - child->geom.width; + child->geom.y = area->y + area->height - child->geom.height; + break; + case AUI_ANCHOR_S: + child->geom.x = area->x + area->width / 2 - child->geom.width / 2; + child->geom.y = area->y + area->height - child->geom.height; + break; + case AUI_ANCHOR_SW: + child->geom.x = area->x; + child->geom.y = area->y + area->height - child->geom.height; + break; + case AUI_ANCHOR_W: + child->geom.x = area->x; + child->geom.y = area->y + area->height / 2 - child->geom.height / 2; + break; + case AUI_ANCHOR_E: + child->geom.x = area->x + area->width - child->geom.width; + child->geom.y = area->y + area->height / 2 - child->geom.height / 2; + break; + default: + break; + break; + } + + child->ops->set_geometry(child, &child->geom); + } + + TAILQ_FOREACH(child, &widget->queue, entries) { + switch (child->type) { + case WIDGET_TYPE_FRAME: + layout_organize(child); + break; + default: + break; + } + } +} + +void +layout_organize(struct aui_widget *widget) +{ + struct aui_container *con = (struct aui_container *)widget; + struct aui_widget *child; + + switch (con->layout_type) { + case AUI_LAYOUT_PLACE: + place(widget); + break; + case AUI_LAYOUT_GRID: + grid(widget); + break; + /* + * Lots of suffering into implementing this one :D + */ + case AUI_LAYOUT_PACK: + pack(widget); + break; + default: + break; + } +} + +int +aui_place(struct aui_widget *widget, struct aui_placepar *par) +{ + int check = can_add(widget, AUI_LAYOUT_PLACE); + struct aui_widget *parent; + struct aui_container *con; + + if (check == -1) + return -1; + + if (par == NULL) + par = &default_place; + + con = (struct aui_container *)widget->parent; + parent = (struct aui_widget *)widget->parent; + con->layout_type = AUI_LAYOUT_PLACE; + widget->placepar = *(par); + widget->mapped = 1; + con->map_count++; + + layout_organize(parent); + + return 0; +} + +int +aui_pack(struct aui_widget *widget, struct aui_packpar *par) +{ + int check = can_add(widget, AUI_LAYOUT_PACK); + struct aui_widget *parent; + struct aui_container *con; + + if (check == -1) + return - 1; + + if (par == NULL) + par = &default_pack; + + con = (struct aui_container *)widget->parent; + parent = widget->parent; + con->layout_type = AUI_LAYOUT_PACK; + widget->packpar = *(par); + widget->mapped = 1; + con->map_count++; + + /* + * This is important for packing order + */ + widget_lead(widget->parent, widget); + + layout_organize(parent); + + return 0; +} + +int +aui_grid(struct aui_widget *widget, struct aui_gridpar *par) +{ + int check = can_add(widget, AUI_LAYOUT_GRID); + struct aui_widget *parent; + struct aui_container *con; + unsigned int *rsizes; + unsigned int *csizes; + unsigned int *rpos; + unsigned int *cpos; + uint8_t *rmaps; + uint8_t *cmaps; + + if (check == -1) + return -1; + + if (par == NULL) + par = &default_grid; + + par->columnspan = (par->columnspan < 1) ? 1 : par->columnspan; + par->rowspan = (par->rowspan < 1) ? 1 : par->rowspan; + con = (struct aui_container *)widget->parent; + parent = widget->parent; + con->layout_type = AUI_LAYOUT_GRID; + widget->gridpar = *(par); + widget->mapped = 1; + con->map_count++; + + con->grid_size[0] = (con->grid_size[0] > (par->column + par->columnspan)) ? + con->grid_size[0] : (par->column + par->columnspan); + + con->grid_size[1] = (con->grid_size[1] > (par->row + par->rowspan)) ? + con->grid_size[1] : (par->row + par->rowspan); + + rmaps = reallocarray(con->row_maps, con->grid_size[1], sizeof(*con->row_maps)); + if (rmaps == NULL) { + fprintf(stderr, "libaui: failed to allocate row maps\n"); + return -1; + } + con->row_maps = rmaps; + + cmaps = reallocarray(con->col_maps, con->grid_size[0], sizeof(*con->col_maps)); + if (cmaps == NULL) { + fprintf(stderr, "libaui: failed to allocate col maps\n"); + return -1; + } + con->col_maps = cmaps; + + + rsizes = reallocarray(con->row_sizes, con->grid_size[1], sizeof(*con->row_sizes)); + if (rsizes == NULL) { + fprintf(stderr, "libaui: failed to allocated row sizes\n"); + return -1; + } + con->row_sizes = rsizes; + + csizes = reallocarray(con->col_sizes, con->grid_size[0], sizeof(*con->col_sizes)); + if (csizes == NULL) { + fprintf(stderr, "libaui: failed to allocated col sizes\n"); + return -1; + } + con->col_sizes = csizes; + + rpos = reallocarray(con->row_pos, con->grid_size[1], sizeof(*con->row_pos)); + if (rpos == NULL) { + fprintf(stderr, "libaui: failed to allocate row positions\n"); + return -1; + } + con->row_pos = rpos; + + cpos = reallocarray(con->col_pos, con->grid_size[0], sizeof(*con->col_pos)); + if (cpos == NULL) { + fprintf(stderr, "libaui: failed to allocate col positions\n"); + return -1; + } + con->col_pos = cpos; + + layout_organize(parent); + + return 0; +} + +int +aui_getplacepar(struct aui_widget *widget, struct aui_placepar *par) +{ + *par = widget->placepar; + return 0; +} + +int +aui_getpackpar(struct aui_widget *widget, struct aui_packpar *par) +{ + *par = widget->packpar; + return 0; +} + +int +aui_getgridpar(struct aui_widget *widget, struct aui_gridpar *par) +{ + *par = widget->gridpar; + return 0; +} blob - /dev/null blob + f85d43f0c4dc673b366f3193971ad988171b2d87 (mode 644) --- /dev/null +++ layout.h @@ -0,0 +1,13 @@ +#ifndef LAYOUT_H +#define LAYOUT_H + +enum aui_layout_type { + AUI_LAYOUT_NONE, + AUI_LAYOUT_PLACE, + AUI_LAYOUT_GRID, + AUI_LAYOUT_PACK +}; + +void layout_organize(struct aui_widget *widget); + +#endif blob - /dev/null blob + 127c9194d4549b7d775d7b50b3e4b39703f9fc2f (mode 644) --- /dev/null +++ openbsd-queue.h @@ -0,0 +1,635 @@ +/* $OpenBSD: queue.h,v 1.46 2020/12/30 13:33:12 millert Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef _SYS_QUEUE_H_ +#define _SYS_QUEUE_H_ + +#ifdef __OpenBSD__ +#include +#endif + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues and XOR simple queues. + * + * + * A singly-linked list is headed by a single forward pointer. The elements + * are singly linked for minimum space and pointer manipulation overhead at + * the expense of O(n) removal for arbitrary elements. New elements can be + * added to the list after an existing element or at the head of the list. + * Elements being removed from the head of the list should use the explicit + * macro for this purpose for optimum efficiency. A singly-linked list may + * only be traversed in the forward direction. Singly-linked lists are ideal + * for applications with large datasets and few or no removals or for + * implementing a LIFO queue. + * + * A list is headed by a single forward pointer (or an array of forward + * pointers for a hash table header). The elements are doubly linked + * so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before + * or after an existing element or at the head of the list. A list + * may only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of the list. New elements can be added to the list before or after + * an existing element, at the head of the list, or at the end of the + * list. A simple queue may only be traversed in the forward direction. + * + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + * + * An XOR simple queue is used in the same way as a regular simple queue. + * The difference is that the head structure also includes a "cookie" that + * is XOR'd with the queue pointer (first, last or next) to generate the + * real pointer value. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) +#define _Q_INVALID ((void *)-1) +#define _Q_INVALIDATE(a) (a) = _Q_INVALID +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST(head); \ + (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + } \ + _Q_INVALIDATE((elm)->field.sle_next); \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods. + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST(head); \ + (var) && ((tvar) = LIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SIMPLEQ_FIRST(head); \ + (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_CONCAT(head1, head2) do { \ + if (!SIMPLEQ_EMPTY((head2))) { \ + *(head1)->sqh_last = (head2)->sqh_first; \ + (head1)->sqh_last = (head2)->sqh_last; \ + SIMPLEQ_INIT((head2)); \ + } \ +} while (0) + +/* + * XOR Simple queue definitions. + */ +#define XSIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqx_first; /* first element */ \ + struct type **sqx_last; /* addr of last next element */ \ + unsigned long sqx_cookie; \ +} + +#define XSIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqx_next; /* next element */ \ +} + +/* + * XOR Simple queue access methods. + */ +#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \ + (unsigned long)(ptr))) +#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first)) +#define XSIMPLEQ_END(head) NULL +#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head)) +#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next)) + + +#define XSIMPLEQ_FOREACH(var, head, field) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) != XSIMPLEQ_END(head); \ + (var) = XSIMPLEQ_NEXT(head, var, field)) + +#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = XSIMPLEQ_FIRST(head); \ + (var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \ + (var) = (tvar)) + +/* + * XOR Simple queue functions. + */ +#define XSIMPLEQ_INIT(head) do { \ + arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqx_next = (head)->sqx_first) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \ + *(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + +#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \ + XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ + (listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \ +} while (0) + +#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqx_first = XSIMPLEQ_XOR(head, \ + (head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \ +} while (0) + +#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \ + (elm)->field.sqx_next)->field.sqx_next) \ + == XSIMPLEQ_XOR(head, NULL)) \ + (head)->sqx_last = \ + XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \ +} while (0) + + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * Tail queue access methods. + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_CONCAT(head1, head2, field) do { \ + if (!TAILQ_EMPTY(head2)) { \ + *(head1)->tqh_last = (head2)->tqh_first; \ + (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \ + (head1)->tqh_last = (head2)->tqh_last; \ + TAILQ_INIT((head2)); \ + } \ +} while (0) + +/* + * Singly-linked Tail queue declarations. + */ +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first; /* first element */ \ + struct type **stqh_last; /* addr of last next element */ \ +} + +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +/* + * Singly-linked Tail queue access methods. + */ +#define STAILQ_FIRST(head) ((head)->stqh_first) +#define STAILQ_END(head) NULL +#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head)) +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#define STAILQ_FOREACH(var, head, field) \ + for ((var) = STAILQ_FIRST(head); \ + (var) != STAILQ_END(head); \ + (var) = STAILQ_NEXT(var, field)) + +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST(head); \ + (var) && ((tvar) = STAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked Tail queue functions. + */ +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((elm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((elm), field) = (elm); \ +} while (0) + +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#define STAILQ_REMOVE_AFTER(head, elm, field) do { \ + if ((STAILQ_NEXT(elm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->stqh_first; \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + STAILQ_REMOVE_AFTER(head, curelm, field); \ + } \ +} while (0) + +#define STAILQ_CONCAT(head1, head2) do { \ + if (!STAILQ_EMPTY((head2))) { \ + *(head1)->stqh_last = (head2)->stqh_first; \ + (head1)->stqh_last = (head2)->stqh_last; \ + STAILQ_INIT((head2)); \ + } \ +} while (0) + +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - offsetof(struct type, field)))) + +#endif /* !_SYS_QUEUE_H_ */ blob - /dev/null blob + edd3117b9e9802fc1c80378cf86ad3ffb7b9016e (mode 644) --- /dev/null +++ primitive.h @@ -0,0 +1,20 @@ +#ifndef PRIMITIVE_H +#define PRIMITIVE_H + +enum primitive_type { + PRIMITIVE_TYPE_RECTANGLE, + PRIMITIVE_TYPE_TEXT, + PRIMITIVE_TYPE_ARC, + PRIMITIVE_TYPE_BITMAP, + PRIMITIVE_TYPE_IMAGE, + PRIMITIVE_TYPE_LINE, + PRIMITIVE_TYPE_OVAL, + PRIMITIVE_TYPE_POLYGON, + PRIMITIVE_TYPE_WINDOW, +}; + +struct primitive { + unsigned int type; +}; + +#endif blob - /dev/null blob + c000b893c06f2f505578722d812040afc3387238 (mode 644) --- /dev/null +++ ui.c @@ -0,0 +1,158 @@ +#include +#include +#include + +#include "event.h" +#include "driver.h" +#include "widget.h" +#include "layout.h" + +struct aui_event_queue ev_queue; + +void +aui_add_event(struct aui_event *event) +{ + struct aui_event *toadd; + + switch (event->type) { + case AUI_EVENT_QUIT: { + struct aui_event_quit *qev = calloc(1, sizeof(struct aui_event_quit)); + memcpy((void *)qev, event, sizeof(struct aui_event_quit)); + + toadd = &qev->event; + break; + } + case AUI_EVENT_RESIZE: { + struct aui_event_resize *erz = calloc(1, sizeof(struct aui_event_resize)); + memcpy((void *)erz, event, sizeof(struct aui_event_resize)); + + /* + printf("libaui: resize %dx%d => %dx%d\n", erz->old_w, erz->old_h, erz->new_w, erz->new_h); + */ + + toadd = &erz->event; + break; + } + case AUI_EVENT_MOUSE_MOTION: { + struct aui_event_mouse_motion *mev = calloc(1, sizeof(struct aui_event_mouse_motion)); + memcpy((void *)mev, event, sizeof(struct aui_event_mouse_motion)); + + toadd = &mev->event; + break; + } + case AUI_EVENT_MOUSE_PRESS: { + struct aui_event_mouse_press *mpe = calloc(1, sizeof(struct aui_event_mouse_press)); + memcpy((void *)mpe, event, sizeof(struct aui_event_mouse_press)); + + toadd = &mpe->event; + break; + } + case AUI_EVENT_MOUSE_RELEASE: { + struct aui_event_mouse_release *mre = calloc(1, sizeof(struct aui_event_mouse_release)); + memcpy((void *)mre, event, sizeof(struct aui_event_mouse_release)); + + toadd = &mre->event; + break; + } + default: + fprintf(stderr, "libaui: failed to add event\n"); + return; + } + + TAILQ_INSERT_TAIL(&ev_queue, toadd, entries); +} + +void +aui_run(void) +{ + while (!LIST_EMPTY(&wlist)) { + driver->ops->handle_events(); + + /* + * Event Management + */ + struct aui_event *ev; + while (!TAILQ_EMPTY(&ev_queue)) { + ev = TAILQ_FIRST(&ev_queue); + TAILQ_REMOVE(&ev_queue, ev, entries); + + switch (ev->type) { + case AUI_EVENT_QUIT: { + struct aui_event_quit *qev = (struct aui_event_quit *)ev; + struct aui_window *aw = ev->aw; + struct aui_widget *ww = (struct aui_widget *)aw; + + ww->ops->free(ww); + + free((void *)qev); + break; + } + case AUI_EVENT_RESIZE: { + struct aui_event_resize *rze = (struct aui_event_resize *)ev; + struct aui_window *aw = ev->aw; + + aw->config.width = rze->new_w; + aw->config.height = rze->new_h; + + driver->ops->resize_window(aw); + free((void *)rze); + + layout_organize((struct aui_widget *)aw); + break; + } + case AUI_EVENT_MOUSE_MOTION: { + struct aui_event_mouse_motion *mev = (struct aui_event_mouse_motion *)ev; + struct aui_window *aw = ev->aw; + struct aui_widget *ww = (struct aui_widget *)aw; + struct widget_ops *ops = ww->ops; + + ops->mouse_hover(ww, mev->x, mev->y); + free((void *)mev); + break; + } + case AUI_EVENT_MOUSE_PRESS: { + struct aui_event_mouse_press *mpe = (struct aui_event_mouse_press *)ev; + struct aui_window *aw = ev->aw; + struct aui_widget *ww = (struct aui_widget *)aw; + struct widget_ops *ops = ww->ops; + + ops->mouse_press(ww, mpe->x, mpe->y, mpe->button); + free((void *)mpe); + break; + } + case AUI_EVENT_MOUSE_RELEASE: { + struct aui_event_mouse_release *mre = (struct aui_event_mouse_release *)ev; + struct aui_window *aw = ev->aw; + struct aui_widget *ww = (struct aui_widget *)aw; + struct widget_ops *ops = ww->ops; + + ops->mouse_release(ww, mre->x, mre->y, mre->button); + free((void *)mre); + break; + } + default: + break; + } + } + + /* + * Draw Calls + */ + struct aui_window *aw; + LIST_FOREACH(aw, &wlist, entries) { + if (aw->draw_flag) { + struct aui_widget *widget = (struct aui_widget *)aw; + driver->ops->render_background(aw); + widget_draw(widget); + driver->ops->render_foreground(aw); + aw->draw_flag = 0; + } + } + } +} + +void +aui_destroy(struct aui_widget *widget) +{ + widget->ops->free(widget); +} blob - /dev/null blob + a75e978fa3653449f0abf805f3abdfdfd0a5cea3 (mode 644) --- /dev/null +++ widget.c @@ -0,0 +1,102 @@ +#include +#include +#include + +#include "driver.h" +#include "widget.h" +#include "primitive.h" + +void +widget_init(struct aui_widget *widget, unsigned int type, struct widget_ops *ops) +{ + TAILQ_INIT(&(widget)->queue); + widget->geom = (struct aui_geometry) { 0 }; + widget->mapped = 0; + + memset(&widget->placepar, 0, sizeof(struct aui_placepar)); + memset(&widget->gridpar, 0, sizeof(struct aui_gridpar)); + memset(&widget->packpar, 0, sizeof(struct aui_packpar)); + + widget->type = type; + widget->ops = ops; +} + +void +widget_add(struct aui_widget *parent, struct aui_widget *child) +{ + child->parent = parent; + child->window = parent->window; + TAILQ_INSERT_TAIL(&parent->queue, child, entries); +} + +void +widget_lead(struct aui_widget *parent, struct aui_widget *child) +{ + TAILQ_REMOVE(&parent->queue, child, entries); + TAILQ_INSERT_TAIL(&parent->queue, child, entries); +} + +void +widget_remove(struct aui_widget *parent, struct aui_widget *child) +{ + TAILQ_REMOVE(&parent->queue, child, entries); +} + +void +widget_draw(struct aui_widget *widget) +{ + if (widget->mapped == 0) + return; + + struct primitive *prim; + + if (widget->geom.width == 0 || widget->geom.height == 0) + return; + + for (int i = 0; i < widget->primitives.count; i++) { + prim = widget->primitives.list[i]; + + switch (prim->type) { + case PRIMITIVE_TYPE_RECTANGLE: + driver->ops->render_rectangle(widget->window, prim); + break; + case PRIMITIVE_TYPE_TEXT: + driver->ops->render_text(widget->window, prim); + break; + default: + break; + } + } + + if (widget->type == WIDGET_TYPE_CANVAS) { + struct aui_canvas *canvas = (struct aui_canvas *)widget; + struct aui_canvas_item *item; + + TAILQ_FOREACH(item, &canvas->queue, entries) { + struct primitive *prim; + for (int i = 0; i < item->primitives.count; i++) { + prim = item->primitives.list[i]; + + switch (prim->type) { + case PRIMITIVE_TYPE_RECTANGLE: + driver->ops->render_rectangle(widget->window, prim); + break; + default: + break; + } + } + } + } + + struct aui_widget *child; + TAILQ_FOREACH(child, &widget->queue, entries) { + widget_draw(child); + } +} + +int +widget_collides_with_point(struct aui_widget *widget, uint16_t x, uint16_t y) +{ + return (x >= widget->geom.x && x < (widget->geom.x + widget->geom.width) && + y >= widget->geom.y && y < (widget->geom.y + widget->geom.height)); +} blob - /dev/null blob + ff0077c16e3465ac05c6d6ec98065e4f0a6f14ba (mode 644) --- /dev/null +++ widget.h @@ -0,0 +1,132 @@ +#ifndef __WIDGET_H__ +#define __WIDGET_H__ + +#include "openbsd-queue.h" + +#include "layout.h" + +/* + * Gives screen position + */ +struct aui_geometry { + int x, y; + uint16_t width; + uint16_t height; +}; + +enum widget_type { + WIDGET_TYPE_WINDOW, + WIDGET_TYPE_FRAME, + WIDGET_TYPE_CANVAS, + WIDGET_TYPE_BUTTON, + WIDGET_TYPE_LABEL, +}; + +struct widget_primitives { + unsigned int count; + struct primitive **list; +}; + +TAILQ_HEAD(aui_widget_queue, aui_widget); +struct aui_widget { + enum widget_type type; + struct aui_geometry geom; + + struct aui_window *window; + struct widget_ops *ops; + + struct aui_placepar placepar; + struct aui_gridpar gridpar; + struct aui_packpar packpar; + struct aui_geometry packarea; + + unsigned int mapped; + struct widget_primitives primitives; + + struct aui_widget *parent; + struct aui_widget_queue queue; + TAILQ_ENTRY(aui_widget) entries; +}; + +struct widget_ops { + void (*mouse_hover) (struct aui_widget *, uint16_t, uint16_t); + void (*mouse_unhover) (struct aui_widget *); + void (*mouse_press) (struct aui_widget *, uint16_t, uint16_t, uint8_t); + void (*mouse_release) (struct aui_widget *, uint16_t, uint16_t, uint8_t); + void (*set_geometry) (struct aui_widget *, struct aui_geometry *); + void (*free) (struct aui_widget *); + struct aui_geometry (*get_min_size) (struct aui_widget *); +}; + +struct aui_container { + struct aui_widget widget; + enum aui_layout_type layout_type; + unsigned int map_count; + + /* + * Grid members + */ + uint8_t *row_maps; + uint8_t *col_maps; + uint32_t grid_size[2]; + unsigned int *row_sizes; + unsigned int *col_sizes; + unsigned int *row_pos; + unsigned int *col_pos; + struct aui_rowpar *rowconfigure; + struct aui_columnpar *columnconfigure; + + struct container_focus { + struct aui_widget *press; + struct aui_widget *hover; + } focus; +}; + +LIST_HEAD(aui_window_list, aui_window); +struct aui_window { + struct aui_container con; + const char *title; + int draw_flag; + struct aui_window_config config; + + LIST_ENTRY(aui_window) entries; +}; + +struct aui_frame { + struct aui_container con; + struct aui_frame_config config; +}; + +struct aui_canvas_item { + unsigned int type; + struct widget_primitives primitives; + struct aui_canvas_item_config config; + TAILQ_ENTRY(aui_canvas_item) entries; +}; + +struct aui_canvas { + struct aui_container con; + TAILQ_HEAD(aui_canvas_item_queue, aui_canvas_item) queue; +}; + +struct aui_button { + struct aui_widget widget; + unsigned int pressed; + struct aui_button_config config; +}; + +struct aui_label { + struct aui_widget widget; + struct aui_label_config config; +}; + +extern struct aui_window_list wlist; + +void widget_init(struct aui_widget*, unsigned int, struct widget_ops*); +void widget_draw(struct aui_widget *); +void widget_add(struct aui_widget *, struct aui_widget *); +void widget_remove(struct aui_widget *, struct aui_widget *); +void widget_lead(struct aui_widget *, struct aui_widget *); +int widget_collides_with_point(struct aui_widget*, uint16_t, uint16_t); + +#endif blob - /dev/null blob + 74f2f8e5ae9aa2bff600fc5eb5a2fe98da520864 (mode 644) --- /dev/null +++ window.c @@ -0,0 +1,145 @@ +#include +#include +#include + +#include "event.h" +#include "driver.h" +#include "widget.h" + +struct aui_window_list wlist; + +static void window_mouse_hover(struct aui_widget *, uint16_t, uint16_t); +static void window_mouse_unhover(struct aui_widget *); +static void window_mouse_press(struct aui_widget *, uint16_t, uint16_t, uint8_t); +static void window_mouse_release(struct aui_widget *, uint16_t, uint16_t, uint8_t); +static void window_free(struct aui_widget *); +static struct aui_geometry window_get_min_size(struct aui_widget *); + +static struct widget_ops window_ops = { + window_mouse_hover, + window_mouse_unhover, + window_mouse_press, + window_mouse_release, + NULL, /* set geomtry */ + window_free, + window_get_min_size, +}; + +static struct aui_window_config default_config = { + .width = 200, + .height = 200, + .title = "aui", +}; + +struct aui_window* +aui_window_new(struct aui_window_config *config) +{ + if (driver == NULL) { + if (driver_open(DRIVER_TYPE_X11) == -1) { + return NULL; + } + TAILQ_INIT(&ev_queue); + LIST_INIT(&wlist); + } + + if (config == NULL) { + config = &default_config; + } + + struct aui_window *aw = driver->ops->create_window(config); + struct aui_widget *widget = (struct aui_widget *)aw; + struct aui_container *con = (struct aui_container *)aw; + con->map_count = 0; + + LIST_INSERT_HEAD(&wlist, aw, entries); + widget_init(widget, WIDGET_TYPE_WINDOW, &window_ops); + + widget->mapped = 1; + + widget->window = aw; + aw->draw_flag = 1; + return aw; +} + +void +window_free(struct aui_widget *widget) +{ + struct aui_window *aw = (struct aui_window *)widget; + + LIST_REMOVE(aw, entries); + driver->ops->delete_window(aw); +} + +static void +window_mouse_hover(struct aui_widget *widget, uint16_t x, uint16_t y) +{ + struct aui_window *aw = (struct aui_window *)widget; + struct aui_container *con = (struct aui_container *)widget; + struct aui_widget *tohover = NULL; + struct aui_widget *child; + + TAILQ_FOREACH(child, &widget->queue, entries) { + if (child->mapped && widget_collides_with_point(child, x, y)) + tohover = child; + } + + if (tohover != con->focus.hover && con->focus.hover != NULL) + con->focus.hover->ops->mouse_unhover(con->focus.hover); + + con->focus.hover = tohover; + + if (tohover != NULL) + tohover->ops->mouse_hover(tohover, x, y); +} + +static void +window_mouse_unhover(struct aui_widget *widget) +{ + +} + +static void +window_mouse_press(struct aui_widget *widget, uint16_t x, uint16_t y, uint8_t button) +{ + struct aui_container *con = (struct aui_container *)widget; + struct aui_window *aw = (struct aui_window *)widget; + struct aui_widget *topress = NULL; + struct aui_widget *child; + + TAILQ_FOREACH(child, &widget->queue, entries) { + if (child->mapped && widget_collides_with_point(child, x, y)) + topress = child; + } + + if (topress != con->focus.press && con->focus.press != NULL) + con->focus.press->ops->mouse_release(con->focus.press, x, y, button); + + con->focus.press = topress; + + if (topress != NULL) + topress->ops->mouse_press(topress, x, y, button); +} + +static void +window_mouse_release(struct aui_widget *widget, uint16_t x, uint16_t y, uint8_t button) +{ + struct aui_container *con = (struct aui_container *)widget; + struct aui_window *aw = (struct aui_window *)widget; + + if (con->focus.press != NULL) { + con->focus.press->ops->mouse_release(con->focus.press, x, y, button); + con->focus.press = NULL; + + window_mouse_hover(widget, x, y); + } +} + +static struct aui_geometry +window_get_min_size(struct aui_widget *widget) +{ + struct aui_window *aw = (struct aui_window *)widget; + struct aui_geometry geom = { 0 }; + geom.width = aw->config.width; + geom.height = aw->config.height; + return geom; +} blob - /dev/null blob + 0f60d614d1ab3cdf8e93ef5c6f4847f6b2e912e1 (mode 644) --- /dev/null +++ xcb.c @@ -0,0 +1,751 @@ +#include +#include +#include +#include + +#include +#include + +#include "font.h" +#include "event.h" +#include "driver.h" +#include "widget.h" +#include "primitive.h" + +struct aui_dri_xcb { + struct aui_dri adr; + + struct pollfd pollfd; /* One reason, POSIX */ + + xcb_connection_t *conn; + xcb_screen_t *screen; + xcb_intern_atom_reply_t *wm_protocols; + xcb_intern_atom_reply_t *wm_delete_window; + + xcb_render_glyphset_t glyphset; + struct glyph *glyphs; + int font_height; + xcb_render_pictformat_t fmt_normal; + xcb_render_pictformat_t fmt_alpha8; + xcb_render_pictformat_t fmt_argb32; +}; + +struct aui_window_xcb { + struct aui_window aw; + + xcb_window_t xw; + xcb_pixmap_t pixmap; + xcb_render_picture_t dst_pict; + xcb_render_picture_t src_pict; +}; + +struct aui_dri *driver; + +static struct aui_window *aui_dri_create_window(struct aui_window_config *); +static void aui_dri_delete_window(struct aui_window *); +static void aui_dri_handle_events(void); +static void aui_dri_resize_window(struct aui_window *); +static void aui_dri_render_background(struct aui_window *); +static void aui_dri_render_foreground(struct aui_window *); + +static struct primitive *aui_dri_create_rectangle(void); +static void aui_dri_delete_rectangle(struct primitive *); +static void aui_dri_render_rectangle(struct aui_window *, struct primitive *); +static void aui_dri_set_rectangle_color(struct aui_window *, struct primitive *, struct aui_color *); +static void aui_dri_set_rectangle_geometry(struct aui_window *, struct primitive *, + struct aui_geometry *); + +static struct primitive *aui_dri_create_text(void); +static void aui_dri_set_text(struct primitive *, const char *, int16_t, int16_t); +static void aui_dri_set_text_color(struct primitive*, struct aui_color*); +static void aui_dri_render_text(struct aui_window *, struct primitive *); +static struct aui_geometry aui_dri_get_text_geometry(struct primitive *); + +struct aui_dri_canvas { + struct aui_canvas can; +}; + +static struct dri_ops aui_dri_ops = { + aui_dri_create_window, + aui_dri_delete_window, + aui_dri_handle_events, + aui_dri_resize_window, + aui_dri_render_background, + aui_dri_render_foreground, + + aui_dri_create_rectangle, + aui_dri_delete_rectangle, + aui_dri_render_rectangle, + aui_dri_set_rectangle_color, + aui_dri_set_rectangle_geometry, + + aui_dri_create_text, + aui_dri_set_text, + aui_dri_set_text_color, + aui_dri_render_text, + aui_dri_get_text_geometry +}; + +struct rectangle { + struct primitive base; + xcb_rectangle_t xrect; + xcb_render_color_t xcolor; +}; + +struct text { + struct primitive base; + const char *data; /* Not allocated, pointer to string literal */ + xcb_render_color_t xcolor; + xcb_render_picture_t xpict; + int16_t x, y; +}; + +static int config_xcb(struct aui_dri_xcb*); +static void set_pixmap_xcb(struct aui_window_xcb *); +static struct aui_window_xcb *find_window_xcb(xcb_window_t); +static void set_name_xcb(struct aui_window_xcb *, const char *); + +int +xcb_driver_open(void) +{ + struct aui_dri_xcb *dri_xcb; + + dri_xcb = malloc(sizeof(struct aui_dri_xcb)); + if (dri_xcb == NULL) + return -1; + + /* Setting up the driver */ + dri_xcb->adr.ops = &aui_dri_ops; + driver = &dri_xcb->adr; + + if (config_xcb(dri_xcb) == -1) + return -1; + + + dri_xcb->font_height = 0; + dri_xcb->glyphs = calloc(126, sizeof(struct glyph)); + for (int i = 32; i < 127; i++) dri_xcb->glyphs[i].charcode = i; + + font_init(); + font_load_glyphs(dri_xcb->glyphs + 32, 127 - 32); + + dri_xcb->glyphset = xcb_generate_id(dri_xcb->conn); + xcb_render_create_glyph_set(dri_xcb->conn, dri_xcb->glyphset, dri_xcb->fmt_alpha8); + + for (int i = 0; i < (127 - 32); i++) { + uint32_t glyphid = 32 + i; + struct glyph *g = &dri_xcb->glyphs[i + 32]; + xcb_render_glyphinfo_t ginfo = { + .width = g->width, + .height = g->height, + .x = g->x, + .y = g->y, + .x_off = g->x_offset, + .y_off = 0, /* We are only rendering horizontaly. This is unecessary */ + }; + + xcb_render_add_glyphs( + dri_xcb->conn, + dri_xcb->glyphset, + 1, + &glyphid, + &ginfo, + g->stride * g->height, + g->bitmap + ); + + if (dri_xcb->font_height < g->y_offset) + dri_xcb->font_height = g->y_offset; + } + + return 0; +} + +static struct aui_window +*aui_dri_create_window(struct aui_window_config *config) +{ + struct aui_window_xcb *aw_xcb; + struct aui_dri_xcb *dri_xcb; + struct aui_window *aw; + + aw_xcb = calloc(1, sizeof(struct aui_window_xcb)); + + if (aw_xcb == NULL) + return NULL; + + dri_xcb = (struct aui_dri_xcb*)driver; + aw = &aw_xcb->aw; + + + int mask = XCB_CW_BACK_PIXMAP | XCB_CW_EVENT_MASK; + uint32_t values[2] = {XCB_NONE, + XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY | + XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | + XCB_EVENT_MASK_POINTER_MOTION | XCB_EVENT_MASK_BUTTON_PRESS| + XCB_EVENT_MASK_BUTTON_RELEASE}; + + aw_xcb->xw = xcb_generate_id(dri_xcb->conn); + xcb_create_window(dri_xcb->conn, + XCB_COPY_FROM_PARENT, + aw_xcb->xw, + dri_xcb->screen->root, + 0, 0, + config->width, config->height, + 0, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + dri_xcb->screen->root_visual, + mask, values); + + xcb_atom_t data[] = { dri_xcb->wm_delete_window->atom }; + xcb_change_property(dri_xcb->conn, + XCB_PROP_MODE_REPLACE, + aw_xcb->xw, + dri_xcb->wm_protocols->atom, + XCB_ATOM_ATOM, + 32, + 1, + (const void *)data); + + struct aui_widget *ww = (struct aui_widget *)aw; + ww->geom.width = config->width; + ww->geom.height = config->height; + set_pixmap_xcb(aw_xcb); + set_name_xcb(aw_xcb, config->title); + xcb_map_window(dri_xcb->conn, aw_xcb->xw); + xcb_flush(dri_xcb->conn); + return aw; +} + +static void +aui_dri_delete_window(struct aui_window *aw) +{ + struct aui_window_xcb *aw_xcb; + struct aui_dri_xcb *dri_xcb; + + dri_xcb = (struct aui_dri_xcb *)driver; + aw_xcb = (struct aui_window_xcb *)aw; + + xcb_destroy_window(dri_xcb->conn, aw_xcb->xw); + free(aw_xcb); + xcb_flush(dri_xcb->conn); +} + +/* + * Formats event sent by xcb into aui events + */ +static void +aui_dri_handle_events() +{ + struct aui_dri_xcb *dri_xcb; + xcb_generic_event_t *event; + struct aui_window_xcb *aw_xcb; + struct aui_window *aw; + struct aui_widget *ww; + xcb_expose_event_t *exe; + xcb_motion_notify_event_t *mne; + xcb_client_message_event_t *cme; + xcb_configure_notify_event_t *cne; + xcb_button_press_event_t *bpe; + xcb_button_release_event_t *bre; + + dri_xcb = (struct aui_dri_xcb *)driver; + + int ret = poll(&dri_xcb->pollfd, 1, 10); + + if (ret < 0) + return; + + while ((event = xcb_poll_for_event(dri_xcb->conn)) != NULL) { + switch (event->response_type & ~0x80) { + case XCB_EXPOSE: + exe = (xcb_expose_event_t *)event; + aw_xcb = find_window_xcb(exe->window); + aw = (struct aui_window *)aw_xcb; + aw->draw_flag = 1; + break; + case XCB_CLIENT_MESSAGE: + cme = (xcb_client_message_event_t *)event; + aw_xcb = find_window_xcb(cme->window); + aw = (struct aui_window *)aw_xcb; + + if (cme->data.data32[0] == dri_xcb->wm_delete_window->atom) { + struct aui_event_quit qev = { + .event = { .type = AUI_EVENT_QUIT, .aw = aw }, + }; + aui_add_event(&qev.event); + } + break; + case XCB_CONFIGURE_NOTIFY: + cne = (xcb_configure_notify_event_t *)event; + aw_xcb = find_window_xcb(cne->window); + aw = (struct aui_window *)aw_xcb; + ww = (struct aui_widget *)aw; + + if (ww->geom.width != cne->width || ww->geom.height != cne->height) { + struct aui_event_resize rse = { + .event = { .type = AUI_EVENT_RESIZE, .aw = aw }, + .old_w = ww->geom.width, + .old_h = ww->geom.height, + .new_w = cne->width, + .new_h = cne->height, + }; + ww->geom.width = cne->width; + ww->geom.height = cne->height; + aui_add_event(&rse.event); + aw->draw_flag = 1; + } + break; + case XCB_MOTION_NOTIFY: + mne = (xcb_motion_notify_event_t *)event; + aw_xcb = find_window_xcb(mne->event); + aw = (struct aui_window *)aw_xcb; + + struct aui_event_mouse_motion mev = { + .event = { .type = AUI_EVENT_MOUSE_MOTION, .aw = aw }, + .x = mne->event_x, .y = mne->event_y + }; + aui_add_event(&mev.event); + break; + case XCB_BUTTON_PRESS: + bpe = (xcb_button_press_event_t *)event; + aw_xcb = find_window_xcb(bpe->event); + aw = (struct aui_window *)aw_xcb; + + struct aui_event_mouse_press mpe = { + .event = { .type = AUI_EVENT_MOUSE_PRESS, .aw = aw }, + .x = bpe->event_x, .y = bpe->event_y, .button = bpe->detail + }; + aui_add_event(&mpe.event); + break; + case XCB_BUTTON_RELEASE: + bre = (xcb_button_release_event_t *)event; + aw_xcb = find_window_xcb(bre->event); + aw = (struct aui_window *)aw_xcb; + + struct aui_event_mouse_release mre = { + .event = { .type = AUI_EVENT_MOUSE_RELEASE, .aw = aw }, + .x = bre->event_x, .y = bre->event_y, .button = bre->detail + }; + aui_add_event(&mre.event); + break; + default: + break; + } + free(event); + } +} + +static void +aui_dri_resize_window(struct aui_window *aw) +{ + set_pixmap_xcb((struct aui_window_xcb *)aw); + aw->draw_flag = 1; +} + +static void +aui_dri_render_background(struct aui_window *aw) +{ + xcb_render_color_t color = { .red = 0xd9d9, .green = 0xd9d9, .blue = 0xd9d9, .alpha = 0xffff }; + struct aui_widget *ww = (struct aui_widget *)aw; + xcb_rectangle_t rects[] = { { .x = 0, .y = 0, .width = ww->geom.width, + .height = ww->geom.height } }; + struct aui_dri_xcb *dri_xcb = (struct aui_dri_xcb *)driver; + struct aui_window_xcb *aw_xcb = (struct aui_window_xcb *)aw; + + xcb_render_fill_rectangles(dri_xcb->conn, + XCB_RENDER_PICT_OP_OVER, + aw_xcb->src_pict, + color, + 1, + rects); +} + +static void +aui_dri_render_foreground(struct aui_window *aw) +{ + struct aui_window_xcb *aw_xcb = (struct aui_window_xcb *)aw; + struct aui_dri_xcb *dri_xcb = (struct aui_dri_xcb *)driver; + struct aui_widget *ww = (struct aui_widget *)aw; + + xcb_render_composite(dri_xcb->conn, + XCB_RENDER_PICT_OP_OVER, + aw_xcb->src_pict, + XCB_NONE, + aw_xcb->dst_pict, + 0, 0, 0, 0, 0, 0, + ww->geom.width, ww->geom.height); + + xcb_flush(dri_xcb->conn); +} + +static struct primitive* +aui_dri_create_rectangle() +{ + struct rectangle *rect = calloc(1, sizeof(struct rectangle)); + + rect->base.type = PRIMITIVE_TYPE_RECTANGLE; + rect->xrect.x = 0; + rect->xrect.y = 0; + rect->xrect.width = 0; + rect->xrect.height = 0; + + rect->xcolor.red = 0; + rect->xcolor.green = 0; + rect->xcolor.blue = 0; + rect->xcolor.alpha = 0; + + return &(rect)->base; +} + +static void +aui_dri_delete_rectangle(struct primitive *prim) +{ + struct rectangle *rect = (struct rectangle *)prim; + free(rect); +} + +static void +aui_dri_render_rectangle(struct aui_window *aw, struct primitive *prim) +{ + struct aui_window_xcb *aw_xcb = (struct aui_window_xcb *)aw; + struct aui_dri_xcb *dri_xcb = (struct aui_dri_xcb *)driver; + struct rectangle *rect = (struct rectangle *)prim; + + if (prim->type != PRIMITIVE_TYPE_RECTANGLE) { + fprintf(stderr, "libaui: attempt to render rectangle with mismatching type.\n"); + return; + } + + xcb_render_fill_rectangles(dri_xcb->conn, + XCB_RENDER_PICT_OP_OVER, + aw_xcb->src_pict, + rect->xcolor, + 1, + &rect->xrect); +} + +static void +aui_dri_set_rectangle_color(struct aui_window *aw, struct primitive *prim, struct aui_color *color) +{ + struct rectangle *rect = (struct rectangle *)prim; + + if (prim->type != PRIMITIVE_TYPE_RECTANGLE) { + fprintf(stderr, "libaui: attempt to set_rectangle_color for mismatching type\n"); + return; + } + + rect->xcolor.red = (color->red * 0xFFFF) / 0xFF; + rect->xcolor.green = (color->green * 0xFFFF) / 0xFF; + rect->xcolor.blue = (color->blue * 0xFFFF) / 0xFF; + rect->xcolor.alpha = (color->alpha * 0xFFFF) / 0xFF; + + aw->draw_flag = 1; +} + +static +void aui_dri_set_rectangle_geometry(struct aui_window *aw, struct primitive *prim, + struct aui_geometry *geom) +{ + struct rectangle *rect = (struct rectangle *)prim; + + rect->xrect.x = geom->x; + rect->xrect.y = geom->y; + rect->xrect.width = geom->width; + rect->xrect.height = geom->height; +} + +static struct primitive* +aui_dri_create_text() +{ + struct aui_dri_xcb *dri_xcb = (struct aui_dri_xcb *)driver; + struct text *t = calloc(1, sizeof(struct text)); + + t->xpict = xcb_generate_id(dri_xcb->conn); + t->xcolor = (xcb_render_color_t) { 0xffff/2, 0xffff/2, 0, 0xffff }; + xcb_render_create_solid_fill(dri_xcb->conn, t->xpict, t->xcolor); + return &(t)->base; +} + +static void +aui_dri_set_text(struct primitive *p, const char *data, int16_t x, int16_t y) +{ + struct text *t = (struct text *)p; + t->base.type = PRIMITIVE_TYPE_TEXT; + t->data = data; + t->x = x; + t->y = y; +} + +static void +aui_dri_set_text_color(struct primitive *p, struct aui_color *c) +{ + struct aui_dri_xcb *dri_xcb = (struct aui_dri_xcb *)driver; + struct text *t = (struct text *)p; + + t->xcolor = (xcb_render_color_t) { + c->red * 0xffff / 0xff, + c->green * 0xffff / 0xff, + c->blue * 0xffff / 0xff, + c->alpha * 0xffff / 0xff + }; + + xcb_render_free_picture(dri_xcb->conn, t->xpict); + t->xpict = xcb_generate_id(dri_xcb->conn); + xcb_render_create_solid_fill(dri_xcb->conn, t->xpict, t->xcolor); +} + +/* + * This renders text without the use of xcb_render utils. It utilises the + * xcb_render_composite_glyphs_8 function which expects an 8 bytes header + * where. Can only be up to 255 characters: + * + * byte0=> len + * byte1-3=> padding + * byte4-5=> dx + * byte6-7=> dy + */ +struct ghead { + uint8_t len; + uint8_t pad[3]; + int16_t dx; + int16_t dy; +}; + +static void +aui_dri_render_text(struct aui_window *aw, struct primitive *p) +{ + struct aui_window_xcb *aw_xcb = (struct aui_window_xcb *)aw; + struct aui_dri_xcb *dri_xcb = (struct aui_dri_xcb *)driver; + struct text *t = (struct text *)p; + + size_t len = (t->data == NULL) ? 0 : strlen(t->data); + struct ghead cmd = { .len = len, .dx = t->x, .dy = t->y }; + + size_t cmd_len = len + 8; + uint8_t *glyphcmds = malloc(cmd_len); + + if (glyphcmds == NULL) + return; + + glyphcmds[0] = cmd.len; + glyphcmds[1] = 0; + glyphcmds[2] = 0; + glyphcmds[3] = 0; + + memcpy(&glyphcmds[4], &cmd.dx, 2); + memcpy(&glyphcmds[6], &cmd.dy, 2); + + memcpy(glyphcmds + 8, t->data, len); + + xcb_render_composite_glyphs_8( + dri_xcb->conn, + XCB_RENDER_PICT_OP_OVER, + t->xpict, + aw_xcb->src_pict, + 0, + dri_xcb->glyphset, + 0, 0, + cmd_len, + glyphcmds); + + free(glyphcmds); +} + +static struct aui_geometry +aui_dri_get_text_geometry(struct primitive *p) +{ + struct aui_dri_xcb *dri_xcb = (struct aui_dri_xcb *)driver; + struct aui_geometry geom = { 0 }; + struct text *t = (struct text *)p; + size_t len = (t->data == NULL) ? 0 : strlen(t->data); + + for (int i = 0; i < len; i++) { + uint32_t glyphid = (uint32_t)t->data[i]; + if (glyphid < 32 || glyphid > 126) { + /* fprintf(stderr, "libaui: unknown geometry for glyph id: %d\n", glyphid); */ + continue; + } + + struct glyph *g = &dri_xcb->glyphs[glyphid]; + geom.width += g->x_offset; + } + geom.height = dri_xcb->font_height; + + return geom; +} + +/* + * XCB section + * + * Function ends with xcb to prevent clashing with xcb + * defined functions. + */ +static int +config_xcb(struct aui_dri_xcb *axcb) +{ + xcb_intern_atom_cookie_t cookies[2]; + + axcb->conn = xcb_connect(NULL, NULL); + + if (xcb_connection_has_error(axcb->conn)) + return -1; + + axcb->screen = xcb_setup_roots_iterator(xcb_get_setup(axcb->conn)).data; + + if (axcb->screen == NULL) + return -1; + + axcb->pollfd.fd = xcb_get_file_descriptor(axcb->conn); + axcb->pollfd.events = POLLIN; + + /* + * Prevents auto delete + */ + cookies[0] = xcb_intern_atom(axcb->conn, 1, strlen("WM_PROTOCOLS"), "WM_PROTOCOLS"); + axcb->wm_protocols = xcb_intern_atom_reply(axcb->conn, cookies[0], NULL); + + cookies[1] = xcb_intern_atom(axcb->conn, 1, strlen("WM_DELETE_WINDOW"), "WM_DELETE_WINDOW"); + axcb->wm_delete_window = xcb_intern_atom_reply(axcb->conn, cookies[1], NULL); + + /* + * XCB Render + */ + xcb_render_query_pict_formats_cookie_t rcookie = xcb_render_query_pict_formats(axcb->conn); + xcb_render_query_pict_formats_reply_t *rreply; + xcb_render_pictscreen_iterator_t rpiter; + + rreply = xcb_render_query_pict_formats_reply(axcb->conn, rcookie, NULL); + rpiter = xcb_render_query_pict_formats_screens_iterator(rreply); + + /* + * This is `NORMAL` format + */ + while (rpiter.rem != 0) { + xcb_render_pictdepth_iterator_t rditer; + rditer = xcb_render_pictscreen_depths_iterator(rpiter.data); + + while (rditer.rem != 0) { + xcb_render_pictvisual_iterator_t rviter; + rviter = xcb_render_pictdepth_visuals_iterator(rditer.data); + + while (rviter.rem != 0) { + if (rviter.data->visual == axcb->screen->root_visual) + axcb->fmt_normal = rviter.data->format; + xcb_render_pictvisual_next(&rviter); + } + xcb_render_pictdepth_next(&rditer); + } + xcb_render_pictscreen_next(&rpiter); + } + + /* + * Formats for `ARGB32` and `ALPHA8` + */ + xcb_render_pictforminfo_iterator_t rfiter; + rfiter = xcb_render_query_pict_formats_formats_iterator(rreply); + + while (rfiter.rem != 0) { + xcb_render_pictforminfo_t *info = rfiter.data; + + if (info->type == XCB_RENDER_PICT_TYPE_DIRECT && + info->depth == 32 && + info->direct.red_shift == 16 && + info->direct.red_mask == 0xff && + info->direct.green_shift == 8 && + info->direct.green_mask == 0xff && + info->direct.blue_shift == 0 && + info->direct.blue_mask == 0xff && + info->direct.alpha_shift == 24 && + info->direct.alpha_mask == 0xff) + axcb->fmt_argb32 = rfiter.data->id; + + if (info->type == XCB_RENDER_PICT_TYPE_DIRECT && + info->depth == 8 && + info->direct.red_shift == 0 && + info->direct.red_mask == 0x00 && + info->direct.green_shift == 0 && + info->direct.green_mask == 0x00 && + info->direct.blue_shift == 0 && + info->direct.blue_mask == 0x00 && + info->direct.alpha_shift == 0 && + info->direct.alpha_mask == 0xff) + axcb->fmt_alpha8 = rfiter.data->id; + xcb_render_pictforminfo_next(&rfiter); + } + + free(rreply); + + return 0; +} + +/* + * Sets pixmap. This is used when the window is being resized to fit with + * the new dimensions + */ +static void +set_pixmap_xcb(struct aui_window_xcb *aw_xcb) +{ + struct aui_dri_xcb *dri_xcb = (struct aui_dri_xcb *)driver; + struct aui_window *aw = (struct aui_window *)aw_xcb; + struct aui_widget *ww = (struct aui_widget *)aw; + + xcb_render_free_picture(dri_xcb->conn, aw_xcb->dst_pict); + xcb_render_free_picture(dri_xcb->conn, aw_xcb->src_pict); + xcb_free_pixmap(dri_xcb->conn, aw_xcb->pixmap); + + aw_xcb->pixmap = xcb_generate_id(dri_xcb->conn); + xcb_create_pixmap(dri_xcb->conn, + 32, + aw_xcb->pixmap, + aw_xcb->xw, + ww->geom.width, + ww->geom.height); + + aw_xcb->src_pict = xcb_generate_id(dri_xcb->conn); + xcb_render_create_picture(dri_xcb->conn, + aw_xcb->src_pict, + aw_xcb->pixmap, + dri_xcb->fmt_argb32, + 0, + NULL); + + aw_xcb->dst_pict = xcb_generate_id(dri_xcb->conn); + xcb_render_create_picture(dri_xcb->conn, + aw_xcb->dst_pict, + aw_xcb->xw, + dri_xcb->fmt_normal, + 0, + NULL); +} + +static struct aui_window_xcb* +find_window_xcb(xcb_window_t xw) +{ + struct aui_window_xcb *aw_xcb; + struct aui_window *aw; + + LIST_FOREACH(aw, &wlist, entries) { + aw_xcb = (struct aui_window_xcb *)aw; + if (aw_xcb->xw == xw) + return aw_xcb; + } + + return NULL; +} + +static void +set_name_xcb(struct aui_window_xcb *aw_xcb, const char *title) +{ + if (title == NULL) return; + + struct aui_dri_xcb *dri_xcb = (struct aui_dri_xcb *)driver; + xcb_change_property(dri_xcb->conn, + XCB_PROP_MODE_REPLACE, + aw_xcb->xw, + XCB_ATOM_WM_NAME, + XCB_ATOM_STRING, + 8, + strlen(title), + title + ); +}