shithub: orca

Download patch

ref: 9d12fed5a69249baf09ae42781ce1c37e21f4968
parent: aba0118f23a1af89fd15c88e6fff2acd7a2b79c7
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Wed Mar 11 14:00:50 EDT 2020

plan9: add "color" command

--- a/orca.man
+++ b/orca.man
@@ -182,8 +182,10 @@
 .B $
 operator. All commands except
 .I undo
-and
+,
 .I redo
+and
+.I color
 have a shorthand equivalent to their first two characters.
 .PP
 Any command can start with
@@ -262,6 +264,9 @@
 .TP
 .B select X Y [W] [H]
 Select the specified rectangle.
+.TP
+.B color [rrggbb ...]
+Change colors. Without any parameters it prints the current values.
 .PP
 .I Orca
 treats
--- a/plan9.c
+++ b/plan9.c
@@ -491,199 +491,6 @@
 	sel.max.y = MAX(0, MIN((int)field.height-1, sel.max.y));
 }
 
-static void
-command(char *s, void (*snapshot)(void))
-{
-	char tmp[256], *argv[8];
-	int argc, x, y;
-
-	snprint(tmp, sizeof(tmp), "%s", s);
-	s = tmp;
-
-	if (s[0] == ',') {
-		snapshot();
-		cur = ZP;
-		sel = Rect(0, 0, field.width, field.height);
-		s++;
-	}
-
-	if (s[0] == '|' || s[0] == '>' || s[0] == '<') {
-		if (s[0] != '>')
-			snapshot();
-		shellpipe(s);
-		return;
-	}
-
-	if ((argc = tokenize(tmp, argv, nelem(argv))) < 1)
-		return;
-	s = argv[0];
-
-	if (s[0] == 'p' && s[1] == 'l') /* play */
-		pause = false;
-	else if (s[0] == 's' && s[1] == 't') /* stop */
-		pause = true;
-	else if (s[0] == 'r' && s[1] == 'u') /* run */
-		forward = true;
-	else if (s[0] == 'c' && s[1] == 'o') /* copy */
-		selcopy();
-	else if (s[0] == 'p' && s[1] == 'a') { /* paste */
-		snapshot();
-		inject("/dev/snarf");
-	} else if (s[0] == 'e' && s[1] == 'r') { /* erase */
-		snapshot();
-		selset('.');
-	} else if (s[0] == 'p' && s[1] == 'r') { /* print */
-		for (y = sel.min.y; y <= sel.max.y; y++) {
-			for (x = sel.min.x; x <= sel.max.x; x++)
-				putchar(field.buffer[x + y*field.width]);
-			putchar('\n');
-		}
-		fflush(stdout);
-	} else if (strcmp(s, "undo") == 0) {
-		undo();
-	} else if (strcmp(s, "redo") == 0) {
-		redo();
-	} else if (argc > 1) {
-		x = atoi(argv[1]);
-
-		if (s[0] == 'b' && s[1] == 'p') /* bpm */
-			apm = bpm = MAX(1, x);
-		else if (s[0] == 'a' && s[1] == 'p') /* apm */
-			apm = MAX(1, x);
-		else if (s[0] == 'f' && s[1] == 'r') /* frame */
-			tick = MAX(0, x);
-		else if (s[0] == 's' && s[1] == 'k') /* skip */
-			tick = MAX(0, tick+x);
-		else if (s[0] == 'r' && s[1] == 'e') /* rewind */
-			tick = MAX(0, tick-x);
-		else if (s[0] == 'i' && s[1] == 'p') /* ip */
-			netdial(argv[1], nil);
-		else if (s[0] == 'u' && s[1] == 'd') /* udp */
-			netdial(nil, argv[1]);
-		else if (s[0] == 'm' && s[1] == 'i') /* midi */
-			midiopen(argv[1]);
-		else if (s[0] == 's' && s[1] == 'e' && argc > 2) { /* select */
-			cur = ZP;
-			sel = ZR;
-			curmove(atoi(argv[1]), atoi(argv[2]));
-			x = argc > 3 ? atoi(argv[3]) : 0;
-			y = argc > 4 ? atoi(argv[4]) : 0;
-			selext(x, y);
-		} else if (s[0] == 'i' && s[1] == 'n') { /* inject */
-			snapshot();
-			if (argc > 2) {
-				cur.x = 0;
-				sel.min.x = 0;
-				curmove(atoi(argv[2]), 0);
-			}
-			if (argc > 3) {
-				cur.y = 0;
-				sel.min.y = 0;
-				curmove(0, atoi(argv[3]));
-			}
-			inject(argv[1]);
-		} else if (s[0] == 'w' && s[1] == 'r') { /* write abcd 13 14 */
-			s = argv[1];
-			x = argc > 2 ? atoi(argv[2]) : cur.x;
-			y = argc > 3 ? atoi(argv[3]) : cur.y;
-			for (; *s != 0; x++, s++)
-				fieldset(x, y, *s);
-		}
-
-		/* FIXME color, find, time */
-	}
-}
-
-static void
-process(Oevent_list *events)
-{
-	int c, n, t;
-	Oevent *e;
-	u8int u[4];
-	char tmp[64];
-
-	for (e = events->buffer; e != events->buffer+events->count; e++) {
-		t = e->any.oevent_type;
-
-		if (midi >= 0) {
-			/*
-			 * USB MIDI allegedly receives bulk transfers of a certain max size
-			 * so a smarter thing to do would be to buffer up all notes on/off
-			 * and write them in bulk after that.
-			 * But a better way to do it would be through midifs, so it's
-			 * implemented once. Still need to list available midi devices anyway.
-			 */
-			if (t == Oevent_type_midi_note) {
-				Oevent_midi_note *m = &e->midi_note;
-
-				if (m->mono) {
-					/* idk if that's the right thing to do, just stop what's been playing on that channel */
-					u[0] = Midicn | 0x8;
-					u[1] = 0x80 | m->channel;
-					u[3] = 0;
-					for (n = 0; n < nelem(noteoff[m->channel]); n++) {
-						if (noteoff[m->channel][n] > 0) {
-							noteoff[m->channel][n] = 0;
-							u[2] = n;
-							write(midi, u, 4);
-						}
-					}
-				}
-
-				u[0] = Midicn | 0x9;
-				u[1] = 0x90 | m->channel;
-				u[2] = (m->octave + 1)*12 + m->note;
-				u[3] = m->velocity;
-				write(midi, u, 4);
-
-				noteoff[m->channel][u[2]] = m->duration + 1;
-				continue;
-			} else if (t == Oevent_type_midi_cc) {
-				Oevent_midi_cc *c = &e->midi_cc;
-				u[0] = Midicn | 0xb;
-				u[1] = 0xb0 | c->channel;
-				u[2] = c->control;
-				u[3] = c->value;
-				write(midi, u, 4);
-				continue;
-			} else if (t == Oevent_type_midi_pb) {
-				Oevent_midi_pb *p = &e->midi_pb;
-				u[0] = Midicn | 0xe;
-				u[1] = 0xe0 | p->channel;
-				u[2] = p->lsb;
-				u[3] = p->msb;
-				write(midi, u, 4);
-				continue;
-			}
-		}
-
-		if (t == Oevent_type_udp_string && udp >= 0) {
-			Oevent_udp_string *u = &e->udp_string;
-			write(udp, u->chars, u->count); /* FIXME show errors */
-			continue;
-		} else if (t == Oevent_type_cmd_string) {
-			Oevent_cmd_string *c = &e->cmd_string;
-			memmove(tmp, c->chars, c->count);
-			tmp[c->count] = 0;
-			command(tmp, nosnapshot);
-		}
-	}
-
-	if (midi >= 0) {
-		u[3] = 0;
-		for (c = 0; c < nelem(noteoff); c++) {
-			for (n = 0; n < nelem(noteoff[c]); n++) {
-				if (noteoff[c][n] > 0 && --noteoff[c][n] == 0) {
-					u[0] = Midicn | 0x8;
-					u[1] = 0x80 | c;
-					u[2] = n;
-					write(midi, u, 4);
-				}
-			}
-		}
-	}
-}
-
 /*
  * nsec() is wallclock and can be adjusted by timesync
  * so need to use cycles() instead, but fall back to
@@ -736,57 +543,6 @@
 }
 
 static void
-orcathread(void *drawchan)
-{
-	vlong start, end, n;
-	vlong processold, processnew;
-	Oevent_list events;
-	int w, h;
-
-	threadsetname("orca/sim");
-
-	oevent_list_init(&events);
-
-	processnew = nanosec();
-	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);
-
-		processold = processnew;
-		processnew = nanosec();
-		process(&events);
-		nbsendul(drawchan, 0);
-
-		forward = false;
-		do {
-			end = 15000000000LL/bpm; /* 1e9*60/4 */
-			n = nanosec() - start;
-			if (n >= end && !pause)
-				break;
-			/* unpause is not precise at all */
-			if (pause || end - n > 750000000LL)
-				sleep(70);
-			else if (end - n > 25000000LL)
-				sleep(20);
-			else if (end - n > 10000000LL)
-				sleep(1);
-		} while (!forward);
-
-		framedev = (processnew - processold - 15000000000LL/bpm)/1000000LL;
-		tick++;
-
-		if (apm < bpm)
-			bpm--;
-		else if (apm > bpm)
-			bpm++;
-	}
-}
-
-static void
 redraw(int complete)
 {
 	static Point oldscroll;
@@ -799,6 +555,8 @@
 	char s[32];
 	Rune c, csel;
 
+	lockdisplay(display);
+
 	/* bottom text is always in the same place */
 	bot.x = screen->r.min.x + Txtoff;
 	bot.y = screen->r.max.y - glyphsz.y*2 - Txtoff;
@@ -991,8 +749,276 @@
 	runestringn(screen, bot, color[Dfhigh], ZP, font, linebuf, i);
 
 	flushimage(display, 1);
+	unlockdisplay(display);
 }
 
