Full Code of Cubified/tuibox for AI

main 28035a91f238 cached
6 files
22.2 KB
7.6k tokens
45 symbols
1 requests
Download .txt
Repository: Cubified/tuibox
Branch: main
Commit: 28035a91f238
Files: 6
Total size: 22.2 KB

Directory structure:
gitextract_aeej1ph8/

├── Makefile
├── README.md
├── demos/
│   ├── demo_basic.c
│   ├── demo_bounce.c
│   └── demo_drag.c
└── tuibox.h

================================================
FILE CONTENTS
================================================

================================================
FILE: Makefile
================================================
all: tuibox

CC=cc

LIBS=-lm
CFLAGS=-O3 -pipe
DEBUGCFLAGS=-Og -pipe -g

.PHONY: tuibox
tuibox:
	$(CC) demos/demo_basic.c -o demos/demo_basic $(LIBS) $(CFLAGS)
	$(CC) demos/demo_bounce.c -o demos/demo_bounce $(LIBS) $(CFLAGS)
	$(CC) demos/demo_drag.c -o demos/demo_drag $(LIBS) $(CFLAGS)


================================================
FILE: README.md
================================================
# tuibox

tuibox ("toybox") is a single-header terminal UI library, capable of creating mouse-driven, interactive applications on the command line.

It is completely dependency-free, using nothing other than ANSI escape sequences to handle parsing input and rendering the screen.  Additionally, its individual parts work largely independently, meaning as much (or as little) of the library as is necessary can be used.

## Demos

