shithub: barrera

ref: a3fb851f0e29e8e0fd50acef703e7d16f809a666
dir: /barrera.c/

View raw version
/*
 * a client for barrier mouse, keyboard and clipboard sharing.
 */
#include <u.h>
#include <libc.h>

/* mouse() ctl */
enum
{
	Mrelative	= (0<<0),
	Mabsolute	= (1<<0),
	Mclearbuttons	= (1<<1),
	Msetbuttons	= (1<<2),
	Mresetbuttons	= (1<<4),
};

/* keyboard() ctl */
enum
{
	Kup		= 1,
	Kdown		= 2,
	Krepeat		= 3,
};

/* clipboard type */
enum
{
	Ftext,
	Fbmp,
	Fhtml,
	Fmax,
};

typedef struct Msg Msg;
struct Msg
{
	uchar		*b;
	uchar		*p;
	uchar		*e;
};

int mouseinfd = -1;
int mousefd = -1;
int kbdfd = -1;
int snarffd = -1;
Msg snarfbuf;

struct {
	int	x;
	int	y;
	int	buttons;
} screenmouse;

struct {
	struct {
		int	x;
		int	y;
	} min, max;
} screensize;

int packmsg(Msg *msg, char *fmt, ...);
int packfmtlen(char *fmt, ...);
int unpackmsg(Msg *msg, char *fmt, ...);

int
vpackmsglen(char *fmt, va_list arg)
{
	int n, q;
	char *vas;
	int vai;

	n = 0;
	while(*fmt){
		if(*fmt!='%'){
			n++;
			fmt++;
			continue;
		}
		fmt++;
		q = 0;
		if((*fmt >= '0') && (*fmt <= '9')){
			q = *(fmt++)-'0';
		}
		switch(*fmt){
		case 'i':
			vai = va_arg(arg, int);
			USED(vai);
			n += q;
			break;
		case '*':
			q = va_arg(arg, int);
			vas = va_arg(arg, char*);
			USED(vas);
			n += 4 + q;
			break;
		case 's':
			vas = va_arg(arg, char*);
			q = strlen(vas);
			n += 4 + q;
			break;
		}
		fmt++;
	}
	return n;
}

int
packmsglen(char *fmt, ...)
{
	va_list arg;
	int n;

	va_start(arg, fmt);
	n = vpackmsglen(fmt, arg);
	va_end(arg);
	return n;
}

int
vunpackmsg(Msg *msg, char *fmt, va_list arg)
{
	int *vai;
	char *sval;

	int n, q;

	n = 0;
	while(*fmt){
		if(*fmt!='%'){
			if(msg->p >= msg->e || *msg->p != *fmt)
				return n;
			msg->p++;
			fmt++;
			continue;
		}
		fmt++;

		q = 0;
		if((*fmt>='0') && (*fmt<='9')){
			q = *fmt-'0';
			fmt++;
		}
		switch(*fmt){
		default:
			return n;
		case 'i':
			if(msg->p+q > msg->e)
				return n;
			if(vai = va_arg(arg, int*)){
				switch(q){
				default:
					return n;
				case 1:
					*vai = 	(unsigned)msg->p[0];
					break;
				case 2:
					*vai = 	(unsigned)msg->p[0]<<8 | (unsigned)msg->p[1];
					break;
				case 4:
					*vai = 	(unsigned)msg->p[0]<<24 | 
						(unsigned)msg->p[1]<<16 | 
						(unsigned)msg->p[2]<<8 | 
						(unsigned)msg->p[3];
					break;
				}
			}
			msg->p+=q;
			break;
		case 's':
			if(unpackmsg(msg, "%4i", &q) != 1)
				return n;
			if(q >= va_arg(arg, int) || msg->p+q > msg->e)
				return n;
			sval = va_arg(arg, char*);
			memmove(sval, msg->p, q);
			sval[q] = '\0';
			msg->p+=q;
			break;
		case '*':
			if(unpackmsg(msg, "%4i", &q) != 1)
				return n;
			if(q > va_arg(arg, int) || msg->p+q > msg->e)
				return n;
			sval = va_arg(arg, char*);
			memmove(sval, msg->p, q);
			msg->p+=q;
			break;
		}

		n++;
		fmt++;
	}
	return n;
}

