shithub: orca

Download patch

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);
 }