XXIIVV

Themes are the standard interface customization for the Hundred Rabbits software.

A theme file is a plain-text .svg with 9 colors, the reason for using a svg file as source is that on most operating system, a preview of the theme color will be generated via the file manager.

Applications supporting the themes ecosystem include Orca, Left, Nasu, Dotgrid and many more.

Nine colors should be more than enough for any interface.

Example

A file should include 9 hex codes of 6 characters, in that specific order to be a valid theme.

<svg width="96px" height="64px" xmlns="http://www.w3.org/2000/svg" baseProfile="full" version="1.1">
	<rect width='96' height='64'  id='background' fill='#E0B1CB'></rect>
	<!-- Foreground -->
	<circle cx='24' cy='24' r='8' id='f_high' fill='#231942'></circle>
	<circle cx='40' cy='24' r='8' id='f_med' fill='#48416d'></circle>
	<circle cx='56' cy='24' r='8' id='f_low' fill='#917296'></circle>
	<circle cx='72' cy='24' r='8' id='f_inv' fill='#E0B1CB'></circle>
	<!-- Background -->
	<circle cx='24' cy='40' r='8' id='b_high' fill='#5E548E'></circle>
	<circle cx='40' cy='40' r='8' id='b_med' fill='#FFFFFF'></circle>
	<circle cx='56' cy='40' r='8' id='b_low' fill='#BE95C4'></circle>
	<circle cx='72' cy='40' r='8' id='b_inv' fill='#9F86C0'></circle>
</svg>
themes_demo.svg 13 lines

Plan9

Below is the Plan9 implementation, the stdout is a sequence of 9 hex strings. Usage is simply themes9 ../themes/apollo.svg

#include <u.h>
#include <libc.h>
#include <draw.h>
#include <event.h>

#define BUFLEN 1024

long theme[9];
Image *bg,
    *f_high, *f_med, *f_low, *f_inv,
    *b_high, *b_med, *b_low, *b_inv;

int
slen(char* s)
{
	int n = 0;
	while(s[n] != '\0' && s[++n])
		;
	return n;
}

char*
sstr(char* src, char* dest, int from, int to)
{
	int i;
	char *a = (char*)src + from, *b = (char*)dest;
	for(i = 0; i < to; i++)
		b[i] = a[i];
	dest[to] = '\0';
	return dest;
}

unsigned char
chex(char c)
{
	if(c >= 'a' && c <= 'f')
		return 10 + c - 'a';
	if(c >= 'A' && c <= 'F')
		return 10 + c - 'A';
	return (c - '0') & 0xF;
}

unsigned long
shex(char* s)
{
	int i, n = 0, l = slen(s);
	for(i = 0; i < l; ++i)
		n |= (chex(s[i]) << ((l - i - 1) * 4));
	return n;
}

void
parse(int fd)
{
	int i, id = 0;
	char buf[BUFLEN], hexs[8];

	read(fd, buf, BUFLEN);

	for(i = 0; i < BUFLEN; ++i) {
		if(buf[i] != '#')
			continue;
		print("#%s ", sstr(buf, hexs, i + 1, 6));
		theme[id] = (shex(hexs) << 8) + 255;
		id++;
	}

	if(id != 9)
		print("Invalid theme");

	close(fd);

	bg = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[0]);
	f_high = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[1]);
	f_med = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[2]);
	f_low = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[3]);
	f_inv = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[4]);
	b_high = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[5]);
	b_med = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[6]);
	b_low = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[7]);
	b_inv = allocimage(display, Rect(0, 0, 1, 1), RGB24, 1, theme[8]);
}

void
redraw(Image* dst)
{
	int size = 20;
	Point s = subpt(screen->r.max, screen->r.min);
	Point c = divpt(s, 2);
	Point m = addpt(screen->r.min, c);
	Point o = addpt(m, (Point){-size * 4, -size * 2});
	draw(screen, screen->r, bg, nil, ZP);

	fillellipse(screen,
	            addpt(o, (Point){size, size}),
	            size, size, f_high, ZP);
	fillellipse(screen,
	            addpt(o, (Point){size * 3, size}),
	            size, size, f_med, ZP);
	fillellipse(screen,
	            addpt(o, (Point){size * 5, size}),
	            size, size, f_low, ZP);
	fillellipse(screen,
	            addpt(o, (Point){size * 7, size}),
	            size, size, f_inv, ZP);
	fillellipse(screen,
	            addpt(o, (Point){size, size * 3}),
	            size, size, b_high, ZP);
	fillellipse(screen,
	            addpt(o, (Point){size * 3, size * 3}),
	            size, size, b_med, ZP);
	fillellipse(screen,
	            addpt(o, (Point){size * 5, size * 3}),
	            size, size, b_low, ZP);
	fillellipse(screen,
	            addpt(o, (Point){size * 7, size * 3}),
	            size, size, b_inv, ZP);
}

void
eresized(int new)
{
	Rectangle r;
	r = screen->r;
	if(new&& getwindow(display, Refnone) < 0)
		fprint(2, "can't reattach to window");
	redraw(screen);
}

void
main(int argc, char** argv)
{
	int fd;
	Mouse m;

	initdraw(0, 0, "Themes");

	if(argc == 1)
		fd = 0;
	else if((fd = open(argv[1], OREAD)) < 0)
		perror(argv[1]);
	parse(fd);

	eresized(0);
	einit(Emouse);

	/* Break on mouse3 */
	for(;;) {
		m = emouse();
		if(m.buttons & 4)
			break;
	}
}
themes9.c 152 lines

POSIX Filter

The follow utility prints the 9 colors to stdout.

#include <stdio.h>

#define BUFLEN 256

int
slen(char* s)
{
	int n = 0;
	while(s[n] != '\0' && s[++n])
		;
	return n;
}

int
cpos(char* s, char c)
{
	int i;
	for(i = 0; i < slen(s); i++)
		if(s[i] == c)
			return i;
	return -1;
}

char*
sstr(char* src, char* dest, int from, int to)
{
	int i;
	char *a = (char*)src + from, *b = (char*)dest;
	for(i = 0; i < to; i++)
		b[i] = a[i];
	dest[to] = '\0';
	return dest;
}

unsigned char
chex(char c)
{
	if(c >= 'a' && c <= 'f')
		return 10 + c - 'a';
	if(c >= 'A' && c <= 'F')
		return 10 + c - 'A';
	return (c - '0') & 0xF;
}

unsigned long
shex(char* s)
{
	int i, n = 0, l = slen(s);
	for(i = 0; i < l; ++i)
		n |= (chex(s[i]) << ((l - i - 1) * 4));
	return n;
}

int
error(char* name)
{
	printf("Error: %s\n", name);
	return 0;
}

int
parse(FILE* f)
{
	int i, id = 0;
	long theme[9];
	char line[BUFLEN], hexs[BUFLEN];
	while(fgets(line, BUFLEN, f)) {
		int split = cpos(line, '#');
		if(split < 0 || id > 9)
			continue;
		sstr(line, hexs, split, 7);
		printf("%s ", hexs);
		theme[id] = shex(hexs + 1);
		id++;
	}
	if(id != 9)
		return error("Invalid theme");
	for(i = 0; i < 9; ++i) {
		printf("%ld ", theme[i]);
	}
	return 1;
}

int
main(int argc, char* argv[])
{
	FILE* input;
	if(argc == 2) {
		input = fopen(argv[1], "rb");
		if(input == NULL)
			return error("Invalid input.\n");
	} else
		input = stdin;
	return parse(input);
}
themes.c 94 lines

incoming(4): ronin dotgrid left nasu