diff options
| author | katalx | 2026-01-28 19:07:23 -0500 |
|---|---|---|
| committer | katalx | 2026-01-28 19:07:23 -0500 |
| commit | 86a10e053154cb52ab7d051b98a6366c6165c02b (patch) | |
| tree | 51fc293b01b24c453d2b2a15db901e55ec065d0b | |
| parent | ccec3e7f471e727fd008eb94454412281b8a4e43 (diff) | |
cleanly separate xlib and menu code
| -rw-r--r-- | main.c | 180 | ||||
| -rw-r--r-- | ui.c | 211 | ||||
| -rw-r--r-- | ui.h | 8 |
3 files changed, 188 insertions, 211 deletions
@@ -2,9 +2,6 @@ * 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 @@ -13,6 +10,7 @@ #include "arena.h" #include "dynarr.h" #include "str.h" +#include "ui.h" typedef DYNARR(char) DynStr; @@ -27,128 +25,100 @@ umod(int a, int b) } int -main(int argc, char **argv) +txt_insert(DynStr *s, int i, Str ins) { - 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); + DA_FIT(s, s->n + ins.n); + if (i < s->n) { + memmove(s->v + i + ins.n, s->v + i, s->n - i); + } + memcpy(s->v + i, ins.s, ins.n); + s->n += ins.n; + return i + ins.n; +} - Font freg = XLoadFont(dsp, "-*-new century schoolbook-medium-r-*-*-24-*-*-*-*-*-*-*"); - Font fital = XLoadFont(dsp, "-*-new century schoolbook-medium-i-*-*-24-*-*-*-*-*-*-*"); +int +txt_delete(DynStr *s, int i, int n) +{ + if (n > i) n = i; + if (n > 0) { + memmove(s->v + i - n, s->v + i, s->n - i); + s->n -= n; + } + return i - n; +} - /* see Xft(3) */ +int +main(int argc, char **argv) +{ + ui_init(argc, argv); DynStr input = { 0 }; - + int inpi = 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" }; + Str optv[] = { + S("she shit on my thang until i vomit"), + S("what"), + S("who are you"), + S("why are you"), + S("where'd you come from") + }; int optc = sizeof optv / sizeof *optv; - - for (;;) { - XEvent ev; - KeySym key; - XNextEvent(dsp, &ev); + if (!optc) goto done; + for (UiEvent ev; ui_wait_event(&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: + case UI_KEY_DOWN: + switch (ev.key.key) { + case UIK_ESCAPE: goto done; - case XK_BackSpace: - if (input.n > 0) input.n--; - goto draw; - case XK_Return: - input.n = 0; + case UIK_BACKSPACE: + inpi = txt_delete(&input, inpi, 1); goto draw; - case XK_Down: + case UIK_RETURN: { + Str o = optv[seli]; + printf("%.*s\n", (int)o.n, o.s); + } goto done; + case UIK_DOWN: seli = umod(seli + 1, optc); goto draw; - case XK_Up: + case UIK_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); + case UIK_LEFT: + if (inpi > 0) inpi--; + goto draw; + case UIK_RIGHT: + if (inpi < input.n) inpi++; + goto draw; + case UIK_HOME: + inpi = 0; + goto draw; + case UIK_END: + inpi = input.n; + goto draw; + default: + if (!ev.key.strn) break; + inpi = txt_insert(&input, inpi, (Str) { ev.key.str, ev.key.strn }); 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 */ + break; + case UI_REDRAW: + draw: + ui_draw( + (Str) { input.v, input.n }, + inpi, + seli, + optv, + optc + ); + break; + default: + break; } } -done: ; - - XUnloadFont(dsp, freg); - XUnloadFont(dsp, fital); - XFreeGC(dsp, gc); - XDestroyWindow(dsp, win); - XCloseDisplay(dsp); +done: + ui_fini(); return 0; } @@ -7,34 +7,34 @@ #include <X11/XKBlib.h> #include <stdio.h> +#include "ui.h" #include "dynarr.h" -typedef DYNARR(char) DynStr; +static Display *dsp; +static Window win; +static int scr; +static unsigned long fg, bg; +static XSizeHints szhint; +static GC gc; -int -umod(int a, int b) -{ - if (a < 0) { - return b + a % b; - } else { - return a % b; - } -} +static Font freg, fital; -int -main(int argc, char **argv) +/* TODO: center menu on screen */ + +void +ui_init(int argc, char **argv) { - Display *dsp = XOpenDisplay(""); - int scr = DefaultScreen(dsp); - unsigned long fg = BlackPixel(dsp, scr); - unsigned long bg = WhitePixel(dsp, scr); + dsp = XOpenDisplay(""); + scr = DefaultScreen(dsp); + fg = BlackPixel(dsp, scr); + bg = WhitePixel(dsp, scr); - XSizeHints szhint = { 0 }; + szhint = (XSizeHints) { 0 }; szhint.width = 350; szhint.height = 250; szhint.flags = PSize; - Window win = XCreateSimpleWindow( + win = XCreateSimpleWindow( dsp, DefaultRootWindow(dsp), szhint.x, szhint.y, @@ -42,108 +42,111 @@ main(int argc, char **argv) 5 /* border width */, fg, bg); + /* TODO: replace with modern XSetWMProperties */ char title[] = "xmenu"; XSetStandardProperties(dsp, win, title, title, None, argv, argc, &szhint); - GC gc = XCreateGC(dsp, win, 0, 0); + 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 */ - } + freg = XLoadFont(dsp, "-*-new century schoolbook-medium-r-*-*-24-*-*-*-*-*-*-*"); + fital = XLoadFont(dsp, "-*-new century schoolbook-medium-i-*-*-24-*-*-*-*-*-*-*"); +} + +UiKey +xksym_to_uik(KeySym sym) +{ + switch (sym) { + case XK_Escape: return UIK_ESCAPE; + case XK_BackSpace: return UIK_BACKSPACE; + case XK_Up: return UIK_UP; + case XK_Down: return UIK_DOWN; + case XK_Left: return UIK_LEFT; + case XK_Right: return UIK_RIGHT; + case XK_Return: return UIK_RETURN; + case XK_Home: return UIK_HOME; + case XK_End: return UIK_END; + case XK_Page_Down: return UIK_PGDN; + case XK_Page_Up: return UIK_PGUP; + default: return UIK_UNKNOWN; + } +} + +int +ui_wait_event(UiEvent *e) +{ + XEvent ev; + KeySym sym; + XNextEvent(dsp, &ev); + switch (ev.type) { + case KeyPress: + e->type = UI_KEY_DOWN; + e->key.strn = XLookupString(&ev.xkey, e->key.str, 10, &sym, 0); + e->key.key = xksym_to_uik(sym); + return 1; + case KeyRelease: + e->type = UI_KEY_UP; + e->key.strn = XLookupString(&ev.xkey, e->key.str, 10, &sym, 0); + e->key.key = xksym_to_uik(sym); + return 1; + case Expose: + e->type = UI_REDRAW; + return 1; } + fprintf(stderr, "[Unknown event %d]\n", ev.type); + return 0; +} -done: ; +void +ui_draw(Str input, int inpi, int seli, Str *optv, int optc) +{ + XWindowAttributes attr; + XClearWindow(dsp, win); + XGetWindowAttributes(dsp, win, &attr); + + /* draw input */ + + XSetFont(dsp, gc, fital); + XDrawString(dsp, win, gc, 16, 24, input.s, input.n); + XFontStruct *f = XQueryFont(dsp, XGContextFromGC(gc)); + int w = XTextWidth(f, input.s, inpi); + XDrawLine(dsp, win, gc, 16 + w, 24 - f->ascent, 16 + w, 24 + f->descent); + + /* draw options */ + + XSetFont(dsp, gc, freg); + for (int i = 0; i < optc; i++) { + if (i == seli) { + int w = XTextWidth(XQueryFont(dsp, + XGContextFromGC(gc)), + optv[i].s, optv[i].n); + 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].s, optv[i].n); + XSetForeground(dsp, gc, fg); + } else { + XDrawString(dsp, win, gc, + 16, 48 + i * 24 + 8, + optv[i].s, optv[i].n); + } + } +} +void +ui_fini(void) +{ XUnloadFont(dsp, freg); XUnloadFont(dsp, fital); XFreeGC(dsp, gc); XDestroyWindow(dsp, win); XCloseDisplay(dsp); - - return 0; } @@ -1,6 +1,8 @@ #ifndef UI_H #define UI_H +#include "str.h" + typedef enum { UI_REDRAW, UI_KEY_DOWN, @@ -8,6 +10,8 @@ typedef enum { } UiEventType; typedef enum { + UIK_ESCAPE, + UIK_BACKSPACE, UIK_UP, UIK_DOWN, UIK_LEFT, @@ -33,9 +37,9 @@ typedef struct { }; } UiEvent; -void ui_init(void); +void ui_init(int argc, char **argv); void ui_fini(void); -void ui_draw(Str input, int seli, Str *opt, int optn); +void ui_draw(Str input, int inpi, int seli, Str *optv, int optc); int ui_wait_event(UiEvent *e); #endif |
