shithub: jot

ref: 9914a8da911d9fdfefd6b090870bbd385cacae69
dir: /main.c/

View raw version
#include "inc.h"
#include <cursor.h>

Keyboardctl *kbctl;
Keyboardctl *fwdkc, kbdctl2;
Mousectl *mctl;
int shiftdown;	// TODO: needed?

Text text;
Image *colors[NCOL];
char filename[1024];
char *startdir;

Cursor quest = {
	{-7,-7},
	{0x0f, 0xf0, 0x1f, 0xf8, 0x3f, 0xfc, 0x7f, 0xfe, 
	 0x7c, 0x7e, 0x78, 0x7e, 0x00, 0xfc, 0x01, 0xf8, 
	 0x03, 0xf0, 0x07, 0xe0, 0x07, 0xc0, 0x07, 0xc0, 
	 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, 0x07, 0xc0, },
	{0x00, 0x00, 0x0f, 0xf0, 0x1f, 0xf8, 0x3c, 0x3c, 
	 0x38, 0x1c, 0x00, 0x3c, 0x00, 0x78, 0x00, 0xf0, 
	 0x01, 0xe0, 0x03, 0xc0, 0x03, 0x80, 0x03, 0x80, 
	 0x00, 0x00, 0x03, 0x80, 0x03, 0x80, 0x00, 0x00, }
};



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

void*
emalloc(ulong size)
{
	void *p;

	p = malloc(size);
	if(p == nil)
		panic("malloc failed");
	memset(p, 0, size);
	return p;
}

void*
erealloc(void *p, ulong size)
{
	p = realloc(p, size);
	if(p == nil)
		panic("realloc failed");
	return p;
}

char*
estrdup(char *s)
{
	char *p;

	p = malloc(strlen(s)+1);
	if(p == nil)
		panic("strdup failed");
	strcpy(p, s);
	return p;
}





/*
 * /dev/snarf updates when the file is closed, so we must open our own
 * fd here rather than use snarffd
 */
void
putsnarf(void)
{
	int fd, i, n;

	if(snarffd<0 || nsnarf==0)
		return;
	fd = open("/dev/snarf", OWRITE|OCEXEC);
	if(fd < 0)
		return;
	/* snarf buffer could be huge, so fprint will truncate; do it in blocks */
	for(i=0; i<nsnarf; i+=n){
		n = nsnarf-i;
		if(n >= 256)
			n = 256;
		if(fprint(fd, "%.*S", n, snarf+i) < 0)
			break;
	}
	close(fd);
}

void
setsnarf(char *s, int ns)
{
	free(snarf);
	snarf = runesmprint("%.*s", ns, s);
	nsnarf = runestrlen(snarf);
	snarfversion++;
}

void
getsnarf(void)
{
	int i, n;
	char *s, *sn;

	if(snarffd < 0)
		return;
	sn = nil;
	i = 0;
	seek(snarffd, 0, 0);
	for(;;){
		if(i > MAXSNARF)
			break;
		if((s = realloc(sn, i+1024+1)) == nil)
			break;
		sn = s;
		if((n = read(snarffd, sn+i, 1024)) <= 0)
			break;
		i += n;
	}
	if(i == 0)
		return;
	sn[i] = 0;
	setsnarf(sn, i);
	free(sn);
}




void
readfile(char *path)
{
	int fd, n, ns;
	char *s, buf[1024];
	Rune *rs;

	fd = open(path, OREAD);
	if(fd < 0)
		return;
	s = nil;
	ns = 0;
	while(n = read(fd, buf, sizeof(buf)), n > 0) {
		s = realloc(s, ns+n);
		memcpy(s+ns, buf, n);
		ns += n;
	}
	close(fd);


	rs = runesmprint("%.*s", ns, s);
	free(s);
	xdelete(&text, 0, text.nr);
	xinsert(&text, rs, runestrlen(rs), 0);
	free(rs);
}

int
writefile(char *path)
{
	int fd;
	char *s;

	fd = create(path, OWRITE|OTRUNC, 0666);
	if(fd < 0)
		return 1;

	s = smprint("%.*S", text.nr, text.r);
	write(fd, s, strlen(s));
	close(fd);
	free(s);
	return 0;
}


void
confused(void)
{
	setcursor(mctl, &quest);
	sleep(300);
	setcursor(mctl, nil);
}

void
editmenu(Text *x, Mousectl *mc)
{
	enum {
		Cut,
		Paste,
		Snarf,
		Plumb,
		Look
	};
	static char *menu2str[] = {
		"cut",
		"paste",
		"snarf",
		"plumb",
		"look",
		nil
	};
	static Menu menu2 = { menu2str };

	switch(menuhit(2, mc, &menu2, nil)){
	case Cut:
		xsnarf(x);
		xcut(x);
		break;
	case Paste:
		xpaste(x);
		break;
	case Snarf:
		xsnarf(x);
		break;
	case Plumb:
		if(xplumb(x, "jot", startdir, 31*1024))
			confused();
		break;
	case Look:
		xlook(x);
		break;
	}
}

