commit - /dev/null
commit + ad1030b41065a5fd42324100b9689f43a9a84b15
blob - /dev/null
blob + 20d4bcef726a4cbf96ffbb605738e9ba4a899377 (mode 644)
--- /dev/null
+++ LICENSE
+Copyright (c) 2026 Onana Onana Xavier Manuel <xavier@onana.net>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED AS IS AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
blob - /dev/null
blob + 526401143b87409307e1d991f80cabac3e6b3de6 (mode 644)
--- /dev/null
+++ Makefile
+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
+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
+#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
+.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
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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
+/*
+ * Canvases do not store pixels but rather the operations taking
+ * place within them. This makes resizing easier.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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
+#include <stdio.h>
+#include <stdlib.h>
+
+#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
+#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
+#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
+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
+/*
+ * Copyright (c) 2026 Onana Onana Xavier Manuel <xavier@onana.net>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED AS IS AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <aui.h>
+
+#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
+#include <fontconfig/fontconfig.h>
+#include <ft2build.h>
+#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
+#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
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#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
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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
+#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
+/* $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 <sys/_null.h>
+#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
+#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
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#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
+#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
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <xcb/xcb.h>
+#include <xcb/render.h>
+
+#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
+ );
+}