Plan 9 from Bell Labs’s /usr/web/sources/contrib/yk/contrib-patch/gui.c

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


#include <u.h>
#include <libc.h>
#include <thread.h>
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <control.h>
#include <bio.h>

typedef struct
{
	char	**s;
	int	n;
}Lines;

typedef struct
{
	char	*name;
	char	*user;
	Lines	detail[4];
}Pkg;

typedef struct
{
	char		*cmd;
	int		p[2];		/* p[1] is write to program; p[0] set to prog fd 0*/
	int		q[2];		/* q[0] is read from program; q[1] set to prog fd 1 */
	Channel	*sync;	/* chan(ulong) */
}Exec;

char *action[] = {
	"Install",
	"Exit",
};

enum {
	Info,
	Files,
	Depends,
	Changes,
};

char *detail[] = {
	"Info",
	"Files",
	"Depends",
	"Changes",
};

#define STACK 1024*16
int	mainstacksize = STACK;
int	ctldeletequits = 1;

Controlset *cs;
Channel *cevent, *cstatus;
Pkg	*pkg;
int npkg;
int nlog;
Pkg *curpkg;

void
status(char *fmt, ...)
{
	va_list arg;
	char *p;

	va_start(arg, fmt);
	p = vsmprint(fmt, arg);
	va_end(arg);
	chanprint(cs->ctl, "log_text add %q", p);
	chanprint(cs->ctl, "log_text scroll 1");
	chanprint(cs->ctl, "log_scroll max %d", ++nlog);
	free(p);
}

void
addline(Lines *l, char *s)
{
	if(l->n%16 == 0)
		l->s = ctlrealloc(l->s, (l->n+16)*sizeof(char*));
	l->s[l->n++] = ctlstrdup(s);
}

void
execproc(void *v)
{
	Channel *sync;
	Exec *e;
	int p[2], q[2];
	char *cmd;

	threadsetname("execproc");
	e = v;
	p[0] = e->p[0];
	p[1] = e->p[1];
	q[0] = e->q[0];
	q[1] = e->q[1];
	cmd = e->cmd;
	sync = e->sync;
	rfork(RFFDG);
	free(e);
	dup(p[0], 0);
	close(p[0]);
	close(p[1]);
	if(q[0]){
		dup(q[1], 1);
		close(q[0]);
		close(q[1]);
	}
	if(1)
		close(2);
	procexecl(sync, "/bin/rc", "rc", "-c", cmd, 0);
	sysfatal("can't exec");
}

void
procruncmd(void *v)
{
	Exec *e;
	Channel *sync, *chan;
	Biobuf bi;
	char *s, *cmd;
	int p[2], q[2];
	void **a;
	
	a = v;
	cmd = a[0];
	chan = a[1];
	free(a);
	if(pipe(p)<0 || pipe(q)<0)
		sysfatal("can't create pipe: %r");

	sync = chancreate(sizeof(ulong), 0);
	if(sync == nil)
		sysfatal("can't create channel: %r");

	e = ctlmalloc(sizeof(Exec));
	e->p[0] = p[0];
	e->p[1] = p[1];
	e->q[0] = q[0];
	e->q[1] = q[1];
	e->cmd = cmd;
	e->sync = sync;
	proccreate(execproc, e, STACK);
	recvul(sync);
	chanfree(sync);
	close(p[0]);
	close(p[1]);
	close(q[1]);
	Binit(&bi, q[0], OREAD);
	while((s=Brdstr(&bi, '\n', 1)) != nil)
		sendp(chan, s);
	Bterm(&bi);
	sendp(chan, nil);
	close(q[0]);
	free(s);
}

void
runcmd(char *cmd, Channel* chan)
{
	void **a;
	
	a = ctlmalloc(sizeof(void*)*2);
	a[0] = cmd;
	a[1] = chan;
	proccreate(procruncmd, a, STACK);
}

void
getfile(int f)
{
	Lines *l;
	char *cmd, *s;
	Channel *c;

	assert(curpkg != nil);
	l = &curpkg->detail[f];
	if(l->n != 0)
		return;

	status("fetching %s for packages %s/%s", detail[f], curpkg->user, curpkg->name);
	cmd = nil;
	switch(f){
	case Info:
		cmd = smprint("contrib/cat /n/sources/contrib/%s/replica/%s/inf | fmt", curpkg->user, curpkg->name);
		break;
	case Files:
		cmd = smprint("contrib/cat /n/sources/contrib/%s/replica/%s/db", curpkg->user, curpkg->name);
		break;
	case Depends:
		cmd = smprint("contrib/cat /n/sources/contrib/%s/replica/%s/dep", curpkg->user, curpkg->name);
		break;
	case Changes:
		cmd = smprint("contrib/cat /n/sources/contrib/%s/replica/%s/chg", curpkg->user, curpkg->name);
		break;
	}
	c = chancreate(sizeof(char *), 0);
	runcmd(cmd, c);
	while((s=recvp(c)) != nil)
		addline(l, s);
	chanfree(c);
}

