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