Plan 9 from Bell Labs’s /usr/web/sources/contrib/cinap_lenrek/synergy/synergyfs.c

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


#include <u.h>
#include <stdio.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <draw.h>

typedef struct Keyboard Keyboard;
typedef struct Mouse Mouse;
typedef struct Queue Queue;
typedef struct Msg Msg;
typedef struct Snarfbuf Snarfbuf;

enum 
{
	Qdir,
	Qcons,
	Qmouse,
	Qsnarf,
	Nqid,
};

/* Mouse.ctl */
enum
{
	Mnone		= 0,
	Mxy			= (1<<0),
	Mxyrelative	= (1<<1),
	Mclearbuttons	= (1<<2),
	Msetbuttons	= (1<<3),
	Mresetbuttons	= (1<<4),
	Mresized		= (1<<5),
	Mmove		= (1<<6),
	Mlocal		= (1<<7),
	Mlocalwrite	= (1<<8),
};

/* Keyboard.ctl */
enum
{
	Knone		= 0,
	Kup			= 1,
	Kdown		= 2,
	Krepeat		= 3,
	Ksend		= 4,
};

enum
{
	Ftext,
	Fbmp,
	Fhtml,
	Fmax,
};

struct Snarfbuf
{
	int			len;
	char			*buf;
};

struct Keyboard
{
	int			ctl;

	int			key;
	int			mod;
	int			button;
	int			repeat;

	int			send;
};

struct Mouse
{
	int			ctl;

	Point			xy;
	int			buttons;
};

struct Queue
{
	Lock			lock;
	Req			*head;
	Req			**tail;
};

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

static char *synergyaddr = nil;

static int mousefd;
static Channel *mousechan;

static int consfd;
static Channel *conschan;

static Queue qmouse;
static Queue qcons;

static Lock snarflock;
static int snarfversion = 0;
static Snarfbuf snarfbuf;
static int snarfchange = 0;

static int screenchange = 0;
static Point screenmouse;

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

static 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 's':
			vas = va_arg(arg, char*);
			n += (4 + strlen(vas));
			break;
		}
		fmt++;
	}
	return n;
}

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

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

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

	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';
			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 = 	(int)msg->p[0];
					break;
				case 2:
					*vai = 	((int)msg->p[0])<<8 | 
							((int)msg->p[1]);
					break;
				case 4:
					*vai = 	((int)msg->p[0])<<24 | 
							((int)msg->p[1])<<16 | 
							((int)msg->p[2])<<8 | 
							((int)msg->p[3]);
					break;
				}
			}
			msg->p+=q;
			break;
		}

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

static 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 '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;
}

static 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;
}

static 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;
}

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

	va_start(arg, fmt);
	l = vpackmsglen(fmt, arg);
	buf = emalloc9p(l+4);
	m.p = buf+4;
	m.e = m.p + l;
	n = vpackmsg(&m, fmt, arg);
	va_end(arg);
	m.p = buf;
	packmsg(&m, "%4i", l);
	if(write(fd, buf, l+4)!=l+4){
		free(buf);
		return -1;
	}
	free(buf);
	return n;
}

static void
initqueue(Queue *q)
{
	q->head = nil;
	q->tail = &(q->head);
}

static void
enqueuereq(Queue *q, Req *x)
{
	lock(&q->lock);
	x->aux = nil;
	*(q->tail) = x;
	q->tail = &x->aux;
	unlock(&q->lock);
}

static Req*
dequeuereq(Queue *q, Req *x)
{
	Req **r;

	lock(&q->lock);
	if(x==nil)
		x = q->head;
	for(r=&(q->head); *r; r=(Req**)&((*r)->aux)){
		if(*r != x)
			continue;
		if((*r = x->aux) == nil)
			q->tail = r;
		x->aux = nil;
		unlock(&q->lock);
		return x;
	}
	unlock(&q->lock);
	return nil;
}

static int
sendqueue(Queue *q, void *buf, int len)
{
	Req *r;

	if(r = dequeuereq(q, nil)){
		if(r->ifcall.count < len){
			respond(r, "buffer too small");
			return 0;
		}
		r->ofcall.count = len;
		memcpy(r->ofcall.data, buf, len);
		respond(r, nil);
		return 1;
	} else {
		return 0;
	}
}

