Plan 9 from Bell Labs’s /sys/src/games/agui/gui.c

Copyright © 2021 Plan 9 Foundation
Distributed under the MIT License.
Download the Plan 9 distribution.


#include	<audio.h>

/* todo:
 *	scroll bar
 *	get text to do something when selected
 *	get some sort of bsearch on text
 */
#define	Basic	10

#define	Butdx	(Basic*7.0)
#define	Butdy	(Basic*3.0)
#define	Lindx	(Basic*10.0)
#define	Lindy	(Basic*2.0)
//#define	Linbdy	(Basic*1.6)
#define	Linbdy	(font->height)
#define	Border	(Basic*0.5)
#define	Bar	(Basic*1.5)

enum
{

	Color1	= DPaleyellow,		/* yellow */
	Color2	= DDarkyellow,
	Color3	= DPalebluegreen,	/* blue green */
	Color4	= DPalegreygreen,
	Color5	= DYellowgreen,		/* green */
	Color6	= DPurpleblue,
	Color7	= 0x9eee4fFF,		/* lime puke */
	Color	= Color7,

	Butup	= 0,
	Butdn,
	Butcont,
	Butmiss,
	Butinit
};

typedef	struct	Frame	Frame;
typedef	void	Fn(Frame*, int, int, Point*);

struct	Frame
{
	int	type;
	int	param;
	char*	label;
	Fn	*hit;
	Image	fgnd;
	Image*	bgnd;
	int	sel;
};

static	Fn	hitabove;
static	Fn	hitbelow;
static	Fn	hitdown;
static	Fn	hitexit;
static	Fn	hitnow;
static	Fn	hitplaying;
static	Fn	hitqueue;
static	Fn	hitrand;
static	Fn	hitregexp;
static	Fn	hitroot;
static	Fn	hitscroll;
static	Fn	hitskip;
static	Fn	hitup;
static	Fn	hitvol;

static	void	drawlab(Frame*);
static	void	drawllab(Frame*);
static	void	drawshade(Frame*);
static	void	drawabove(Frame*);
static	void	drawbelow(Frame*);
static	void	bhighlight(Frame*, int, Image*);

extern	void	setvolume(int);
extern	int	getvolume(void);

static	Frame*	aboveframe;
static	Frame*	belowframe;
static	Image*	color1;		/* 25% of pure color */
static	Image*	color2;		/* 50% of pure color */
static	Image*	color3;		/* 75% of pure color */
static	Image*	color4;		/* pure color */
static	Frame*	cursongframe;
static	Frame*	lastdown;
static	int	nline;
static	int	obuttons;
static	Frame*	reframe;
static	int	ren;
static	char	restring[100];
static	char	volstring[10];
static	Point	charsize;
static	int	volume;
static	int	firstline;

static
Frame	frame[] =
{
	{ Tlab,0, "<<",		hitup },
	{ Tlab,1, ">>",		hitdown },
	{ Tlab,2, volstring,	hitvol },
	{ Tlab,3, "now",	hitnow },
	{ Tlab,4, "rand",	hitrand },
	{ Tlab,5, "skip",	hitskip },
	{ Tlab,6, "stop",	hitskip },
	{ Tlab,7, "exit",	hitexit },

	{ Tlco,0, "queue>",	hitqueue },
	{ Tlco,1, "root>",	hitroot },

	{ Tmco,0, "playing:",	hitplaying },
	{ Tmco,1, "search:",	hitregexp },

	{ Trco,0, nowplaying,	hitplaying },
	{ Trco,1, restring,	hitregexp },

	{ Tabv,0, "",		hitabove },
	{ Tscr,0, "",		hitscroll },
	{ Tblo,0, "",		hitbelow },
};

void
guiinit(void)
{
	if(initdraw(0, 0, "audio") < 0) {
		print("initdraw %r\n");
		exits("initdraw");
	}
}

