ref: 9e4a5b55ba158232bf2781f5914260ff74da5dde
dir: /rd-thread.c/
#include <u.h> #include <libc.h> #include <draw.h> #include <thread.h> #include <keyboard.h> #include <mouse.h> #include <cursor.h> #include <auth.h> #include "dat.h" #include "fns.h" #define STACK 8192 Rdp conn = { .fd = -1, .depth = 16, .windom = "", .passwd = "", .shell = "", .rwd = "", }; Mousectl *mousectl; Keyboardctl *keyboardctl; char Eshort[]= "short data"; char Esmall[]= "buffer too small"; char Ebignum[]= "number too big"; void sendmouse(Rdp* c, Mouse m); static void keyboardthread(void*); static void mousethread(void*); static void snarfthread(void*); static void usage(void) { fprint(2, "usage: rd [-0A] [-T title] [-a depth] [-c wdir] [-d dom] [-k keyspec] [-n term] [-s shell] [net!]server[!port]\n"); threadexitsall("usage"); } void threadmain(int argc, char *argv[]) { int doauth; char *server, *addr, *keyspec; UserPasswd *creds; Rdp* c; c = &conn; keyspec = ""; doauth = 1; ARGBEGIN { case 'A': doauth = 0; break; case 'k': keyspec = EARGF(usage()); break; case 'T': c->label = strdup(EARGF(usage())); break; case 'd': c->windom = strdup(EARGF(usage())); break; case 's': c->shell = strdup(EARGF(usage())); break; case 'c': c->rwd = strdup(EARGF(usage())); break; case 'a': c->depth = atol(EARGF(usage())); break; case '0': c->wantconsole = 1; break; default: usage(); } ARGEND if (argc != 1) usage(); server = argv[0]; c->local = getenv("sysname"); c->user = getenv("user"); if(c->local == nil) sysfatal("set $sysname or use -n\n"); if(c->user == nil) sysfatal("set $user"); if(doauth){ creds = auth_getuserpasswd(auth_getkey, "proto=pass service=rdp %s", keyspec); if(creds == nil) fprint(2, "factotum: %r\n"); else { c->user = creds->user; c->passwd = creds->passwd; } }else c->user = ""; if(c->label == nil) c->label = smprint("rd %s", server); initvc(c); addr = netmkaddr(server, "tcp", "3389"); c->fd = dial(addr, nil, nil, nil); if(c->fd < 0) sysfatal("dial %s: %r", addr); if(x224handshake(c) < 0) sysfatal("X.224 handshake: %r"); initscreen(c); if(rdphandshake(c) < 0) sysfatal("handshake: %r"); mousectl = initmouse(nil, screen); if(mousectl == nil){ fprint(2, "rd: can't initialize mouse: %r\n"); exits("mouse"); } keyboardctl = initkeyboard(nil); if(keyboardctl == nil){ fprint(2, "rd: can't initialize keyboard: %r\n"); exits("keyboard"); } proccreate(keyboardthread, c, STACK); proccreate(mousethread, c, STACK); proccreate(snarfthread, c, STACK); threadsetname("mainthread"); readnet(c); x224hangup(c); if(!c->active) threadexitsall(nil); if(c->hupreason) sysfatal("disconnect reason code %d", c->hupreason); sysfatal("hangup"); } void initscreen(Rdp* c) { if(initdraw(drawerror, nil, c->label) < 0) sysfatal("initdraw: %r"); display->locking = 1; unlockdisplay(display); c->ysz = Dy(screen->r); c->xsz = (Dx(screen->r) +3) & ~3; } void readnet(Rdp* c) { Msg r; for(;;){ if(readmsg(c, &r) <= 0) return; switch(r.type){ case Mclosing: return; case Mvchan: scanvc(c, &r); break; case Aupdate: scanupdates(c, &r); break; case 0: fprint(2, "unsupported PDU\n"); break; default: fprint(2, "r.type %d is not expected\n", r.type); } } } void scanupdates(Rdp* c, Msg* m) { int n; uchar *p, *ep; Share u; p = m->data; ep = m->data + m->ndata; for(; p < ep; p += n){ n = m->getshare(&u, p, ep-p); if(n < 0) sysfatal("scanupdates: %r"); switch(u.type){ default: if(u.type != 0) fprint(2, "scanupdates: unhandled %d\n", u.type); break; case ShDeactivate: deactivating(c, &u); break; case ShActivate: // server may engage capability re-exchange activating(c, &u); break; case ShEinfo: c->hupreason = u.err; break; case ShUorders: scanorders(c, &u); break; case ShUimg: scanimgupdate(c, &u); break; case ShUcmap: scancmap(c, &u); break; case ShUwarp: warpmouse(u.x, u.y); break; case Aflow: break; } } } void scanimgupdate(Rdp *c, Share* as) { uchar* p, *ep; int n, err, nr; static Image* img; Rectangle r, rs, d; Imgupd iu; assert(as->type == ShUimg); p = as->data; ep = as->data + as->ndata; nr = as->nrect; rs = rectaddpt(Rpt(ZP, Pt(c->xsz, c->ysz)), screen->r.min); if(display->locking) lockdisplay(display); if(img==nil || !eqrect(img->r, rs)){ if(img != nil) freeimage(img); img = allocimage(display, rs, c->chan, 0, DNofill); if(img == nil) sysfatal("scanimgupdate: %r"); } while(p<ep && nr>0){ /* 2.2.9.1.1.3.1.2.2 Bitmap Data (TS_BITMAP_DATA) */ if((n = getimgupd(&iu, p, ep-p)) < 0) sysfatal("getimgupd: %r"); if(iu.depth != img->depth) sysfatal("bad image depth"); d.min = Pt(iu.x, iu.y); d.max = Pt(iu.xm+1, iu.ym+1); r.min = ZP; r.max = Pt(iu.xsz, iu.ysz); r = rectaddpt(r, img->r.min); err = (iu.iscompr? loadrle : loadbmp)(img, r, iu.bytes, iu.nbytes, c->cmap); if(err < 0) sysfatal("%r"); draw(screen, rectaddpt(d, screen->r.min), img, nil, img->r.min); p += n; nr--; } flushimage(display, 1); if(display->locking) unlockdisplay(display); } void scancmap(Rdp* c, Share* as) { int i, n; uchar *p, *ep, *cmap; p = as->data; ep = as->data + as->ndata; cmap = c->cmap; n = GSHORT(p+4); p += 8; if(n > sizeof(c->cmap)){ fprint(2, "scancmap: data too big"); return; } if(p+3*n > ep) sysfatal(Eshort); for(i = 0; i<n; p+=3) cmap[i++] = rgb2cmap(p[0], p[1], p[2]); } static void keyboardthread(void* v) { Rune r; Rdp* c; c = v; threadsetname("keyboardthread"); for(;;){ recv(keyboardctl->c, &r); sendkbd(c, r); } } static void mousethread(void* v) { Rdp* c; enum { MResize, MMouse, NMALT }; static Alt alts[NMALT+1]; c = v; threadsetname("mousethread"); alts[MResize].c = mousectl->resizec; alts[MResize].v = nil; alts[MResize].op = CHANRCV; alts[MMouse].c = mousectl->c; alts[MMouse].v = &mousectl->Mouse; alts[MMouse].op = CHANRCV; for(;;){ switch(alt(alts)){ case MResize: eresized(c, 1); break; case MMouse: sendmouse(c, mousectl->Mouse); break; } } } void warpmouse(int x, int y) { moveto(mousectl, Pt(x, y)); } static void snarfthread(void* v) { Rdp* c; c = v; threadsetname("snarfthread"); initsnarf(); pollsnarf(c); threadexits("snarf eof"); }