ref: e1c4261fb2cb095a18d79c3a4374c7eff74bf892
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 > 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 {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) continue; if(map[i][0]==key){ n = map[i][1]; 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 { if(key&~0x7F) break; n = 1; 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; default: b[0] = key&0x7F; } }else b[0] = key&0x7F; } fprint(kbdfd, "%c%.*s%c", ctl==Kup?'R':'r', n, (char*)b, 0); break; } } 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; } 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); }