summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile30
-rw-r--r--arena.h149
-rw-r--r--dynarr.h59
-rw-r--r--main.c154
-rw-r--r--str.h152
-rw-r--r--ui.c149
-rw-r--r--ui.h41
7 files changed, 734 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6fbfe1f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,30 @@
+EXE = xmenu
+OBJ != find . -name '*.c' | sed -e 's/\.c$$/.o/' -e 's|^\./||'
+
+CFLAGS += -Wall -Wpedantic -I/usr/X11R7/include
+LDFLAGS += -s -lX11 -lxcb -lXt -lXau -lXdmcp -L/usr/X11R7/lib -static
+
+PREFIX ?= ${HOME}
+BINDIR ?= ${PREFIX}/bin
+
+.PHONY: all install clean run
+
+all: ${EXE}
+
+install: ${EXE}
+ install -d ${BINDIR}
+ @for e in ${EXE}; do echo install $$e ${BINDIR};\
+ install $$e ${BINDIR}; done
+
+uninstall:
+ @for e in ${EXE}; do echo rm -f ${BINDIR}/$$e;\
+ rm -f ${BINDIR}/$$e; done
+
+clean:
+ rm -fv ${EXE} ${OBJ}
+
+run: ${EXE}
+ ls | ./${EXE}
+
+${EXE}: ${OBJ}
+ ${CC} ${OBJ} -o ${EXE} ${LDFLAGS}
diff --git a/arena.h b/arena.h
new file mode 100644
index 0000000..7013eb7
--- /dev/null
+++ b/arena.h
@@ -0,0 +1,149 @@
+#ifndef ARENA_H
+#define ARENA_H
+
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+
+typedef struct ArenaPg {
+ struct ArenaPg *prev, *next;
+ char *beg, *end;
+ char data[];
+} ArenaPg;
+
+typedef struct {
+ ArenaPg *pg;
+ char *beg;
+} ArenaMark;
+
+typedef struct {
+ ArenaPg *cur, *tail;
+} Arena;
+
+#define new(a, t)\
+ (t*)arena_zeroed(arena_alloc(a, sizeof(t), _Alignof(t)), sizeof(t))
+
+#define new_arr(a, t, n)\
+ arena_alloc(a, sizeof(t) * (n), _Alignof(t))
+
+#define resize(a, p, old, new)\
+ arena_realloc(a, p, (old) * sizeof(*(p)), (new) * sizeof(*(p)),\
+ _Alignof(__typeof__(*(p))))
+
+void arena_free(Arena *a);
+
+void arena_save(Arena *a, ArenaMark *m);
+void arena_load(Arena *a, ArenaMark *m);
+
+void arena_reset(Arena *a);
+void arena_reserve(Arena *a, ptrdiff_t n);
+
+void *arena_alloc(Arena *a, ptrdiff_t n, ptrdiff_t align);
+void *arena_realloc(Arena *a, void *ptr, ptrdiff_t old, ptrdiff_t new, ptrdiff_t align);
+void *arena_zeroed(void *p, size_t n);
+
+#define ARENA_BACKEND_MALLOC 0
+#define ARENA_BACKEND_MMAP 1
+
+#ifndef ARENA_BACKEND
+#if defined(__linux__)
+# define ARENA_BACKEND ARENA_BACKEND_MMAP
+#else
+# define ARENA_BACKEND ARENA_BACKEND_MALLOC
+#endif
+#endif
+
+#ifdef ARENA_IMPL
+
+#include <stdio.h>
+#include <stdlib.h>
+static void arena_pg_alloc_fail(void) {
+ fprintf(stderr, "failed to allocate arena page\n");
+ abort();
+}
+
+#if ARENA_BACKEND == ARENA_BACKEND_MMAP
+#include <sys/mman.h>
+#include <unistd.h>
+#define ARENA_PG_SIZE sysconf(_SC_PAGESIZE)
+static inline void *arena_pg_alloc(ptrdiff_t n) {
+ void *p = mmap(NULL, n, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ return p == MAP_FAILED ? NULL : p;
+}
+static inline void arena_pg_free(void *ptr, ptrdiff_t n) { munmap(ptr, n); }
+#elif ARENA_BACKEND == ARENA_BACKEND_MALLOC
+#define ARENA_PG_SIZE 8192
+static inline void *arena_pg_alloc(ptrdiff_t n) { return malloc(n); }
+static inline void arena_pg_free(void *ptr, ptrdiff_t n) { free(ptr); (void)n; }
+#endif
+
+void arena_free(Arena *a) {
+ while (a->tail) {
+ a->cur = a->tail->prev;
+ arena_pg_free(a->tail, (uintptr_t)(a->tail->end - (char*)a->tail));
+ a->tail = a->cur;
+ }
+}
+
+void arena_reserve(Arena *a, ptrdiff_t n) {
+ while (a->cur && a->cur->beg + n >= a->cur->end) a->cur = a->cur->next;
+ if (a->cur) return;
+ ptrdiff_t cap = n + sizeof(ArenaPg);
+ cap += (uintptr_t)-cap & (ARENA_PG_SIZE - 1);
+ ArenaPg *p = arena_pg_alloc(cap);
+ if (!p) arena_pg_alloc_fail();
+ p->next = NULL;
+ p->prev = a->tail;
+ p->beg = p->data;
+ p->end = (char*)p + cap;
+ if (a->tail) a->tail->next = p;
+ a->cur = (a->tail = p);
+}
+
+void *arena_alloc(Arena *a, ptrdiff_t n, ptrdiff_t align) {
+ arena_reserve(a, n + (align - 1));
+ char *ptr = a->cur->beg + (-(uintptr_t)a->cur->beg & (align - 1));
+ a->cur->beg = ptr + n;
+ return ptr;
+}
+
+void *arena_realloc(Arena *a, void *ptr, ptrdiff_t old, ptrdiff_t new, ptrdiff_t align) {
+ if (a->cur && ptr == a->cur->beg - old && (char*)ptr + new < a->cur->end) {
+ a->cur->beg += new - old;
+ return ptr;
+ } else {
+ void *p = arena_alloc(a, new, align);
+ if (ptr) memcpy(p, ptr, old);
+ return p;
+ }
+}
+
+void *arena_zeroed(void *p, size_t n) {
+ memset(p, 0, n);
+ return p;
+}
+
+void arena_reset(Arena *a) {
+ if (!a->cur) return;
+ while (a->cur->prev) {
+ a->cur->beg = a->cur->data;
+ a->cur = a->cur->prev;
+ }
+ a->cur->beg = a->cur->data;
+}
+
+void arena_save(Arena *a, ArenaMark *m) {
+ m->pg = a->cur;
+ if (a->cur) m->beg = a->cur->beg;
+}
+
+void arena_load(Arena *a, ArenaMark *m) {
+ while (a->cur && a->cur != m->pg) {
+ a->cur->beg = a->cur->data;
+ a->cur = a->cur->prev;
+ }
+ if (a->cur) a->cur->beg = m->beg;
+}
+
+#endif
+#endif
diff --git a/dynarr.h b/dynarr.h
new file mode 100644
index 0000000..327d5e7
--- /dev/null
+++ b/dynarr.h
@@ -0,0 +1,59 @@
+#ifndef DYNARR_H
+#define DYNARR_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <err.h>
+
+#define DYNARR(T) struct { uint32_t n, c_; T *v; }
+
+#define DA_INIT_CAP 32
+#define DA_ELEM(da, n) ((n) * sizeof(*(da)->v))
+#define DA_BIT_CEIL (
+
+static inline uint32_t da_bit_ceil(uint32_t x) {
+ return x ? 1U << (32 - __builtin_clz(x - 1)) : 0;
+}
+
+/* malloc */
+
+#define DA_FIT(da, n) do {\
+ if ((n) > (da)->c_) {\
+ (da)->c_ = da_bit_ceil((uint32_t)(n));\
+ (da)->v = realloc((da)->v, DA_ELEM(da, (da)->c_));\
+ }\
+ if (!(da)->v) err(1, "dynarr fit");\
+} while(0)
+#define DA_GROW(da, n_) DA_FIT(da, (da)->n + (n_))
+#define DA_PUSH(da, ...) do {\
+ DA_GROW(da, 1);\
+ (da)->v[(da)->n++] = (__VA_ARGS__);\
+} while(0)
+#define DA_PUSH_MULT(da, o, n_) do {\
+ DA_GROW(da, n_);\
+ memcpy((da)->v + (da)->n, (o), DA_ELEM(da, n_));\
+ (da)->n += (n_);\
+} while(0)
+
+/* arena */
+
+#define DA_AFIT(da, a, n) do {\
+ if ((n) > (da)->c_) {\
+ uint32_t da_fit_c = da_bit_ceil((uint32_t)(n));\
+ (da)->v = resize(a, (da)->v, (da)->c_, da_fit_c);\
+ (da)->c_ = da_fit_c;\
+ }\
+} while(0)
+#define DA_AGROW(da, a, n_) DA_AFIT(da, a, (da)->n + (n_))
+#define DA_APUSH(da, a, ...) do {\
+ DA_AGROW(da, a, 1);\
+ (da)->v[(da)->n++] = (__VA_ARGS__);\
+} while(0)
+#define DA_APUSH_MULT(da, a, o, n_) do {\
+ DA_AGROW(da, a, n_);\
+ memcpy((da)->v + (da)->n, (o), DA_ELEM(da, n_));\
+ (da)->n += (n_);\
+} while(0)
+
+#endif
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..108c275
--- /dev/null
+++ b/main.c
@@ -0,0 +1,154 @@
+/* xmenu
+ * a simple fuzzy-selection menu made with Xlib
+ */
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/XKBlib.h>
+#include <stdio.h>
+
+#define ARENA_IMPL
+#define STR_IMPL
+
+#include "arena.h"
+#include "dynarr.h"
+#include "str.h"
+
+typedef DYNARR(char) DynStr;
+
+int
+umod(int a, int b)
+{
+ if (a < 0) {
+ return b + a % b;
+ } else {
+ return a % b;
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ Display *dsp = XOpenDisplay("");
+ int scr = DefaultScreen(dsp);
+ unsigned long fg = BlackPixel(dsp, scr);
+ unsigned long bg = WhitePixel(dsp, scr);
+
+ XSizeHints szhint = { 0 };
+ szhint.width = 350;
+ szhint.height = 250;
+ szhint.flags = PSize;
+
+ Window win = XCreateSimpleWindow(
+ dsp,
+ DefaultRootWindow(dsp),
+ szhint.x, szhint.y,
+ szhint.width, szhint.height,
+ 5 /* border width */,
+ fg, bg);
+
+ char title[] = "xmenu";
+ XSetStandardProperties(dsp, win, title, title,
+ None, argv, argc, &szhint);
+
+ GC gc = XCreateGC(dsp, win, 0, 0);
+ XSetBackground(dsp, gc, bg);
+ XSetForeground(dsp, gc, fg);
+
+ XSelectInput(dsp, win, ButtonPressMask | KeyPressMask | ExposureMask);
+ XMapRaised(dsp, win);
+
+ Font freg = XLoadFont(dsp, "-*-new century schoolbook-medium-r-*-*-24-*-*-*-*-*-*-*");
+ Font fital = XLoadFont(dsp, "-*-new century schoolbook-medium-i-*-*-24-*-*-*-*-*-*-*");
+
+ /* see Xft(3) */
+
+ DynStr input = { 0 };
+
+ int seli = 0;
+
+ const char *optv[] = { "she shit on my thang until i vomit", "what", "who are you", "why are you", "where'd you come from" };
+ int optc = sizeof optv / sizeof *optv;
+
+ for (;;) {
+ XEvent ev;
+ KeySym key;
+ XNextEvent(dsp, &ev);
+ switch (ev.type) {
+ case KeyPress: {
+ char kbuf[32];
+ KeyCode kc = ev.xkey.keycode;
+ KeySym sym = XkbKeycodeToKeysym(dsp, kc, 0,
+ !!(ev.xkey.state & ShiftMask));
+ int rune = ksym_to_unicode(sym);
+ switch (sym) {
+ case XK_Escape:
+ goto done;
+ case XK_BackSpace:
+ if (input.n > 0) input.n--;
+ goto draw;
+ case XK_Return:
+ input.n = 0;
+ goto draw;
+ case XK_Down:
+ seli = umod(seli + 1, optc);
+ goto draw;
+ case XK_Up:
+ seli = umod(seli - 1, optc);
+ goto draw;
+ default: {
+ int n = XLookupString(&ev.xkey, kbuf, 10, &key, 0);
+ if (!n) break;
+ DA_PUSH_MULT(&input, kbuf, n);
+ seli = 0;
+ goto draw;
+ } break;
+ }
+ } break;
+ case Expose: {
+ draw:;
+ XWindowAttributes attr;
+ XClearWindow(dsp, win);
+ XGetWindowAttributes(dsp, win, &attr);
+ //XGetSizeHints(dsp, win, &szhint);
+ XSetFont(dsp, gc, fital);
+ int w = XTextWidth(XQueryFont(dsp, XGContextFromGC(gc)), input.v, input.n);
+ DA_PUSH(&input, '_');
+ XDrawString(dsp, win, gc, 16, 24, input.v, input.n);
+ input.n--;
+ XSetFont(dsp, gc, freg);
+ for (int i = 0; i < optc; i++) {
+ if (i == seli) {
+ int w = XTextWidth(XQueryFont(dsp,
+ XGContextFromGC(gc)),
+ optv[i], strlen(optv[i]));
+ XFillRectangle(dsp, win, gc,
+ 16, 28 + i * 24 + 8,
+ w, 24);
+ XSetForeground(dsp, gc, bg);
+ XDrawString(dsp, win, gc,
+ 16, 48 + i * 24 + 8,
+ optv[i], strlen(optv[i]));
+ XSetForeground(dsp, gc, fg);
+ } else {
+ XDrawString(dsp, win, gc,
+ 16, 48 + i * 24 + 8,
+ optv[i], strlen(optv[i]));
+ }
+
+ }
+ } break;
+ /* TODO: figure out quit events */
+ }
+ }
+
+done: ;
+
+ XUnloadFont(dsp, freg);
+ XUnloadFont(dsp, fital);
+ XFreeGC(dsp, gc);
+ XDestroyWindow(dsp, win);
+ XCloseDisplay(dsp);
+
+ return 0;
+}
diff --git a/str.h b/str.h
new file mode 100644
index 0000000..b10bc64
--- /dev/null
+++ b/str.h
@@ -0,0 +1,152 @@
+#ifndef STR_H
+#define STR_H
+
+#include <string.h>
+#include <stddef.h>
+
+#include "arena.h"
+
+typedef struct {
+ char *s;
+ ptrdiff_t n;
+} Str;
+
+typedef struct {
+ Str head, tail;
+} Cut;
+
+#define S(s) (Str){s,sizeof(s)-1}
+
+char *str_to_cstr(Str s, Arena *a);
+Str str_from_cstr(const char *s);
+int str_eql(Str a, Str b);
+int str_starts(Str a, Str b);
+int str_ends(Str a, Str b);
+void str_catc(Str *a, char b, Arena *m);
+Str str_skip(Str a, ptrdiff_t n);
+int is_space(char c);
+Str str_trim_left(Str a);
+Str str_trim_right(Str a);
+Str str_trim(Str a);
+Cut str_cut(Str s, char c);
+Str str_findc(Str s, char c);
+Str str_find(Str haystack, Str needle);
+int str_contains(Str a, Str b);
+Str str_dup(Str a, Arena *m);
+void str_cat(Str *a, Str b, Arena *m);
+Str str_replace_end(Str s, Str a, Str b, Arena *m);
+
+#ifdef STR_IMPL
+
+/* conversions */
+
+char *str_to_cstr(Str s, Arena *a) {
+ char *r = new_arr(a, char, s.n + 1);
+ memcpy(r, s.s, s.n);
+ r[s.n] = 0;
+ return r;
+}
+
+Str str_from_cstr(const char *s) {
+ return (Str) { (char*)s, strlen(s) };
+}
+
+/* pure functions */
+
+int str_eql(Str a, Str b) {
+ return a.n == b.n && !memcmp(a.s, b.s, b.n);
+}
+
+int str_starts(Str a, Str b) {
+ return a.n >= b.n && !memcmp(a.s, b.s, b.n);
+}
+
+int str_ends(Str a, Str b) {
+ return a.n >= b.n && !memcmp(&a.s[a.n - b.n], b.s, b.n);
+}
+
+void str_catc(Str *a, char b, Arena *m) {
+ a->s = resize(m, a->s, a->n, a->n + 1);
+ a->s[a->n++] = b;
+}
+
+Str str_skip(Str a, ptrdiff_t n) {
+ return (Str) { a.s + n, a.n - n };
+}
+
+int is_space(char c) {
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r';
+}
+
+Str str_trim_left(Str a) {
+ while (a.n > 0 && is_space(a.s[0])) a.s++, a.n--;
+ return a;
+}
+
+Str str_trim_right(Str a) {
+ while (a.n > 0 && is_space(a.s[a.n - 1])) a.n--;
+ return a;
+}
+
+Str str_trim(Str a) {
+ return str_trim_left(str_trim_right(a));
+}
+
+/* splitting, searching */
+
+Cut str_cut(Str s, char c) {
+ char *p = memchr(s.s, c, s.n);
+ if (!p) {
+ return (Cut) { s, { &s.s[s.n], 0 } };
+ } else {
+ return (Cut) {
+ { s.s, p - s.s },
+ { p + 1, &s.s[s.n] - (p + 1) }
+ };
+ }
+}
+
+Str str_findc(Str s, char c) {
+ char *p = memchr(s.s, c, s.n);
+ return p ? (Str) { p, s.n - (p - s.s) } : (Str) { &s.s[s.n], 0 };
+}
+
+Str str_find(Str haystack, Str needle) {
+ if (needle.n < 1) return haystack;
+ while (haystack.n > 0) {
+ haystack = str_findc(haystack, needle.s[0]);
+ if (str_starts(haystack, needle)) break;
+ if (haystack.n > 0) haystack = str_skip(haystack, 1);
+ }
+ return haystack;
+}
+
+int str_contains(Str a, Str b) {
+ return str_find(a, b).n > 0;
+}
+
+/* allocating */
+
+Str str_dup(Str a, Arena *m) {
+ char *s = new_arr(m, char, a.n);
+ memcpy(s, a.s, a.n);
+ a.s = s;
+ return a;
+}
+
+void str_cat(Str *a, Str b, Arena *m) {
+ a->s = resize(m, a->s, a->n, a->n + b.n);
+ memcpy(&a->s[a->n], b.s, b.n);
+ a->n += b.n;
+}
+
+Str str_replace_end(Str s, Str a, Str b, Arena *m) {
+ if (!str_ends(s, a)) return s;
+ char *p = new_arr(m, char, s.n + b.n - a.n);
+ memcpy(p, s.s, s.n - a.n);
+ memcpy(p + s.n - a.n, b.s, b.n);
+ return (Str) { p, s.n + b.n - a.n };
+}
+
+#endif
+#endif
diff --git a/ui.c b/ui.c
new file mode 100644
index 0000000..542cb8b
--- /dev/null
+++ b/ui.c
@@ -0,0 +1,149 @@
+/* xmenu
+ * a simple fuzzy-selection menu made with Xlib
+ */
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/XKBlib.h>
+#include <stdio.h>
+
+#include "dynarr.h"
+
+typedef DYNARR(char) DynStr;
+
+int
+umod(int a, int b)
+{
+ if (a < 0) {
+ return b + a % b;
+ } else {
+ return a % b;
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ Display *dsp = XOpenDisplay("");
+ int scr = DefaultScreen(dsp);
+ unsigned long fg = BlackPixel(dsp, scr);
+ unsigned long bg = WhitePixel(dsp, scr);
+
+ XSizeHints szhint = { 0 };
+ szhint.width = 350;
+ szhint.height = 250;
+ szhint.flags = PSize;
+
+ Window win = XCreateSimpleWindow(
+ dsp,
+ DefaultRootWindow(dsp),
+ szhint.x, szhint.y,
+ szhint.width, szhint.height,
+ 5 /* border width */,
+ fg, bg);
+
+ char title[] = "xmenu";
+ XSetStandardProperties(dsp, win, title, title,
+ None, argv, argc, &szhint);
+
+ GC gc = XCreateGC(dsp, win, 0, 0);
+ XSetBackground(dsp, gc, bg);
+ XSetForeground(dsp, gc, fg);
+
+ XSelectInput(dsp, win, ButtonPressMask | KeyPressMask | ExposureMask);
+ XMapRaised(dsp, win);
+
+ Font freg = XLoadFont(dsp, "-*-new century schoolbook-medium-r-*-*-24-*-*-*-*-*-*-*");
+ Font fital = XLoadFont(dsp, "-*-new century schoolbook-medium-i-*-*-24-*-*-*-*-*-*-*");
+
+ /* see Xft(3) */
+
+ DynStr input = { 0 };
+
+ int seli = 0;
+
+ const char *optv[] = { "she shit on my thang until i vomit", "what", "who are you", "why are you", "where'd you come from" };
+ int optc = sizeof optv / sizeof *optv;
+
+ for (;;) {
+ XEvent ev;
+ KeySym key;
+ XNextEvent(dsp, &ev);
+ switch (ev.type) {
+ case KeyPress: {
+ char kbuf[32];
+ KeyCode kc = ev.xkey.keycode;
+ KeySym sym = XkbKeycodeToKeysym(dsp, kc, 0,
+ !!(ev.xkey.state & ShiftMask));
+ int rune = ksym_to_unicode(sym);
+ switch (sym) {
+ case XK_Escape:
+ goto done;
+ case XK_BackSpace:
+ if (input.n > 0) input.n--;
+ goto draw;
+ case XK_Return:
+ input.n = 0;
+ goto draw;
+ case XK_Down:
+ seli = umod(seli + 1, optc);
+ goto draw;
+ case XK_Up:
+ seli = umod(seli - 1, optc);
+ goto draw;
+ default: {
+ int n = XLookupString(&ev.xkey, kbuf, 10, &key, 0);
+ if (!n) break;
+ DA_PUSH_MULT(&input, kbuf, n);
+ seli = 0;
+ goto draw;
+ } break;
+ }
+ } break;
+ case Expose: {
+ draw:;
+ XWindowAttributes attr;
+ XClearWindow(dsp, win);
+ XGetWindowAttributes(dsp, win, &attr);
+ //XGetSizeHints(dsp, win, &szhint);
+ XSetFont(dsp, gc, fital);
+ int w = XTextWidth(XQueryFont(dsp, XGContextFromGC(gc)), input.v, input.n);
+ DA_PUSH(&input, '_');
+ XDrawString(dsp, win, gc, 16, 24, input.v, input.n);
+ input.n--;
+ XSetFont(dsp, gc, freg);
+ for (int i = 0; i < optc; i++) {
+ if (i == seli) {
+ int w = XTextWidth(XQueryFont(dsp,
+ XGContextFromGC(gc)),
+ optv[i], strlen(optv[i]));
+ XFillRectangle(dsp, win, gc,
+ 16, 28 + i * 24 + 8,
+ w, 24);
+ XSetForeground(dsp, gc, bg);
+ XDrawString(dsp, win, gc,
+ 16, 48 + i * 24 + 8,
+ optv[i], strlen(optv[i]));
+ XSetForeground(dsp, gc, fg);
+ } else {
+ XDrawString(dsp, win, gc,
+ 16, 48 + i * 24 + 8,
+ optv[i], strlen(optv[i]));
+ }
+
+ }
+ } break;
+ /* TODO: figure out quit events */
+ }
+ }
+
+done: ;
+
+ XUnloadFont(dsp, freg);
+ XUnloadFont(dsp, fital);
+ XFreeGC(dsp, gc);
+ XDestroyWindow(dsp, win);
+ XCloseDisplay(dsp);
+
+ return 0;
+}
diff --git a/ui.h b/ui.h
new file mode 100644
index 0000000..9263a28
--- /dev/null
+++ b/ui.h
@@ -0,0 +1,41 @@
+#ifndef UI_H
+#define UI_H
+
+typedef enum {
+ UI_REDRAW,
+ UI_KEY_DOWN,
+ UI_KEY_UP
+} UiEventType;
+
+typedef enum {
+ UIK_UP,
+ UIK_DOWN,
+ UIK_LEFT,
+ UIK_RIGHT,
+ UIK_RETURN,
+ UIK_HOME,
+ UIK_END,
+ UIK_PGUP,
+ UIK_PGDN,
+ UIK_UNKNOWN
+} UiKey;
+
+typedef struct {
+ UiKey key;
+ char str[16];
+ int strn;
+} UiKeyEvent;
+
+typedef struct {
+ UiEventType type;
+ union {
+ UiKeyEvent key;
+ };
+} UiEvent;
+
+void ui_init(void);
+void ui_fini(void);
+void ui_draw(Str input, int seli, Str *opt, int optn);
+int ui_wait_event(UiEvent *e);
+
+#endif