void
setdetail(int d)
{
	Lines *l;
	int i;

	chanprint(cs->ctl, "detail_text clear");
	for(i=0; i<nelem(detail); i++)
		chanprint(cs->ctl, "%s value %d", detail[i], i==d? 1: 0);

	if(curpkg == nil)
		return;
	getfile(d);
	l = &curpkg->detail[d];
	if(l->n == -1)
		return;
	for(i=0; i<l->n; i++)
		chanprint(cs->ctl, "detail_text add %q", l->s[i]);
	chanprint(cs->ctl, "detail_scroll max %d", l->n);
}

int
pkgcmp(void *a, void *b)
{
	return strcmp(((Pkg*)a)->name, ((Pkg*)b)->name);
}

void
mkpkgs(void)
{
	Channel *c;
	char *cmd, *s, *p;
	int i, w, h, mw;

	status("fetching package list...");
	c = chancreate(sizeof(char *), 0);
	cmd = smprint("contrib/listall");
	runcmd(cmd, c);
	npkg = 0;
	pkg = nil;
	while((s=recvp(c)) != nil) {
		pkg = ctlrealloc(pkg, sizeof(Pkg)*(npkg+1));
		p = strchr(s, '/');
		if(p == nil)
			sysfatal("bad list file: %r");
		*p = '\0';
		pkg[npkg].user = ctlstrdup(s);
		pkg[npkg].name = ctlstrdup(p+1);
		memset(pkg[npkg].detail, 0, sizeof(Lines)*4);
		npkg++;
		free(s);
	}
	chanfree(c);
	qsort(pkg, npkg, sizeof(Pkg), pkgcmp);
	mw = 0;
	for(i=0; i<npkg; i++){
		chanprint(cs->ctl, "list_text add %q", pkg[i].name);
		w = stringwidth(font, pkg[i].name);
		if(w > mw)
			mw = w;
	}
	w = mw+4;
	h = font->height+4;
	chanprint(cs->ctl, "list_text size %d %d %d %d\n", w, h, w, 2048);
	chanprint(cs->ctl, "list_scroll max %d", npkg);
	status("done.");
	resizecontrolset(cs);
}

void
install(void)
{
	char *s;

	if(curpkg != nil){
		status("trying to install %s/%s", curpkg->user, curpkg->name);
		s = smprint("contrib/install %s/%s", curpkg->user, curpkg->name);
		runcmd(s, cstatus);
	}
	chanprint(cs->ctl, "Install value 0");
}

void
initcolors(void)
{
	Image *i;

	i = allocimagemix(display, DPaleyellow, DWhite);
	namectlimage(i, "textbg");
	i = allocimagemix(display, DPalebluegreen, DWhite);
	namectlimage(i, "butbg");
	i = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DYellowgreen);
	namectlimage(i, "scrollbg");
	i = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
	namectlimage(i, "selbg");
	i = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DBlue);
	namectlimage(i, "pressbg");
}

void
mkbutton(char *s)
{
	int w, h;

	createtextbutton(cs, s);
	chanprint(cs->ctl, "%s text %s", s, s);
	chanprint(cs->ctl, "%s pressedtextcolor pressbg", s);
	chanprint(cs->ctl, "%s image butbg", s);
	chanprint(cs->ctl, "%s mask transparent", s);
	chanprint(cs->ctl, "%s align center", s);
	chanprint(cs->ctl, "%s border 1", s);
	w = stringwidth(font, s)+4;
	h = font->height + 4;
	chanprint(cs->ctl, "%s size %d %d %d %d", s, w, h, 1024, h);
	controlwire(controlcalled(s), "event", cevent);
}

void
mktext(char *s, char *sbg)
{
	createtext(cs, s);
	chanprint(cs->ctl, "%s border 1", s);
	chanprint(cs->ctl, "%s image textbg", s);
	chanprint(cs->ctl, "%s selectcolor %s", s, sbg);
}

void
mkscroll(char *s, char *text)
{
	createslider(cs, s);
	chanprint(cs->ctl, "%s border 1", s);
	chanprint(cs->ctl, "%s image scrollbg", s);
	chanprint(cs->ctl, "%s indicatorcolor textbg", s);
	chanprint(cs->ctl, "%s size 12 10 12 2048", s);
	chanprint(cs->ctl, "%s absolute 0", s);
	chanprint(cs->ctl, "%s format '%%q: %s topline %%d'", s, text);
	controlwire(controlcalled(s), "event", cs->ctl);
}