static void
mousereadproc(void *)
{
	for(;;){
		char buf[50];
		Mouse mouse;
		char c;
		int x, y, b;

		if(readn(mousefd, buf, 49)!=49){
			fprint(2, "error reading mouse\n");
			exits("readmouse");
		}
		buf[49] = '\0';
		sscanf(buf, "%c%11d %11d %11d", &c, &x, &y, &b);

		mouse.ctl = Mlocal | Mxy | Mclearbuttons | Msetbuttons;
		if(c == 'r'){
       		        if(getwindow(display, Refnone) < 0)
          		           sysfatal("resize failed: %r");
			mouse.ctl |= Mresized;
		}
		mouse.xy.x = x;
		mouse.xy.y = y;
		mouse.buttons = b;
		send(mousechan, &mouse);
	}
}

static void
mousewrite(Req *req)
{
	Mouse mouse;
	char buf[50];
	int x, y;

	if(req->ifcall.count >= sizeof(buf)){
		respond(req, "buffer too big");
		return;
	}
	memcpy(buf, req->ifcall.data, req->ifcall.count);
	buf[req->ifcall.count] = '\0';	
	if(sscanf(buf, "m%d %d", &x, &y)!=2){
		respond(req, "bad data");
		return;
	}
	req->ofcall.count = req->ifcall.count;
	respond(req, nil);
	mouse.ctl = Mlocalwrite | Mmove | Mxy;
	mouse.xy.x = x;
	mouse.xy.y = y;

	send(mousechan, &mouse);
}

static void
consreadproc(void *)
{
	Keyboard k;

	for(;;){
		uchar b[1];

		if(readn(consfd, b, sizeof(b))!=sizeof(b)){
			fprint(2, "error reading cons\n");
			exits("readcons");
		}

		k.ctl = Ksend;
		k.send = b[0];
		send(conschan, &k);
	}
}

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

		sleep(200);
		fd = open("#v/vgactl", OWRITE);
		if(fd < 0)
			return;
		fprint(fd, "blank");
		close(fd);
	} else {
		Mouse m;
		m.ctl = Mmove;
		send(mousechan, &m);
	}
}

/* synergy protocol handling */

