shithub: mez

ref: a959605dff6d7ffffbca9441db4e57b98b261b71
dir: /guitest.c/

View raw version
#include <u.h>
#include <libc.h>
#include <draw.h>
#include <cursor.h>
#include <thread.h>
#include <mouse.h>
#include <keyboard.h>
#include <frame.h>
#include "mez.h"

Image *nicksscreen, *cmdscreen, *nickscols[NCOL], *cols[NCOL], *cmdcols[NCOL];
char *nickswsys = "/mnt/nickswsys", *cmdwsys = "/mnt/cmdwsys";
Screen *_nicksscreen, *_cmdscreen;

char buf[32000];
Rune lorem[32000];

enum
{
	MOUSE,
	KEYBD,
	RESIZE,
	NICKSMOUSE,
	NICKSKEYBD,
	NICKSRESIZE,
	CMDMOUSE,
	CMDKEYBD,
	CMDRESIZE,
	NCHAN,

	Scrollwid = 12,		/* width of scroll bar */
	Scrollgap = 4,		/* gap right of scroll bar */
	Margin = 4,	/* margin around text */
};

void
error(char *s)
{
	fprint(2, "%s: error: %s: %r\n", argv0, s);
	threadexits(s);
}

void*
emallocz(ulong sz, int clr)
{
	void *p;

	if((p = mallocz(sz, clr)) == nil)
		error("Out of memory here");
	setmalloctag(p, getcallerpc(&sz));
	return p;
}

void*
erealloc(void *ptr, ulong size)
{
	void *p;

	if((p = realloc(ptr, size)) == nil)
		error("Out of memory there");
	setmalloctag(p, getcallerpc(&ptr));
	return p;
}

uint
lines(Rune *text)
{
	Rune *e;
	uint l;

	e = text;
	for(l = 0; e != nil; l++)
		e = runestrchr(e+1, L'\n');
	return l;
}

#define max(a, b) ((a)>(b)?(a):(b))
#define min(a, b) ((a)<(b)?(a):(b))

void
textdraw(Text *t)
{
	Rectangle scrpos;
	Image *screen;

	screen = *t->screen;
	draw(screen, screen->r, t->cols[BACK], nil, ZP);
	draw(screen, t->scrollr, t->cols[BORD], nil, ZP);

	scrpos = t->scrollr;
	if(t->nlines > 0){
		scrpos.min.y = scrpos.min.y+max(0, Dy(scrpos))*t->origin/t->nlines;
		scrpos.max.y = scrpos.min.y+Dy(t->scrollr)*t->Frame.maxlines/t->nlines;
	}
	scrpos = insetrect(scrpos, 1);
	draw(screen, scrpos, t->cols[BACK], nil, ZP);

	frinsert(t, t->text+t->lines[t->origin], t->text+t->textlen, 0);
	flushimage(display, 1);
}

void
textinit(Text *t)
{
	Image *screen;

	screen = *t->screen;
	t->scrollr = screen->r;
	t->scrollr.max.x = screen->r.min.x + Scrollwid + 1;

	t->bodyr.min.x = screen->r.min.x + Scrollwid + Scrollgap + Margin;
	t->bodyr.min.y = screen->r.min.y;
	t->bodyr.max = screen->r.max;

	if(t->text == nil){
		t->textcap = 8192;
		t->text = emallocz(t->textcap*sizeof(*t->text), 1);
		t->textlen = 0;
	}

	if(t->lines == nil){
		t->linescap = 1024;
		t->lines = emallocz(t->linescap*sizeof(*t->lines), 1);
		t->nlines = 0;
		t->origin = 0;
	}

	frclear(t, 0);
	frinit(t, t->bodyr, display->defaultfont, screen, t->cols);
}

void
createwindow(char *wsys, Image **screen, Screen **_screen, Rectangle r)
{
	static char s[512];
	char *wsysv;
	int fd;

	if((wsysv = getenv("wsys")) == nil)
		sysfatal("cannot find $wsys: %r");
	if((fd = open(wsysv, ORDWR)) < 0)
		sysfatal("cannot open $wsys: %r");
	free(wsysv);
	snprint(s, sizeof(s), "new -r %d %d %d %d", r.min.x, r.min.y, r.max.x, r.max.y);
	if(mount(fd, -1, wsys, MREPL, s) < 0)
		sysfatal("cannot create new window: %r");
	close(fd);
	snprint(s, sizeof(s), "%s/label", wsys);
	if((fd = open(s, OWRITE)) < 0)
		sysfatal("cannot open label: %r");
	write(fd, "guitest", sizeof("guitest"));
	close(fd);
	snprint(s, sizeof(s), "%s/winname", wsys);
	if(gengetwindow(display, s, screen, _screen, Refnone) < 0)
		sysfatal("cannot get window: %r");
}

