diff options
| author | katalx | 2026-01-28 18:26:48 -0500 |
|---|---|---|
| committer | katalx | 2026-01-28 18:26:48 -0500 |
| commit | ccec3e7f471e727fd008eb94454412281b8a4e43 (patch) | |
| tree | 039d86b29a9596198d6ac2c2f15628424f6a7942 | |
uhhh git
| -rw-r--r-- | Makefile | 30 | ||||
| -rw-r--r-- | arena.h | 149 | ||||
| -rw-r--r-- | dynarr.h | 59 | ||||
| -rw-r--r-- | main.c | 154 | ||||
| -rw-r--r-- | str.h | 152 | ||||
| -rw-r--r-- | ui.c | 149 | ||||
| -rw-r--r-- | ui.h | 41 |
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} @@ -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 @@ -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; +} @@ -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 @@ -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; +} @@ -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 |