void
filemenu(Text *x, Mousectl *mc)
{
	USED(x);
	enum {
		Write,
		Exit
	};
	static char *str[] = {
		"write",
		"exit",
		nil
	};
	static Menu menu = { str };

	switch(menuhit(3, mc, &menu, nil)){
	case Write:
		if(filename[0] == '\0'){
			fwdkc = &kbdctl2;
			enter("file", filename, sizeof(filename), mc, fwdkc, nil);
			fwdkc = nil;
		}
		if(writefile(filename)){
			memset(filename, 0, sizeof(filename));
			confused();
		}
		break;
	case Exit:
		threadexitsall(nil);
	}
}

void
mousectl(Text *x, Mousectl *mc)
{
	int but;

	for(but = 1; but < 6; but++)
		if(mc->buttons == 1<<(but-1))
			goto found;
	return;
found:

/*	if(shiftdown && but > 3)
		wkeyctl(w, but == 4 ? Kscrolloneup : Kscrollonedown);
	else*/ if(ptinrect(mc->xy, x->scrollr) || but > 3)
		xscroll(x, mc, but);
	else if(but == 1)
		xselect(x, mc);
	else if(but == 2)
		editmenu(x, mc);
	else if(but == 3)
		filemenu(x, mc);
}

void
keyctl(Text *x, Rune r)
{
	int nlines, n;

	nlines = x->maxlines;	/* need signed */
	switch(r){

	/* Scrolling */
	case Kscrollonedown:
		n = mousescrollsize(x->maxlines);
		xscrolln(x, max(n, 1));
		break;
	case Kdown:
		xscrolln(x, shiftdown ? 1 : nlines/3);
		break;
	case Kpgdown:
		xscrolln(x, nlines*2/3);
		break;
	case Kscrolloneup:
		n = mousescrollsize(x->maxlines);
		xscrolln(x, -max(n, 1));
		break;
	case Kup:
		xscrolln(x, -(shiftdown ? 1 : nlines/3));
		break;
	case Kpgup:
		xscrolln(x, -nlines*2/3);
		break;

	case Khome:
		xshow(x, 0);
		break;
	case Kend:
		xshow(x, x->nr);
		break;

	/* Cursor movement */
	case Kleft:
		if(x->q0 > 0)
			xplacetick(x, x->q0-1);
		break;
	case Kright:
		if(x->q1 < x->nr)
			xplacetick(x, x->q1+1);
		break;
	case CTRL('A'):
		while(x->q0 > 0 && x->r[x->q0-1] != '\n' &&
		      x->q0 != x->qh)
			x->q0--;
		xplacetick(x, x->q0);
		break;
	case CTRL('E'):
		while(x->q0 < x->nr && x->r[x->q0] != '\n')
			x->q0++;
		xplacetick(x, x->q0);
		break;
	case CTRL('B'):
		xplacetick(x, x->qh);
		break;

	case Kesc:
		xsnarf(x);
		xcut(x);
		break;
	case Kdel:
		xtype(x, CTRL('H'));
		break;

	default:
		xtype(x, r);
		break;
	}
}

void
setsize(Text *x)
{
	Rectangle scrollr, textr;

	draw(screen, screen->r, colors[BACK], nil, ZP);
	scrollr = textr = insetrect(screen->r, 1);
	scrollr.max.x = scrollr.min.x + 12;
	textr.min.x = scrollr.max.x + 4;
	xinit(x, textr, scrollr, font, screen, colors);
}

void
mthread(void*)
{
	while(readmouse(mctl) != -1){
		mousectl(&text, mctl);
	}
}

void
kbthread(void*)
{
	Rune r;

	for(;;){
		r = recvul(kbctl->c);
		if(fwdkc)
			send(fwdkc->c, &r);
		else
			keyctl(&text, r);
		flushimage(display, 1);
	}
}

void
resthread(void*)
{
	for(;;){
		recvul(mctl->resizec);
		if(getwindow(display, Refnone) < 0)
			sysfatal("resize failed: %r");

		setsize(&text);
	}
}

void
threadmain(int argc, char *argv[])
{
	char buf[1024];
//	newwindow(nil);

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

	kbctl = initkeyboard("/dev/cons");
	if(kbctl == nil)
		sysfatal("initkeyboard: %r");
	kbdctl2.c = chancreate(sizeof(Rune), 20);

	mctl = initmouse("/dev/mouse", screen);
	if(mctl == nil)
		sysfatal("initmouse: %r");
	snarffd = open("/dev/snarf", OREAD|OCEXEC);
	if(getwd(buf, sizeof(buf)) == nil)
		startdir = estrdup(".");
	else
		startdir = estrdup(buf);

	colors[BACK] = allocimagemix(display, DPaleyellow, DWhite);
	colors[HIGH] = allocimage(display, Rect(0,0,1,1), screen->chan, 1, DDarkyellow);
	colors[BORD] = allocimage(display, Rect(0,0,2,2), screen->chan, 1, DYellowgreen);
	colors[TEXT] = display->black;
	colors[HTEXT] = display->black;

	setsize(&text);

	timerinit();
	threadcreate(mthread, nil, mainstacksize);
	threadcreate(kbthread, nil, mainstacksize);
	threadcreate(resthread, nil, mainstacksize);

	if(argc > 1){
		strncpy(filename, argv[1], sizeof(filename));
		readfile(filename);
	}
}