void
textsetline(Text *t, uint sol)
{
	if(t->nlines == t->linescap){
		t->linescap *= 2;
		t->lines = erealloc(t->lines, t->linescap*sizeof(*t->lines));
	}
	t->lines[t->nlines++] = sol;
}

void
textaddlines(Text *t, Rune *s)
{
	Rune *rp;
	uint sol;
	int len;

	len = runestrlen(s);
	while(len > t->textcap-t->textlen+1){
		t->textcap *= 2;
		t->text = erealloc(t->text, t->textcap*sizeof(*t->text));
	}
	runestrecpy(t->text+t->textlen, t->text+t->textcap, s);
	sol = t->textlen;
	for(rp = s; rp = runestrchr(rp, L'\n'); rp++){
		textsetline(t, sol);
		sol = rp + 1 - s + t->textlen;
	}
	t->textlen += len;
}

void
textresize(Text *t)
{
	static char s[512];

	snprint(s, sizeof(s), "%s/winname", t->wsys);
	if(gengetwindow(display, s, t->screen, t->_screen, Refnone) < 0)
		sysfatal("%s: %r", argv0);
	textinit(t);
	textdraw(t);
}

void
textscroll(Text *t, Mousectl *mc)
{
	uint pos, i, diff;

	while(mc->buttons == 2){
		t->origin = min(max(0, mc->xy.y-t->scrollr.min.y)*t->nlines/Dy(t->scrollr), t->nlines-1);
		textdraw(t);
		readmouse(mc);
	}
	while(mc->buttons == 1){
		pos = frcharofpt(t, (Point){t->bodyr.min.x+1, mc->xy.y});
		for(i = t->origin; t->lines[t->origin]+pos > t->lines[i]; i++);
		t->origin -= i-t->origin;
		textdraw(t);
		readmouse(mc);
	}
	while(mc->buttons == 4){
		pos = frcharofpt(t, (Point){t->bodyr.min.x+1, mc->xy.y});
		for(i = t->origin; t->lines[t->origin]+pos > t->lines[i]; i++);
		t->origin = min(i, t->nlines-1);
		textdraw(t);
		readmouse(mc);
	}
}

void
textmouse(Text *t, Mousectl *mc)
{
	if(ptinrect(mc->xy, t->scrollr)){
		textscroll(t, mc);
		return;
	}
}

void
initmousekbd(char *wsys, Image *screen, Mousectl **mc, Keyboardctl **kc)
{
	static char s[512];

	snprint(s, sizeof(s), "%s/mouse", wsys);
	if((*mc = initmouse(s, screen)) == nil)
		sysfatal("initmouse failed: %r");
	snprint(s, sizeof(s), "%s/cons", wsys);
	if((*kc = initkeyboard(s)) == nil)
		sysfatal("initkeyboard failed: %r");
}

