shithub: lola

ref: 769b10f0ceafba306a3b53bee27f633e67ab11d3
dir: /wctl.c/

View raw version
#include "inc.h"

/*
 * TODO: i feel like this could use some cleanup
 */

char	Ebadwr[]		= "bad rectangle in wctl request";
char	Ewalloc[]		= "window allocation failed in wctl request";

/* >= Top are disallowed if mouse button is pressed */
enum
{
	New,
	Resize,
	Move,
	Scroll,
	Noscroll,
	Set,
	Top,
	Bottom,
	Current,
	Hide,
	Unhide,
	Delete,
};

static char *cmds[] = {
	[New]	= "new",
	[Resize]	= "resize",
	[Move]	= "move",
	[Scroll]	= "scroll",
	[Noscroll]	= "noscroll",
	[Set]		= "set",
	[Top]	= "top",
	[Bottom]	= "bottom",
	[Current]	= "current",
	[Hide]	= "hide",
	[Unhide]	= "unhide",
	[Delete]	= "delete",
	nil
};

enum
{
	Cd,
	Deltax,
	Deltay,
	Hidden,
	Id,
	Maxx,
	Maxy,
	Minx,
	Miny,
	PID,
	R,
	Scrolling,
	Noscrolling,
};

static char *params[] = {
	[Cd]	 			= "-cd",
	[Deltax]			= "-dx",
	[Deltay]			= "-dy",
	[Hidden]			= "-hide",
	[Id]				= "-id",
	[Maxx]			= "-maxx",
	[Maxy]			= "-maxy",
	[Minx]			= "-minx",
	[Miny]			= "-miny",
	[PID]				= "-pid",
	[R]				= "-r",
	[Scrolling]			= "-scroll",
	[Noscrolling]		= "-noscroll",
	nil
};

static int
word(char **sp, char *tab[])
{
	char *s, *t;
	int i;

	s = *sp;
	while(isspacerune(*s))
		s++;
	t = s;
	while(*s!='\0' && !isspacerune(*s))
		s++;
	for(i=0; tab[i]!=nil; i++)
		if(strncmp(tab[i], t, strlen(tab[i])) == 0){
			*sp = s;
			return i;
	}
	return -1;
}

int
set(int sign, int neg, int abs, int pos)
{
	if(sign < 0)
		return neg;
	if(sign > 0)
		return pos;
	return abs;
}

void
shift(int *minp, int *maxp, int min, int max)
{
	if(*maxp > max){
		*minp += max-*maxp;
		*maxp = max;
	}
	if(*minp < min){
		*maxp += min-*minp;
		if(*maxp > max)
			*maxp = max;
		*minp = min;
	}
}

Rectangle
rectonscreen(Rectangle r)
{
	shift(&r.min.x, &r.max.x, screen->r.min.x, screen->r.max.x);
	shift(&r.min.y, &r.max.y, screen->r.min.y, screen->r.max.y);
	return r;
}

/* permit square brackets, in the manner of %R */
int
riostrtol(char *s, char **t)
{
	int n;

	while(*s!='\0' && (*s==' ' || *s=='\t' || *s=='['))
		s++;
	if(*s == '[')
		s++;
	n = strtol(s, t, 10);
	if(*t != s)
		while((*t)[0] == ']')
			(*t)++;
	return n;
}

