ref: 2ee329ad6c911022fe4f2ea972c3642eb000c1bc
dir: /barrera.c/
/*
* 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 > 4*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;
}
void
writemsg(char *fmt, ...)
{
static Msg m;
va_list arg;
int l;
va_start(arg, fmt);
l = 4 + vpackmsglen(fmt, arg);
sizemsg(&m, l);
packmsg(&m, "%4i", l-4);
vpackmsg(&m, fmt, arg);
va_end(arg);
if(write(1, m.b, l)!=l)
sysfatal("writemsg: %r");
}
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;
writemsg("Barrier%2i%2i%s", 1, 6, sysname());
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 */
writemsg("CALV");
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);
writemsg("DCLP%1i%4i%1i%s", i, seq, 1, size);
for(l = 0; l < z; l += n){
n = z - l;
if(n > 1024)
n = 1024;
writemsg("DCLP%1i%4i%1i%*", i, seq, 2, n, snarfbuf.p+l);
}
writemsg("DCLP%1i%4i%1i%s", i, seq, 3, "");
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();
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); /* current mouse position */
}
if(readsnarf()){
for(i=0; i<2; i++){
writemsg("CCLP%1i%4i", i, seq);
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);
}