| git.druid.rocks | index | militantprogrammer | astral_canvas | src/ | api/ | canvas.h |
src/api/canvas.h
#ifndef CANVAS_H
#define CANVAS_H
/*
* Public Astral Canvas API (libcanvas).
*
* Main thread: A_Init, A_Create*, A_SetParent / A_SetChildren, A_RegisterInput,
* then while (A_Wait()) { branch on app statics }.
* A_Wait presents the previous frame, then runs input (callbacks on main).
* The lib thread applies the API→lib ring and owns Vulkan + the widget tree.
*
* Status: 0 success, -1 error. Any other code is lib-specific only if
* documented; undefined values are treated as -1.
*/
#include <stdbool.h>
#include <stdint.h>
#ifndef CANVAS_RING_SIZE
/* Max API commands queued in one frame (compile-time). Overflow is an error.
* Override with -DCANVAS_RING_SIZE=N or #define before this include. */
#define CANVAS_RING_SIZE 100
#endif
/*
* RGBA 0–255. Complex mode honors a; simple mode accepts a and draws opaque.
*/
typedef struct {
unsigned char r, g, b, a;
} Color;
/*
* Theme / per-widget look. font empty → library default TTF (when shipped).
* fontSize is in the same units as layout (see Pending: coordinate space).
*/
typedef struct {
Color backgroundColor, foregroundColor, textColor;
char font[256];
int fontSize;
} Style;
/*
* Top-left of a widget in its parent’s space (window page if unparented).
* Units not fully pinned (Pending).
*/
typedef struct {
int x, y;
} Position2D;
/* Width and height of a widget’s box. */
typedef struct {
int width, height;
} Size;
/*
* Discriminator for the widget struct. create_widget(void *args) casts args
* from this type (button fields, box fields, …).
*/
typedef enum {
A_WIDGET_PAGE = 0, /* Layer with its own box; window is page 0 */
A_WIDGET_BOX, /* Bounding box / clip / anchor for children */
A_WIDGET_BUTTON, /* Click runs create callback; optional key bind */
A_WIDGET_LABEL, /* Static text; wraps to the box */
A_WIDGET_TEXT, /* Text block (wrap to box) */
A_WIDGET_TEXTFIELD, /* Editable text; focus + typing */
A_WIDGET_IMAGE, /* Bitmap/texture in the box */
A_WIDGET_CHECKBOX, /* Boolean toggle */
A_WIDGET_SLIDER /* Numeric drag */
} WidgetType;
/*
* Public handle. id is stable (pid-like) for edit/delete. id 0 is the
* window page after a successful A_Init, or a failed create.
*/
typedef struct {
unsigned short id;
WidgetType widgetType;
} Widget;
/* Display server. AUTO picks Wayland if WAYLAND_DISPLAY is set, else X11. */
typedef enum {
A_BACKEND_AUTO = 0,
A_BACKEND_X11,
A_BACKEND_WAYLAND
} A_Backend;
/* Quality path; all three still use Vulkan. */
typedef enum {
A_MODE_COMPLEX = 0, /* Honor alpha; images (and later shaders) allowed */
A_MODE_SIMPLE, /* Alpha discarded (opaque); no images/shaders */
A_MODE_TEXT /* Labels/buttons only; still Vulkan */
} A_Mode;
/*
* Passed to A_Init. NULL opt → auto backend, complex mode, scale 0 (default),
* built-in dark theme.
*/
typedef struct {
A_Backend backend;
A_Mode mode;
float scale; /* 0 = default */
Style theme;
} CanvasOptions;
/*
* Signature of the lib’s internal close action. The function itself is
* static in the lib — this header must not call it. Bind with the
* CANVAS_CLOSE token on a button or A_RegisterInput.
*/
typedef void (*CANVAS_CLOSE_CALLBACK)(void);
/* Token: WM × and Quit should invoke the internal close (teardown, exit 0). */
#define CANVAS_CLOSE ((void *)(uintptr_t)1)
/*
* Create queues, widget tree, Vulkan, and the lib thread. Maps a window
* of width×height titled title. Returns the window page (id 0) or a
* failed widget; on failure A_LastError() explains (e.g. Vulkan instance).
*/
Widget A_Init(int width, int height, const char *title, const CanvasOptions *opt);
/* Last error string from a -1 path; empty if none. */
const char *A_LastError(void);
/*
* One frame: present the previous snapshot, then input (marshal callbacks
* onto this thread), drain both rings dry (blocking). Returns 0 if the
* window is still open. Duplicate call in the same frame is a soft crash
* (log, free all, exit -1). WM close is an input interrupt on stage 2
* and runs the internal close callback (exit 0).
*/
int A_Wait(void);
/* Non-zero if the lib thread is running and ready. */
int A_IsOpen(void);
/*
* Ask the lib thread to stop (legacy explicit teardown after the wait
* loop). Prefer binding CANVAS_CLOSE so close goes through the internal
* callback. Returns 0.
*/
int A_CloseWindow(void);
/*
* Queue a style onto the window page (id 0). Returns that page handle.
* Live theme change after Init.
*/
Widget A_SetStyle(Style theme);
/* Queue a shader path onto widget (post-1.0; stored on the node now). */
void A_ApplyShader(Widget *widget, char *shader);
/* Attach child under parent in the tree. Returns 0 or -1. */
int A_SetParent(Widget child, Widget parent);
/* Attach count children under parent, in order. Returns 0 or -1. */
int A_SetChildren(Widget parent, Widget *children, int count);
/*
* Create helpers: enqueue a widget of that type. Duplicate x,y,width,height
* vs any existing widget is a soft crash. Sibling overlap warns.
* Position is relative to the parent once attached (default: page 0).
*/
Widget A_CreatePage(Position2D position, Size size);
Widget A_CreateBox(Position2D position, Size size);
/* callback: app function or CANVAS_CLOSE. Click runs it on the main thread. */
Widget A_CreateButton(Position2D position, Size size, char *text, void *callback);
Widget A_CreateLabel(Position2D position, int fontSize, char *value);
Widget A_CreateText(Position2D position, int fontSize, char *value);
Widget A_CreateTextField(Position2D position, Size size, char *value);
Widget A_CreateImage(Position2D position, Size size, char *path);
Widget A_CreateCheckbox(Position2D position, Size size);
Widget A_CreateSlider(Position2D position, Size size);
/* Queue destruction of widget (not the window page). */
void A_DestroyWidget(Widget widget);
/* Modifier bits; OR with A_KEY(sym) or A_SCROLL. */
#define A_KEY_CTRL (1u << 16)
#define A_KEY_ALT (1u << 17)
#define A_KEY_SHIFT (1u << 18)
#define A_SCROLL (1u << 19)
#define A_KEY(sym) ((uint32_t)(sym) & 0xffffu)
/* Packed keymap for A_RegisterInput (modifiers | key or A_SCROLL). */
typedef uint32_t A_Keymap;
/*
* Bind input. callbackOrValue is a function, a write-target (e.g. int *
* for scroll), or CANVAS_CLOSE. argsOrFlag is extra args or a flag.
* Example: A_RegisterInput(A_KEY_CTRL | A_KEY('s'), openSettings, NULL);
*/
void A_RegisterInput(A_Keymap input, void *callbackOrValue, void *argsOrFlag);
#endif