aboutsummaryrefslogtreecommitdiff
path: root/colitur-mu.c
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukasz@arcofasiagroup.com>2026-08-21 10:23:52 +0200
committerLukasz Kasprzak <lukasz@arcofasiagroup.com>2026-08-21 10:23:52 +0200
commit5d1abc9111a2a448f05462e6140fadb7448218e6 (patch)
tree6eefaf104d40241b27f9afc2cf686e3c62337f7b /colitur-mu.c
parent69add3767988d0dfc6ecac37405cfb10abe8dbb7 (diff)
downloadcolitur-x-5d1abc9111a2a448f05462e6140fadb7448218e6.tar.gz
colitur-x-5d1abc9111a2a448f05462e6140fadb7448218e6.zip
split: colitur-mu moves to colitur-experiments
This repo now holds one finished program rather than a finished one and a sketch. The microui front-end, its Xlib backend and the vendored microui went to ../colitur-experiments with their history intact. Makefile, config.h and README trimmed to match.
Diffstat (limited to 'colitur-mu.c')
-rw-r--r--colitur-mu.c232
1 files changed, 0 insertions, 232 deletions
diff --git a/colitur-mu.c b/colitur-mu.c
deleted file mode 100644
index 1907274..0000000
--- a/colitur-mu.c
+++ /dev/null
@@ -1,232 +0,0 @@
-/* colitur-mu -- an editable microui front-end for colitur.
- *
- * Unlike colitur-x, which only renders, this one lets you CHANGE the
- * settings colitur was invoked with -- language, book-name form, numbering
- * tradition, year -- and re-runs it. It never reimplements any calendar
- * logic: every value on screen came out of `colitur emit --format csv`.
- *
- * SPDX-License-Identifier: GPL-3.0-or-later
- */
-#define _POSIX_C_SOURCE 200809L
-#include <X11/Xlib.h>
-#include <X11/keysym.h>
-#include <X11/Xutil.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-
-#include "vendor/microui.h"
-#include "mu_x11.h"
-#include "config.h"
-
-#define MAXDAYS 400
-#define F 16
-
-typedef struct { char *raw; char *f[F]; } Row;
-
-static Row rows[MAXDAYS];
-static int nrows, sel;
-static char status[256] = "ready";
-
-/* --- settings the UI edits ---------------------------------------------- */
-static int year = 2027;
-static const char *langs[] = { "la", "en" };
-static const char *books[] = { "abbr", "full" };
-static const char *traditions[] = { "vulgate", "modern" };
-static int lang_i, book_i, trad_i;
-
-static int split(char *line, char **out)
-{
- int n = 0; char *p = line;
- while (n < F) {
- if (*p == '"') {
- out[n++] = ++p;
- while (*p && *p != '"') p++;
- if (*p) *p++ = 0;
- if (*p == ',') p++;
- } else {
- out[n++] = p;
- while (*p && *p != ',') p++;
- if (*p) *p++ = 0; else break;
- }
- }
- return n;
-}
-
-/* Shell out to colitur with the current settings. This is the whole point:
- the front-end owns no calendar logic, only the invocation. */
-static void reload(void)
-{
- for (int i = 0; i < nrows; i++) free(rows[i].raw);
- nrows = 0; sel = 0;
-
- char cmd[512];
- snprintf(cmd, sizeof cmd,
- "colitur emit --format csv --from %d --to %d "
- "--lang %s --sigla-book %s --sigla-tradition %s 2>&1",
- year, year, langs[lang_i], books[book_i], traditions[trad_i]);
-
- FILE *p = popen(cmd, "r");
- if (!p) { snprintf(status, sizeof status, "cannot run colitur"); return; }
-
- char buf[4096];
- if (!fgets(buf, sizeof buf, p)) { pclose(p); snprintf(status, sizeof status, "no output"); return; }
- if (strncmp(buf, "date,", 5)) { /* colitur printed an error */
- buf[strcspn(buf, "\n")] = 0;
- snprintf(status, sizeof status, "%.200s", buf);
- pclose(p); return;
- }
- while (nrows < MAXDAYS && fgets(buf, sizeof buf, p)) {
- buf[strcspn(buf, "\n")] = 0;
- char *d = strdup(buf);
- if (!d) break;
- rows[nrows].raw = d;
- if (split(d, rows[nrows].f) >= 12) nrows++; else free(d);
- }
- pclose(p);
- snprintf(status, sizeof status, "%d days ยท %s", nrows, cmd + 8);
-}
-
-/* --- one frame ---------------------------------------------------------- */
-/* One frame. NOTE the explicit row heights: mu_layout_row's height
- argument is stored verbatim as the row size, so passing 0 lays every
- control out zero pixels tall -- they are "drawn", invisibly, and the
- result looks like a layout bug rather than a height bug. */
-static void frame(mu_Context *ctx, int W, int H)
-{
- /* Column widths in CHARACTERS, not pixels. Hardcoded pixel widths are
- silently wrong the moment config.h names a different font size: at
- Terminus 20 a 110px "date" column truncates an ISO date. Row height
- likewise follows the font. */
- int ch = mux_text_width(NULL, "0", 1);
- int RH = mux_text_height(NULL) + 10;
- if (!mu_begin_window_ex(ctx, "colitur", mu_rect(0, 0, W, H),
- MU_OPT_NOTITLE | MU_OPT_NORESIZE | MU_OPT_NOCLOSE))
- return;
-
- int wy[] = { 11*ch, 3*ch, 3*ch, 7*ch, -1 };
- mu_layout_row(ctx, 5, wy, RH);
- mu_label(ctx, "Year");
- if (mu_button(ctx, "-")) { year--; reload(); }
- if (mu_button(ctx, "+")) { year++; reload(); }
- char y[16]; snprintf(y, sizeof y, "%d", year);
- mu_label(ctx, y);
- mu_label(ctx, "");
-
- int w4[] = { 11*ch, 9*ch, 9*ch, -1 };
- struct { const char *cap; const char **opts; int *idx; } sets[] = {
- { "Language", langs, &lang_i },
- { "Book names", books, &book_i },
- { "Numbering", traditions, &trad_i },
- };
- for (size_t r = 0; r < sizeof sets / sizeof *sets; r++) {
- mu_layout_row(ctx, 4, w4, RH);
- mu_label(ctx, sets[r].cap);
- for (int i = 0; i < 2; i++) {
- char lbl[48];
- snprintf(lbl, sizeof lbl, "%s%s",
- *sets[r].idx == i ? "\xe2\x97\x8f " : " ", sets[r].opts[i]);
- if (mu_button_ex(ctx, lbl, 0, MU_OPT_ALIGNCENTER)) {
- *sets[r].idx = i; reload();
- }
- }
- mu_label(ctx, "");
- }
-
- int w1[] = { -1 };
- mu_layout_row(ctx, 1, w1, RH);
- mu_label(ctx, status);
-
- int listh = H - 7 * RH - 4 * RH;
- if (listh < 100) listh = 100;
- mu_layout_row(ctx, 1, w1, listh);
- mu_begin_panel(ctx, "days");
- int wrow[] = { 11*ch, 11*ch, 34*ch, -1 };
- for (int i = 0; i < nrows; i++) {
- mu_layout_row(ctx, 4, wrow, RH - 2);
- if (mu_button(ctx, rows[i].f[0])) sel = i;
- mu_label(ctx, rows[i].f[9]);
- mu_label(ctx, rows[i].f[6]);
- char cit[160];
- snprintf(cit, sizeof cit, "%s %s", rows[i].f[13], rows[i].f[14]);
- mu_label(ctx, cit);
- }
- mu_end_panel(ctx);
-
- if (nrows) {
- char d[600];
- mu_layout_row(ctx, 1, w1, RH);
- snprintf(d, sizeof d, "%s %s", rows[sel].f[0], rows[sel].f[6]);
- mu_label(ctx, d);
- mu_layout_row(ctx, 1, w1, RH);
- snprintf(d, sizeof d, "%s %s %s Epistola %s Evangelium %s",
- rows[sel].f[3], rows[sel].f[9], rows[sel].f[11],
- rows[sel].f[13], rows[sel].f[14]);
- mu_label(ctx, d);
- }
- mu_end_window(ctx);
-}
-
-int main(void)
-{
- Display *dpy = XOpenDisplay(NULL);
- if (!dpy) { fputs("cannot open display\n", stderr); return 1; }
- int scr = DefaultScreen(dpy);
- int W = 1000, H = 700;
- Window win = XCreateSimpleWindow(dpy, RootWindow(dpy, scr), 0, 0, W, H, 0,
- BlackPixel(dpy, scr), BlackPixel(dpy, scr));
- XStoreName(dpy, win, "colitur-mu");
- XSelectInput(dpy, win, ExposureMask | KeyPressMask | KeyReleaseMask |
- ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
- StructureNotifyMask);
- XMapWindow(dpy, win);
- mux_init(dpy, win, font);
-
- mu_Context *ctx = malloc(sizeof *ctx);
- mu_init(ctx);
- ctx->text_width = mux_text_width;
- ctx->text_height = mux_text_height;
-
- reload();
-
- /* An immediate-mode UI is a RENDER LOOP, not an event handler. Drawing
- only in response to an X event is wrong twice over: microui's first
- frame merely creates its containers and the second is what lays them
- out, and a container's scroll state settles a frame late. Drain the
- queue, then always draw. */
- for (int running = 1; running;) {
- while (XPending(dpy)) {
- XEvent ev;
- XNextEvent(dpy, &ev);
- switch (ev.type) {
- case ConfigureNotify: W = ev.xconfigure.width; H = ev.xconfigure.height; break;
- case MotionNotify: mu_input_mousemove(ctx, ev.xmotion.x, ev.xmotion.y); break;
- case ButtonPress:
- if (ev.xbutton.button == 4) mu_input_scroll(ctx, 0, -30);
- else if (ev.xbutton.button == 5) mu_input_scroll(ctx, 0, 30);
- else mu_input_mousedown(ctx, ev.xbutton.x, ev.xbutton.y, MU_MOUSE_LEFT);
- break;
- case ButtonRelease:
- if (ev.xbutton.button < 4)
- mu_input_mouseup(ctx, ev.xbutton.x, ev.xbutton.y, MU_MOUSE_LEFT);
- break;
- case KeyPress: {
- KeySym k = XLookupKeysym(&ev.xkey, 0);
- if (k == XK_q) running = 0;
- if (k == XK_Down && sel < nrows - 1) sel++;
- if (k == XK_Up && sel > 0) sel--;
- break;
- }
- }
- }
- mu_begin(ctx);
- frame(ctx, W, H);
- mu_end(ctx);
- mux_present(ctx, W, H, mu_color(20, 20, 20, 255));
- XFlush(dpy);
- nanosleep(&(struct timespec){ 0, 16000000 }, NULL); /* ~60 Hz */
- }
- XCloseDisplay(dpy);
- return 0;
-}