int
vpackmsg(Msg *msg, char *fmt, va_list arg)
{
	int vai;
	char *vas;

	int n, q;

	n = 0;
	while(*fmt){
		if(*fmt!='%'){
			if(msg->p >= msg->e)
				return n;
			*msg->p++ = *fmt++;
			continue;
		}
		fmt++;

		q = 0;
		if((*fmt >= '0') && (*fmt <= '9')){
			q = *(fmt++)-'0';
		}
		switch(*fmt){
		default:
			return n;
		case 'i':
			if(msg->p+q > msg->e)
				return n;
			vai = va_arg(arg, int);
			switch(q){
			default:
				return n;
			case 1:
				msg->p[0] = vai		& 0xff;
				break;
			case 2:
				msg->p[0] = vai>>8	& 0xff;
				msg->p[1] = vai		& 0xff;
				break;
			case 4:
				msg->p[0] = (vai>>24)	& 0xff;
				msg->p[1] = (vai>>16)	& 0xff;
				msg->p[2] = (vai>>8)	& 0xff;
				msg->p[3] = vai		& 0xff;
				break;
			}
			msg->p += q;
			break;
		case '*':
			q = va_arg(arg, int);
			vas = va_arg(arg, char*);
			if(0){
		case 's':
				vas = va_arg(arg, char*);
				q = strlen(vas);
			}
			if(msg->p + 4 + q > msg->e)
				return n;
			packmsg(msg, "%4i", q);
			if(q > 0)
				memcpy(msg->p, vas, q);
			msg->p += q;
			break;
		}
		n++;
		fmt++;
	}
	return n;
}

int
unpackmsg(Msg *msg, char *fmt, ...)
{
	va_list arg;
	int n;

	va_start(arg, fmt);
	n = vunpackmsg(msg, fmt, arg);
	va_end(arg);
	return n;
}

int
packmsg(Msg *msg, char *fmt, ...)
{
	va_list arg;
	int n;

	va_start(arg, fmt);
	n = vpackmsg(msg, fmt, arg);
	va_end(arg);
	return n;
}

void
sizemsg(Msg *m, int len)
{
	int n = m->e - m->p;

	if(len < 0 || len > 1024*1024)
		sysfatal("bad message size: %d", len);
	if(len > n){
		memmove(m->b, m->p, n);
		m->b = realloc(m->b, len);
		if(m->b == nil)
			sysfatal("out of memory: %r");
		m->p = m->b;
	}
	m->e = m->p + len;
}

int
writemsg(char *fmt, ...)
{
	static Msg m;
	va_list arg;
	int n, l;

	va_start(arg, fmt);
	l = 4 + vpackmsglen(fmt, arg);
	sizemsg(&m, l);
	packmsg(&m, "%4i", l-4);
	n = vpackmsg(&m, fmt, arg);
	va_end(arg);
	if(write(1, m.b, l)!=l)
		return -1;
	return n;
}

void
readscreenmouse(void)
{
	char buf[1+4*12+1], *field[4];
	int n;

	if(mouseinfd < 0)
		return;
	n = pread(mouseinfd, buf, sizeof(buf)-1, 0);
	if(n < 0)
		return;
	buf[n] = '\0';
	if(tokenize(buf+1, field, 4) < 3)
		return;
	screenmouse.x = atoi(field[0]);
	screenmouse.y = atoi(field[1]);
	screenmouse.buttons = atoi(field[2]);
}

void
readscreensize(void)
{
	char buf[60+1], *field[5];
	int fd, n;

	fd = open("/dev/screen", OREAD);
	if(fd < 0)
		return;
	n = read(fd, buf, sizeof(buf)-1);
	close(fd);
	if(n < 0)
		return;
	buf[n] = '\0';
	if(tokenize(buf, field, 5) != 5)
		return;
	screensize.min.x = atoi(field[1]);
	screensize.min.y = atoi(field[2]);
	screensize.max.x = atoi(field[3]);
	screensize.max.y = atoi(field[4]);
}

void
mouse(int ctl, int x, int y, int buttons)
{
	if(ctl & Mclearbuttons)
		screenmouse.buttons = 0;
	else if(ctl & Msetbuttons)
		screenmouse.buttons |= buttons;
	else if(ctl & Mresetbuttons)
		screenmouse.buttons &= ~buttons;
	if(ctl & Mabsolute){
		screenmouse.x = x;
		screenmouse.y = y;
	} else {
		screenmouse.x += x;
		screenmouse.y += y;
	}
	fprint(mousefd, "A%11d %11d %11d %11d",
		screenmouse.x, screenmouse.y, screenmouse.buttons, 0);
}

