shithub: picker

Download patch

ref: 35d6448581414f216d1295437ad3a911ea6699e4
parent: cab743dec18069d730108f8f8295185cd801a968
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Sat Mar 14 19:35:12 EDT 2020

-e option, snarfing, refactoring, and a man page

--- /dev/null
+++ b/LICENSE
@@ -1,0 +1,1 @@
+Public domain.
--- a/mkfile
+++ b/mkfile
@@ -1,7 +1,7 @@
 </$objtype/mkfile
 
 TARG=picker
-BIN=/$objtype/bin/
+BIN=/$objtype/bin
 MAN=/sys/man/1
 
 OFILES=\
--- a/picker.c
+++ b/picker.c
@@ -23,7 +23,7 @@
 };
 
 typedef struct Color Color;
-typedef struct Mode Mode;
+typedef struct Space Space;
 
 struct Color {
 	Rectangle r;
@@ -33,7 +33,7 @@
 	Image *i;
 };
 
-struct Mode {
+struct Space {
 	char *name;
 	char opt;
 	char single;
@@ -82,7 +82,7 @@
 	v[2] = rgb[2];
 }
 
-static Mode modes[] = {
+static Space spaces[] = {
 	{
 		.name = "HSLuv",
 		.opt = 's',
@@ -109,14 +109,17 @@
 	},
 };
 
-static char *menu2i[nelem(modes)+1];
+static char *menu2i[nelem(spaces)+2] = {
+	"snarf",
+};
 static Menu menu2 = { .item = menu2i };
 
 static Color *colors;
 static int ncolors, curcolor, nchan;
 static Rectangle srects[3];
-static Mode *mode;
+static Space *space;
 static Image *bg;
+static char hex[12];
 
 static ulong
 rgba2u(double *rgba)
@@ -146,15 +149,15 @@
 	b = buf[si];
 
 	memmove(c, colors[curcolor].v, sizeof(c));
-	if (mode->single) {
+	if (space->single) {
 		memset(c, 0, 3*sizeof(double));
 		c[si] = colors[curcolor].v[si];
 	}
-	dt = mode->max[si] / w;
+	dt = space->max[si] / w;
 	mi = c[si] / dt;
 	c[si] = 0.0;
 	for (i = n = 0; i < w; i++, n += 4) {
-		mode->torgb(c, rgba);
+		space->torgb(c, rgba);
 		rgba[3] = c[3];
 		u = rgba2u(rgba);
 		u = setalpha(u, u&0xff);
@@ -174,7 +177,7 @@
 			b[n+3] = (u>>24) & 0xff;
 		}
 
-		c[si] = MIN(mode->max[si], c[si] + dt);
+		c[si] = MIN(space->max[si], c[si] + dt);
 	}
 	loadimage(s, rect, b, 4*w);
 
@@ -186,7 +189,6 @@
 {
 	Rectangle r, cr;
 	Image *im;
-	char hex[8];
 	int i, colw;
 
 	lockdisplay(display);
@@ -241,10 +243,10 @@
 
 	/* current color in hex */
 	if (nchan > 3)
-		sprint(hex, "#%08lux", colors[curcolor].u);
+		sprint(hex, "%08lux", colors[curcolor].u);
 	else
-		sprint(hex, "#%06lux", colors[curcolor].u>>8);
-	r.min.x += Dx(r)/2 - 7*stringwidth(font, "#")/2;
+		sprint(hex, "%06lux", colors[curcolor].u>>8);
+	r.min.x += Dx(r)/2 - stringwidth(font, hex)/2;
 	r.min.y += Dy(r)/2 - font->height/2;
 	stringbg(screen, r.min, display->white, ZP, font, hex, display->black, ZP);
 
@@ -257,9 +259,9 @@
 {
 	int i;
 
-	print("usage: %s [-", argv0);
-	for (i = 0; i < nelem(modes); i++)
-		print("%c", modes[i].opt);
+	print("usage: %s [-e] [-", argv0);
+	for (i = 0; i < nelem(spaces); i++)
+		print("%c", spaces[i].opt);
 	print("] [-a] rrggbb[aa] ...\n");
 
 	threadexitsall("usage");
@@ -289,6 +291,50 @@
 	free(d);
 }
 
+static int
+printcolor(void)
+{
+	int n;
+	char buf[16];
+
+	if (nchan < 4)
+		n = sprint(buf, "%d %06lux\n", curcolor, colors[curcolor].u>>8);
+	else
+		n = sprint(buf, "%d %08lux\n", curcolor, colors[curcolor].u);
+
+	return write(1, buf, n) == n ? 0 : -1;
+}
+
+static int
+str2color(char *s, Color *c)
+{
+	vlong v;
+	char *e;
+	int i;
+
+	if (strlen(s) != nchan*2) {
+		werrstr("wrong number of components");
+		return -1;
+	}
+	if ((v = strtoll(s, &e, 16)) == 0 && (e == s || *e || v < 0)) {
+		werrstr("invalid color");
+		return -1;
+	}
+	if (nchan < 4) {
+		v <<= 8;
+		v |= 0xff;
+	}
+	c->u = v;
+	for (i = 0; i < 4; i++) {
+		c->rgba[i] = (double)((v>>24)&0xff) / 255.0;
+		v <<= 8;
+	}
+	c->v[3] = c->rgba[3];
+	space->fromrgb(c->rgba, c->v);
+
+	return 0;
+}
+
 void
 threadmain(int argc, char **argv)
 {
@@ -303,26 +349,29 @@
 		{ nil, nil, CHANEND },
 	};
 	Color *c;
-	char *s, buf[16];
-	vlong v;
-	int i, j;
+	int i, once;
+	ulong u;
 
-	mode = &modes[0];
+	space = &spaces[0];
 	nchan = 3;
+	once = 0;
 	ARGBEGIN{
 	case 'a':
 		nchan = 4;
 		break;
+	case 'e':
+		once = 1;
+		break;
 	default:
-		mode = nil;
-		for (i = 0; i < nelem(modes); i++) {
-			if (modes[i].opt == ARGC()) {
-				mode = &modes[i];
+		space = nil;
+		for (i = 0; i < nelem(spaces); i++) {
+			if (spaces[i].opt == ARGC()) {
+				space = &spaces[i];
 				break;
 			}
 		}
-		if (mode == nil) {
-			fprint(2, "unknown mode '%c'\n", ARGC());
+		if (space == nil) {
+			fprint(2, "unknown color space '%c'\n", ARGC());
 			usage();
 		}
 		break;
@@ -335,29 +384,14 @@
 	}
 	colors = calloc(ncolors, sizeof(Color));
 	for (i = 0; i < ncolors; i++) {
-		if (strlen(argv[i]) != nchan*2) {
-			fprint(2, "wrong number of components: '%s'\n", argv[i]);
+		if (str2color(argv[i], &colors[i]) != 0) {
+			fprint(2, "'%s': %r\n", argv[i]);
 			usage();
 		}
-		if ((v = strtoll(argv[i], &s, 16)) == 0 && (s == argv[i] || *s || v < 0)) {
-			fprint(2, "invalid color: '%s'\n", argv[i]);
-			usage();
-		}
-		if (nchan < 4) {
-			v <<= 8;
-			v |= 0xff;
-		}
-		colors[i].u = v;
-		for (j = 0; j < 4; j++) {
-			colors[i].rgba[j] = (double)((v>>24)&0xff) / 255.0;
-			v <<= 8;
-		}
-		colors[i].v[3] = colors[i].rgba[3];
-		mode->fromrgb(colors[i].rgba, colors[i].v);
 	}
 
-	for (i = 0; i < nelem(modes); i++)
-		menu2i[i] = modes[i].name;
+	for (i = 0; i < nelem(spaces); i++)
+		menu2i[i+1] = spaces[i].name;
 
 	if (initdraw(nil, nil, "picker") < 0)
 		sysfatal("initdraw: %r");
@@ -406,21 +440,15 @@
 					Rectangle r = srects[i];
 					r.max.x += 1;
 					if (ptinrect(m.xy, r)) {
-						ulong u;
-
-						c->v[i] = MIN(mode->max[i], (double)(m.xy.x - r.min.x) * mode->max[i]/(double)(Dx(r)-1));
+						c->v[i] = MIN(space->max[i], (double)(m.xy.x - r.min.x) * space->max[i]/(double)(Dx(r)-1));
 						c->rgba[3] = c->v[3];
 changed:
-						mode->torgb(c->v, c->rgba);
+						space->torgb(c->v, c->rgba);
 						u = rgba2u(c->rgba);
 						if (c->u != u) {
 							c->u = u;
-							if (nchan < 4)
-								j = sprint(buf, "%d %06lux\n", curcolor, u>>8);
-							else
-								j = sprint(buf, "%d %08lux\n", curcolor, u);
-							if (write(1, buf, j) != j)
-								goto end;
+							if (!once)
+								printcolor();
 						}
 						redraw();
 						goto next;
@@ -434,13 +462,29 @@
 					}
 				}
 			} else if (m.buttons == 2) {
-				if ((i = menuhit(2, mctl, &menu2, nil)) >= 0 && i < nelem(modes)) {
-					mode = &modes[i];
-					mode->fromrgb(c->rgba, c->v);
-					for (i = 0; i < 3; i++)
-						c->v[i] = MAX(0.0, MIN(mode->max[i], c->v[i]));
-					goto changed;
+				if ((i = menuhit(2, mctl, &menu2, nil)) >= 0) {
+					if (i == 0) {
+						int f;
+						if ((f = open("/dev/snarf", OWRITE)) >= 0) {
+							write(f, hex, strlen(hex));
+							close(f);
+						}
+					} else {
+						space = &spaces[i];
+						space->fromrgb(c->rgba, c->v);
+						for (i = 0; i < 3; i++)
+							c->v[i] = MAX(0.0, MIN(space->max[i], c->v[i]));
+						goto changed;
+					}
 				}
+			} else if (m.buttons == 4) {
+				if (enter(nchan < 4 ? "rgb:" : "rgba:", hex+1, sizeof(hex)-1, mctl, kctl, nil) > 0) {
+					u = c->u;
+					if (str2color(hex+1, c) == 0 && c->u != u) {
+						c->u = ~c->u; /* just for the update to kick in */
+						goto changed;
+					}
+				}
 			}
 			break;
 
@@ -452,5 +496,15 @@
 	}
 
 end:
+	if (once) {
+		for (i = 0; i < ncolors; i++) {
+			if (nchan < 4)
+				print("%06lux ", colors[i].u>>8);
+			else
+				print("%08lux ", colors[i].u);
+		}
+		print("\n");
+	}
+
 	threadexitsall(nil);
 }
--- /dev/null
+++ b/picker.man
@@ -1,0 +1,61 @@
+.TH PICKER 1
+.SH NAME
+picker \- a color picker
+.SH SYNOPSIS
+.B picker
+[
+.I -e
+] [
+.I -a
+] [
+.I -slr
+]
+COLOR ...
+.SH DESCRIPTION
+.I Picker
+is a tool designed to mainly be used by other programs in order to
+change a color palette dynamically, showing changes in real time.
+Colors are supplied as command line argument, each is encoded as
+.I RGB
+(or
+.I RGBA
+if
+.I -a
+option was used)
+in hex form, i.e.
+.I ff0000
+for red in
+.I RGB
+mode, or
+.I ff0000ff
+in
+.I RGBA
+mode.
+.PP
+Different color spaces are provided and accessible through middle
+button menu, or as command line arguments:
+.TP
+.B -s
+HSLuv.
+.TP
+.B -l
+HPLuv.
+.TP
+.B -r
+RGB.
+.PP
+Switching between palette can be done by either a mouse click, or
+left/right arrows on the keyboard. With the right button click you
+can enter color as
+.I RGB(A)
+manually.
+.PP
+For each change of a color, picker writes to stdout its index and the
+new color value, separated by a single space.  Pass
+.I -e
+option if you want
+.I picker
+to print all the colors only once you exit the program, as a single
+line.
+.SH SOURCE
+https://github.com/ftrvxmtrx/picker