void
eresized(int new)
{
	int w, x, y, n, m;
	Frame *p;
	Box r;

	if(new && getwindow(display, Refmesg) < 0) {
		print("can't reattach to window\n");
		exits("reshap");
	}

	for(p=frame; p<frame+nelem(frame); p++) {
		p->fgnd = *screen;
		switch(p->type) {
		default:
			print("unknown frame type %d\n", p->type);
			exits("init");

		case Tlab:		/* top row labels */
			w = p->fgnd.r.max.x - p->fgnd.r.min.x - 2*Border;
			n = p->param;
			m = 8;
			if(volume < 0) {
				m = 7;
				if(n >= 2)
					n--;
			}
			x = p->fgnd.r.min.x + Border +
				(w - Butdx*m) / 2 +n*Butdx;

			y = p->fgnd.r.min.y + Border;
			if(x < 0) {
				print("window not wide enough\n");
				exits("width");
			}
			r = Rect(x,y, x+Butdx,y+Butdy);
			break;
		case Tlco:		/* left column */
			x = p->fgnd.r.min.x + Border;
			y = p->fgnd.r.min.y + Border +
				Butdy + p->param*Lindy;
			r = Rect(x,y, x+Lindx,y+Lindy);
			break;
		case Tmco:		/* middle column */
			x = p->fgnd.r.min.x + Border + Lindx;
			y = p->fgnd.r.min.y + Border + Butdy + p->param*Lindy;
			r = Rect(x,y, x+Lindx,y+Lindy);
			break;
		case Trco:		/* right column */
			x = p->fgnd.r.min.x + Border + Lindx*2;
			y = p->fgnd.r.min.y + Border + Butdy + p->param*Lindy;
			r = Rect(x,y, p->fgnd.r.max.x-Border,y+Lindy);
			break;
		case Tabv:		/* above */
			x = p->fgnd.r.min.x + Border;
			y = p->fgnd.r.min.y + Border + Butdy + 2*Lindy;
			r = Rect(x,y, p->fgnd.r.max.x-Border,y+Lindy);
			break;
		case Tscr:		/* scroll bar */
			x = p->fgnd.r.min.x + Border;
			y = p->fgnd.r.min.y + Border + Butdy + 3*Lindy;
			nline = (p->fgnd.r.max.y - y - Border - 4) / Linbdy;
			r = Rect(x,y, x+Bar,y+nline*Linbdy+4);
			break;
		case Tblo:		/* below */
			x = p->fgnd.r.min.x + Border + Bar;
			y = p->fgnd.r.min.y + Border + Butdy + 3*Lindy;
			if(nline < 10) {
				print("window not tall enough\n");
				exits("height");
			}
			r = Rect(x,y, p->fgnd.r.max.x-Border,y+nline*Linbdy+4);
			break;
		}
		p->fgnd.clipr = r;
	}

	if(color1 == 0) {
		color1 = allocimagemix(display, Color, DWhite);
		color3 = allocimagemix(display, DWhite, Color);
		color4 = allocimage(display, Rect(0,0,1,1), screen->chan, 1, Color);
		if(screen->depth <= 8) {
			/* use stipple pattern */
			color2 = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DWhite);
			draw(color2, Rect(0,0,1,1), color4, nil, ZP);
			draw(color2, Rect(1,1,2,2), color4, nil, ZP);
		} else {
			/* use solid color, blended using alpha */
			Image *mask;
			mask = allocimage(display, Rect(0,0,1,1), GREY8, 1, 0x7F7F7FFF);
			color2 = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DWhite);
			draw(color2, color2->r, color4, mask, ZP);
		}
	}

	if(lastdown && obuttons)
		lastdown->hit(lastdown, obuttons, Butmiss, 0);
	lastdown = 0;
	obuttons = 0;
	firstline = 0;
	draw(screen, screen->r, display->white, nil, ZP);
	for(p=frame; p<frame+nelem(frame); p++)
		p->hit(p, 0, Butinit, 0);

	if(reframe == 0 ||
	   cursongframe == 0 ||
	   aboveframe == 0 ||
	   belowframe == 0) {
		print("no frame\n");
		exits("re");
	}
}

void
mkgui(void)
{
	volume = getvolume();
	charsize = stringsize(font, "t");
	eresized(0);
}

int
inside(Box r, Point p)
{

	if(p.x < r.min.x ||
	   p.x >= r.max.x ||
	   p.y < r.min.y ||
	   p.y >= r.max.y)
		return 0;
	return 1;
}