void
screensaver(int on)
{
	if(on){
		int fd;

		fd = open("/dev/mousectl", OWRITE);
		if(fd < 0)
			return;
		fprint(fd, "blank");
		close(fd);
	} else {
		mouse(Mrelative, 0, 0, 0);
	}
}

static int map[][5] = {
{0xef08,	1,	0x08, 0x00, 0x00},	// del
{0xef09,	1,	0x09, 0x00, 0x00},	// tab?
{0xef0d,	1,	0x0a, 0x00, 0x00},	// enter
{0xef1b,	1,	0x1b, 0x00, 0x00},	// esc
{0xef50,	3,	0xef, 0x80, 0x8d},	// home
{0xef51,	3,	0xef, 0x80, 0x91},	// left
{0xef52,	3,	0xef, 0x80, 0x8e},	// up
{0xef53,	3,	0xef, 0x80, 0x92},	// right
{0xef54,	3,	0xef, 0xa0, 0x80},	// down
{0xef55,	3,	0xef, 0x80, 0x8f},	// page up
{0xef56,	3,	0xef, 0x80, 0x93},	// page down
{0xef57,	3,	0xef, 0x80, 0x98},	// end
{0xef63,	3,	0xef, 0x80, 0x94},	// ins
{0xefbe,	3,	0xef, 0x80, 0x81},	// F1
{0xefbf,	3,	0xef, 0x80, 0x82},	// F2
{0xefc0,	3,	0xef, 0x80, 0x83},	// F3
{0xefc1,	3,	0xef, 0x80, 0x84},	// F4
{0xefc2,	3,	0xef, 0x80, 0x85},	// F5
{0xefc3,	3,	0xef, 0x80, 0x86},	// F6
{0xefc4,	3,	0xef, 0x80, 0x87},	// F7
{0xefc5,	3,	0xef, 0x80, 0x88},	// F8
{0xefc6,	3,	0xef, 0x80, 0x89},	// F9
{0xefc7,	3,	0xef, 0x80, 0x8a},	// F10
{0xefc8,	3,	0xef, 0x80, 0x8b},	// F11
{0xefc9,	3,	0xef, 0x80, 0x8c},	// F12
{0xefe1, 	3,	0xef, 0x80, 0x96},	// shift (Kshift)
{0xefe3,	3,	0xef, 0x80, 0x97},	// ctl (Kctl)
{0xefe9,	3,	0xef, 0x80, 0x95},	// alt left (Kalt)
{0xefea,	3,	0xef, 0xa1, 0xa7},	// alt right (Kaltgr)
{0xefeb,	3,	0xef, 0xa1, 0xa8},	// windows key (Kmod4)
{0xefff,	1,	0x7f, 0x00, 0x00},	// del
{0x0000,	0,	0x00, 0x00, 0x00},
};

void
keyboard(int ctl, int key, int mod, int btn, int repeat)
{
	uchar b[3];
	int i, n;

	USED(btn);
	USED(repeat);

	for(i=0; map[i][0]; i++)
		if(map[i][0]>=key)
			break;

	if(map[i][0] == key){
		n = map[i][1];
		switch(n){
		default:
			return;
		case 3:
			b[2] = map[i][4]&0xff;
		case 2:
			b[1] = map[i][3]&0xff;
		case 1:
			b[0] = map[i][2]&0xff;
		}
	} else {
		if(key&~0x7F)
			return;
		n = 1;
		b[0] = key&0x7F;
		if(mod == 2) {
			switch(key) {
			case 'h':
				b[0] = 0x08;
				break;
			case 'u':
				b[0] = 0x15;
				break;
			case 'w':
				b[0] = 0x17;
				break;
			case 'd':
				b[0] = 0x04;
				break;
			case 'a':
				b[0] = 0x01;
				break;
			case 'e':
				b[0] = 0x05;
				break;
			}
		}
	}
	fprint(kbdfd, "%c%.*s%c", ctl==Kup?'R':'r', n, (char*)b, 0);
}

int
readsnarf(void)
{
	static Dir *olddir;
	Dir *d;
	int n, l;

	if(snarffd < 0)
		return 0;
	d = dirfstat(snarffd);
	if(d == nil)
		return 0;
	if(olddir == nil || olddir->qid.vers != d->qid.vers){
		free(olddir);
		olddir = d;
	} else {
		free(d);
		return 0;
	}

	seek(snarffd, 0, 0);
	for(l = 0;; l+=n){
		sizemsg(&snarfbuf, 4+4+4 + l + 1024);
		if((n = read(snarffd, snarfbuf.e - 1024, 1024)) <= 0)
			break;
	}
	if(l <= 0){
		sizemsg(&snarfbuf, 0);
		return 0;
	}
	sizemsg(&snarfbuf, 4+4+4 + l);
	packmsg(&snarfbuf, "%4i%4i%4i", 1, Ftext, l);
	snarfbuf.p -= 4+4+4;
	return 1;
}

