428 lines
7.6 KiB
C
428 lines
7.6 KiB
C
#include "vnc.h"
|
|
#include "vncv.h"
|
|
|
|
static struct {
|
|
char *name;
|
|
int num;
|
|
} enctab[] = {
|
|
"copyrect", EncCopyRect,
|
|
"corre", EncCorre,
|
|
"hextile", EncHextile,
|
|
"raw", EncRaw,
|
|
"rre", EncRre,
|
|
"mousewarp", EncMouseWarp,
|
|
"desktopsize", EncDesktopSize,
|
|
"xdesktopsize", EncXDesktopSize,
|
|
};
|
|
|
|
static uchar *pixbuf;
|
|
static uchar *linebuf;
|
|
static int vpixb;
|
|
static int pixb;
|
|
static void (*pixcp)(uchar*, uchar*);
|
|
|
|
static void
|
|
vncsetdim(Vnc *v, Rectangle dim)
|
|
{
|
|
v->dim = rectsubpt(dim, dim.min);
|
|
linebuf = realloc(linebuf, v->dim.max.x * vpixb);
|
|
pixbuf = realloc(pixbuf, v->dim.max.x * pixb * v->dim.max.y);
|
|
if(linebuf == nil || pixbuf == nil)
|
|
sysfatal("can't allocate pix decompression storage");
|
|
lockdisplay(display);
|
|
adjustwin(v, 0);
|
|
unlockdisplay(display);
|
|
}
|
|
|
|
static void
|
|
vncrdcolor(Vnc *v, uchar *color)
|
|
{
|
|
vncrdbytes(v, color, vpixb);
|
|
|
|
if(cvtpixels)
|
|
(*cvtpixels)(color, color, 1);
|
|
}
|
|
|
|
void
|
|
sendencodings(Vnc *v)
|
|
{
|
|
char *f[16];
|
|
int enc[16], nenc, i, j, nf;
|
|
|
|
nf = tokenize(encodings, f, nelem(f));
|
|
nenc = 0;
|
|
for(i=0; i<nf; i++){
|
|
for(j=0; j<nelem(enctab); j++)
|
|
if(strcmp(f[i], enctab[j].name) == 0)
|
|
break;
|
|
if(j == nelem(enctab)){
|
|
print("warning: unknown encoding %s\n", f[i]);
|
|
continue;
|
|
}
|
|
enc[nenc++] = enctab[j].num;
|
|
}
|
|
|
|
vnclock(v);
|
|
vncwrchar(v, MSetEnc);
|
|
vncwrchar(v, 0);
|
|
vncwrshort(v, nenc);
|
|
for(i=0; i<nenc; i++)
|
|
vncwrlong(v, enc[i]);
|
|
vncflush(v);
|
|
vncunlock(v);
|
|
}
|
|
|
|
void
|
|
requestupdate(Vnc *v, int incremental)
|
|
{
|
|
Rectangle r;
|
|
|
|
lockdisplay(display);
|
|
flushimage(display, 1);
|
|
r = rectsubpt(screen->r, screen->r.min);
|
|
unlockdisplay(display);
|
|
vnclock(v);
|
|
if(incremental == 0 && (v->canresize&2)!=0 && !eqrect(r, v->dim)){
|
|
vncwrchar(v, MSetDesktopSize);
|
|
vncwrchar(v, 0);
|
|
vncwrpoint(v, r.max);
|
|
vncwrchar(v, 1);
|
|
vncwrchar(v, 0);
|
|
vncwrlong(v, v->screen[0].id);
|
|
vncwrrect(v, r);
|
|
vncwrlong(v, v->screen[0].flags);
|
|
} else
|
|
rectclip(&r, v->dim);
|
|
vncwrchar(v, MFrameReq);
|
|
vncwrchar(v, incremental);
|
|
vncwrrect(v, r);
|
|
vncflush(v);
|
|
vncunlock(v);
|
|
}
|
|
|
|
static Rectangle
|
|
clippixbuf(Rectangle r, int maxx, int maxy)
|
|
{
|
|
int y, h, stride1, stride2;
|
|
|
|
if(r.min.x > maxx || r.min.y > maxy){
|
|
r.max.x = 0;
|
|
return r;
|
|
}
|
|
if(r.max.y > maxy)
|
|
r.max.y = maxy;
|
|
if(r.max.x <= maxx)
|
|
return r;
|
|
|
|
stride2 = Dx(r) * pixb;
|
|
r.max.x = maxx;
|
|
stride1 = Dx(r) * pixb;
|
|
h = Dy(r);
|
|
for(y = 0; y < h; y++)
|
|
memmove(&pixbuf[y * stride1], &pixbuf[y * stride2], stride1);
|
|
|
|
return r;
|
|
}
|
|
|
|
/* must be called with display locked */
|
|
static void
|
|
updatescreen(Rectangle r)
|
|
{
|
|
int b, bb;
|
|
|
|
lockdisplay(display);
|
|
if(r.max.x > Dx(screen->r) || r.max.y > Dy(screen->r)){
|
|
r = clippixbuf(r, Dx(screen->r), Dy(screen->r));
|
|
if(r.max.x == 0){
|
|
unlockdisplay(display);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* assume load image fails only because of resize
|
|
*/
|
|
b = Dx(r) * pixb * Dy(r);
|
|
bb = loadimage(screen, rectaddpt(r, screen->r.min), pixbuf, b);
|
|
if(bb != b && verbose)
|
|
fprint(2, "loadimage %d on %R for %R returned %d: %r\n", b, rectaddpt(r, screen->r.min), screen->r, bb);
|
|
unlockdisplay(display);
|
|
}
|
|
|
|
static void
|
|
fillrect(Rectangle r, int stride, uchar *color)
|
|
{
|
|
int x, xe, y, off;
|
|
|
|
y = r.min.y;
|
|
off = y * stride;
|
|
for(; y < r.max.y; y++){
|
|
xe = off + r.max.x * pixb;
|
|
for(x = off + r.min.x * pixb; x < xe; x += pixb)
|
|
(*pixcp)(&pixbuf[x], color);
|
|
off += stride;
|
|
}
|
|
}
|
|
|
|
static void
|
|
loadbuf(Vnc *v, Rectangle r, int stride)
|
|
{
|
|
int off, y;
|
|
|
|
if(cvtpixels){
|
|
y = r.min.y;
|
|
off = y * stride;
|
|
for(; y < r.max.y; y++){
|
|
vncrdbytes(v, linebuf, Dx(r) * vpixb);
|
|
(*cvtpixels)(&pixbuf[off + r.min.x * pixb], linebuf, Dx(r));
|
|
off += stride;
|
|
}
|
|
}else{
|
|
y = r.min.y;
|
|
off = y * stride;
|
|
for(; y < r.max.y; y++){
|
|
vncrdbytes(v, &pixbuf[off + r.min.x * pixb], Dx(r) * pixb);
|
|
off += stride;
|
|
}
|
|
}
|
|
}
|
|
|
|
static Rectangle
|
|
hexrect(ushort u)
|
|
{
|
|
int x, y, w, h;
|
|
|
|
x = u>>12;
|
|
y = (u>>8)&15;
|
|
w = ((u>>4)&15)+1;
|
|
h = (u&15)+1;
|
|
|
|
return Rect(x, y, x+w, y+h);
|
|
}
|
|
|
|
|
|
static void
|
|
dohextile(Vnc *v, Rectangle r, int stride)
|
|
{
|
|
ulong bg, fg, c;
|
|
int enc, nsub, sx, sy, w, h, th, tw;
|
|
Rectangle sr, ssr;
|
|
|
|
fg = bg = 0;
|
|
h = Dy(r);
|
|
w = Dx(r);
|
|
for(sy = 0; sy < h; sy += HextileDim){
|
|
th = h - sy;
|
|
if(th > HextileDim)
|
|
th = HextileDim;
|
|
for(sx = 0; sx < w; sx += HextileDim){
|
|
tw = w - sx;
|
|
if(tw > HextileDim)
|
|
tw = HextileDim;
|
|
|
|
sr = Rect(sx, sy, sx + tw, sy + th);
|
|
enc = vncrdchar(v);
|
|
if(enc & HextileRaw){
|
|
loadbuf(v, sr, stride);
|
|
continue;
|
|
}
|
|
|
|
if(enc & HextileBack)
|
|
vncrdcolor(v, (uchar*)&bg);
|
|
fillrect(sr, stride, (uchar*)&bg);
|
|
|
|
if(enc & HextileFore)
|
|
vncrdcolor(v, (uchar*)&fg);
|
|
|
|
if(enc & HextileRects){
|
|
nsub = vncrdchar(v);
|
|
(*pixcp)((uchar*)&c, (uchar*)&fg);
|
|
while(nsub-- > 0){
|
|
if(enc & HextileCols)
|
|
vncrdcolor(v, (uchar*)&c);
|
|
ssr = rectaddpt(hexrect(vncrdshort(v)), sr.min);
|
|
fillrect(ssr, stride, (uchar*)&c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
dorectangle(Vnc *v)
|
|
{
|
|
ulong type;
|
|
long n, stride;
|
|
ulong color;
|
|
Point p;
|
|
Rectangle r, subr, maxr;
|
|
|
|
r = vncrdrect(v);
|
|
type = vncrdlong(v);
|
|
switch(type){
|
|
case EncMouseWarp:
|
|
mousewarp(r.min);
|
|
return;
|
|
case EncDesktopSize:
|
|
v->canresize |= 1;
|
|
vncsetdim(v, r);
|
|
return;
|
|
case EncXDesktopSize:
|
|
v->canresize |= 2;
|
|
n = vncrdlong(v)>>24;
|
|
if(n <= 0)
|
|
break;
|
|
v->screen[0].id = vncrdlong(v);
|
|
v->screen[0].rect = vncrdrect(v);
|
|
v->screen[0].flags = vncrdlong(v);
|
|
while(--n > 0){
|
|
vncrdlong(v);
|
|
vncrdrect(v);
|
|
vncrdlong(v);
|
|
}
|
|
vncsetdim(v, v->screen[0].rect);
|
|
return;
|
|
}
|
|
|
|
if(!rectinrect(r, v->dim))
|
|
sysfatal("bad rectangle from server: %R not in %R", r, v->dim);
|
|
maxr = rectsubpt(r, r.min);
|
|
stride = maxr.max.x * pixb;
|
|
|
|
switch(type){
|
|
default:
|
|
sysfatal("bad rectangle encoding from server");
|
|
break;
|
|
case EncRaw:
|
|
loadbuf(v, maxr, stride);
|
|
updatescreen(r);
|
|
break;
|
|
|
|
case EncCopyRect:
|
|
p = vncrdpoint(v);
|
|
lockdisplay(display);
|
|
p = addpt(p, screen->r.min);
|
|
r = rectaddpt(r, screen->r.min);
|
|
draw(screen, r, screen, nil, p);
|
|
unlockdisplay(display);
|
|
break;
|
|
|
|
case EncRre:
|
|
case EncCorre:
|
|
n = vncrdlong(v);
|
|
vncrdcolor(v, (uchar*)&color);
|
|
fillrect(maxr, stride, (uchar*)&color);
|
|
while(n-- > 0){
|
|
vncrdcolor(v, (uchar*)&color);
|
|
if(type == EncRre)
|
|
subr = vncrdrect(v);
|
|
else
|
|
subr = vncrdcorect(v);
|
|
if(!rectinrect(subr, maxr))
|
|
sysfatal("bad encoding from server");
|
|
fillrect(subr, stride, (uchar*)&color);
|
|
}
|
|
updatescreen(r);
|
|
break;
|
|
|
|
case EncHextile:
|
|
dohextile(v, r, stride);
|
|
updatescreen(r);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
pixcp8(uchar *dst, uchar *src)
|
|
{
|
|
*dst = *src;
|
|
}
|
|
|
|
static void
|
|
pixcp16(uchar *dst, uchar *src)
|
|
{
|
|
*(ushort*)dst = *(ushort*)src;
|
|
}
|
|
|
|
static void
|
|
pixcp32(uchar *dst, uchar *src)
|
|
{
|
|
*(ulong*)dst = *(ulong*)src;
|
|
}
|
|
|
|
static void
|
|
pixcp24(uchar *dst, uchar *src)
|
|
{
|
|
dst[0] = src[0];
|
|
dst[1] = src[1];
|
|
dst[2] = src[2];
|
|
}
|
|
|
|
static int
|
|
calcpixb(int bpp)
|
|
{
|
|
if(bpp / 8 * 8 != bpp)
|
|
sysfatal("can't handle your screen");
|
|
return bpp / 8;
|
|
}
|
|
|
|
void
|
|
readfromserver(Vnc *v)
|
|
{
|
|
uchar type;
|
|
uchar junk[100];
|
|
long n;
|
|
|
|
vpixb = calcpixb(v->bpp);
|
|
pixb = calcpixb(screen->depth);
|
|
switch(pixb){
|
|
case 1:
|
|
pixcp = pixcp8;
|
|
break;
|
|
case 2:
|
|
pixcp = pixcp16;
|
|
break;
|
|
case 3:
|
|
pixcp = pixcp24;
|
|
break;
|
|
case 4:
|
|
pixcp = pixcp32;
|
|
break;
|
|
default:
|
|
sysfatal("can't handle your screen: bad depth %d", pixb);
|
|
}
|
|
vncsetdim(v, v->dim);
|
|
for(;;){
|
|
type = vncrdchar(v);
|
|
switch(type){
|
|
default:
|
|
sysfatal("bad message from server");
|
|
break;
|
|
case MFrameUpdate:
|
|
vncrdchar(v);
|
|
n = vncrdshort(v);
|
|
while(n-- > 0)
|
|
dorectangle(v);
|
|
requestupdate(v, 1);
|
|
break;
|
|
|
|
case MSetCmap:
|
|
vncrdbytes(v, junk, 3);
|
|
n = vncrdshort(v);
|
|
vncgobble(v, n*3*2);
|
|
break;
|
|
|
|
case MBell:
|
|
break;
|
|
|
|
case MSAck:
|
|
break;
|
|
|
|
case MSCut:
|
|
vncrdbytes(v, junk, 3);
|
|
n = vncrdlong(v);
|
|
writesnarf(v, n);
|
|
break;
|
|
}
|
|
}
|
|
}
|