void
guimouse(Mouse m)
{
	int b, x;
	Frame *p;

	b = m.buttons & (But1|But2|But3);
	if(obuttons == 0 && b == 0)
		return;

	for(p=frame; p < frame+nelem(frame); p++)
		if(inside(p->fgnd.clipr, m.xy))
			break;
	if(p >= frame+nelem(frame))
		p = 0;

/*
 * state machine for each frame for each button.
 *	Butinit (Butdn Butcont* (Butup|Butmiss))*
 */

	if(lastdown != p) {
		/* here mouse not in same window */
		if(lastdown && obuttons)
			lastdown->hit(lastdown, obuttons, Butmiss, 0);
		lastdown = 0;
		if(p && b) {
			p->hit(p, b, Butdn, &m.xy);
			lastdown = p;
		}
	} else
	if(p) {
		/* here mouse in same window */
		if(x = b & obuttons)	/* button continuing */
			p->hit(p, x, Butcont, &m.xy);
		if(x = obuttons & ~b)	/* button release */
			p->hit(p, x, Butup, &m.xy);
		if(x = b & ~obuttons)	/* button down */
			p->hit(p, x, Butdn, &m.xy);
	}
	obuttons = b;
}

void
guikbd(int c)
{
	switch(c) {
	case 'U'-'@':
		ren = 0;
		break;

	case '\b':
		if(ren > 0)
			ren--;
		break;

	case '\n':
		resetline = 1;
		execute(restring);
		return;

	default:
		restring[ren] = c;
		if(ren < nelem(restring)-1)
			ren++;
	}
	restring[ren] = 0;
	drawllab(reframe);
}

void
guiupdate(ulong u)
{

	if(u & Dreshape)
		eresized(0);
	if(u & Dvolume)
		if(volume >= 0)
			setvolume(volume);
	if(u & Dcursong)
		drawllab(cursongframe);
	if(u & Dselect) {
		firstline = 0;
		drawabove(aboveframe);
		drawbelow(belowframe);
	}
	if(u & Dscroll)
		drawbelow(belowframe);
}

static	void
hitup(Frame *f, int, int a, Point*)
{
	int n;

	switch(a) {
	case Butdn:
		f->bgnd = color2;
		break;
	case Butup:
		abovdisp++;
		if(abovdisp >= nabove)
			abovdisp--;
		n = nabove - abovdisp - 1;
		if(n >= 0 && n < nabove)
			expand(above[n], 0);
	case Butmiss:
	case Butinit:
		f->bgnd = color1;
		break;
	}
	if(a != Butcont)
		drawlab(f);
}

static	void
hitdown(Frame *f, int, int a, Point*)
{
	int n;

	switch(a) {
	case Butdn:
		f->bgnd = color2;
		break;
	case Butup:
		abovdisp--;
		if(abovdisp < 0)
			abovdisp++;
		n = nabove - abovdisp - 1;
		if(n >= 0 && n < nabove)
			expand(above[n], 0);
	case Butmiss:
	case Butinit:
		f->bgnd = color1;
		break;
	}
	if(a != Butcont)
		drawlab(f);
}

static	void
hitvol(Frame *f, int, int a, Point *p)
{
	int n;

	if(volume < 0)
		return;
	n = volume;
	if(p) {
		n = f->fgnd.clipr.min.x;
		n = (p->x - n) * 100 / (f->fgnd.clipr.max.x - n);
	}
	if(n < 0)
		n = 0;
	if(n > 99)
		n = 99;

	switch(a) {
	case Butdn:
		f->bgnd = color2;
		break;
	case Butup:
		volume = n;
		update |= Dvolume;
		f->bgnd = color1;
		break;
	case Butinit:
	case Butmiss:
		f->bgnd = color1;
		n = volume;
		break;
	}
	strcpy(volstring, "vol  X");
	if(n >= 10)
		volstring[4] = n/10 + '0';
	volstring[5] = n%10 + '0';
	drawlab(f);
}

static	void
hitnow(Frame *f, int, int a, Point*)
{
	switch(a) {
	case Butcont:
		return;
	case Butdn:
		f->bgnd = color2;
		break;
	case Butup:
		now = !now;
	case Butmiss:
	case Butinit:
		f->bgnd = color1;
		if(now)
			f->bgnd = color3;
		break;
	}
	drawlab(f);
}