void
io(void)
{
	static Msg m;
	char size[32];
	int clip[2] = {0};
	int screenchange = 0;

	int l, i, x, y, z, seq, on, key, btn, rep, mod, major, minjor, mark, n, cfmt;
	uint msgid;

	seq = 0;
	for(;;){
		sizemsg(&m, 4);
		if(readn(0, m.p, 4) != 4)
			sysfatal("read msg size failed: %r");
		if(unpackmsg(&m, "%4i", &l) != 1)
			sysfatal("short msg size");
		sizemsg(&m, l);
		if(readn(0, m.p, l) != l)
			sysfatal("read msg failed: %r");
		if(unpackmsg(&m, "%4i", &msgid) != 1)
			sysfatal("short msg id");

#define MSGID(c1,c2,c3,c4)	c1<<24|c2<<16|c3<<8|c4
		switch(msgid){
		default:
		unhandled:
			fprint(2, "unhandled: %c%c%c%c\n",
				(char)((msgid>>24)&0xFF),
				(char)((msgid>>16)&0xFF),
				(char)((msgid>>8)&0xFF),
				(char)((msgid>>0)&0xFF));
			break;

		case MSGID('B','a','r','r'):	/* hello from server */
			if(unpackmsg(&m, "ier%2i%2i", &major, &minjor)!=2)
				goto unhandled;
			if(writemsg("Barrier%2i%2i%s", 1, 6, sysname())!=3)
				return;
			break;

		case MSGID('Q','I','N','F'):	/* query info from server */
			screenchange = 1;
			break;

		case MSGID('C','I','A','K'):	/* info acknowledge */
			screenchange = 0;
			break;

		case MSGID('C','A','L','V'):
			/* Keep alive ping */
			if(writemsg("CALV")!=0)
				return;
			break;

		case MSGID('C','N','O','P'):	/* nop */
			break;

		case MSGID('C','B','Y','E'):
			return;

		case MSGID('C','I','N','N'):	/* enter */
			if(unpackmsg(&m, "%2i%2i%4i%2i", &x, &y, &seq, &mod)!=4)
				goto unhandled;
			mouse(Mabsolute | Mclearbuttons, x, y, 0);
			screenchange = 1;
			break;

		case MSGID('C','C','L','P'):	/* grab clipboard */
			if(unpackmsg(&m, "%1i", &i)!=1 || i < 0 || i >= nelem(clip))
				goto unhandled;
			clip[i] = 0;
			break;
		case MSGID('C','O','U','T'):	/* leave */
			z = snarfbuf.e - snarfbuf.p;
			if(z <= 0)
				break;
			for(i = 0; i < nelem(clip); i++){
				if(!clip[i])
					continue;
				snprint(size, sizeof(size), "%d", z);
				if(writemsg("DCLP%1i%4i%1i%s", i, seq, 1, size) != 4)
					return;
				for(l = 0; l < z; l += n){
					n = z - l;
					if(n > 1024)
						n = 1024;
					if(writemsg("DCLP%1i%4i%1i%*", i, seq, 2, n, snarfbuf.p+l) != 4)
						return;
				}
				if(writemsg("DCLP%1i%4i%1i%s", i, seq, 3, "") != 4)
					return;
				clip[i] = 0;
			}
			sizemsg(&snarfbuf, 0);
			break;

		case MSGID('C','R','O','P'):	/* reset options */
			break;

		case MSGID('C','S','E','C'):	/* screensaver */
			if(unpackmsg(&m, "%1i", &on)!=1)
				goto unhandled;
			screensaver(on);
			break;

		case MSGID('D','C','L','P'):	/* clipboard data */
			if(unpackmsg(&m, "%1i%4i%1i", nil, nil, &mark)!=3)
				goto unhandled;

			switch(mark){
			default:
				goto unhandled;
			case 1:
				sizemsg(&snarfbuf, 0);
				break;
			case 2:
				if(unpackmsg(&m, "%4i", &l) != 1 || m.e - m.p != l)
					goto unhandled;
				sizemsg(&snarfbuf, (snarfbuf.e - snarfbuf.p) + l);
				memmove(snarfbuf.e - l, m.p, l);
				break;
			case 3:
				if(unpackmsg(&snarfbuf, "%4i", nil) != 1)
					break;
				while(unpackmsg(&snarfbuf, "%4i%4i", &cfmt, &l) == 2){
					if(snarfbuf.e - snarfbuf.p < l)
						break;
					if(cfmt != Ftext || l == 0){
						m.p += l;
						continue;
					}
					if(snarffd >= 0)
						close(snarffd);
					snarffd = create("/dev/snarf", ORDWR, 0600);
					if(snarffd >= 0)
						write(snarffd, snarfbuf.p, l);
					break;
				}
				sizemsg(&snarfbuf, 0);
				break;
			}
			/* skip readsnarf() */
			continue;

		case MSGID('D','K','D','N'):	/* keydown */
			if(unpackmsg(&m, "%2i%2i%2i", &key, &mod, &btn)!=3)
				goto unhandled;
			keyboard(Kdown, key, mod, btn, 0);
			break;

		case MSGID('D','K','U','P'):	/* keyup */
			if(unpackmsg(&m, "%2i%2i%2i", &key, &mod, &btn)!=3)
				goto unhandled;
			keyboard(Kup, key, mod, btn, 1);
			break;

		case MSGID('D','K','R','P'):	/* keyrepeat */
			if(unpackmsg(&m, "%2i%2i%2i%2i", &key, &mod, &rep, &btn)!=4)
				goto unhandled;
			keyboard(Krepeat, key, mod, btn, rep);
			break;

		case MSGID('D','M','D','N'):	/* mousedown */
			if(unpackmsg(&m, "%1i", &btn)!=1)
				goto unhandled;
			mouse(Msetbuttons, 0, 0, 1<<(btn-1));
			screenchange = 1;
			break;

		case MSGID('D','M','U','P'):	/* mouseup */
			if(unpackmsg(&m, "%1i", &btn)!=1)
				goto unhandled;
			mouse(Mresetbuttons, 0, 0, 1<<(btn-1));
			screenchange = 1;
			break;

		case MSGID('D','M','M','V'):	/* mousemove */
			if(unpackmsg(&m, "%2i%2i", &x, &y)!=2)
				goto unhandled;
			if(!screenchange)
				mouse(Mabsolute, x, y, 0);
			break;

		case MSGID('D','M','R','M'):	/* mousemove relative */
			if(unpackmsg(&m, "%2i%2i", &x, &y)!=2)
				goto unhandled;
			if(!screenchange)
				mouse(Mrelative, x, y, 0);
			break;

		case MSGID('D', 'M', 'W', 'M'): /* mouse wheel */
			if(unpackmsg(&m, "%2i%2i", &x, &y) != 2)
				goto unhandled;
			x = (x<<16)>>16;
			y = (y<<16)>>16;
			mouse(Msetbuttons, 0, 0, y>0? 1<<3: 1<<4);
			mouse(Mresetbuttons, 0, 0, y>0? 1<<3: 1<<4);
			break;

		case MSGID('D','S','O','P'):	/* ??? */
			break;
		}

		if(screenchange == 1){
			screenchange = 2;

			/* debounce */
			sleep(50);

			readscreensize();
			readscreenmouse();
			if(writemsg("DINF%2i%2i%2i%2i%2i%2i%2i", 
				screensize.min.x,
				screensize.min.y,
				screensize.max.x,
				screensize.max.y,
				0,			/* size of warp zone (obsolete) */
				screenmouse.x, 
				screenmouse.y)!=7)	/* current mouse position */
				return;
		}

		if(readsnarf()){
			for(i=0; i<2; i++){
				if(writemsg("CCLP%1i%4i", i, seq)!=2)
					return;
				clip[i] = 1;
			}
		}
	}
}

void
usage(void)
{
	fprint(2, "usage: tlsclient tcp!server!24800 %s\n", argv0);
	exits("usage");
}

void
main(int argc, char** argv)
{
	ARGBEGIN{
	default:
		usage();
	}ARGEND

	if(argc)
		usage();

	if((kbdfd = open("/dev/kbdin", OWRITE)) < 0)
		sysfatal("open: %r");

	if((mousefd = open("/dev/mousein", OWRITE)) < 0)
		sysfatal("open: %r");

	/* optional */
	mouseinfd = open("/dev/mousein", OREAD);
	snarffd = open("/dev/snarf", ORDWR);
	readsnarf();

	io();

	exits(nil);
}