ref: e1c4261fb2cb095a18d79c3a4374c7eff74bf892
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Sun Jan 1 22:09:30 EST 2023
barrera: initial commit
--- /dev/null
+++ b/barrera.c
@@ -1,0 +1,784 @@
+/*
+ * 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);
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,7 @@
+</$objtype/mkfile
+
+BIN=/$objtype/bin
+TARG=barrera
+OFILES=barrera.$O
+
+</sys/src/cmd/mkone