+static void
+command(char *s, void (*snapshot)(void))
+{
+	Image *im;
+	char tmp[256], *argv[8];
+	int argc, x, y, i;
+	ulong c;
+
+	snprint(tmp, sizeof(tmp), "%s", s);
+	s = tmp;
+
+	if (s[0] == ',') {
+		snapshot();
+		cur = ZP;
+		sel = Rect(0, 0, field.width, field.height);
+		s++;
+	}
+
+	if (s[0] == '|' || s[0] == '>' || s[0] == '<') {
+		if (s[0] != '>')
+			snapshot();
+		shellpipe(s);
+		return;
+	}
+
+	if ((argc = tokenize(tmp, argv, nelem(argv))) < 1)
+		return;
+	s = argv[0];
+
+	if (s[0] == 'p' && s[1] == 'l') /* play */
+		pause = false;
+	else if (s[0] == 's' && s[1] == 't') /* stop */
+		pause = true;
+	else if (s[0] == 'r' && s[1] == 'u') /* run */
+		forward = true;
+	else if (s[0] == 'c' && s[1] == 'o' && (s[2] == 0 || s[2] == 'p')) /* copy */
+		selcopy();
+	else if (s[0] == 'p' && s[1] == 'a') { /* paste */
+		snapshot();
+		inject("/dev/snarf");
+	} else if (s[0] == 'e' && s[1] == 'r') { /* erase */
+		snapshot();
+		selset('.');
+	} else if (s[0] == 'p' && s[1] == 'r') { /* print */
+		for (y = sel.min.y; y <= sel.max.y; y++) {
+			for (x = sel.min.x; x <= sel.max.x; x++)
+				putchar(field.buffer[x + y*field.width]);
+			putchar('\n');
+		}
+		fflush(stdout);
+	} else if (strcmp(s, "undo") == 0) {
+		undo();
+	} else if (strcmp(s, "redo") == 0) {
+		redo();
+	} else if (s[0] == 'c' && s[1] == 'o') { /* color ffffff 111111 232323 ... */
+		if (argc < 2) {
+			for (i = 0; i < Numcolors; i++)
+				print("%06x ", theme[i]>>8);
+			print("\n");
+			fflush(stdout);
+		} else {
+			lockdisplay(display);
+			for (i = 1; i < Numcolors+1 && i < argc; i++) {
+				if ((c = strtoul(argv[i], &s, 16)) == 0 && s == argv[i])
+					continue;
+				c = c<<8 | 0xff;
+				if ((im = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, c)) == nil)
+					continue;
+				freeimage(color[i-1]);
+				theme[i-1] = c;
+				color[i-1] = im;
+			}
+			unlockdisplay(display);
+			redraw(1);
+		}
+	} else if (argc > 1) {
+		x = atoi(argv[1]);
+
+		if (s[0] == 'b' && s[1] == 'p') /* bpm */
+			apm = bpm = MAX(1, x);
+		else if (s[0] == 'a' && s[1] == 'p') /* apm */
+			apm = MAX(1, x);
+		else if (s[0] == 'f' && s[1] == 'r') /* frame */
+			tick = MAX(0, x);
+		else if (s[0] == 's' && s[1] == 'k') /* skip */
+			tick = MAX(0, tick+x);
+		else if (s[0] == 'r' && s[1] == 'e') /* rewind */
+			tick = MAX(0, tick-x);
+		else if (s[0] == 'i' && s[1] == 'p') /* ip */
+			netdial(argv[1], nil);
+		else if (s[0] == 'u' && s[1] == 'd') /* udp */
+			netdial(nil, argv[1]);
+		else if (s[0] == 'm' && s[1] == 'i') /* midi */
+			midiopen(argv[1]);
+		else if (s[0] == 's' && s[1] == 'e' && argc > 2) { /* select */
+			cur = ZP;
+			sel = ZR;
+			curmove(atoi(argv[1]), atoi(argv[2]));
+			x = argc > 3 ? atoi(argv[3]) : 0;
+			y = argc > 4 ? atoi(argv[4]) : 0;
+			selext(x, y);
+		} else if (s[0] == 'i' && s[1] == 'n') { /* inject */
+			snapshot();
+			if (argc > 2) {
+				cur.x = 0;
+				sel.min.x = 0;
+				curmove(atoi(argv[2]), 0);
+			}
+			if (argc > 3) {
+				cur.y = 0;
+				sel.min.y = 0;
+				curmove(0, atoi(argv[3]));
+			}
+			inject(argv[1]);
+		} else if (s[0] == 'w' && s[1] == 'r') { /* write abcd 13 14 */
+			s = argv[1];
+			x = argc > 2 ? atoi(argv[2]) : cur.x;
+			y = argc > 3 ? atoi(argv[3]) : cur.y;
+			for (; *s != 0; x++, s++)
+				fieldset(x, y, *s);
+		}
+
+		/* FIXME find, time */
+	}
+}
+
+static void
+process(Oevent_list *events)
+{
+	int c, n, t;
+	Oevent *e;
+	u8int u[4];
+	char tmp[64];
+
+	for (e = events->buffer; e != events->buffer+events->count; e++) {
+		t = e->any.oevent_type;
+
+		if (midi >= 0) {
+			/*
+			 * USB MIDI allegedly receives bulk transfers of a certain max size
+			 * so a smarter thing to do would be to buffer up all notes on/off
+			 * and write them in bulk after that.
+			 * But a better way to do it would be through midifs, so it's
+			 * implemented once. Still need to list available midi devices anyway.
+			 */
+			if (t == Oevent_type_midi_note) {
+				Oevent_midi_note *m = &e->midi_note;
+
+				if (m->mono) {
+					/* idk if that's the right thing to do, just stop what's been playing on that channel */
+					u[0] = Midicn | 0x8;
+					u[1] = 0x80 | m->channel;
+					u[3] = 0;
+					for (n = 0; n < nelem(noteoff[m->channel]); n++) {
+						if (noteoff[m->channel][n] > 0) {
+							noteoff[m->channel][n] = 0;
+							u[2] = n;
+							write(midi, u, 4);
+						}
+					}
+				}
+
+				u[0] = Midicn | 0x9;
+				u[1] = 0x90 | m->channel;
+				u[2] = (m->octave + 1)*12 + m->note;
+				u[3] = m->velocity;
+				write(midi, u, 4);
+
+				noteoff[m->channel][u[2]] = m->duration + 1;
+				continue;
+			} else if (t == Oevent_type_midi_cc) {
+				Oevent_midi_cc *c = &e->midi_cc;
+				u[0] = Midicn | 0xb;
+				u[1] = 0xb0 | c->channel;
+				u[2] = c->control;
+				u[3] = c->value;
+				write(midi, u, 4);
+				continue;
+			} else if (t == Oevent_type_midi_pb) {
+				Oevent_midi_pb *p = &e->midi_pb;
+				u[0] = Midicn | 0xe;
+				u[1] = 0xe0 | p->channel;
+				u[2] = p->lsb;
+				u[3] = p->msb;
+				write(midi, u, 4);
+				continue;
+			}
+		}
+
+		if (t == Oevent_type_udp_string && udp >= 0) {
+			Oevent_udp_string *u = &e->udp_string;
+			write(udp, u->chars, u->count); /* FIXME show errors */
+			continue;
+		} else if (t == Oevent_type_cmd_string) {
+			Oevent_cmd_string *c = &e->cmd_string;
+			memmove(tmp, c->chars, c->count);
+			tmp[c->count] = 0;
+			command(tmp, nosnapshot);
+		}
+	}
+
+	if (midi >= 0) {
+		u[3] = 0;
+		for (c = 0; c < nelem(noteoff); c++) {
+			for (n = 0; n < nelem(noteoff[c]); n++) {
+				if (noteoff[c][n] > 0 && --noteoff[c][n] == 0) {
+					u[0] = Midicn | 0x8;
+					u[1] = 0x80 | c;
+					u[2] = n;
+					write(midi, u, 4);
+				}
+			}
+		}
+	}
+}
+
+static void
+orcathread(void *drawchan)
+{
+	vlong start, end, n;
+	vlong processold, processnew;
+	Oevent_list events;
+	int w, h;
+
+	threadsetname("orca/sim");
+
+	oevent_list_init(&events);
+
+	processnew = nanosec();
+	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);
+
+		processold = processnew;
+		processnew = nanosec();
+		process(&events);
+		nbsendul(drawchan, 0);
+
+		forward = false;
+		do {
+			end = 15000000000LL/bpm; /* 1e9*60/4 */
+			n = nanosec() - start;
+			if (n >= end && !pause)
+				break;
+			/* unpause is not precise at all */
+			if (pause || end - n > 750000000LL)
+				sleep(70);
+			else if (end - n > 25000000LL)
+				sleep(20);
+			else if (end - n > 10000000LL)
+				sleep(1);
+		} while (!forward);
+
+		framedev = (processnew - processold - 15000000000LL/bpm)/1000000LL;
+		tick++;
+
+		if (apm < bpm)
+			bpm--;
+		else if (apm > bpm)
+			bpm++;
+	}
+}
+
 static int
 fieldload(char *path)
 {
@@ -1126,6 +1152,7 @@
 	threadsetname("plumb");
 	if ((f = plumbopen("orca", OREAD)) >= 0) {
 		while ((m = plumbrecv(f)) != nil) {
+			snapshot();
 			inject(m->data);
 			plumbfree(m);
 		}