static	void
hitrand(Frame *f, int, int a, Point*)
{
	switch(a) {
	case Butcont:
		return;
	case Butdn:
		f->bgnd = color2;
		break;
	case Butup:
		random = !random;
	case Butmiss:
	case Butinit:
		f->bgnd = color1;
		if(random)
			f->bgnd = color3;
		break;
	}
	drawlab(f);
}

static	void
hitskip(Frame *f, int, int a, Point*)
{
	switch(a) {
	case Butcont:
		return;
	case Butdn:
		f->bgnd = color2;
		break;
	case Butup:
		skipsong(strcmp(f->label, "skip"));
	case Butmiss:
	case Butinit:
		f->bgnd = color1;
		break;
	}
	drawlab(f);
}

static	void
hitexit(Frame *f, int, int a, Point*)
{
	switch(a) {
	case Butcont:
		return;
	case Butdn:
		f->bgnd = color2;
		break;
	case Butup:
		doexit();
	case Butmiss:
	case Butinit:
		f->bgnd = color1;
		break;
	}
	drawlab(f);
}

static	void
hitqueue(Frame *f, int b, int a, Point*)
{
	switch(a) {
	case Butcont:
		return;
	case Butdn:
		f->bgnd = color2;
		break;
	case Butup:
		if(b & But1)
			expand(Oqueue, 1);
		if(b & But2)
			play(Oqueue);
		if(b & But3)
			mark(Oqueue);
	case Butmiss:
	case Butinit:
		f->bgnd = color1;
		break;
	}
	drawllab(f);
}

static	void
hitplaying(Frame *f, int b, int a, Point*)
{
	switch(a) {
	case Butcont:
		return;
	case Butdn:
		f->bgnd = color2;
		break;
	case Butup:
		if(b & But1)
			expand(Oplaying, 1);
		if(b & But2)
			play(Oplaying);
		if(b & But2)
			mark(Oplaying);
	case Butmiss:
		f->bgnd = color1;
		break;
	case Butinit:
		cursongframe = f;
		f->bgnd = color1;
		break;
	}
	drawllab(f);
}

static	void
hitroot(Frame *f, int b, int a, Point*)
{
	switch(a) {
	case Butcont:
		return;
	case Butdn:
		f->bgnd = color2;
		break;
	case Butup:
		if(b & But1)
			expand(Oroot, 1);
		if(b & But2)
			play(Oroot);
		if(b & But3)
			mark(Oroot);
	case Butmiss:
	case Butinit:
		f->bgnd = color1;
		break;
	}
	drawllab(f);
}

static	void
hitregexp(Frame *f, int, int a, Point*)
{
	switch(a) {
	case Butcont:
		return;
	case Butdn:
		f->bgnd = color2;
		break;
	case Butup:
	case Butmiss:
		f->bgnd = color1;
		break;
	case Butinit:
		reframe = f;
		f->bgnd = color1;
		break;
	}
	drawllab(f);
}

static	void
hitabove(Frame *f, int b, int a, Point*)
{
	int n;
	long o;

	switch(a) {
	case Butcont:
		return;
	case Butdn:
		f->bgnd = color2;
		break;
	case Butup:
		n = nabove - abovdisp - 1;
		if(n >= 0 && n < nabove) {
			o = above[n];
			if(b & But1)
				expand(o, 1);
			if(b & But2)
				play(o);
			if(b & But3)
				mark(o);
		}
	case Butmiss:
		f->bgnd = color1;
		break;
	case Butinit:
		aboveframe = f;
		f->bgnd = color1;
		break;
	}
	drawabove(f);
}

static	void
hitscroll(Frame *f, int b, int a, Point *p)
{
	int n, fl;

	n = nbelow;
	if(p)
		n = (p->y - f->fgnd.clipr.min.y) / Linbdy;
	if(n < 0)
		n = nbelow;

	switch(a) {
	case Butup:
		fl = firstline;
		if(b & But1)
			fl = firstline - n;
		if(b & But2)
			fl = n;
		if(b & But3)
			fl = firstline + n;
		if(fl > nbelow-1)
			fl = nbelow-1;
		if(fl < 0)
			fl = 0;
		if(fl != firstline) {
			firstline = fl;
			update |= Dscroll;
		}
		break;
	}
}