[demo_basic.c](https://github.com/Cubified/tuibox/blob/main/demos/demo_basic.c):  Basic button demo w/ truecolor background
- (Note:  Ugly color banding is from GIF compression)

![demo_basic.gif](https://github.com/Cubified/tuibox/blob/main/demos/demo_basic.gif)

[demo_bounce.c](https://github.com/Cubified/tuibox/blob/main/demos/demo_bounce.c):  Bouncing box using custom render loop

![demo_bounce.gif](https://github.com/Cubified/tuibox/blob/main/demos/demo_bounce.gif)

[demo_drag.c](https://github.com/Cubified/tuibox/blob/main/demos/demo_drag.c):  Click and drag

![demo_drag.gif](https://github.com/Cubified/tuibox/blob/main/demos/demo_drag.gif)

[bdfedit](https://github.com/Cubified/bdfedit), a bitmap font editor built using `tuibox`:

![demo_bdfedit.gif](https://github.com/Cubified/tuibox/blob/main/demos/demo_bdfedit.gif)

[colorslide](https://github.com/Cubified/colorslide), an RGBA/HSL/CMYK color picker built using `tuibox`:

![demo_colorslide.gif](https://github.com/Cubified/tuibox/blob/main/demos/demo_colorslide.gif)

[vt100utils](https://github.com/Cubified/vt100utils), an ANSI graphics escape sequence encoder/decoder that can couple with `tuibox` for content-aware text processing:

![demo_vt100utils.gif](https://github.com/Cubified/tuibox/blob/main/demos/demo_vt100utils.gif)

## Features

tuibox currently contains the following:

- Event-driven terminal render loop
- Mouse click and hover events on individual UI elements
- Keyboard events (including escape sequence events, such as arrow keys)
- Render caching (controlled by a user-defined dirty bit)
- Completely dependency-free, using pure ANSI escape sequences (no ncurses)
- Incrementally-adoptable -- rendering, events, and loops can all be used independently

## Design Overview

The basic hierarchy of a UI looks like this:

- UI:  A collection of screens
    - Screen:  A collection of boxes
        - Box:  Any UI element
            - X/Y, width/height
            - Render function
            - Click event function (optional)
            - Hover event function (optional)
            - Arbitrary user data (see below example)
- Main loop:  Infinite `read()` loop gathering user input
    - Update:  Parses user input and fires events (if applicable)

## Example Code

A complete example is as follows:

```c
/* Global UI struct */
ui_t u;

/* Function that runs on box click */
void click(ui_box_t *b, int x, int y){
  b->data1 = "you clicked me!";
  ui_draw(&u);
}

/* Function that runs on box hover */
void hover(ui_box_t *b, int x, int y, int down){
  b->data1 = "you hovered me!";
  ui_draw(&u);
}

void stop(){
  ui_free(&u);
  exit(0);
}

int main(){
  /* Initialize UI data structures */
  ui_new(0, &u);

  /* Add a new text box to screen 0 */
  ui_text(
    UI_CENTER_X, UI_CENTER_Y,
    "hello world!",
    0,
    click, hover,
    &u
  );

  /* Register an event on the q key */
  ui_key("q", stop, &u);

  /* Render the screen */
  ui_draw(&u);

  ui_loop(&u){
    /* Check for mouse/keyboard events */
    ui_update(&u);
  }

  return 0;
}
```

## Compiling and Running Demos

To compile all demos in one go:

     $ make

     $ ./demo_basic
     $ ./demo_bounce
     $ ./demo_drag

## Libraries/See Also

- [vec](https://github.com/rxi/vec):  Dynamic arrays used to store events and boxes
- [bdfedit](https://github.com/Cubified/bdfedit):  Bitmap font editor written alongside `tuibox`


================================================
FILE: demos/demo_basic.c
================================================
/*
 * demo_basic.c: a simple tuibox.h demo
 */

#include <math.h>

#include "../tuibox.h"

/* Global UI struct */
ui_t u;

/* Functions that generate box contents */
void text(ui_box_t *b, char *out){
  sprintf(out, "%s", (char*)b->data1);
}

void draw(ui_box_t *b, char *out){
  int x, y, len = 0, max = MAXCACHESIZE;

  sprintf(out, "");
  for(y=0;y<b->h;y++){
    for(x=0;x<b->w;x++){
      /* Truecolor string to generate gradient */
      len += sprintf(out + len, "\x1b[48;2;%i;%i;%im ", (int)round(255 * ((double)x / (double)b->w)), (int)round(255 * ((double)y / (double)b->h)), (int)round(255 * ((double)x * (double)y / ((double)b->w * (double)b->h))));

      if(len + 1024 > max){
        out = realloc(out, (max *= 2));
      }
    }
    strcat(out + len, "\x1b[0m\n");
  }
}

/* Function that runs on box click */
void click(ui_box_t *b, int x, int y){
  b->data1 = "\x1b[0m                \n  you clicked me!  \n                ",
  ui_draw_one(b, 1, &u);
}

/* Function that runs on box hover */
void hover(ui_box_t *b, int x, int y, int down){
  b->data1 = "\x1b[0m                \n  you hovered me!  \n                ",
  ui_draw_one(b, 1, &u);
}

void stop(){
  ui_free(&u);
  exit(0);
}

int main(){
  /* Initialize UI data structures */
  ui_new(0, &u);

  /* Add new UI elements to screen 0 */
  ui_add(
    1, 1,
    u.ws.ws_col, u.ws.ws_row,
    0,
    NULL, 0,
    draw,
    NULL,
    NULL,
    NULL,
    NULL,
    &u
  );

  ui_text(
    ui_center_x(19, &u), UI_CENTER_Y,
    "\x1b[0m                   \n    click on me!   \n                   ",
    0,
    click, hover,
    &u
  );

  /* Register an event on the q key */
  ui_key("q", stop, &u);

  /* Render the screen */
  ui_draw(&u);

  ui_loop(&u){
    /* Check for mouse/keyboard events */
    ui_update(&u);
  }

  return 0;
}


================================================
FILE: demos/demo_bounce.c
================================================
/*
 * demo_bounce.c: bouncing physics box
 */

#include <math.h>
#include <signal.h>

#include "../tuibox.h"

ui_t u;

void draw(ui_box_t *b, char *out){
  int x, y;
  char tmp[256];

  sprintf(out, "\x1b[48;2;255;255;255m");
  for(y=0;y<b->h;y++){
    for(x=0;x<b->w;x++){
      strcat(out, " ");
    }
    strcat(out, "\n");
  }
}

void stop(){
  ui_free(&u);
  exit(0);
}

int main(){
  char watch = 0;
  int id;
  ui_box_t *b;

  double vx = 2.0, vy = 0.0,
         ax = 0.0, ay = 0.1;

  ui_new(0, &u);

  /* Add new UI elements to screen 0 */
  id = ui_add(
    10, 10,
    20, 10,
    0,
    &watch, 1, /* Dirty bit: Run draw() once, and never again */
    draw,
    NULL, NULL,
    NULL, NULL,
    &u
  );
  b = ui_get(id, &u);

  signal(SIGTERM, stop);
  signal(SIGQUIT, stop);
  signal(SIGINT,  stop);

  for(;;){
    b->x = round((double)b->x + vx);
    b->y = round((double)b->y + vy);

    vx += ax;
    vy += ay;

    if(b->x < 1){
      b->x = 1;
      vx *= -1.0;
    } else if(b->x+b->w > u.ws.ws_col){
      b->x = u.ws.ws_col - b->w;
      vx *= -1.0;
    }

    if(b->y < 1){
      b->y = 1;
      vy *= -1.0;
    } else if(b->y+b->h > u.ws.ws_row){
      b->y = u.ws.ws_row - b->h;
      vy *= -1.0;
    }

    ui_draw(&u);
    usleep(10000);
  }

  return 0;
}


================================================
FILE: demos/demo_drag.c
================================================
/*
 * demo_drag.c: click and drag
 */

#include <math.h>

#include "../tuibox.h"

/* Global UI struct */
ui_t u;

int sx = 15, sy = 7;

/* Functions that generate box contents */
void text(ui_box_t *b, char *out){
  sprintf(out, "%s", (char*)b->data1);
}

void draw(ui_box_t *b, char *out){
  int x, y;
  char tmp[256];

  sprintf(out, "");
  for(y=0;y<b->h;y++){
    for(x=0;x<b->w;x++){
      /* Truecolor string to generate gradient */
      sprintf(tmp, "\x1b[48;2;%i;%i;%im ", (int)round(255 * ((double)x * (double)y / ((double)b->w * (double)b->h))), (int)round(255 * ((double)y / (double)b->h)), (int)round(255 * ((double)x / (double)b->w)));
      strcat(out, tmp);
    }
    strcat(out, "\x1b[0m\n");
  }
}

/* Function that runs on box click */
void click(ui_box_t *b, int x, int y){
  sx = x-b->x;
  sy = y-b->y;
}

/* Function that runs on box hover */
void hover(ui_box_t *b, int x, int y, int down){
  if(down){
    b->x = x-sx;
    b->y = y-sy;
    ui_draw(&u);
  }
}

void stop(){
  ui_free(&u);
  exit(0);
}

int main(){
  char watch = 0;

  /* Initialize UI data structures */
  ui_new(0, &u);

  /* Add new UI elements to screen 0 */
  ui_add(
    10, 10,
    30, 15,
    0,
    &watch, 1,
    draw,
    click,
    hover,
    NULL,
    NULL,
    &u
  );

  /* Register an event on the q key */
  ui_key("q", stop, &u);

  /* Render the screen */
  ui_draw(&u);

  ui_loop(&u){
    /* Check for mouse/keyboard events */
    ui_update(&u);
  }

  return 0;
}


================================================
FILE: tuibox.h
================================================
/*
 * tuibox.h: simple tui library
 */

#ifndef TUIBOX_H
#define TUIBOX_H

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <sys/ioctl.h>

/** BEGIN vec.h **/

/** 
 * Copyright (c) 2014 rxi
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the MIT license. See LICENSE for details.
 */

#ifndef VEC_H
#define VEC_H

#include <stdlib.h>
#include <string.h>

#define VEC_VERSION "0.2.1"


#define vec_unpack_(v)\
  (char**)&(v)->data, &(v)->length, &(v)->capacity, sizeof(*(v)->data)


#define vec_t(T)\
  struct { T *data; int length, capacity; }


#define vec_init(v)\
  memset((v), 0, sizeof(*(v)))


#define vec_deinit(v)\
  ( free((v)->data),\
    vec_init(v) ) 


#define vec_push(v, val)\
  ( vec_expand_(vec_unpack_(v)) ? -1 :\
    ((v)->data[(v)->length++] = (val), 0), 0 )


#define vec_pop(v)\
  (v)->data[--(v)->length]


#define vec_splice(v, start, count)\
  ( vec_splice_(vec_unpack_(v), start, count),\
    (v)->length -= (count) )


#define vec_swapsplice(v, start, count)\
  ( vec_swapsplice_(vec_unpack_(v), start, count),\
    (v)->length -= (count) )


#define vec_insert(v, idx, val)\
  ( vec_insert_(vec_unpack_(v), idx) ? -1 :\
    ((v)->data[idx] = (val), 0), (v)->length++, 0 )
    

#define vec_sort(v, fn)\
  qsort((v)->data, (v)->length, sizeof(*(v)->data), fn)


#define vec_swap(v, idx1, idx2)\
  vec_swap_(vec_unpack_(v), idx1, idx2)


#define vec_truncate(v, len)\
  ((v)->length = (len) < (v)->length ? (len) : (v)->length)


#define vec_clear(v)\
  ((v)->length = 0)


#define vec_first(v)\
  (v)->data[0]


#define vec_last(v)\
  (v)->data[(v)->length - 1]


#define vec_reserve(v, n)\
  vec_reserve_(vec_unpack_(v), n)

 
#define vec_compact(v)\
  vec_compact_(vec_unpack_(v))


#define vec_pusharr(v, arr, count)\
  do {\
    int i__, n__ = (count);\
    if (vec_reserve_po2_(vec_unpack_(v), (v)->length + n__) != 0) break;\
    for (i__ = 0; i__ < n__; i__++) {\
      (v)->data[(v)->length++] = (arr)[i__];\
    }\
  } while (0)


#define vec_extend(v, v2)\
  vec_pusharr((v), (v2)->data, (v2)->length)


#define vec_find(v, val, idx)\
  do {\
    for ((idx) = 0; (idx) < (v)->length; (idx)++) {\
      if ((v)->data[(idx)] == (val)) break;\
    }\
    if ((idx) == (v)->length) (idx) = -1;\
  } while (0)


#define vec_remove(v, val)\
  do {\
    int idx__;\
    vec_find(v, val, idx__);\
    if (idx__ != -1) vec_splice(v, idx__, 1);\
  } while (0)


#define vec_reverse(v)\
  do {\
    int i__ = (v)->length / 2;\
    while (i__--) {\
      vec_swap((v), i__, (v)->length - (i__ + 1));\
    }\
  } while (0)


#define vec_foreach(v, var, iter)\
  if  ( (v)->length > 0 )\
  for ( (iter) = 0;\
        (iter) < (v)->length && (((var) = (v)->data[(iter)]), 1);\
        ++(iter))


#define vec_foreach_rev(v, var, iter)\
  if  ( (v)->length > 0 )\
  for ( (iter) = (v)->length - 1;\
        (iter) >= 0 && (((var) = (v)->data[(iter)]), 1);\
        --(iter))


#define vec_foreach_ptr(v, var, iter)\
  if  ( (v)->length > 0 )\
  for ( (iter) = 0;\
        (iter) < (v)->length && (((var) = &(v)->data[(iter)]), 1);\
        ++(iter))


#define vec_foreach_ptr_rev(v, var, iter)\
  if  ( (v)->length > 0 )\
  for ( (iter) = (v)->length - 1;\
        (iter) >= 0 && (((var) = &(v)->data[(iter)]), 1);\
        --(iter))



int vec_expand_(char **data, int *length, int *capacity, int memsz);
int vec_reserve_(char **data, int *length, int *capacity, int memsz, int n);
int vec_reserve_po2_(char **data, int *length, int *capacity, int memsz,
                     int n);
int vec_compact_(char **data, int *length, int *capacity, int memsz);
int vec_insert_(char **data, int *length, int *capacity, int memsz,
                int idx);
void vec_splice_(char **data, int *length, int *capacity, int memsz,
                 int start, int count);
void vec_swapsplice_(char **data, int *length, int *capacity, int memsz,
                     int start, int count);
void vec_swap_(char **data, int *length, int *capacity, int memsz,
               int idx1, int idx2);


typedef vec_t(void*) vec_void_t;
typedef vec_t(char*) vec_str_t;
typedef vec_t(int) vec_int_t;
typedef vec_t(char) vec_char_t;
typedef vec_t(float) vec_float_t;
typedef vec_t(double) vec_double_t;

int vec_expand_(char **data, int *length, int *capacity, int memsz) {
  if (*length + 1 > *capacity) {
    void *ptr;
    int n = (*capacity == 0) ? 1 : *capacity << 1;
    ptr = realloc(*data, n * memsz);
    if (ptr == NULL) return -1;
    *data = ptr;
    *capacity = n;
  }
  return 0;
}


int vec_reserve_(char **data, int *length, int *capacity, int memsz, int n) {
  (void) length;
  if (n > *capacity) {
    void *ptr = realloc(*data, n * memsz);
    if (ptr == NULL) return -1;
    *data = ptr;
    *capacity = n;
  }
  return 0;
}


int vec_reserve_po2_(
  char **data, int *length, int *capacity, int memsz, int n
) {
  int n2 = 1;
  if (n == 0) return 0;
  while (n2 < n) n2 <<= 1;
  return vec_reserve_(data, length, capacity, memsz, n2);
}


int vec_compact_(char **data, int *length, int *capacity, int memsz) {
  if (*length == 0) {
    free(*data);
    *data = NULL;
    *capacity = 0;
    return 0;
  } else {
    void *ptr;
    int n = *length;
    ptr = realloc(*data, n * memsz);
    if (ptr == NULL) return -1;
    *capacity = n;
    *data = ptr;
  }
  return 0;
}


int vec_insert_(char **data, int *length, int *capacity, int memsz,
                 int idx
) {
  int err = vec_expand_(data, length, capacity, memsz);
  if (err) return err;
  memmove(*data + (idx + 1) * memsz,
          *data + idx * memsz,
          (*length - idx) * memsz);
  return 0;
}


void vec_splice_(char **data, int *length, int *capacity, int memsz,
                 int start, int count
) {
  (void) capacity;
  memmove(*data + start * memsz,
          *data + (start + count) * memsz,
          (*length - start - count) * memsz);
}


void vec_swapsplice_(char **data, int *length, int *capacity, int memsz,
                     int start, int count
) {
  (void) capacity;
  memmove(*data + start * memsz,
          *data + (*length - count) * memsz,
          count * memsz);
}


void vec_swap_(char **data, int *length, int *capacity, int memsz,
               int idx1, int idx2 
) {
  unsigned char *a, *b, tmp;
  int count;
  (void) length;
  (void) capacity;
  if (idx1 == idx2) return;
  a = (unsigned char*) *data + idx1 * memsz;
  b = (unsigned char*) *data + idx2 * memsz;
  count = memsz;
  while (count--) {
    tmp = *a;
    *a = *b;
    *b = tmp;
    a++, b++;
  }
}

#endif

/** END vec.h **/

/*
 * PREPROCESSOR
 */
#define MAXCACHESIZE 65535

#define CURSOR_Y(b) (b->y+(n+1)+(u->canscroll ? u->scroll : 0))

#define box_contains(x, y, b) (x >= b->x && x <= b->x + b->w && y >= b->y && y <= b->y + b->h)

#define ui_screen(s, u) u->screen = s;u->force = 1

#define ui_center_x(w, u) (((u)->ws.ws_col - w) / 2)
#define ui_center_y(h, u) (((u)->ws.ws_row - h) / 2)

#define UI_CENTER_X -1
#define UI_CENTER_Y -1

/* The argument isn't actually necessary here, but it helps with design consistency */
#define ui_loop(u) char buf[64];int n;while((n=read(STDIN_FILENO, buf, sizeof(buf))) > 0)

#define ui_update(u) _ui_update(buf, n, u)

#define ui_get(id, u) ((u)->b.data[id])

#define COORDINATE_DECODE() \
  tok = strtok(NULL, ";"); \
  x = atoi(tok); \
  tok = strtok(NULL, ";"); \
  y = strtol(tok, NULL, 10) - (u->canscroll ? u->scroll : 0)

#define CLICK_COMPARATOR(x, y, tmp) \
  (u->click == tmp || \
   (box_contains(x, y, tmp) && u->click == NULL))

#define HOVER_COMPARATOR(x, y, tmp) \
  (box_contains(x, y, tmp))

#define LOOP_AND_EXECUTE(f, c) \
  do { \
    vec_foreach(&(u->b), tmp, ind){ \
      if(tmp->screen == u->screen && \
         f != NULL && \
         (c ? CLICK_COMPARATOR(x, y, tmp) : HOVER_COMPARATOR(x, y, tmp)) \
      ){ \
        f(tmp, x, y, u->mouse); \
        if(c){ \
          u->click = tmp; \
        } \
      } \
    } \
  } while(0)

/*
 * TYPES
 */
typedef void (*func)();

typedef struct ui_box_t {
  int id;
  int x, y;
  int w, h;
  int screen;
  char *cache;
  char *watch;
  char last;
  func draw;
  func onclick;
  func onhover;
  void *data1;
  void *data2;
} ui_box_t;

typedef struct ui_evt_t {
  char *c;
  func f;
} ui_evt_t;

typedef vec_t(ui_box_t*) vec_box_t;
typedef vec_t(ui_evt_t*) vec_evt_t;

typedef struct ui_t {
  struct termios tio;
  struct winsize ws;
  vec_box_t b;
  vec_evt_t e;
  ui_box_t *click;
  int mouse, screen,
      scroll, canscroll,
      id, force;
} ui_t;

/* =========================== */

/*
 * Initializes a new UI struct,
 *   puts the terminal into raw
 *   mode, and prints out the
 *   necessary escape codes
 *   for mouse support.
 */
void ui_new(int s, ui_t *u){
  struct termios raw;

  ioctl(STDOUT_FILENO, TIOCGWINSZ, &(u->ws));

  tcgetattr(STDIN_FILENO, &(u->tio));
  raw = u->tio;
  raw.c_lflag &= ~(ECHO | ICANON);
  tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);

  vec_init(&(u->b));
  vec_init(&(u->e));

  u->click = NULL;

  printf("\x1b[?1049h\x1b[0m\x1b[2J\x1b[?1003h\x1b[?1015h\x1b[?1006h\x1b[?25l");

  u->mouse = 0;

  u->screen = s;
  u->scroll = 0;
  u->canscroll = 1;
  
  u->id = 0;

  u->force = 0;
}

/*
 * Frees the given UI struct,
 *   and takes the terminal
 *   out of raw mode.
 */
void ui_free(ui_t *u){
  ui_box_t *val;
  ui_evt_t *evt;
  int i;
  char *term;

  printf("\x1b[0m\x1b[2J\x1b[?1049l\x1b[?1003l\x1b[?1015l\x1b[?1006l\x1b[?25h");
  tcsetattr(STDIN_FILENO, TCSAFLUSH, &(u->tio));

  vec_foreach(&(u->b), val, i){
    free(val->cache);
    free(val);
  }
  vec_deinit(&(u->b));

  vec_foreach(&(u->e), evt, i){
    free(evt);
  }
  vec_deinit(&(u->e));

  term = getenv("TERM");
  if(strncmp(term, "screen", 6) == 0 ||
     strncmp(term, "tmux", 4) == 0){
    printf("Note: Terminal multiplexer detected.\n  For best performance (i.e. reduced flickering), running natively inside\n  a GPU-accelerated terminal such as alacritty or kitty is recommended.\n");
  }
}

/*
 * Adds a new box to the UI.
 *
 * This function is very simple in
 *   nature, but the variety of
 *   properties associated with
 *   an individual box makes it
 *   intimidating to look at.
 * TODO: Find some way to
 *   strip this down.
 */
int ui_add(
  int x, int y, int w, int h, int screen,
  char *watch, char initial,
  func draw, func onclick, func onhover,
  void *data1, void *data2,
  ui_t *u
){
  ui_box_t *b = malloc(sizeof(ui_box_t));

  b->id = u->id++;

  b->x = (x == UI_CENTER_X ? ui_center_x(w, u) : x);
  b->y = (y == UI_CENTER_Y ? ui_center_y(h, u) : y);
  b->w = w;
  b->h = h;

  b->screen = u->screen;

  b->watch = watch;
  b->last = initial;

  b->draw = draw;
  b->onclick = onclick;
  b->onhover = onhover;

  b->data1 = data1;
  b->data2 = data2;

  b->cache = malloc(MAXCACHESIZE);
  draw(b, b->cache);
  b->cache = realloc(b->cache, strlen(b->cache) * 2);

  vec_push(&(u->b), b);

  return b->id;
}

/*
 * Adds a new key event listener
 *   to the UI.
 */
void ui_key(char *c, func f, ui_t *u){
  ui_evt_t *e = malloc(sizeof(ui_evt_t));
  e->c = c;
  e->f = f;

  vec_push(&(u->e), e);
}

/*
 * Clears all elements from
 *   the UI.
 */
void ui_clear(ui_t *u){
  int tmp = u->screen;

  ui_free(u);
  ui_new(tmp, u);
}

/*
 * Draws a single box to the
 *   screen.
 */
void ui_draw_one(ui_box_t *tmp, int flush, ui_t *u){
  char *buf, *tok;
  int n = -1;

  if(tmp->screen != u->screen) return;
  
  buf = calloc(1, strlen(tmp->cache) * 2);
  if(u->force ||
     tmp->watch == NULL ||
     *(tmp->watch) != tmp->last
  ){
    tmp->draw(tmp, buf);
    if(tmp->watch != NULL) tmp->last = *(tmp->watch);
    strcpy(tmp->cache, buf);
  } else {
    /* buf is allocated proportionally to tmp->cache, so strcpy is safe */
    strcpy(buf, tmp->cache);
  }
  tok = strtok(buf, "\n");
  while(tok != NULL){
    if(tmp->x > 0 &&
       tmp->x < u->ws.ws_col &&
       CURSOR_Y(tmp) > 0 &&
       CURSOR_Y(tmp) < u->ws.ws_row){
      printf("\x1b[%i;%iH%s", CURSOR_Y(tmp), tmp->x, tok);
      n++;
    }
    tok = strtok(NULL, "\n");
  }
  free(buf);

  if(flush) fflush(stdout);
}

/*
 * Draws all boxes to the screen.
 */
void ui_draw(ui_t *u){
  ui_box_t *tmp;
  int i;

  printf("\x1b[0m\x1b[2J");

  vec_foreach(&(u->b), tmp, i){
    ui_draw_one(tmp, 0, u);
  }
  fflush(stdout);
  u->force = 0;
}

/*
 * Forces a redraw of the screen,
 *   updating all boxes' caches.
 */
void ui_redraw(ui_t *u){
  u->force = 1;
  ui_draw(u);
}

/*
 * Handles mouse and keyboard
 *   events, given a read()
 *   buffer.
 *
 * This is prefixed with an underscore
 *   to ensure consistency with the
 *   ui_loop macro, ensuring that the
 *   variables buf and n remain
 *   opaque to the user.
 */
void _ui_update(char *c, int n, ui_t *u){
  ui_box_t *tmp;
  ui_evt_t *evt;
  int ind, x, y;
  char cpy[n], *tok;

  if(n >= 4 &&
     c[0] == '\x1b' &&
     c[1] == '[' &&
     c[2] == '<'){
    strncpy(cpy, c, n);
    tok = strtok(cpy+3, ";");
    
    switch(tok[0]){
      case '0':
        u->mouse = (strchr(c, 'm') == NULL);
        COORDINATE_DECODE();
        LOOP_AND_EXECUTE(tmp->onclick, 1);
        if(!u->mouse){
          u->click = NULL;
        }
        break;
      case '3':
        u->mouse = (strcmp(tok, "32") == 0);
        COORDINATE_DECODE();
        LOOP_AND_EXECUTE(tmp->onhover, u->mouse);
        break;
      case '6':
        if(u->canscroll){
          u->scroll += (4 * (tok[1] == '4')) - 2;
          printf("\x1b[0m\x1b[2J");
          ui_draw(u);
        }
        break;
    }
  }

  vec_foreach(&(u->e), evt, ind){
    if(strncmp(c, evt->c, strlen(evt->c)) == 0) evt->f();
  }
}

/*
 * HELPERS
 */
void _ui_text(ui_box_t *b, char *out){
  sprintf(out, "%s", (char*)b->data1);
}

int ui_text(
  int x, int y, char *str,
  int screen,
  func click, func hover,
  ui_t *u
){
  return ui_add(
    x, y,
    strlen(str), 1,
    screen,
    NULL, 0,
    _ui_text,
    click,
    hover,
    str,
    NULL,
    u
  );
}

#endif
Download .txt
gitextract_aeej1ph8/

├── Makefile
├── README.md
├── demos/
│   ├── demo_basic.c
│   ├── demo_bounce.c
│   └── demo_drag.c
└── tuibox.h
Download .txt
SYMBOL INDEX (45 symbols across 4 files)

FILE: demos/demo_basic.c
  function text (line 13) | void text(ui_box_t *b, char *out){
  function draw (line 17) | void draw(ui_box_t *b, char *out){
  function click (line 35) | void click(ui_box_t *b, int x, int y){
  function hover (line 41) | void hover(ui_box_t *b, int x, int y, int down){
  function stop (line 46) | void stop(){
  function main (line 51) | int main(){

FILE: demos/demo_bounce.c
  function draw (line 12) | void draw(ui_box_t *b, char *out){
  function stop (line 25) | void stop(){
  function main (line 30) | int main(){

FILE: demos/demo_drag.c
  function text (line 15) | void text(ui_box_t *b, char *out){
  function draw (line 19) | void draw(ui_box_t *b, char *out){
  function click (line 35) | void click(ui_box_t *b, int x, int y){
  function hover (line 41) | void hover(ui_box_t *b, int x, int y, int down){
  function stop (line 49) | void stop(){
  function main (line 54) | int main(){

FILE: tuibox.h
  type vec_void_t (line 190) | typedef vec_t(void*) vec_void_t;
  type vec_str_t (line 191) | typedef vec_t(char*) vec_str_t;
  type vec_int_t (line 192) | typedef vec_t(int) vec_int_t;
  type vec_char_t (line 193) | typedef vec_t(char) vec_char_t;
  type vec_float_t (line 194) | typedef vec_t(float) vec_float_t;
  type vec_double_t (line 195) | typedef vec_t(double) vec_double_t;
  function vec_expand_ (line 197) | int vec_expand_(char **data, int *length, int *capacity, int memsz) {
  function vec_reserve_ (line 210) | int vec_reserve_(char **data, int *length, int *capacity, int memsz, int...
  function vec_reserve_po2_ (line 222) | int vec_reserve_po2_(
  function vec_compact_ (line 232) | int vec_compact_(char **data, int *length, int *capacity, int memsz) {
  function vec_insert_ (line 250) | int vec_insert_(char **data, int *length, int *capacity, int memsz,
  function vec_splice_ (line 262) | void vec_splice_(char **data, int *length, int *capacity, int memsz,
  function vec_swapsplice_ (line 272) | void vec_swapsplice_(char **data, int *length, int *capacity, int memsz,
  function vec_swap_ (line 282) | void vec_swap_(char **data, int *length, int *capacity, int memsz,
  type ui_box_t (line 362) | typedef struct ui_box_t {
  type ui_evt_t (line 377) | typedef struct ui_evt_t {
  type vec_box_t (line 382) | typedef vec_t(ui_box_t*) vec_box_t;
  type vec_evt_t (line 383) | typedef vec_t(ui_evt_t*) vec_evt_t;
  type ui_t (line 385) | typedef struct ui_t {
  function ui_new (line 405) | void ui_new(int s, ui_t *u){
  function ui_free (line 438) | void ui_free(ui_t *u){
  function ui_add (line 476) | int ui_add(
  function ui_key (line 517) | void ui_key(char *c, func f, ui_t *u){
  function ui_clear (line 529) | void ui_clear(ui_t *u){
  function ui_draw_one (line 540) | void ui_draw_one(ui_box_t *tmp, int flush, ui_t *u){
  function ui_draw (line 577) | void ui_draw(ui_t *u){
  function ui_redraw (line 594) | void ui_redraw(ui_t *u){
  function _ui_update (line 610) | void _ui_update(char *c, int n, ui_t *u){
  function _ui_text (line 655) | void _ui_text(ui_box_t *b, char *out){
  function ui_text (line 659) | int ui_text(
Condensed preview — 6 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (24K chars).
[
  {
    "path": "Makefile",
    "chars": 287,
    "preview": "all: tuibox\n\nCC=cc\n\nLIBS=-lm\nCFLAGS=-O3 -pipe\nDEBUGCFLAGS=-Og -pipe -g\n\n.PHONY: tuibox\ntuibox:\n\t$(CC) demos/demo_basic.c"
  },
  {
    "path": "README.md",
    "chars": 3829,
    "preview": "# tuibox\n\ntuibox (\"toybox\") is a single-header terminal UI library, capable of creating mouse-driven, interactive applic"
  },
  {
    "path": "demos/demo_basic.c",
    "chars": 1814,
    "preview": "/*\n * demo_basic.c: a simple tuibox.h demo\n */\n\n#include <math.h>\n\n#include \"../tuibox.h\"\n\n/* Global UI struct */\nui_t u"
  },
  {
    "path": "demos/demo_bounce.c",
    "chars": 1283,
    "preview": "/*\n * demo_bounce.c: bouncing physics box\n */\n\n#include <math.h>\n#include <signal.h>\n\n#include \"../tuibox.h\"\n\nui_t u;\n\nv"
  },
  {
    "path": "demos/demo_drag.c",
    "chars": 1476,
    "preview": "/*\n * demo_drag.c: click and drag\n */\n\n#include <math.h>\n\n#include \"../tuibox.h\"\n\n/* Global UI struct */\nui_t u;\n\nint sx"
  },
  {
    "path": "tuibox.h",
    "chars": 14002,
    "preview": "/*\n * tuibox.h: simple tui library\n */\n\n#ifndef TUIBOX_H\n#define TUIBOX_H\n\n#include <stdio.h>\n#include <stdlib.h>\n#inclu"
  }
]

About this extraction

This page contains the full source code of the Cubified/tuibox GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 6 files (22.2 KB), approximately 7.6k tokens, and a symbol index with 45 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!