void
mklabel(char *s)
{
	int w, h;

	createlabel(cs, s);
	chanprint(cs->ctl, "%s align center", s);
	chanprint(cs->ctl, "%s border 1", s);
	chanprint(cs->ctl, "%s image paleyellow", s);
	chanprint(cs->ctl, "%s value %q", s, s);
	w = stringwidth(font, s)+4;
	h = font->height+4;
	chanprint(cs->ctl, "%s size %d %d %d %d", s, w, h, 1024, h);
}

void
mkcontrols(void)
{
	int i;

	createcolumn(cs, "window");
	createrow(cs, "browser");
	chanprint(cs->ctl, "browser separation %d", 1);
	createrow(cs, "log");
	createrow(cs, "buttons");
	chanprint(cs->ctl, "window add browser log buttons");
	createcolumn(cs, "list");
	mklabel("Packages");
	mktext("list_text", "selbg");
	controlwire(controlcalled("list_text"), "event", cevent);
	mkscroll("list_scroll", "list_text");
	chanprint(cs->ctl, "list add Packages list_text");

	createrow(cs, "detail");
	createcolumn(cs, "detail_content");
	createrow(cs, "detail_buttons");
	for(i=0; i<nelem(detail); i++){
		mkbutton(detail[i]);
		chanprint(cs->ctl, "detail_buttons add %s", detail[i]);
	}
	mktext("detail_text", "textbg");
	mkscroll("detail_scroll", "detail_text");
	chanprint(cs->ctl, "detail_content add detail_buttons detail_text");
	chanprint(cs->ctl, "detail add detail_scroll detail_content");

	mktext("log_text", "white");
	chanprint(cs->ctl, "log_text size %d %d %d %d\n", 10, (font->height*4)+4, 2048, (font->height*4)+4);
	mkscroll("log_scroll", "log_text");
	chanprint(cs->ctl, "log add log_scroll log_text");

	for(i=0; i<nelem(action); i++){
		mkbutton(action[i]);
		chanprint(cs->ctl, "buttons add %s", action[i]);
	}
	chanprint(cs->ctl, "browser add list_scroll list detail");
	activate(controlcalled("window"));
}

void
resizecontrolset(Controlset *)
{
	if(getwindow(display, Refnone) < 0)
		sysfatal("resize failed: %r");

	chanprint(cs->ctl, "window size");
	chanprint(cs->ctl, "window rect %R", screen->r);
	chanprint(cs->ctl, "log_scroll vis '%d'", Dy(controlcalled("log_scroll")->rect)/font->height);
	chanprint(cs->ctl, "list_scroll vis '%d'", Dy(controlcalled("list_scroll")->rect)/font->height);
	chanprint(cs->ctl, "detail_scroll vis '%d'", Dy(controlcalled("detail_scroll")->rect)/font->height);
	chanprint(cs->ctl, "window show");
}

void
work(void)
{
	char *s, *args[8];
	int n, i;
	enum { AEvent, AStatus, NALT};
	static Alt alts[NALT];
	alts[AEvent].c = cevent;
	alts[AEvent].v = &s;
	alts[AEvent].op = CHANRCV;
	alts[AStatus].c = cstatus;
	alts[AStatus].v = &s;
	alts[AStatus].op = CHANRCV;
	alts[NALT].op = CHANEND;
	
	for(;;){
		switch(alt(alts)){
		case AStatus:
			if(s){
				status(s);
				free(s);
			}
			break;
		case AEvent:
			n = tokenize(s, args, nelem(args));
			if(n == 3){
				if(strcmp(args[0], "Exit:")==0)
					threadexitsall(nil);
				if(strcmp(args[0], "Install:")==0){
					install();
				}else{
					for(i=0; i<nelem(detail); i++)
						if(strncmp(args[0], detail[i], strlen(detail[i])) == 0){
							setdetail(i);
							break;
						}
				}
			}else if(n==4 && strcmp(args[0], "list_text:")==0){
				i = atoi(args[2]);
				if(i>=0 && i<npkg)
					curpkg = &pkg[i];
				setdetail(Info);
			}
			free(s);
			break;
		}
	}
}

void
threadmain(int argc, char *argv[])
{
	ARGBEGIN{
	}ARGEND
	
	if(initdraw(0, 0, "contrib") < 0)
		sysfatal("can't open display");

	initcontrols();
	cs = newcontrolset(screen, nil, nil, nil);
	cs->clicktotype = 1;
	cevent = chancreate(sizeof(char *), 0);
	cstatus = chancreate(sizeof(char *), 0);
	if(cevent==nil || cstatus==nil)
		sysfatal("can't create channels. %r");
	initcolors();
	mkcontrols();
	resizecontrolset(cs);
	mkpkgs();
	work();
}

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@9p.io.