static	void
hitbelow(Frame *f, int b, int a, Point *p)
{
	int n;
	long o;

	n = nbelow;
	if(p)
		n = (p->y - f->fgnd.clipr.min.y) / Linbdy;
	if(n < 0)
		n = nbelow;
	n += firstline;

	switch(a) {
	case Butdn:
		f->sel = n;
		bhighlight(f, n, color2);
		break;
	case Butup:
		if(n < nbelow) {
			o = below[n];
			if(b & But1)
				expand(o, 1);
			if(b & But2)
				play(o);
			if(b & But3)
				mark(o);
		}
	case Butmiss:
		bhighlight(f, f->sel, color1);
		break;
	case Butcont:
		if(n != f->sel) {
			bhighlight(f, f->sel, color1);
			f->sel = n;
			bhighlight(f, n, color2);
		}
		break;
	case Butinit:
		f->bgnd = color1;
		belowframe = f;
		drawbelow(f);
		break;
	}
}

static	void
drawshade(Frame *f)
{
	Box r;

	r = f->fgnd.clipr;
	draw(&f->fgnd, r, f->bgnd, nil, r.min);
	draw(&f->fgnd,
		Rect(r.min.x,r.min.y, r.min.x+1,r.max.y),
		display->white, nil, ZP);
	draw(&f->fgnd,
		Rect(r.min.x,r.min.y, r.max.x,r.min.y+1),
		display->white, nil, ZP);
	draw(&f->fgnd,
		Rect(r.max.x-1,r.min.y, r.max.x,r.max.y),
		display->black, nil, ZP);
	draw(&f->fgnd,
		Rect(r.min.x,r.max.y-1, r.max.x,r.max.y),
		display->black, nil, ZP);
}

static	void
drawlab(Frame *f)
{
	Point pt;

	drawshade(f);
	pt = stringsize(font, f->label);
	pt.x = f->fgnd.clipr.min.x +
		(f->fgnd.clipr.max.x -
		f->fgnd.clipr.min.x - pt.x) / 2;
	pt.y = f->fgnd.clipr.min.y +
		(f->fgnd.clipr.max.y -
		f->fgnd.clipr.min.y - pt.y) / 2;
	string(&f->fgnd, pt, display->black, ZP, font, f->label);
}

static	void
drawllab(Frame *f)
{
	Point pt;

	drawshade(f);
	pt.x = f->fgnd.clipr.min.x + charsize.x;
	pt.y = f->fgnd.clipr.min.y + (f->fgnd.clipr.max.y - f->fgnd.clipr.min.y - charsize.y) / 2;
	string(&f->fgnd, pt, display->black, ZP, font, f->label);
}

static	void
drawabove(Frame *f)
{
	int i;

	drawshade(f);
	i = nabove - abovdisp - 1;
	if(i >= 0 && i < nabove) {
		aboveframe->label = tstring(above[i]);
		drawllab(f);
	}
}

static	void
drawbelow(Frame *f)
{
	int i;

	drawshade(f);
	for(i=0; i<nline; i++)
		bhighlight(f, i+firstline, 0);
}

static	void
bhighlight(Frame *f, int n, Image *color)
{
	Box r, t;

	if(n < 0 || n >= nbelow ||
	   n < firstline || n >= nline+firstline)
		return;

	r.min.x = f->fgnd.clipr.min.x;
	r.min.y = f->fgnd.clipr.min.y +
		(Linbdy - charsize.y) / 2 +
		(n-firstline)*Linbdy;
	r.max.x = f->fgnd.clipr.max.x - 2;
	r.max.y = r.min.y + Linbdy;
	if(color) {
		t = r;
		t.min.x += 1;
		if(n == firstline)
			t.min.y += 1;
		if(n == nline+firstline-1)
			t.max.y -= 1;
		draw(&f->fgnd, t, color, nil, t.min);
	}
	r.min.x += charsize.x;
	string(&f->fgnd, r.min, display->black, ZP, font, tstring(below[n]));
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@plan9.bell-labs.com.