shithub: orca

Download patch

ref: 46544df18a733fdcc2e43dde43b396e719f1a0b1
parent: b2bacf2993e5aef1de9edaa2e6f271cbb6030ad2
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Wed Feb 12 15:20:49 EST 2020

plan9: fix timing, fast bpm changing, handle more keys

--- a/plan9.c
+++ b/plan9.c
@@ -12,6 +12,8 @@
 
 enum {
 	Txtoff = 16,
+
+	Msgredraw = 0,
 };
 
 static struct {
@@ -22,7 +24,7 @@
 static Rune *linebuf;
 static Usz tick;
 static int gridw = 8, gridh = 8;
-static int bpm = 120, insert = 1;
+static int bpm = 120, insert = 1, pause;
 static int curx, cury;
 static Image *curbg;
 static int charw, charh;
@@ -100,8 +102,6 @@
 		}
 	}
 
-	sleep(150);
-
 	for (i = 0; i < nelem(noteoff); i++) {
 		if (noteoff[i].at > 0 && noteoff[i].at < tick) {
 			write(1, noteoff[i].u, 4);
@@ -110,17 +110,76 @@
 	}
 }
 
+/*
+ * nsec() is wallclock and can be adjusted by timesync
+ * so need to use cycles() instead
+ *
+ * "fasthz" is how many ticks there are in a second
+ * can be read from /dev/time
+ */
+static uvlong
+nanosec(void)
+{
+	static double fasthz = 0.0;
+	uvlong x;
+	int f, n, i;
+	char tmp[128], *e;
+
+	if (fasthz < 1.0) {
+		if ((f = open("/dev/time", OREAD)) < 0)
+			sysfatal("failed to open /dev/time");
+		if ((n = read(f, tmp, sizeof(tmp)-1)) < 2)
+			sysfatal("failed to read /dev/time");
+		tmp[n] = 0;
+		e = tmp;
+		for (i = 0; i < 3; i++)
+			strtoll(e, &e, 10);
+		fasthz = strtod(e, nil);
+		if (fasthz < 1.0)
+			sysfatal("failed to read fasthz");
+		close(f);
+	}
+	cycles(&x);
+	return (double)x / (fasthz / 1000000000.0);
+}
+
 static void
-orcathread(void *)
+orcathread(void *drawchan)
 {
-	int w, h;
+	vlong start, end, n, oldn;
+	int w, h, oldbpm;
 
-	w = field.width;
-	h = field.height;
-	mbuffer_clear(mbuf.buffer, h, w);
-	oevent_list_clear(&events);
-	orca_run(field.buffer, mbuf.buffer, h, w, tick, &events, 0);
-	process(&events);
+	threadsetname("orca/sim");
+	for (;;) {
+		start = nanosec();
+		w = field.width;
+		h = field.height;
+		mbuffer_clear(mbuf.buffer, h, w);
+		oevent_list_clear(&events);
+		orca_run(field.buffer, mbuf.buffer, h, w, tick, &events, 0);
+
+		process(&events);
+		tick++;
+		nbsendul(drawchan, Msgredraw);
+
+		oldn = start;
+		end = start + 1;
+		oldbpm = 0;
+		for (n = start; pause || n < end; n = nanosec()) {
+			if (bpm != oldbpm) {
+				end = start + (15000000000.0 / (double)bpm); /* 10^9 * 60 / 4 */
+				oldbpm = bpm;
+			}
+
+			/* doesn't suppose to jump back, but just in case do _something_ */
+			if (n < oldn)
+				end -= oldn - n;
+
+			oldn = n;
+			yield();
+			sleep(5);
+		}
+	}
 }
 
 static void
@@ -201,6 +260,8 @@
 		werrstr(field_load_error_string(e));
 		return -1;
 	}
+	curx = MIN(curx, field.width-1);
+	cury = MIN(cury, field.height-1);
 
 	return 0;
 }
@@ -239,13 +300,14 @@
 		{ nil, &m, CHANRCV },
 		{ nil, nil, CHANRCV },
 		{ nil, &key, CHANRCV },
-		{ nil, nil,  CHANNOBLK},
+		{ nil, nil, CHANRCV },
+		{ nil, nil, CHANEND },
 	};
 
 	USED(argc, argv);
 
 	srand(time(0));
-	threadsetname("orca");
+	threadsetname("orca/draw");
 
 	if(initdraw(nil, nil, "orca") < 0)
 		sysfatal("initdraw: %r");
@@ -257,6 +319,7 @@
 	a[0].c = mctl->c;
 	a[1].c = mctl->resizec;
 	a[2].c = kctl->c;
+	a[3].c = chancreate(sizeof(ulong), 0); /* FIXME should it be buffered instead? */
 
 	curbg = allocimage(display, Rect(0, 0, 1, 1), RGBA32, 1, DYellow);
 	charw = stringwidth(font, "X");
@@ -272,10 +335,10 @@
 	mbuf_reusable_ensure_size(&mbuf, h, w);
 	oevent_list_init(&events);
 
-	for (tick = 0;; tick++) {
-		orcathread(nil);
-		redraw();
+	threadcreate(orcathread, a[3].c, mainstacksize);
 
+	for (;;) {
+		redraw();
 		oldw = w = field.width;
 		oldh = h = field.height;
 
@@ -327,10 +390,22 @@
 			case Kright:
 				curx = MIN(w-1, curx+1);
 				break;
+			case Khome:
+				curx = 0;
+				break;
+			case Kend:
+				curx = field.width-1;
+				break;
+			case Kpgup:
+				cury = 0;
+				break;
+			case Kpgdown:
+				cury = field.height-1;
+				break;
 			case 0x11: /* C-q */
 				goto end;
 			case 0x12: /* C-r */
-				tick = -1;
+				tick = 0;
 				break;
 			case '(':
 				w = MAX(1, w-gridw);
@@ -358,6 +433,9 @@
 					insert = 1;
 				/* FIXME else remove selection */
 				break;
+			case ' ':
+				pause = !pause;
+				break;
 			default:
 				if (key == Kdel)
 					key = '.';
@@ -381,6 +459,9 @@
 					MIN(field.height, copyfield.height), MIN(field.width, copyfield.width)
 				);
 			}
+
+		case 3: /* redraw */
+			break;
 		}
 
 		if (w != oldw || h != oldh) {