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