static void
synergyproc(void *)
{
	int clip[2];

	int fd;
	uchar *buf = nil;
	int buflen = 0;
	int oldsnarfversion;
	int ignoremove;

	ignoremove = 0;
	oldsnarfversion = snarfversion;
	fd = -1;
reconnect:
	if(fd >= 0)
		close(fd);
	fd = dial(synergyaddr, 0, 0, 0);
	if(fd < 0) {
		sleep(10000);
		goto reconnect;
	}
	for(;;){
		Mouse mouse;
		Keyboard keyboard;
		int i, x, y, seq, on, key, btn, rep, mod, major, minjor, cfmt;
		ulong msgid;

		Msg m;
		int l;

		if(snarfchange){
			snarfchange = 0;
			for(i=0; i<2; i++){
				clip[i] = 1;
				if(writemsg(fd, "CCLP%1i%4i", i, seq)!=2)
					goto reconnect;
			}
		}
		if(screenchange){
			screenchange = 0;
			if(writemsg(fd, "DINF%2i%2i%2i%2i%2i%2i%2i", 
				screen->r.min.x,
				screen->r.min.y,
				screen->r.max.x,
				screen->r.max.y,
				0,			/* size of warp zone (obsolete) */
				screenmouse.x, 
				screenmouse.y		/* current mouse position */
			)!=7){
				goto reconnect;
			}
			ignoremove = 1;
		}

		if(buflen < 4){
			buflen = 256;
			buf = erealloc9p(buf, buflen);
		}

		if(readn(fd, buf, 4)!=4){
			fprint(2,"read msg size failed: %r\n");
			goto reconnect;
		}
		m.p = buf;
		m.e = m.p + buflen;
		unpackmsg(&m, "%4i", &l);
		if(l<4 || l>1024*1024){
			fprint(2, "invalid msg size\n");
			goto reconnect;
		}
		if(buflen < l){
			buflen = l;
			buf = erealloc9p(buf, buflen);
		}
		m.p = buf;
		m.e = m.p + buflen;
		if(readn(fd, m.p, l)!=l){
			fprint(2, "read msg failed: %r\n");
			goto reconnect;
		}

		m.e = m.p + l;
		unpackmsg(&m, "%4i", &msgid);

#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('Q','I','N','F'):	/* query info from server */
			screenchange = 1;
			break;

		case MSGID('S','y','n','e'):	/* hello from server */
			if(unpackmsg(&m, "rgy%2i%2i", &major, &minjor)!=2)
				goto unhandled;
			if(writemsg(fd, "Synergy%2i%2i%s", 1, 3, sysname())!=3)
				goto reconnect;
			break;

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

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

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

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

		case MSGID('C','I','N','N'):	/* enter */
			oldsnarfversion = snarfversion;
			if(unpackmsg(&m, "%2i%2i%4i%2i", &x, &y, &seq, &mod)!=4)
				goto unhandled;
			mouse.xy.x = x;
			mouse.xy.y = y;
			mouse.ctl = Mxy | Mmove | Mclearbuttons;
			send(mousechan, &mouse);
			break;

		case MSGID('C','C','L','P'):	/* grab clipboard */
			if(unpackmsg(&m, "%1i", &i)!=1)
				goto unhandled;
			clip[i] = 0;
			break;

		case MSGID('C','O','U','T'):	/* leave */
			lock(&snarflock);
			if(snarfversion>oldsnarfversion && snarfbuf.len>0){
				Msg om;
				uchar *buf;
				int l;
				char fmt[] = "%4iDCLP%1i%4i%4i%4i%4i%4i";

				l = packmsglen(fmt, 0,0,0,0,0,0,0);
				buf = emalloc9p(l);

				for(i=0; i<2; i++){
					/* ignore the clipboards we dont own */
					if(clip[i]==0)
						continue;

					om.p = buf;
					om.e = buf + l;
					packmsg(&om, fmt,
						(l - 4) + snarfbuf.len,// message length
						i,				// clipboard id
						seq,				// sequence number from CINN
						4+4+4+snarfbuf.len,	// size of clipboard data
						1,				// num of clipboard records
						Ftext,			// 1st record type
						snarfbuf.len);		// 1st record size
					if(write(fd, buf, l)!=l){
						free(buf);
						unlock(&snarflock);
						goto reconnect;
					}
					if(write(fd, snarfbuf.buf, snarfbuf.len)!=snarfbuf.len){
						free(buf);
						unlock(&snarflock);
						goto reconnect;
					}
				}

				free(buf);
			}
			unlock(&snarflock);
			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%4i", nil, nil, nil)!=3)
				goto unhandled;

			/*
			 * this can fail if number of formats is 0, so we
			 * just break out
			 */
			if(unpackmsg(&m, "%4i%4i%4i",
				nil,		// number of formats
				&cfmt,	// 1st format type
				&buflen	// 1st format size
			)!=3)
				break;

			/* we only handle HTML and Text */
			if((cfmt!=Ftext) && (cfmt!=Fhtml))
				break;

			/* check remaining data length */
			if((m.e-m.p) < buflen)
				buflen = (m.e-m.p);
			if(buflen <= 0)
				break;

			lock(&snarflock);
			snarfbuf.buf = erealloc9p(snarfbuf.buf, buflen+1);
			snarfbuf.len = buflen;
			memcpy(snarfbuf.buf, m.p, buflen);
			snarfbuf.buf[buflen] = '\0';
			snarfversion++; 
			oldsnarfversion = snarfversion;
			unlock(&snarflock);
			break;

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

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

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

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

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

		case MSGID('D','M','M','V'):	/* mousemove */
			if(ignoremove)
				break;
			if(unpackmsg(&m, "%2i%2i", &x, &y)!=2)
				goto unhandled;
			mouse.xy.x = x;
			mouse.xy.y = y;
			mouse.ctl = Mxy | Mmove;
			send(mousechan, &mouse);
			break;

		case MSGID('D','M','R','M'):	/* mousemove relative */
			if(ignoremove)
				break;
			if(unpackmsg(&m, "%2i%2i", &x, &y)!=2)
				goto unhandled;
			mouse.xy.x = x;
			mouse.xy.y = y;
			mouse.ctl = Mxyrelative | Mmove;
			send(mousechan, &mouse);
			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.ctl = Msetbuttons;
			if(y > 0)
				mouse.buttons = 1<<3;
			else
				mouse.buttons = 1<<4;
			send(mousechan, &mouse);
			mouse.ctl = Mresetbuttons;
			send(mousechan, &mouse);
			break;

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

static void
mousechangeproc(void *)
{
	int resized;
	int ignorelocal;
	int ignoremove;

	Mouse mouse;
	Mouse m;

	resized = 0;
	ignorelocal = 0;
	ignoremove = 0;

	// initial mouse state
	mouse.buttons = mouse.xy.x = mouse.xy.y = 0;

	while(recv(mousechan, &m) > 0){
		vlong msec;
		char buf[50];

		if(m.ctl & Mresized){
			if(m.ctl & Mxy){
				screenmouse.x = m.xy.x;
				screenmouse.y = m.xy.y;
			}
			screenchange = 1;
			resized = 1;
		}

		if(m.ctl & Mlocalwrite){
			if(m.ctl & Mxy){
				screenmouse.x = m.xy.x;
				screenmouse.y = m.xy.y;
			}
			screenchange = 1;
		}

		// ignore local mouse events
		if(!resized && ignorelocal && ignorelocal-- && (m.ctl&Mlocal))
			continue;

		if(m.ctl & Mxy) {
			mouse.xy.x = m.xy.x;
			mouse.xy.y = m.xy.y;
		}
		if(m.ctl & Mxyrelative){
			mouse.xy.x += m.xy.x;
			mouse.xy.y += m.xy.y;
		}
	
		if(m.ctl & Mclearbuttons)
			mouse.buttons = 0;
		if(m.ctl & Msetbuttons)
			mouse.buttons |= m.buttons;
		if(m.ctl & Mresetbuttons)
			mouse.buttons &= ~m.buttons;

		msec = nsec()/1000000LL;

		snprint(buf, sizeof(buf), "%c%11d %11d %11d %11lud ",
			resized ? 'r' : 'm',
			mouse.xy.x, 
			mouse.xy.y, 
			mouse.buttons,
			(ulong)msec);

		if((!ignoremove && (m.ctl&Mmove)) || resized) {
			// writing mousefd to set cursor causes a read in mousereadproc()
			// that has local button values, so we ignore any Mlocal in the 
			// next 4 mousec recv()s that are not Mresized
			ignorelocal = 6;
			write(mousefd, buf, 49);
		}

		/*
		 * while we processing this message (write to /dev/mouse to set cursor pos), 
		 * maybe synergyproc has already send a Mmove-message to mousechan, so we just
		 * ignore the next Mmove message. we just dont want the cursor
		 * to jump arround :-)
		 */
		ignoremove = 0;
		if(m.ctl&Mlocalwrite)
			ignoremove = 1;

		if(sendqueue(&qmouse, buf, 49)){
			// only reset resized state if we are able to notify the client
			resized = 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
{0xefff,	1,	0x7f, 0x00, 0x00},	// del
{0x0000,	0,	0x00, 0x00, 0x00},
};

static void
conschangeproc(void *)
{
	Keyboard k;

	while(recv(conschan, &k) > 0){
		uchar b[3];
		int n;
		int i;
		int r;

		n = 0;
		switch(k.ctl){
		default:
			k.repeat = 0;
			break;

		case Ksend:
			n = 1;
			b[0] = k.send&0xFF;
			k.repeat = 1;
			break;

		case Kdown:
			k.repeat = 1;
		case Krepeat:
			// we dont want to loop that long
			if(k.repeat > 20)
				k.repeat = 20;
			for(i=0; map[i][0]; i++){
				if(map[i][0]<k.key)
					continue;
				if(map[i][0]==k.key){
					n = map[i][1];
					//fprint(2, "n=%d\n", n);
					switch(n){
					case 3:
						b[0] = map[i][2]&0xff;
						b[1] = map[i][3]&0xff;
						b[2] = map[i][4]&0xff;
						break;
					case 2:
						b[0] = map[i][2]&0xff;
						b[1] = map[i][3]&0xff;
						break;
					case 1:
						b[0] = map[i][2]&0xff;
						break;
					}
				} else {
					//fprint(2, "no match 0x%x\n", k.key);
					if(k.key&~0x7F){
						n = 0;
						break;
					}
					n = 1;
					if(k.mod == 2) {
						switch(k.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;
						default:
							b[0] = k.key&0x7F;
						}
					}else
						b[0] = k.key&0x7F;
				}
				break;
			}
			break;
		}

		for(r=0; r<k.repeat; r++){
			for(i=0; i<n; i++){
				int try;
				try = 4;
				while(--try && sendqueue(&qcons, &b[i], 1)==0){
					sleep(4);
				}
			}
		}
	}
}

int
fillstat(ulong qid, Dir *d)
{
	memset(d, 0, sizeof(Dir));

	d->uid = "synergy";
	d->gid = "synergy";
	d->muid = "";
	d->qid = (Qid){qid, 0, 0};
	d->atime = time(0);

	switch(qid) {
	case Qdir:
		d->name = "/";
		d->qid.type = QTDIR;
		d->mode = DMDIR|0777;
		break;
	case Qcons:
		d->name = "cons";
		d->mode = 0666;
		break;
	case Qmouse:
		d->name = "mouse";
		d->mode = 0666;
		break;
	case Qsnarf:
		d->qid.vers = snarfversion;
		d->name = "snarf";
		d->mode = 0666;
		break;
	}
	return 1;
}


int
readtopdir(Fid*, uchar *buf, long off, int cnt, int blen)
{
	int i, m, n;
	long pos;
	Dir d;

	n = 0;
	pos = 0;
	for (i = 1; i < Nqid; i++){
		fillstat(i, &d);
		m = convD2M(&d, &buf[n], blen-n);
		if(off <= pos){
			if(m <= BIT16SZ || m > cnt)
				break;
			n += m;
			cnt -= m;
		}
		pos += m;
	}
	return n;
}

static void
fsattach(Req *r)
{
	char *spec;

	spec = r->ifcall.aname;
	if(spec && spec[0]) {
		respond(r, "invalid attach specifier");
		return;
	}

	r->fid->qid = (Qid){Qdir, 0, QTDIR};
	r->ofcall.qid = r->fid->qid;
	respond(r, nil);
}

static void
fsstat(Req *r)
{
	fillstat((ulong)r->fid->qid.path, &r->d);

	r->d.name = estrdup9p(r->d.name);
	r->d.uid = estrdup9p(r->d.uid);
	r->d.gid = estrdup9p(r->d.gid);
	r->d.muid = estrdup9p(r->d.muid);

	respond(r, nil);
}

static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
	switch((ulong)fid->qid.path) {
	case Qdir:
		if (strcmp(name, "..") == 0) {
			*qid = (Qid){Qdir, 0, QTDIR};
			fid->qid = *qid;
			return nil;
		}
		if (strcmp(name, "cons") == 0) {
			*qid = (Qid){Qcons, 0, 0};
			fid->qid = *qid;
			return nil;
		}
		if (strcmp(name, "mouse") == 0) {
			*qid = (Qid){Qmouse, 0, 0};
			fid->qid = *qid;
			return nil;
		}
		if (strcmp(name, "snarf") == 0) {
			*qid = (Qid){Qsnarf, 0, 0};
			qid->vers = snarfversion;
			fid->qid = *qid;
			return nil;
		}
		return "file not found";
		
	default:
		return "walk in non-directory";
	}
}


static void
fsopen(Req *r)
{
	int omode;
	Fid *fid;
	ulong path;

	fid = r->fid;
	path = (ulong)fid->qid.path;
	omode = r->ifcall.mode;
	
	switch(path) {
	case Qdir:
		if (omode == OREAD)
			respond(r, nil);
		else
			respond(r, "permission denied");
		return;
	default:
		switch(path) {
		case Qcons:
		case Qmouse:
			goto permok;
		case Qsnarf:
			// create a temporary Snarfbuf if opend writable and attach it
			// to fid->aux
			omode &= OMASK;
			if((omode==OWRITE) || (omode==ORDWR)){
				Snarfbuf *b;

				b = emalloc9p(sizeof(Snarfbuf));
				b->len = 0;
				b->buf = nil;
				fid->aux = b;
			} else {
				fid->aux = nil;
			}
			goto permok;
		}
		respond(r, "permission denied");
		return;
permok:
		/*  handle open */
		respond(r, nil);
		return;
	}
}

static void
fsread(Req *r)
{
	uchar *buf;
	long count;
	vlong offset;

	offset = r->ifcall.offset;
	count = r->ifcall.count;
	buf = (uchar*)r->ofcall.data;

	switch((ulong)r->fid->qid.path) {
	case Qdir:
		r->ofcall.count = readtopdir(r->fid, buf, offset, count, count);
		respond(r, nil);
		return;

	case Qcons:
		enqueuereq(&qcons, r);
		break;

	case Qmouse:
		enqueuereq(&qmouse, r);
		break;

	case Qsnarf:
		lock(&snarflock);
		if(snarfbuf.len - offset < count)
			count = snarfbuf.len - offset;
		if(count < 0)
			count = 0;
		if(count > 0)
			memcpy(buf, snarfbuf.buf + offset, count);
		r->ofcall.count = count;
		unlock(&snarflock);

		respond(r, nil);
		break;
	}
}

static void
fswrite(Req *r)
{
	char e[ERRMAX];
	int c;

	switch((ulong)r->fid->qid.path) {
	default:
		respond(r, "bug in fs");
		break;

	case Qcons:
		if((c = write(consfd, r->ifcall.data, r->ifcall.count)) < 0){
			rerrstr(e, sizeof(e));
			respond(r, e);
		}
		r->ofcall.count = c;
		respond(r, nil);
		break;

	case Qmouse:
		mousewrite(r);
		break;

	case Qsnarf:
		/*
		 * if snarf was opend writable, we had attached a temporary 
		 * Snarfbuf* to fid->aux
		 */
		if(r->fid->aux){
			int count;

			if((count = r->ifcall.count) > 0){
				Snarfbuf *b;
				char *p;

				b = r->fid->aux;
				p = erealloc9p(b->buf, b->len + count + 1);
				memcpy(p + b->len, r->ifcall.data, count);
				b->buf = p;
				b->len += count;
				b->buf[b->len] = '\0';
			}
			r->ofcall.count = count;
			respond(r, nil);
		} else {
			// something wrong in fsopen
			respond(r, "bug in fs");
		}
		break;
	}
}

static void
fsflush(Req *r)
{
	switch((ulong)r->oldreq->fid->qid.path) {
	case Qcons:
		if(dequeuereq(&qcons, r->oldreq)){
			respond(r->oldreq, "interrupted");
		}
		break;
	case Qmouse:
		if(dequeuereq(&qmouse, r->oldreq)){
			respond(r->oldreq, "interrupted");
		}
		break;
	}
	respond(r, nil);
}

static void
fsdestroyfid(Fid *fid)
{
	switch((ulong)fid->qid.path){
	case Qsnarf:
		if(fid->aux){
			Snarfbuf *b;

			b = fid->aux;
			fid->aux = nil;

			if((b->len > 0) && b->buf){

				lock(&snarflock);
				if(snarfbuf.buf)
					free(snarfbuf.buf);
				snarfbuf.buf = b->buf;
				snarfbuf.len = b->len;
				snarfversion++;

				// this fires the CCLP message in synergyproc
				snarfchange = 1;
				unlock(&snarflock);

				b->buf = nil;
				b->len = 0;
			}
			free(b);
		}
		break;
	}
}

Srv fs = {
	.attach=			fsattach,
	.walk1=			fswalk1,
	.open=			fsopen,
	.read=			fsread,
	.write=			fswrite,
	.stat=			fsstat,
	.flush=			fsflush,
	.destroyfid=		fsdestroyfid,
};

void
usage(void)
{
	fprint(2, "usage: synergyfs [-m mntpnt] [net!]server!24800\n");
	exits("usage");
}

void
threadmain(int argc, char** argv)
{
	char* mtpt = "/dev";

	ARGBEGIN{
	case 'm':
		mtpt = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND
	
	if(initdraw(nil, nil, argv[0]) < 0){
		fprint(2, "stats: initdraw failed: %r\n");
		exits("initdraw");
	}

	synergyaddr = argv[argc-1];

	if(synergyaddr==nil)
		usage();

	snarfbuf.buf = nil;
	snarfbuf.len = 0;

	screenmouse.x = 0;
	screenmouse.y = 0;

	mousechan = chancreate(sizeof(Mouse), 0);
	conschan = chancreate(sizeof(Keyboard), 0);

	if((mousefd = open("/dev/mouse", ORDWR)) < 0){
		fprint(2, "error open mouse: %r\m");
		exits("open");
	}
	if((consfd = open("/dev/cons", ORDWR)) < 0){
		fprint(2, "error open cons: %r\m");
		exits("open");
	}
	fprint(consfd, "rawon");

	initqueue(&qcons);
	initqueue(&qmouse);

	proccreate(mousereadproc, nil, 8192);
	proccreate(mousechangeproc, nil, 8192);
	proccreate(consreadproc, nil, 8192);
	proccreate(conschangeproc, nil, 8192);
	proccreate(synergyproc, nil, 8192);

	threadpostmountsrv(&fs, "synergy", mtpt, MBEFORE);
}

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.