Wctlcmd
parsewctl(char *s, Rectangle r)
{
	Wctlcmd cmd;

	int n, nt, param, xy, sign;
	char *f[2], *t;

	cmd.pid = 0;
	cmd.hidden = FALSE;
	cmd.scrolling = scrolling;
	cmd.dir = nil;
	cmd.error = nil;
	cmd.cmd = word(&s, cmds);
	if(cmd.cmd < 0)
		goto Lose;
	if(cmd.cmd == New)
		r = newrect();

	while((param = word(&s, params)) >= 0){
		switch(param){	/* special cases */
		case Hidden:
			cmd.hidden = TRUE;
			continue;
		case Scrolling:
			cmd.scrolling = TRUE;
			continue;
		case Noscrolling:
			cmd.scrolling = FALSE;
			continue;
		case R:
			r.min.x = riostrtol(s, &t);
			if(t == s)
				goto Lose;
			s = t;
			r.min.y = riostrtol(s, &t);
			if(t == s)
				goto Lose;
			s = t;
			r.max.x = riostrtol(s, &t);
			if(t == s)
				goto Lose;
			s = t;
			r.max.y = riostrtol(s, &t);
			if(t == s)
				goto Lose;
			s = t;
			continue;
		}
		while(isspacerune(*s))
			s++;
		if(param == Cd){
			cmd.dir = s;
			if((nt = gettokens(cmd.dir, f, nelem(f), " \t\r\n\v\f")) < 1)
				goto Lose;
			n = strlen(cmd.dir);
			if(cmd.dir[0] == '\'' && cmd.dir[n-1] == '\'')
				(cmd.dir++)[n-1] = '\0'; /* drop quotes */
			s += n+(nt-1);
			continue;
		}
		sign = 0;
		if(*s == '-'){
			sign = -1;
			s++;
		}else if(*s == '+'){
			sign = +1;
			s++;
		}
		if(!isdigitrune(*s))
			goto Lose;
		xy = riostrtol(s, &s);
		switch(param){
		case -1:
			cmd.error = "unrecognized wctl parameter";
			return cmd;
		case Minx:
			r.min.x = set(sign, r.min.x-xy, xy, r.min.x+xy);
			break;
		case Miny:
			r.min.y = set(sign, r.min.y-xy, xy, r.min.y+xy);
			break;
		case Maxx:
			r.max.x = set(sign, r.max.x-xy, xy, r.max.x+xy);
			break;
		case Maxy:
			r.max.y = set(sign, r.max.y-xy, xy, r.max.y+xy);
			break;
		case Deltax:
			r.max.x = set(sign, r.max.x-xy, r.min.x+xy, r.max.x+xy);
			break;
		case Deltay:
			r.max.y = set(sign, r.max.y-xy, r.min.y+xy, r.max.y+xy);
			break;
		case Id:
			cmd.id = xy;
			break;
		case PID:
			cmd.pid = xy;
			break;
		}
	}
	cmd.r = rectonscreen(rectaddpt(r, screen->r.min));
	while(isspacerune(*s))
		s++;
	if(cmd.cmd != New && *s != '\0'){
		cmd.error = "extraneous text in wctl message";
		return cmd;
	}
	cmd.args = s;
	return cmd;
Lose:
	cmd.error = "unrecognized wctl command";
	return cmd;
}

char*
wctlcmd(Window *w, Rectangle r, int cmd)
{
	switch(cmd){
	case Move:
		if(!goodrect(r))
			return Ebadwr;
		wmove(w, r.min);
		break;
	case Resize:
		if(!goodrect(r))
			return Ebadwr;
		wresize(w, r);
		break;
// TODO: these three work somewhat differently in rio
	case Top:
		wraise(w);
		break;
	case Bottom:
		wlower(w);
		break;
	case Current:
		if(w->hidden)
			return "window is hidden";
		wfocus(w);
		wraise(w);
		break;
	case Hide:
		switch(whide(w)){
		case -1: return "window already hidden";
		case 0: return "hide failed";
		}
		break;
	case Unhide:
		switch(wunhide(w)){
		case -1: return "window not hidden";
		case 0: return "hide failed";
		}
		break;
	case Delete:
		wdelete(w);
		break;
	case Scroll:
		w->scrolling = TRUE;
		xshow(&w->text, w->text.nr);
		wsendmsg(w, Wakeup);
		break;
	case Noscroll:
		w->scrolling = FALSE;
		wsendmsg(w, Wakeup);
		break;
	default:
		return "invalid wctl message";
	}
	return nil;
}

char*
wctlnew(Wctlcmd cmd)
{
	Window *w;
	char *argv[4], **args;

	w = wcreate(cmd.r, cmd.hidden, cmd.scrolling);
	assert(w);
	args = nil;
	if(cmd.pid == 0){
		argv[0] = "rc";
		argv[1] = "-c";
		while(isspacerune(*cmd.args))
			cmd.args++;
		if(*cmd.args == '\0'){
			argv[1] = "-i";
			argv[2] = nil;
		}else{
			argv[2] = cmd.args;
			argv[3] = nil;
		}
		args = argv;
	}
	if(wincmd(w, cmd.pid, cmd.dir, args) == 0)
		return "window creation failed";		
	return nil;
}

char*
writewctl(Window *w, char *data)
{
	Rectangle r;
	Wctlcmd cmd;

	if(w == nil)
		r = ZR;
	else
		r = rectsubpt(w->img->r, screen->r.min);
	cmd = parsewctl(data, r);
	if(cmd.error)
		return cmd.error;

	if(cmd.id != 0){
		w = wfind(cmd.id);
		if(w == nil)
			return "no such window id";
	}

	if(w == nil && cmd.cmd != New)
		return "command needs to be run within a window";

	switch(cmd.cmd){
	case New:
		return wctlnew(cmd);
	case Set:
		if(cmd.pid > 0)
			wsetpid(w, cmd.pid, 0);
		return nil;
	default:
		incref(w);
		cmd.error = wctlcmd(w, cmd.r, cmd.cmd);
		wrelease(w);
		return cmd.error;
	}
}