ref: f274c92b3e2b60f2c2b0b56cf94cd8f0a758748b
parent: dd470b171237d28bca26abd57bdecbbedba13f48
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Tue Feb 11 21:29:34 EST 2020
plan9: field resizing, loading files, rudimentary editing
--- a/plan9.c
+++ b/plan9.c
@@ -7,229 +7,316 @@
#include <keyboard.h>
#include <thread.h>
+#define MIN(x,y) ((x)<(y)?(x):(y))
+#define MAX(x,y) ((x)>(y)?(x):(y))
+
+enum {
+ Txtoff = 16,
+};
+
static struct {
- u8int u[4];
- int at;
+ u8int u[4];
+ Usz at;
}noteoff[16*128]; /* 16 channels, 128 notes each */
-static char *buf;
+static Rune *linebuf;
+static Usz tick;
+static int gridw = 8, gridh = 8;
+static int curx, cury;
+static Image *curbg;
-Usz orca_round_up_power2(Usz x) {
- assert(x <= SIZE_MAX / 2 + 1);
- x -= 1;
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
-#if SIZE_MAX > UINT32_MAX
- x |= x >> 32;
-#endif
- return x + 1;
+Usz
+orca_round_up_power2(Usz x)
+{
+ x -= 1;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ return x + 1;
}
-bool orca_is_valid_glyph(Glyph c) {
- if (c >= '0' && c <= '9')
- return true;
- if (c >= 'A' && c <= 'Z')
- return true;
- if (c >= 'a' && c <= 'z')
- return true;
- switch (c) {
- case '!':
- case '#':
- case '%':
- case '*':
- case '.':
- case ':':
- case ';':
- case '=':
- case '?':
- return true;
- }
- return false;
+bool
+orca_is_valid_glyph(Glyph c)
+{
+ if (c >= '0' && c <= '9')
+ return true;
+ if (c >= 'A' && c <= 'Z')
+ return true;
+ if (c >= 'a' && c <= 'z')
+ return true;
+ switch (c) {
+ case '!':
+ case '#':
+ case '%':
+ case '*':
+ case '.':
+ case ':':
+ case ';':
+ case '=':
+ case '?':
+ return true;
+ }
+ return false;
}
static void
-usage(void)
+process(Oevent_list *events)
{
- print("usage: orca [-q] [-t ticks] [-f file]\n");
- exits("usage");
+ int i, off;
+ Oevent *e;
+ u8int u[4];
+
+ for (e = events->buffer, i = 0; i < events->count; i++, e++) {
+ if (e->any.oevent_type == Oevent_type_midi_note) {
+ Oevent_midi_note const *n = &e->midi_note;
+ u[0] = 1;
+ u[1] = 0x90 | n->channel;
+ u[2] = (n->octave + 1)*12 + n->note;
+ u[3] = n->velocity;
+ write(1, u, 4);
+
+ off = n->channel*128 + u[2];
+ noteoff[off].u[1] = 0x80 | n->channel;
+ noteoff[off].u[2] = u[2];
+ noteoff[off].u[3] = 0;
+ noteoff[off].at = tick + n->duration;
+ }
+ }
+
+ sleep(150);
+
+ for (i = 0; i < nelem(noteoff); i++) {
+ if (noteoff[i].at > 0 && noteoff[i].at < tick) {
+ write(1, noteoff[i].u, 4);
+ noteoff[i].at = 0;
+ }
+ }
}
static void
-process(Oevent_list *el, int tick)
+redraw(Field *f)
{
- int i, off;
- Oevent *e;
- u8int u[4];
+ Point p, top;
+ Rune cursor;
+ int x, y;
- for (i = 0; i < el->count; i++) {
- e = &el->buffer[i];
- if (e->any.oevent_type == Oevent_type_midi_note) {
- Oevent_midi_note const *n = &e->midi_note;
- u[0] = 1;
- u[1] = 0x90 | n->channel;
- u[2] = (n->octave + 1)*12 + n->note;
- u[3] = n->velocity;
- write(1, u, 4);
+ draw(screen, screen->r, display->black, nil, ZP);
+ p = screen->r.min;
+ p.x += Txtoff;
+ p.y += Txtoff;
+ top = p;
+ for (y = 0; y < f->height; y++) {
+ for (x = 0; x < f->width; x++) {
+ Rune c = f->buffer[f->width*y + x];
+ if (c == L'.')
+ c = L'·';
+ linebuf[x] = c;
+ if (y == cury && x == curx)
+ cursor = c;
+ }
+ linebuf[x] = 0;
+ runestring(screen, p, display->white, ZP, font, linebuf);
+ p.y += font->height;
+ }
- off = n->channel*128 + u[2];
- noteoff[off].u[1] = 0x80 | n->channel;
- noteoff[off].u[2] = u[2];
- noteoff[off].u[3] = 0;//u[3];
- noteoff[off].at = tick + n->duration;
- }
- }
+ p.y += 2 * font->height;
+ p.x = screen->r.min.x + Txtoff;
+ runesprint(linebuf, "%udx%ud", f->width, f->height);
+ runestring(screen, p, display->white, ZP, font, linebuf);
+ p.x += stringwidth(font, "99999x99999 ");
+ runesprint(linebuf, "%ludf", tick);
+ runestring(screen, p, display->white, ZP, font, linebuf);
- sleep(150);
+ /* cursor */
+ p = top;
+ p.x += curx*stringwidth(font, " ");
+ p.y += cury*font->height;
+ runestringnbgop(screen, p, display->black, ZP, font, &cursor, 1, curbg, ZP, SoverD);
- for (i = 0; i < nelem(noteoff); i++) {
- if (noteoff[i].at > 0 && noteoff[i].at < tick) {
- write(1, noteoff[i].u, 4);
- noteoff[i].at = 0;
- }
- }
+ flushimage(display, 1);
}
-static void
-field_draw(Field *f, int tick)
+static int
+loadfield(Field *field, char *path)
{
- Point p;
- int x, y;
+ Field_load_error e;
- lockdisplay(display);
- draw(screen, screen->r, display->black, nil, ZP);
- p = screen->r.min;
- p.x += 8;
- p.y += 8;
- for (y = 0; y < f->height; y++) {
- for (x = 0; x < f->width; x++) {
- u8int c = f->buffer[f->width*y + x];
- buf[x] = (c > '\n' && c < 128) ? c : '?';
- }
- buf[x] = 0;
- string(screen, p, display->white, ZP, display->defaultfont, buf);
- p.y += display->defaultfont->height;
- }
+ if ((e = field_load_file(path, field)) != Field_load_error_ok) {
+ werrstr(field_load_error_string(e));
+ return -1;
+ }
- p.x = screen->r.min.x + 8;
- p.y += 2 * display->defaultfont->height;
- sprint(buf, "%df", tick);
- string(screen, p, display->white, ZP, display->defaultfont, buf);
+ return 0;
+}
- flushimage(display, 1);
- unlockdisplay(display);
+static void
+screensize(int *w, int *h)
+{
+ *w = (Dx(screen->r) - 2*Txtoff) / stringwidth(font, "X");
+ *h = ((Dy(screen->r) - 2*Txtoff) - 3*font->height) / font->height;
}
+static void
+select(void)
+{
+}
+
+static char *menu3i[] = {
+ "load",
+ "save",
+ nil,
+};
+
+static Menu menu3 = {
+ .item = menu3i,
+};
+
void
threadmain(int argc, char **argv)
{
- const char *input_file = "/fd/0";
- int ticks = 1;
- bool print_output = true;
- Field field;
- Mousectl *mctl;
- Keyboardctl *kctl;
- Rune key;
- Mouse m;
- Alt a[] = {
- { nil, &m, CHANRCV },
- { nil, nil, CHANRCV },
- { nil, &key, CHANRCV },
- { nil, nil, CHANNOBLK},
- };
+ Mbuf_reusable mbuf;
+ Oevent_list events;
+ Field field;
+ Mousectl *mctl;
+ Keyboardctl *kctl;
+ Rune key;
+ Mouse m;
+ char tmp[256];
+ int oldw, oldh, w, h;
+ Alt a[] = {
+ { nil, &m, CHANRCV },
+ { nil, nil, CHANRCV },
+ { nil, &key, CHANRCV },
+ { nil, nil, CHANNOBLK},
+ };
- ARGBEGIN{
- case 't':
- ticks = atoi(EARGF(usage()));
- break;
- case 'f':
- input_file = EARGF(usage());
- break;
- case 'q':
- print_output = false;
- break;
- }ARGEND;
+ USED(argc, argv);
- field_init(&field);
- Field_load_error fle = field_load_file(input_file, &field);
- if (fle != Field_load_error_ok) {
- field_deinit(&field);
- char const *errstr = "Unknown";
- switch (fle) {
- case Field_load_error_ok:
- break;
- case Field_load_error_cant_open_file:
- errstr = "Unable to open file";
- break;
- case Field_load_error_too_many_columns:
- errstr = "Grid file has too many columns";
- break;
- case Field_load_error_too_many_rows:
- errstr = "Grid file has too many rows";
- break;
- case Field_load_error_no_rows_read:
- errstr = "Grid file has no rows";
- break;
- case Field_load_error_not_a_rectangle:
- errstr = "Grid file is not a rectangle";
- break;
- }
- exits(errstr);
- }
- buf = malloc(field.width+1);
- memset(noteoff, 0, sizeof(noteoff));
- Mbuf_reusable mbuf_r;
- mbuf_reusable_init(&mbuf_r);
- mbuf_reusable_ensure_size(&mbuf_r, field.height, field.width);
- Oevent_list oevent_list;
- oevent_list_init(&oevent_list);
- Usz max_ticks = (Usz)ticks;
+ srand(time(0));
+ threadsetname("orca");
- if(initdraw(nil, nil, "orca") < 0)
- sysfatal("initdraw: %r");
- if ((mctl = initmouse(nil, screen)) == nil)
- sysfatal("initmouse: %r");
- if ((kctl = initkeyboard(nil)) == nil)
- sysfatal("initkeyboard: %r");
+ if(initdraw(nil, nil, "orca") < 0)
+ sysfatal("initdraw: %r");
+ if ((mctl = initmouse(nil, screen)) == nil)
+ sysfatal("initmouse: %r");
+ if ((kctl = initkeyboard(nil)) == nil)
+ sysfatal("initkeyboard: %r");
- a[0].c = mctl->c;
- a[1].c = mctl->resizec;
- a[2].c = kctl->c;
- unlockdisplay(display);
+ a[0].c = mctl->c;
+ a[1].c = mctl->resizec;
+ a[2].c = kctl->c;
- srand(time(0));
- threadsetname("orca");
+ curbg = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, DYellow);
- for (Usz i = 0; i < max_ticks; ++i) {
- mbuffer_clear(mbuf_r.buffer, field.height, field.width);
- oevent_list_clear(&oevent_list);
- orca_run(field.buffer, mbuf_r.buffer, field.height, field.width, i,
- &oevent_list, 0);
- process(&oevent_list, i);
- field_draw(&field, i);
+ screensize(&w, &h);
+ field_init_fill(&field, h, w, '.');
- switch (alt(a)) {
- case 0: /* mouse */
- break;
- case 1: /* resize */
- getwindow(display, Refnone);
- break;
- case 2: /* keyboard */
- switch (key) {
- case Kdel:
- goto end;
- }
- }
- }
+ linebuf = malloc(sizeof(Rune)*MAX(w+1, 64));
+ memset(noteoff, 0, sizeof(noteoff));
+ mbuf_reusable_init(&mbuf);
+ mbuf_reusable_ensure_size(&mbuf, h, w);
+ oevent_list_init(&events);
+ for (tick = 0;; tick++) {
+ mbuffer_clear(mbuf.buffer, h, w);
+ oevent_list_clear(&events);
+ orca_run(field.buffer, mbuf.buffer, h, w, tick, &events, 0);
+ process(&events);
+ redraw(&field);
+
+ oldw = w = field.width;
+ oldh = h = field.height;
+
+ switch (alt(a)) {
+ case 0: /* mouse */
+ switch (m.buttons & 7) {
+ case 1:
+ select();
+ break;
+ case 2:
+ break;
+ case 4:
+ switch (menuhit(3, mctl, &menu3, nil)) {
+ case 0:
+ tmp[0] = 0;
+ if (enter("load file:", tmp, sizeof(tmp), mctl, kctl, nil) > 0) {
+ if (loadfield(&field, tmp) == 0) {
+ w = field.width;
+ h = field.height;
+ } else {
+ /* FIXME display the error */
+ }
+ }
+ break;
+ }
+ break;
+ }
+ break;
+
+ case 1: /* resize */
+ getwindow(display, Refnone);
+ break;
+
+ case 2: /* keyboard */
+ switch (key) {
+ case Kup:
+ cury = MAX(0, cury-1);
+ break;
+ case Kdown:
+ cury = MIN(h-1, cury+1);
+ break;
+ case Kleft:
+ curx = MAX(0, curx-1);
+ break;
+ case Kright:
+ curx = MIN(w-1, curx+1);
+ break;
+ case 0x11: /* C-q */
+ goto end;
+ case 0x12: /* C-r */
+ tick = -1;
+ break;
+ case '(':
+ w = MAX(1, w-gridw);
+ break;
+ case ')':
+ w += gridw;
+ break;
+ case '_':
+ h = MAX(1, h-gridh);
+ break;
+ case '+':
+ h += gridh;
+ break;
+ default:
+ if (key == Kdel)
+ key = '.';
+ if (orca_is_valid_glyph(key))
+ field.buffer[curx + w*cury] = key;
+ else
+ fprint(2, "unhandled key %04x\n", key);
+ break;
+ }
+
+ if (w != oldw || h != oldh) {
+ field_resize_raw(&field, h, w);
+ if (w >= oldw && h >= oldh)
+ memset(field.buffer + oldw*oldh, '.', w*h - oldw*oldh);
+ }
+ }
+
+ if (w != oldw || h != oldh) {
+ mbuf_reusable_ensure_size(&mbuf, h, w);
+ linebuf = realloc(linebuf, sizeof(Rune)*MAX(w+1, 64));
+ }
+ }
+
end:
- mbuf_reusable_deinit(&mbuf_r);
- oevent_list_deinit(&oevent_list);
- if (print_output)
- field_fput(&field, stderr);
- field_deinit(&field);
+ mbuf_reusable_deinit(&mbuf);
+ oevent_list_deinit(&events);
+ field_deinit(&field);
- threadexitsall(nil);
+ threadexitsall(nil);
}