void
threadmain(int argc, char **argv)
{
	Chan *chan;
	Keyboardctl *kc, *nickskc, *cmdkc;
	Mousectl *mc, *nicksmc, *cmdmc;
	Rune r, *rs;
	Rectangle dim;
	Text *cmd;
	char *s, *nicksf;
	int len, fd;

	nicksf = nil;
	ARGBEGIN{
	default:
		sysfatal("foo");
	case 'n':
		nicksf = EARGF(sysfatal("foo"));
		break;
	}ARGEND

	len = readn(0, buf, sizeof(buf));
	buf[len] = '\0';
	for(s = buf, rs = lorem; *s != '\0'; s+=len, rs++)
		len = chartorune(rs, s);
	*rs = L'\0';

	if(initdraw(nil, nil, "guitest") < 0)
		sysfatal("initdraw: %r");

	cols[BACK] = allocimagemix(display, DPurpleblue, DWhite);
	cols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
	cols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
	cols[TEXT] = display->black;
	cols[HTEXT] = display->black;
	nickscols[BACK] = allocimagemix(display, DCyan, DWhite);
	nickscols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
	nickscols[BORD] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPurpleblue);
	nickscols[TEXT] = display->black;
	nickscols[HTEXT] = display->black;
	cmdcols[BACK] = allocimagemix(display, DPalebluegreen, DWhite);
	cmdcols[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DPalegreygreen);
	cmdcols[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DPurpleblue);
	cmdcols[TEXT] = display->black;
	cmdcols[HTEXT] = display->black;

	chan = emallocz(sizeof(*chan), 1);

	memmove(chan->body.cols, cols, sizeof(cols));
	chan->body.screen = &screen;
	chan->body._screen = &_screen;
	chan->body.wsys = "/dev";
	textinit(&chan->body);
	textaddlines(&chan->body, lorem);
	lorem[0] = L'\0';

	if(nicksf != nil){
		if((fd = open(nicksf, OREAD)) < 0)
			sysfatal("Could not open nicks file");
		len = readn(fd, buf, sizeof(buf));
		close(fd);
		buf[len] = '\0';
		for(s = buf, rs = lorem; *s != '\0'; s+=len, rs++)
			len = chartorune(rs, s);
		*rs = L'\0';
	}

	dim = (Rectangle){
		(Point){screen->r.min.x - Dx(screen->r)/3, screen->r.min.y-4},
		(Point){screen->r.min.x-4, screen->r.max.y+4}
	};
	createwindow(nickswsys, &nicksscreen, &_nicksscreen, dim);

	memmove(chan->nicks.cols, nickscols, sizeof(nickscols));
	chan->nicks.screen = &nicksscreen;
	chan->nicks._screen = &_nicksscreen;
	chan->nicks.wsys = nickswsys;
	textinit(&chan->nicks);
	textaddlines(&chan->nicks, lorem);

	cmd = emallocz(sizeof(*cmd), 1);
	dim = (Rectangle){
		(Point){screen->r.min.x - Dx(screen->r)/3, screen->r.min.y-Dy(screen->r)/4},
		(Point){screen->r.max.x+4, screen->r.min.y-4}
	};
	createwindow(cmdwsys, &cmdscreen, &_cmdscreen, dim);
	memmove(cmd->cols, cmdcols, sizeof(cmdcols));
	cmd->screen = &cmdscreen;
	cmd->_screen = &_cmdscreen;
	cmd->wsys = cmdwsys;
	textinit(cmd);

	textdraw(&chan->body);
	textdraw(&chan->nicks);
	textdraw(cmd);
	flushimage(display, 1);

	initmousekbd("/dev", screen, &mc, &kc);
	initmousekbd(nickswsys, nicksscreen, &nicksmc, &nickskc);
	initmousekbd(cmdwsys, cmdscreen, &cmdmc, &cmdkc);

	Alt a[NCHAN+1] = {
		[MOUSE] {mc->c, nil, CHANRCV},
		[KEYBD] {kc->c, &r, CHANRCV},
		[RESIZE] {mc->resizec, nil, CHANRCV},
		[NICKSMOUSE] {nicksmc->c, nil, CHANRCV},
		[NICKSKEYBD] {nickskc->c, &r, CHANRCV},
		[NICKSRESIZE] {nicksmc->resizec, nil, CHANRCV},
		[CMDMOUSE] {cmdmc->c, nil, CHANRCV},
		[CMDKEYBD] {cmdkc->c, &r, CHANRCV},
		[CMDRESIZE] {cmdmc->resizec, nil, CHANRCV},
		[NCHAN] {nil, nil, CHANEND},
	};
	for(;;)switch(alt(a)){
	default:
		break;
	case NICKSKEYBD:
	case CMDKEYBD:
	case KEYBD:
		if(r == Kdel)
			goto end;
		break;
	case MOUSE:
		textmouse(&chan->body, mc);
		break;
	case NICKSMOUSE:
		textmouse(&chan->nicks, nicksmc);
		break;
	case CMDMOUSE:
		textmouse(cmd, cmdmc);
		break;
	case RESIZE:
		textresize(&chan->body);
		break;
	case NICKSRESIZE:
		textresize(&chan->nicks);
		break;
	case CMDRESIZE:
		textresize(cmd);
		break;
	}
end:
	unmount(nil, nickswsys);
	unmount(nil, cmdwsys);
	threadexitsall(0);
}