diff options
Diffstat (limited to 'krdc/vnc')
-rw-r--r-- | krdc/vnc/Makefile.am | 16 | ||||
-rw-r--r-- | krdc/vnc/colour.c | 415 | ||||
-rw-r--r-- | krdc/vnc/d3des.c | 440 | ||||
-rw-r--r-- | krdc/vnc/d3des.h | 51 | ||||
-rw-r--r-- | krdc/vnc/desktop.c | 1613 | ||||
-rw-r--r-- | krdc/vnc/hextile.c | 129 | ||||
-rw-r--r-- | krdc/vnc/kvncview.cpp | 828 | ||||
-rw-r--r-- | krdc/vnc/kvncview.h | 121 | ||||
-rw-r--r-- | krdc/vnc/pointerlatencyometer.h | 83 | ||||
-rw-r--r-- | krdc/vnc/rfbproto.c | 1335 | ||||
-rw-r--r-- | krdc/vnc/rfbproto.h | 957 | ||||
-rw-r--r-- | krdc/vnc/sockets.c | 325 | ||||
-rw-r--r-- | krdc/vnc/threads.cpp | 392 | ||||
-rw-r--r-- | krdc/vnc/threads.h | 126 | ||||
-rw-r--r-- | krdc/vnc/tight.c | 610 | ||||
-rw-r--r-- | krdc/vnc/vncauth.c | 161 | ||||
-rw-r--r-- | krdc/vnc/vncauth.h | 30 | ||||
-rw-r--r-- | krdc/vnc/vnchostpref.cpp | 127 | ||||
-rw-r--r-- | krdc/vnc/vnchostpref.h | 52 | ||||
-rw-r--r-- | krdc/vnc/vncprefs.ui | 165 | ||||
-rw-r--r-- | krdc/vnc/vncprefs.ui.h | 53 | ||||
-rw-r--r-- | krdc/vnc/vnctypes.h | 73 | ||||
-rw-r--r-- | krdc/vnc/vncviewer.h | 186 | ||||
-rw-r--r-- | krdc/vnc/zlib.c | 157 |
24 files changed, 8445 insertions, 0 deletions
diff --git a/krdc/vnc/Makefile.am b/krdc/vnc/Makefile.am new file mode 100644 index 00000000..23956297 --- /dev/null +++ b/krdc/vnc/Makefile.am @@ -0,0 +1,16 @@ +KDE_CXXFLAGS = $(USE_THREADS) + +METASOURCES = AUTO + +noinst_LTLIBRARIES = libvnc.la + +libvnc_la_SOURCES = kvncview.cpp threads.cpp colour.c d3des.c desktop.c \ + rfbproto.c sockets.c vncauth.c vncprefs.ui vnchostpref.cpp + +noinst_HEADERS = kvncview.h rfbproto.h vncviewer.h vnctypes.h vncauth.h \ + pointerlatencyometer.h threads.h d3des.h vnchostpref.h + +libvnc_la_LIBADD = $(LIB_KDEUI) $(LIBJPEG) +libvnc_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) + +INCLUDES= -I$(top_srcdir)/krdc -I.. $(all_includes) diff --git a/krdc/vnc/colour.c b/krdc/vnc/colour.c new file mode 100644 index 00000000..a51d6e61 --- /dev/null +++ b/krdc/vnc/colour.c @@ -0,0 +1,415 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * colour.c - functions to deal with colour - i.e. RFB pixel formats, X visuals + * and colormaps. Thanks to Grant McDorman for some of the ideas used here. + */ + +#include "vncviewer.h" +#include <limits.h> + + +#define INVALID_PIXEL 0xffffffff +#define MAX_CMAP_SIZE 256 +#define BGR233_SIZE 256 +unsigned long BGR233ToPixel[BGR233_SIZE]; + +Colormap cmap; +Visual *vis; +unsigned int visdepth, visbpp; +Bool allocColorFailed = False; + +static int nBGR233ColoursAllocated; + +static int GetBPPForDepth(int depth); +static void SetupBGR233Map(void); +static void AllocateExactBGR233Colours(void); +static Bool AllocateBGR233Colour(int r, int g, int b); + + +/* + * SetVisualAndCmap() deals with the wonderful world of X "visuals" (which are + * equivalent to the RFB protocol's "pixel format"). Having decided on the + * best visual, it also creates a colormap if necessary, sets the appropriate + * resources on the toplevel widget, and sets up the myFormat structure to + * describe the pixel format in terms that the RFB server will be able to + * understand. + * + * The algorithm for deciding which visual to use is as follows: + * + * If forceOwnCmap is true then we try to use a PseudoColor visual - we first + * see if there's one of the same depth as the RFB server, followed by an 8-bit + * deep one. + * + * If forceTrueColour is true then we try to use a TrueColor visual - if + * requestedDepth is set then it must be of that depth, otherwise any depth + * will be used. + * + * Otherwise, we use the X server's default visual and colormap. If this is + * TrueColor then we just ask the RFB server for this format. If the default + * isn't TrueColor, or if useBGR233 is true, then we ask the RFB server for + * BGR233 pixel format and use a lookup table to translate to the nearest + * colours provided by the X server. + */ + +void +SetVisualAndCmap() +{ + /* just use default visual and colormap */ + + vis = DefaultVisual(dpy,DefaultScreen(dpy)); + visdepth = DefaultDepth(dpy,DefaultScreen(dpy)); + visbpp = GetBPPForDepth(visdepth); + cmap = DefaultColormap(dpy,DefaultScreen(dpy)); + + if (!appData.useBGR233 && (vis->class == TrueColor)) { + + myFormat.bitsPerPixel = visbpp; + myFormat.depth = visdepth; + myFormat.trueColour = 1; + myFormat.bigEndian = (ImageByteOrder(dpy) == MSBFirst); + myFormat.redShift = ffs(vis->red_mask) - 1; + myFormat.greenShift = ffs(vis->green_mask) - 1; + myFormat.blueShift = ffs(vis->blue_mask) - 1; + myFormat.redMax = vis->red_mask >> myFormat.redShift; + myFormat.greenMax = vis->green_mask >> myFormat.greenShift; + myFormat.blueMax = vis->blue_mask >> myFormat.blueShift; + + fprintf(stderr, + "Using default colormap which is TrueColor. Pixel format:\n"); + PrintPixelFormat(&myFormat); + return; + } + + appData.useBGR233 = True; + + myFormat.bitsPerPixel = 8; + myFormat.depth = 8; + myFormat.trueColour = 1; + myFormat.bigEndian = 0; + myFormat.redMax = 7; + myFormat.greenMax = 7; + myFormat.blueMax = 3; + myFormat.redShift = 0; + myFormat.greenShift = 3; + myFormat.blueShift = 6; + + fprintf(stderr, + "Using default colormap and translating from BGR233. Pixel format:\n"); + PrintPixelFormat(&myFormat); + + SetupBGR233Map(); +} + + +/* + * GetBPPForDepth looks through the "pixmap formats" to find the bits-per-pixel + * for the given depth. + */ + +static int +GetBPPForDepth(int depth) +{ + XPixmapFormatValues *format; + int nformats; + int i; + int bpp; + + format = XListPixmapFormats(dpy, &nformats); + + for (i = 0; i < nformats; i++) { + if (format[i].depth == depth) + break; + } + + if (i == nformats) { + fprintf(stderr,"no pixmap format for depth %d???\n", depth); + exit(1); + } + + bpp = format[i].bits_per_pixel; + + XFree(format); + + if (bpp != 1 && bpp != 8 && bpp != 16 && bpp != 32) { + fprintf(stderr,"Can't cope with %d bits-per-pixel. Sorry.\n", bpp); + exit(1); + } + + return bpp; +} + + + +/* + * SetupBGR233Map() sets up the BGR233ToPixel array. + * + * It calls AllocateExactBGR233Colours to allocate some exact BGR233 colours + * (limited by space in the colormap and/or by the value of the nColours + * resource). If the number allocated is less than BGR233_SIZE then it fills + * the rest in using the "nearest" colours available. How this is done depends + * on the value of the useSharedColours resource. If it's false, we use only + * colours from the exact BGR233 colours we've just allocated. If it's true, + * then we also use other clients' "shared" colours available in the colormap. + */ + +static void +SetupBGR233Map(void) +{ + int r, g, b; + long i; + unsigned long nearestPixel = 0; + int cmapSize; + XColor cmapEntry[MAX_CMAP_SIZE]; + Bool exactBGR233[MAX_CMAP_SIZE]; + Bool shared[MAX_CMAP_SIZE]; + Bool usedAsNearest[MAX_CMAP_SIZE]; + int nSharedUsed = 0; + + if (visdepth > 8) { + appData.nColours = 256; /* ignore nColours setting for > 8-bit deep */ + } + + for (i = 0; i < BGR233_SIZE; i++) { + BGR233ToPixel[i] = INVALID_PIXEL; + } + + AllocateExactBGR233Colours(); + + fprintf(stderr,"Got %d exact BGR233 colours out of %d\n", + nBGR233ColoursAllocated, appData.nColours); + + if (nBGR233ColoursAllocated < BGR233_SIZE) { + + if (visdepth > 8) { /* shouldn't get here */ + fprintf(stderr,"Error: couldn't allocate BGR233 colours even though " + "depth is %d\n", visdepth); + exit(1); + } + + cmapSize = (1 << visdepth); + + for (i = 0; i < cmapSize; i++) { + cmapEntry[i].pixel = i; + exactBGR233[i] = False; + shared[i] = False; + usedAsNearest[i] = False; + } + + XQueryColors(dpy, cmap, cmapEntry, cmapSize); + + /* mark all our exact BGR233 pixels */ + + for (i = 0; i < BGR233_SIZE; i++) { + if (BGR233ToPixel[i] != INVALID_PIXEL) + exactBGR233[BGR233ToPixel[i]] = True; + } + + if (appData.useSharedColours) { + + /* Try to find existing shared colours. This is harder than it sounds + because XQueryColors doesn't tell us whether colours are shared, + private or unallocated. What we do is go through the colormap and for + each pixel try to allocate exactly its RGB values. If this returns a + different pixel then it's definitely either a private or unallocated + pixel, so no use to us. If it returns us the same pixel again, then + it's likely that it's a shared colour - however, it is possible that + it was actually an unallocated pixel, which we've now allocated. We + minimise this possibility by going through the pixels in reverse order + - this helps becuse the X server allocates new pixels from the lowest + number up, so it should only be a problem for the lowest unallocated + pixel. Got that? */ + + for (i = cmapSize-1; i >= 0; i--) { + if (!exactBGR233[i] && + XAllocColor(dpy, cmap, &cmapEntry[i])) { + + if (cmapEntry[i].pixel == (unsigned long) i) { + + shared[i] = True; /* probably shared */ + + } else { + + /* "i" is either unallocated or private. We have now unnecessarily + allocated cmapEntry[i].pixel. Free it. */ + + XFreeColors(dpy, cmap, &cmapEntry[i].pixel, 1, 0); + } + } + } + } + + /* Now fill in the nearest colours */ + + for (r = 0; r < 8; r++) { + for (g = 0; g < 8; g++) { + for (b = 0; b < 4; b++) { + if (BGR233ToPixel[(b<<6) | (g<<3) | r] == INVALID_PIXEL) { + + unsigned long minDistance = ULONG_MAX; + + for (i = 0; i < cmapSize; i++) { + if (exactBGR233[i] || shared[i]) { + unsigned long distance + = (abs(cmapEntry[i].red - r * 65535 / 7) + + abs(cmapEntry[i].green - g * 65535 / 7) + + abs(cmapEntry[i].blue - b * 65535 / 3)); + + if (distance < minDistance) { + minDistance = distance; + nearestPixel = i; + } + } + } + + BGR233ToPixel[(b<<6) | (g<<3) | r] = nearestPixel; + if (shared[nearestPixel] && !usedAsNearest[nearestPixel]) + nSharedUsed++; + usedAsNearest[nearestPixel] = True; + } + } + } + } + + /* Tidy up shared colours which we allocated but aren't going to use */ + + for (i = 0; i < cmapSize; i++) { + if (shared[i] && !usedAsNearest[i]) { + XFreeColors(dpy, cmap, (unsigned long *)&i, 1, 0); + } + } + + fprintf(stderr,"Using %d existing shared colours\n", nSharedUsed); + } +} + + +/* + * AllocateExactBGR233Colours() attempts to allocate each of the colours in the + * BGR233 colour cube, stopping when an allocation fails. The order it does + * this in is such that we should get a fairly well spread subset of the cube, + * however many allocations are made. There's probably a neater algorithm for + * doing this, but it's not obvious to me anyway. The way this algorithm works + * is: + * + * At each stage, we introduce a new value for one of the primaries, and + * allocate all the colours with the new value of that primary and all previous + * values of the other two primaries. We start with r=0 as the "new" value + * for r, and g=0, b=0 as the "previous" values of g and b. So we get: + * + * New primary value Previous values of other primaries Colours allocated + * ----------------- ---------------------------------- ----------------- + * r=0 g=0 b=0 r0 g0 b0 + * g=7 r=0 b=0 r0 g7 b0 + * b=3 r=0 g=0,7 r0 g0 b3 + * r0 g7 b3 + * r=7 g=0,7 b=0,3 r7 g0 b0 + * r7 g0 b3 + * r7 g7 b0 + * r7 g7 b3 + * g=3 r=0,7 b=0,3 r0 g3 b0 + * r0 g3 b3 + * r7 g3 b0 + * r7 g3 b3 + * ....etc. + * */ + +static void +AllocateExactBGR233Colours(void) +{ + int rv[] = {0,7,3,5,1,6,2,4}; + int gv[] = {0,7,3,5,1,6,2,4}; + int bv[] = {0,3,1,2}; + int rn = 0; + int gn = 1; + int bn = 1; + int ri, gi, bi; + + nBGR233ColoursAllocated = 0; + + while (1) { + if (rn == 8) + break; + + ri = rn; + for (gi = 0; gi < gn; gi++) { + for (bi = 0; bi < bn; bi++) { + if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi])) + return; + } + } + rn++; + + if (gn == 8) + break; + + gi = gn; + for (ri = 0; ri < rn; ri++) { + for (bi = 0; bi < bn; bi++) { + if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi])) + return; + } + } + gn++; + + if (bn < 4) { + + bi = bn; + for (ri = 0; ri < rn; ri++) { + for (gi = 0; gi < gn; gi++) { + if (!AllocateBGR233Colour(rv[ri], gv[gi], bv[bi])) + return; + } + } + bn++; + } + } +} + + +/* + * AllocateBGR233Colour() attempts to allocate the given BGR233 colour as a + * shared colormap entry, storing its pixel value in the BGR233ToPixel array. + * r is from 0 to 7, g from 0 to 7 and b from 0 to 3. It fails either when the + * allocation fails or when we would exceed the number of colours specified in + * the nColours resource. + */ + +static Bool +AllocateBGR233Colour(int r, int g, int b) +{ + XColor c; + + if (nBGR233ColoursAllocated >= appData.nColours) + return False; + + c.red = r * 65535 / 7; + c.green = g * 65535 / 7; + c.blue = b * 65535 / 3; + + if (!XAllocColor(dpy, cmap, &c)) + return False; + + BGR233ToPixel[(b<<6) | (g<<3) | r] = c.pixel; + + nBGR233ColoursAllocated++; + + return True; +} diff --git a/krdc/vnc/d3des.c b/krdc/vnc/d3des.c new file mode 100644 index 00000000..8a358ce6 --- /dev/null +++ b/krdc/vnc/d3des.c @@ -0,0 +1,440 @@ +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and + * triple-length support removed for use in VNC. Also the bytebit[] array + * has been reversed so that the most significant bit in each byte of the + * key is ignored, not the least significant. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* D3DES (V5.09) - + * + * A portable, public domain, version of the Data Encryption Standard. + * + * Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge. + * Thanks to: Dan Hoey for his excellent Initial and Inverse permutation + * code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis + * Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau, + * for humouring me on. + * + * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. + * (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. + */ + +#include "d3des.h" + +static void scrunch(unsigned char *, unsigned long *); +static void unscrun(unsigned long *, unsigned char *); +static void desfunc(unsigned long *, unsigned long *); +static void cookey(unsigned long *); + +static unsigned long KnL[32] = { 0L }; +static unsigned long KnR[32] = { 0L }; +static unsigned long Kn3[32] = { 0L }; +static unsigned char Df_Key[24] = { + 0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef, + 0xfe,0xdc,0xba,0x98,0x76,0x54,0x32,0x10, + 0x89,0xab,0xcd,0xef,0x01,0x23,0x45,0x67 }; + +static unsigned short bytebit[8] = { + 01, 02, 04, 010, 020, 040, 0100, 0200 }; + +static unsigned long bigbyte[24] = { + 0x800000L, 0x400000L, 0x200000L, 0x100000L, + 0x80000L, 0x40000L, 0x20000L, 0x10000L, + 0x8000L, 0x4000L, 0x2000L, 0x1000L, + 0x800L, 0x400L, 0x200L, 0x100L, + 0x80L, 0x40L, 0x20L, 0x10L, + 0x8L, 0x4L, 0x2L, 0x1L }; + +/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */ + +static unsigned char pc1[56] = { + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; + +static unsigned char totrot[16] = { + 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 }; + +static unsigned char pc2[48] = { + 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, + 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, + 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 }; + +void deskey(key, edf) /* Thanks to James Gillogly & Phil Karn! */ +unsigned char *key; +int edf; +{ + register int i, j, l, m, n; + unsigned char pc1m[56], pcr[56]; + unsigned long kn[32]; + + for ( j = 0; j < 56; j++ ) { + l = pc1[j]; + m = l & 07; + pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0; + } + for( i = 0; i < 16; i++ ) { + if( edf == DE1 ) m = (15 - i) << 1; + else m = i << 1; + n = m + 1; + kn[m] = kn[n] = 0L; + for( j = 0; j < 28; j++ ) { + l = j + totrot[i]; + if( l < 28 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 28; j < 56; j++ ) { + l = j + totrot[i]; + if( l < 56 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 0; j < 24; j++ ) { + if( pcr[pc2[j]] ) kn[m] |= bigbyte[j]; + if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j]; + } + } + cookey(kn); + return; + } + +static void cookey(raw1) +register unsigned long *raw1; +{ + register unsigned long *cook, *raw0; + unsigned long dough[32]; + register int i; + + cook = dough; + for( i = 0; i < 16; i++, raw1++ ) { + raw0 = raw1++; + *cook = (*raw0 & 0x00fc0000L) << 6; + *cook |= (*raw0 & 0x00000fc0L) << 10; + *cook |= (*raw1 & 0x00fc0000L) >> 10; + *cook++ |= (*raw1 & 0x00000fc0L) >> 6; + *cook = (*raw0 & 0x0003f000L) << 12; + *cook |= (*raw0 & 0x0000003fL) << 16; + *cook |= (*raw1 & 0x0003f000L) >> 4; + *cook++ |= (*raw1 & 0x0000003fL); + } + usekey(dough); + return; + } + +void cpkey(into) +register unsigned long *into; +{ + register unsigned long *from, *endp; + + from = KnL, endp = &KnL[32]; + while( from < endp ) *into++ = *from++; + return; + } + +void usekey(from) +register unsigned long *from; +{ + register unsigned long *to, *endp; + + to = KnL, endp = &KnL[32]; + while( to < endp ) *to++ = *from++; + return; + } + +void des(inblock, outblock) +unsigned char *inblock, *outblock; +{ + unsigned long work[2]; + + scrunch(inblock, work); + desfunc(work, KnL); + unscrun(work, outblock); + return; + } + +static void scrunch(outof, into) +register unsigned char *outof; +register unsigned long *into; +{ + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into++ |= (*outof++ & 0xffL); + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into |= (*outof & 0xffL); + return; + } + +static void unscrun(outof, into) +register unsigned long *outof; +register unsigned char *into; +{ + *into++ = (*outof >> 24) & 0xffL; + *into++ = (*outof >> 16) & 0xffL; + *into++ = (*outof >> 8) & 0xffL; + *into++ = *outof++ & 0xffL; + *into++ = (*outof >> 24) & 0xffL; + *into++ = (*outof >> 16) & 0xffL; + *into++ = (*outof >> 8) & 0xffL; + *into = *outof & 0xffL; + return; + } + +static unsigned long SP1[64] = { + 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L, + 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L, + 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L, + 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L, + 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L, + 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L, + 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L, + 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L, + 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L, + 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L, + 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L, + 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L, + 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L, + 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L }; + +static unsigned long SP2[64] = { + 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L, + 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L, + 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L, + 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L, + 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L, + 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L, + 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L, + 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L, + 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L, + 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L, + 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L, + 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L, + 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L, + 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L, + 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L, + 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L }; + +static unsigned long SP3[64] = { + 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L, + 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L, + 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L, + 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L, + 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L, + 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L, + 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L, + 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L, + 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L, + 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L, + 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L, + 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L, + 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L, + 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L, + 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L, + 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L }; + +static unsigned long SP4[64] = { + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L, + 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L, + 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L, + 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L, + 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L, + 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L, + 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L, + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L, + 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L, + 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L }; + +static unsigned long SP5[64] = { + 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L, + 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L, + 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L, + 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L, + 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L, + 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L, + 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L, + 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L, + 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L, + 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L, + 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L, + 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L, + 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L, + 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L, + 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L, + 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L }; + +static unsigned long SP6[64] = { + 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L, + 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L, + 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L, + 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L, + 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L, + 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L, + 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L, + 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L, + 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L, + 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L, + 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L, + 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L, + 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L, + 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L }; + +static unsigned long SP7[64] = { + 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L, + 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L, + 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L, + 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L, + 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L, + 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L, + 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L, + 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L, + 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L, + 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L, + 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L, + 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L, + 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L, + 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L, + 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L, + 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L }; + +static unsigned long SP8[64] = { + 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L, + 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L, + 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L, + 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L, + 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L, + 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L, + 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L, + 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L, + 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L, + 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L, + 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L, + 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L, + 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L, + 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L, + 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L, + 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L }; + +static void desfunc(block, keys) +register unsigned long *block, *keys; +{ + register unsigned long fval, work, right, leftt; + register int round; + + leftt = block[0]; + right = block[1]; + work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; + right ^= work; + leftt ^= (work << 4); + work = ((leftt >> 16) ^ right) & 0x0000ffffL; + right ^= work; + leftt ^= (work << 16); + work = ((right >> 2) ^ leftt) & 0x33333333L; + leftt ^= work; + right ^= (work << 2); + work = ((right >> 8) ^ leftt) & 0x00ff00ffL; + leftt ^= work; + right ^= (work << 8); + right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL; + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL; + + for( round = 0; round < 8; round++ ) { + work = (right << 28) | (right >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = right ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + leftt ^= fval; + work = (leftt << 28) | (leftt >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = leftt ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + right ^= fval; + } + + right = (right << 31) | (right >> 1); + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = (leftt << 31) | (leftt >> 1); + work = ((leftt >> 8) ^ right) & 0x00ff00ffL; + right ^= work; + leftt ^= (work << 8); + work = ((leftt >> 2) ^ right) & 0x33333333L; + right ^= work; + leftt ^= (work << 2); + work = ((right >> 16) ^ leftt) & 0x0000ffffL; + leftt ^= work; + right ^= (work << 16); + work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; + leftt ^= work; + right ^= (work << 4); + *block++ = right; + *block = leftt; + return; + } + +/* Validation sets: + * + * Single-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef + * Plain : 0123 4567 89ab cde7 + * Cipher : c957 4425 6a5e d31d + * + * Double-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 + * Plain : 0123 4567 89ab cde7 + * Cipher : 7f1d 0a77 826b 8aff + * + * Double-length key, double-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 + * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff + * Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7 + * + * Triple-length key, single-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 + * Plain : 0123 4567 89ab cde7 + * Cipher : de0b 7c06 ae5e 0ed5 + * + * Triple-length key, double-length plaintext - + * Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567 + * Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff + * Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5 + * + * d3des V5.0a rwo 9208.07 18:44 Graven Imagery + **********************************************************************/ diff --git a/krdc/vnc/d3des.h b/krdc/vnc/d3des.h new file mode 100644 index 00000000..ea3da44c --- /dev/null +++ b/krdc/vnc/d3des.h @@ -0,0 +1,51 @@ +/* + * This is D3DES (V5.09) by Richard Outerbridge with the double and + * triple-length support removed for use in VNC. + * + * These changes are: + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* d3des.h - + * + * Headers and defines for d3des.c + * Graven Imagery, 1992. + * + * Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge + * (GEnie : OUTER; CIS : [71755,204]) + */ + +#define EN0 0 /* MODE == encrypt */ +#define DE1 1 /* MODE == decrypt */ + +extern void deskey(unsigned char *, int); +/* hexkey[8] MODE + * Sets the internal key register according to the hexadecimal + * key contained in the 8 bytes of hexkey, according to the DES, + * for encryption or decryption according to MODE. + */ + +extern void usekey(unsigned long *); +/* cookedkey[32] + * Loads the internal key register with the data in cookedkey. + */ + +extern void cpkey(unsigned long *); +/* cookedkey[32] + * Copies the contents of the internal key register into the storage + * located at &cookedkey[0]. + */ + +extern void des(unsigned char *, unsigned char *); +/* from[8] to[8] + * Encrypts/Decrypts (according to the key currently loaded in the + * internal key register) one block of eight bytes at address 'from' + * into the block at address 'to'. They can be the same. + */ + +/* d3des.h V5.09 rwo 9208.04 15:06 Graven Imagery + ********************************************************************/ diff --git a/krdc/vnc/desktop.c b/krdc/vnc/desktop.c new file mode 100644 index 00000000..f5a60966 --- /dev/null +++ b/krdc/vnc/desktop.c @@ -0,0 +1,1613 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * Copyright (C) 2002 Tim Jansen. All Rights Reserved. + * Copyright (C) 1999-2001 Anders Lindström + * + * + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * tim@tjansen.de: - removed stuff for krdc + * - merged with shm.c and misc.c + * - added FillRectangle and Sync methods to draw only on + * the image + * - added Zoom functionality, based on rotation funcs from + * SGE by Anders Lindström) + * - added support for softcursor encoding + * + */ + +/* + * desktop.c - functions to deal with "desktop" window. + */ + +#include <X11/Xlib.h> +#include <sys/ipc.h> +#include <sys/shm.h> +#include <X11/extensions/XShm.h> +#include <math.h> +#include <limits.h> +#include "vncviewer.h" + +static XShmSegmentInfo shminfo; +static Bool caughtShmError = False; +static Bool needShmCleanup = False; + +static XShmSegmentInfo zoomshminfo; +static Bool caughtZoomShmError = False; +static Bool needZoomShmCleanup = False; + +static Bool gcInited = False; +GC gc; +GC srcGC, dstGC; /* used for debugging copyrect */ +Dimension dpyWidth, dpyHeight; + +static XImage *image = NULL; +Bool useShm = True; + +static Bool zoomActive = False; +static int zoomWidth, zoomHeight; +static XImage *zoomImage = NULL; +static Bool useZoomShm = True; + +/* for softcursor */ +static char *savedArea = NULL; + +typedef enum { + SOFTCURSOR_UNDER, + SOFTCURSOR_PART_UNDER, + SOFTCURSOR_UNAFFECTED +} SoftCursorState; + +typedef int Sint32; +typedef short Sint16; +typedef char Sint8; +typedef unsigned int Uint32; +typedef unsigned short Uint16; +typedef unsigned char Uint8; + +typedef struct { + int w, h; + unsigned int pitch; + void *pixels; + int BytesPerPixel; +} Surface; + +typedef struct { + Sint16 x, y; + Uint16 w, h; +} Rect; + +static void bgr233cpy(CARD8 *dst, CARD8 *src, int len); +static void CopyDataToScreenRaw(char *buf, int x, int y, int width, int height); +static void CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width,int height); +static void FillRectangleBGR233(CARD8 buf, int x, int y, int width,int height); +static int CheckRectangle(int x, int y, int width, int height); +static SoftCursorState getSoftCursorState(int x, int y, int width, int height); +static void discardCursorSavedArea(void); +static void saveCursorSavedArea(void); + +static void ZoomInit(void); +static void transformZoomSrc(int six, int siy, int siw, int sih, + int *dix, int *diy, int *diw, int *dih, + int srcW, int dstW, int srcH, int dstH); +static void transformZoomDst(int *six, int *siy, int *siw, int *sih, + int dix, int diy, int diw, int dih, + int srcW, int dstW, int srcH, int dstH); +static void ZoomSurfaceSrcCoords(int x, int y, int w, int h, + int *dix, int *diy, int *diw, int *dih, + Surface * src, Surface * dst); +static void ZoomSurfaceCoords32(int sx, int sy, int sw, int sh, + int dx, int dy, Surface * src, Surface * dst); +static void sge_transform(Surface *src, Surface *dst, float xscale, float yscale, + Uint16 qx, Uint16 qy); + + +void +DesktopInit(Window win) +{ + XGCValues gcv; + + image = CreateShmImage(); + + if (!image) { + useShm = False; + image = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL, + si.framebufferWidth, si.framebufferHeight, + BitmapPad(dpy), 0); + + image->data = calloc(image->bytes_per_line * image->height, 1); + if (!image->data) { + fprintf(stderr,"malloc failed\n"); + exit(1); + } + } + + gc = XCreateGC(dpy,win,0,NULL); + + gcv.function = GXxor; + gcv.foreground = 0x0f0f0f0f; + srcGC = XCreateGC(dpy,win,GCFunction|GCForeground,&gcv); + gcv.foreground = 0xf0f0f0f0; + dstGC = XCreateGC(dpy,win,GCFunction|GCForeground,&gcv); + gcInited = True; +} + +/* + * DrawScreenRegionX11Thread + * Never call from any other desktop.c function, only for X11 thread + */ + +void +DrawScreenRegionX11Thread(Window win, int x, int y, int width, int height) { + zoomActive = False; + zoomWidth = 0; + zoomHeight = 0; + + if (!image) + return; + + if (useShm) + XShmPutImage(dpy, win, gc, image, x, y, x, y, width, height, False); + else + XPutImage(dpy, win, gc, image, x, y, x, y, width, height); +} + +/* + * CheckRectangle + */ + +static int CheckRectangle(int x, int y, int width, int height) { + if ((x < 0) || (y < 0)) + return 0; + + if (((x+width) > si.framebufferWidth) || ((y+height) > si.framebufferHeight)) + return 0; + + return 1; +} + +static +void bgr233cpy(CARD8 *dst, CARD8 *src, int len) { + int i; + CARD16 *d16; + CARD32 *d32; + + switch (visbpp) { + case 8: + for (i = 0; i < len; i++) + *(dst++) = (CARD8) BGR233ToPixel[*(src++)]; + break; + case 16: + d16 = (CARD16*) dst; + for (i = 0; i < len; i++) + *(d16++) = (CARD16) BGR233ToPixel[*(src++)]; + break; + case 32: + d32 = (CARD32*) dst; + for (i = 0; i < len; i++) + *(d32++) = (CARD32) BGR233ToPixel[*(src++)]; + break; + default: + fprintf(stderr, "Unsupported softcursor depth %d\n", visbpp); + } +} + + +/* + * CopyDataToScreen. + */ + +void +CopyDataToScreen(char *buf, int x, int y, int width, int height) +{ + SoftCursorState s; + + if (!CheckRectangle(x, y, width, height)) + return; + + LockFramebuffer(); + s = getSoftCursorState(x, y, width, height); + if (s == SOFTCURSOR_PART_UNDER) + undrawCursor(); + + if (!appData.useBGR233) + CopyDataToScreenRaw(buf, x, y, width, height); + else + CopyBGR233ToScreen((CARD8 *)buf, x, y, width, height); + + if (s != SOFTCURSOR_UNAFFECTED) + drawCursor(); + + UnlockFramebuffer(); + SyncScreenRegion(x, y, width, height); +} + +/* + * CopyDataToScreenRaw. + */ + +static void +CopyDataToScreenRaw(char *buf, int x, int y, int width, int height) +{ + int h; + int widthInBytes = width * visbpp / 8; + int scrWidthInBytes = image->bytes_per_line; + char *scr = (image->data + y * scrWidthInBytes + + x * visbpp / 8); + + for (h = 0; h < height; h++) { + memcpy(scr, buf, widthInBytes); + buf += widthInBytes; + scr += scrWidthInBytes; + } +} + +/* + * CopyBGR233ToScreen. + */ + +static void +CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width, int height) +{ + int p, q; + int xoff = 7 - (x & 7); + int xcur; + int fbwb = si.framebufferWidth / 8; + CARD8 *scr1 = ((CARD8 *)image->data) + y * fbwb + x / 8; + CARD8 *scrt; + CARD8 *scr8 = ((CARD8 *)image->data) + y * si.framebufferWidth + x; + CARD16 *scr16 = ((CARD16 *)image->data) + y * si.framebufferWidth + x; + CARD32 *scr32 = ((CARD32 *)image->data) + y * si.framebufferWidth + x; + + switch (visbpp) { + + /* thanks to Chris Hooper for single bpp support */ + + case 1: + for (q = 0; q < height; q++) { + xcur = xoff; + scrt = scr1; + for (p = 0; p < width; p++) { + *scrt = ((*scrt & ~(1 << xcur)) + | (BGR233ToPixel[*(buf++)] << xcur)); + + if (xcur-- == 0) { + xcur = 7; + scrt++; + } + } + scr1 += fbwb; + } + break; + + case 8: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(scr8++) = BGR233ToPixel[*(buf++)]; + } + scr8 += si.framebufferWidth - width; + } + break; + + case 16: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(scr16++) = BGR233ToPixel[*(buf++)]; + } + scr16 += si.framebufferWidth - width; + } + break; + + case 32: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(scr32++) = BGR233ToPixel[*(buf++)]; + } + scr32 += si.framebufferWidth - width; + } + break; + } +} + +/* + * FillRectangle8. + */ + +void +FillRectangle8(CARD8 fg, int x, int y, int width, int height) +{ + SoftCursorState s; + + if (!CheckRectangle(x, y, width, height)) + return; + + s = getSoftCursorState(x, y, width, height); + if (s == SOFTCURSOR_PART_UNDER) + undrawCursor(); + + if (!appData.useBGR233) { + int h; + int widthInBytes = width * visbpp / 8; + int scrWidthInBytes = image->bytes_per_line; + + char *scr = (image->data + y * scrWidthInBytes + + x * visbpp / 8); + + for (h = 0; h < height; h++) { + memset(scr, fg, widthInBytes); + scr += scrWidthInBytes; + } + } else { + FillRectangleBGR233(fg, x, y, width, height); + } + + if (s != SOFTCURSOR_UNAFFECTED) + drawCursor(); +} + +/* + * FillRectangleBGR233. + */ + +static void +FillRectangleBGR233(CARD8 fg, int x, int y, int width, int height) +{ + int p, q; + int xoff = 7 - (x & 7); + int xcur; + int fbwb = si.framebufferWidth / 8; + CARD8 *scr1 = ((CARD8 *)image->data) + y * fbwb + x / 8; + CARD8 *scrt; + CARD8 *scr8 = ((CARD8 *)image->data) + y * si.framebufferWidth + x; + CARD16 *scr16 = ((CARD16 *)image->data) + y * si.framebufferWidth + x; + CARD32 *scr32 = ((CARD32 *)image->data) + y * si.framebufferWidth + x; + + unsigned long fg233 = BGR233ToPixel[fg]; + + switch (visbpp) { + + /* thanks to Chris Hooper for single bpp support */ + + case 1: + for (q = 0; q < height; q++) { + xcur = xoff; + scrt = scr1; + for (p = 0; p < width; p++) { + *scrt = ((*scrt & ~(1 << xcur)) + | (fg233 << xcur)); + + if (xcur-- == 0) { + xcur = 7; + scrt++; + } + } + scr1 += fbwb; + } + break; + + case 8: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(scr8++) = fg233; + } + scr8 += si.framebufferWidth - width; + } + break; + + case 16: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(scr16++) = fg233; + } + scr16 += si.framebufferWidth - width; + } + break; + + case 32: + for (q = 0; q < height; q++) { + for (p = 0; p < width; p++) { + *(scr32++) = fg233; + } + scr32 += si.framebufferWidth - width; + } + break; + } +} + +/* + * FillRectangle16 + */ + +void +FillRectangle16(CARD16 fg, int x, int y, int width, int height) +{ + int i, h; + int scrWidthInBytes = image->bytes_per_line; + + char *scr = (image->data + y * scrWidthInBytes + + x * visbpp / 8); + CARD16 *scr16; + SoftCursorState s; + + if (!CheckRectangle(x, y, width, height)) + return; + + s = getSoftCursorState(x, y, width, height); + if (s == SOFTCURSOR_PART_UNDER) + undrawCursor(); + + for (h = 0; h < height; h++) { + scr16 = (CARD16*) scr; + for (i = 0; i < width; i++) + scr16[i] = fg; + scr += scrWidthInBytes; + } + + if (s != SOFTCURSOR_UNAFFECTED) + drawCursor(); +} + +/* + * FillRectangle32 + */ + +void +FillRectangle32(CARD32 fg, int x, int y, int width, int height) +{ + int i, h; + int scrWidthInBytes = image->bytes_per_line; + SoftCursorState s; + + char *scr = (image->data + y * scrWidthInBytes + + x * visbpp / 8); + CARD32 *scr32; + + if (!CheckRectangle(x, y, width, height)) + return; + + s = getSoftCursorState(x, y, width, height); + if (s == SOFTCURSOR_PART_UNDER) + undrawCursor(); + + for (h = 0; h < height; h++) { + scr32 = (CARD32*) scr; + for (i = 0; i < width; i++) + scr32[i] = fg; + scr += scrWidthInBytes; + } + + if (s != SOFTCURSOR_UNAFFECTED) + drawCursor(); +} + +/* + * CopyDataFromScreen. + */ + +void +CopyDataFromScreen(char *buf, int x, int y, int width, int height) +{ + int widthInBytes = width * visbpp / 8; + int scrWidthInBytes = image->bytes_per_line; + char *src = (image->data + y * scrWidthInBytes + + x * visbpp / 8); + int h; + + if (!CheckRectangle(x, y, width, height)) + return; + + for (h = 0; h < height; h++) { + memcpy(buf, src, widthInBytes); + src += scrWidthInBytes; + buf += widthInBytes; + } +} + +/* + * CopyArea + */ + +void +CopyArea(int srcX, int srcY, int width, int height, int x, int y) +{ + int widthInBytes = width * visbpp / 8; + SoftCursorState sSrc, sDst; + + LockFramebuffer(); + sSrc = getSoftCursorState(srcX, srcY, width, height); + sDst = getSoftCursorState(x, y, width, height); + if ((sSrc != SOFTCURSOR_UNAFFECTED) || + (sDst == SOFTCURSOR_PART_UNDER)) + undrawCursor(); + + if ((srcY+height < y) || (y+height < srcY) || + (srcX+width < x) || (x+width < srcX)) { + + int scrWidthInBytes = image->bytes_per_line; + char *src = (image->data + srcY * scrWidthInBytes + + srcX * visbpp / 8); + char *dst = (image->data + y * scrWidthInBytes + + x * visbpp / 8); + int h; + + if (!CheckRectangle(srcX, srcY, width, height)) { + UnlockFramebuffer(); + return; + } + if (!CheckRectangle(x, y, width, height)) { + UnlockFramebuffer(); + return; + } + + for (h = 0; h < height; h++) { + memcpy(dst, src, widthInBytes); + src += scrWidthInBytes; + dst += scrWidthInBytes; + } + } + else { + char *buf = malloc(widthInBytes*height); + if (!buf) { + UnlockFramebuffer(); + fprintf(stderr, "Out of memory, CopyArea impossible\n"); + return; + } + CopyDataFromScreen(buf, srcX, srcY, width, height); + CopyDataToScreenRaw(buf, x, y, width, height); + free(buf); + } + if ((sSrc != SOFTCURSOR_UNAFFECTED) || + (sDst != SOFTCURSOR_UNAFFECTED)) + drawCursor(); + UnlockFramebuffer(); + SyncScreenRegion(x, y, width, height); +} + +void SyncScreenRegion(int x, int y, int width, int height) { + int dx, dy, dw, dh; + + if (zoomActive) { + Surface src, dest; + src.w = si.framebufferWidth; + src.h = si.framebufferHeight; + src.pitch = image->bytes_per_line; + src.pixels = image->data; + src.BytesPerPixel = visbpp / 8; + dest.w = zoomWidth; + dest.h = zoomHeight; + dest.pitch = zoomImage->bytes_per_line; + dest.pixels = zoomImage->data; + dest.BytesPerPixel = visbpp / 8; + ZoomSurfaceSrcCoords(x, y, width, height, &dx, &dy, &dw, &dh, &src, &dest); + } + else { + dx = x; dy = y; + dw = width; dh = height; + } + DrawScreenRegion(dx, dy, dw, dh); +} + +void SyncScreenRegionX11Thread(int x, int y, int width, int height) { + int dx, dy, dw, dh; + + if (zoomActive) { + Surface src, dest; + src.w = si.framebufferWidth; + src.h = si.framebufferHeight; + src.pitch = image->bytes_per_line; + src.pixels = image->data; + src.BytesPerPixel = visbpp / 8; + dest.w = zoomWidth; + dest.h = zoomHeight; + dest.pitch = zoomImage->bytes_per_line; + dest.pixels = zoomImage->data; + dest.BytesPerPixel = visbpp / 8; + ZoomSurfaceSrcCoords(x, y, width, height, &dx, &dy, &dw, &dh, &src, &dest); + } + else { + dx = x; dy = y; + dw = width; dh = height; + } + DrawAnyScreenRegionX11Thread(dx, dy, dw, dh); +} + +/* + * ToplevelInitBeforeRealization sets the title, geometry and other resources + * on the toplevel window. + */ + +void +ToplevelInit() +{ + dpyWidth = WidthOfScreen(DefaultScreenOfDisplay(dpy)); + dpyHeight = HeightOfScreen(DefaultScreenOfDisplay(dpy)); +} + +/* + * Cleanup - perform shm cleanup operations prior to exiting. + */ + +void +Cleanup() +{ + if (useShm || useZoomShm) + ShmCleanup(); +} + +void +ShmCleanup() +{ + fprintf(stderr,"ShmCleanup called\n"); + if (needShmCleanup) { + shmdt(shminfo.shmaddr); + shmctl(shminfo.shmid, IPC_RMID, 0); + needShmCleanup = False; + } + if (needZoomShmCleanup) { + shmdt(zoomshminfo.shmaddr); + shmctl(zoomshminfo.shmid, IPC_RMID, 0); + needZoomShmCleanup = False; + } +} + +static int +ShmCreationXErrorHandler(Display *d, XErrorEvent *e) +{ + caughtShmError = True; + return 0; +} + +XImage * +CreateShmImage() +{ + XImage *_image; + XErrorHandler oldXErrorHandler; + + if (!XShmQueryExtension(dpy)) + return NULL; + + _image = XShmCreateImage(dpy, vis, visdepth, ZPixmap, NULL, &shminfo, + si.framebufferWidth, si.framebufferHeight); + if (!_image) return NULL; + + shminfo.shmid = shmget(IPC_PRIVATE, + _image->bytes_per_line * _image->height, + IPC_CREAT|0777); + + if (shminfo.shmid == -1) { + XDestroyImage(_image); + return NULL; + } + + shminfo.shmaddr = _image->data = shmat(shminfo.shmid, 0, 0); + + if (shminfo.shmaddr == (char *)-1) { + XDestroyImage(_image); + shmctl(shminfo.shmid, IPC_RMID, 0); + return NULL; + } + + shminfo.readOnly = True; + + oldXErrorHandler = XSetErrorHandler(ShmCreationXErrorHandler); + XShmAttach(dpy, &shminfo); + XSync(dpy, False); + XSetErrorHandler(oldXErrorHandler); + + if (caughtShmError) { + XDestroyImage(_image); + shmdt(shminfo.shmaddr); + shmctl(shminfo.shmid, IPC_RMID, 0); + return NULL; + } + + needShmCleanup = True; + + fprintf(stderr,"Using shared memory PutImage\n"); + + return _image; +} + +void undrawCursor() { + int x, y, w, h; + + if ((imageIndex < 0) || !savedArea) + return; + + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &x, &y, &w, &h); + + if ((w < 1) || (h < 1)) + return; + + CopyDataToScreenRaw(savedArea, x, y, w, h); + discardCursorSavedArea(); +} + +static void drawCursorImage() { + int x, y, w, h, pw, pixelsLeft, processingMask; + int skipLeft, skipRight; + PointerImage *pi = &pointerImages[imageIndex]; + CARD8 *img = (CARD8*) pi->image; + CARD8 *imgEnd = &img[pi->len]; + CARD8 *fb; + + /* check whether the source image has ended (image broken) */ +#define CHECK_IMG(x) if (&img[x] > imgEnd) goto imgError + +/* check whether the end of the framebuffer has been reached (last line) */ +#define CHECK_END() if ((wl == 0) && (h == 1)) return + +/* skip x pixels in the source (x must be < pixelsLeft!) */ +#define SKIP_IMG(x) if ((x > 0) && !processingMask) { \ + CHECK_END(); \ + img += pw * x; \ + CHECK_IMG(0); \ + } + +/* skip x pixels in source and destination */ +#define SKIP_PIXELS(x) { int wl = x; \ + while (pixelsLeft <= wl) { \ + wl -= pixelsLeft; \ + SKIP_IMG(pixelsLeft); \ + CHECK_END(); \ + pixelsLeft = *(img++); \ + CHECK_IMG(0); \ + processingMask = processingMask ? 0 : 1; \ + } \ + pixelsLeft -= wl; \ + SKIP_IMG(wl); \ + } + + if (!img) + return; + + x = cursorX - pi->hotX; + y = cursorY - pi->hotY; + w = pi->w; + h = pi->h; + + if (!rectsIntersect(x, y, w, h, + 0, 0, si.framebufferWidth, si.framebufferHeight)) { + fprintf(stderr, "intersect abort\n"); + return; + } + + pw = myFormat.bitsPerPixel / 8; + processingMask = 1; + pixelsLeft = *(img++); + +/* at this point everything is initialized for the macros */ + + /* skip/clip bottom lines */ + if ((y+h) > si.framebufferHeight) + h = si.framebufferHeight - y; + + /* Skip invisible top lines */ + while (y < 0) { + SKIP_PIXELS(w); + y++; + h--; + } + + /* calculate left/right clipping */ + if (x < 0) { + skipLeft = -x; + w += x; + x = 0; + } + else + skipLeft = 0; + + if ((x+w) > si.framebufferWidth) { + skipRight = (x+w) - si.framebufferWidth; + w = si.framebufferWidth - x; + } + else + skipRight = 0; + + fb = (CARD8*) image->data + y * image->bytes_per_line + x * visbpp / 8; + + /* Paint the thing */ + while (h > 0) { + SKIP_PIXELS(skipLeft); + + { + CARD8 *fbx = fb; + int wl = w; + while (pixelsLeft <= wl) { + wl -= pixelsLeft; + if ((pixelsLeft > 0) && !processingMask) { + int pl = pw * pixelsLeft; + CHECK_IMG(pl); + if (!appData.useBGR233) + memcpy(fbx, img, pl); + else + bgr233cpy(fbx, img, pixelsLeft); + img += pl; + } + + CHECK_END(); + fbx += pixelsLeft * visbpp / 8; + pixelsLeft = *(img++); + + CHECK_IMG(0); + processingMask = processingMask ? 0 : 1; + } + pixelsLeft -= wl; + if ((wl > 0) && !processingMask) { + int pl = pw * wl; + CHECK_IMG(pl); + if (!appData.useBGR233) + memcpy(fbx, img, pl); + else + bgr233cpy(fbx, img, wl); + img += pl; + } + } + + SKIP_PIXELS(skipRight); + fb += image->bytes_per_line; + h--; + } + return; + +imgError: + fprintf(stderr, "Error in softcursor image %d\n", imageIndex); + pointerImages[imageIndex].set = 0; +} + +static void discardCursorSavedArea() { + if (savedArea) + free(savedArea); + savedArea = 0; +} + +static void saveCursorSavedArea() { + int x, y, w, h; + + if (imageIndex < 0) + return; + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &x, &y, &w, &h); + if ((w < 1) || (h < 1)) + return; + discardCursorSavedArea(); + savedArea = malloc(h*image->bytes_per_line); + if (!savedArea) { + fprintf(stderr,"malloc failed, saving cursor not possible\n"); + exit(1); + } + CopyDataFromScreen(savedArea, x, y, w, h); +} + +void drawCursor() { + saveCursorSavedArea(); + drawCursorImage(); +} + +void getBoundingRectCursor(int cx, int cy, int _imageIndex, + int *x, int *y, int *w, int *h) { + int nx, ny, nw, nh; + + if ((_imageIndex < 0) || !pointerImages[_imageIndex].set) { + *x = 0; + *y = 0; + *w = 0; + *h = 0; + return; + } + + nx = cx - pointerImages[_imageIndex].hotX; + ny = cy - pointerImages[_imageIndex].hotY; + nw = pointerImages[_imageIndex].w; + nh = pointerImages[_imageIndex].h; + if (nx < 0) { + nw += nx; + nx = 0; + } + if (ny < 0) { + nh += ny; + ny = 0; + } + if ((nx+nw) > si.framebufferWidth) + nw = si.framebufferWidth - nx; + if ((ny+nh) > si.framebufferHeight) + nh = si.framebufferHeight - ny; + if ((nw <= 0) || (nh <= 0)) { + *x = 0; + *y = 0; + *w = 0; + *h = 0; + return; + } + + *x = nx; + *y = ny; + *w = nw; + *h = nh; +} + +static SoftCursorState getSoftCursorState(int x, int y, int w, int h) { + int cx, cy, cw, ch; + + if (imageIndex < 0) + return SOFTCURSOR_UNAFFECTED; + + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &cx, &cy, &cw, &ch); + + if ((cw == 0) || (ch == 0)) + return SOFTCURSOR_UNAFFECTED; + + if (!rectsIntersect(x, y, w, h, cx, cy, cw, ch)) + return SOFTCURSOR_UNAFFECTED; + if (rectContains(x, y, w, h, cx, cy, cw, ch)) + return SOFTCURSOR_UNDER; + else + return SOFTCURSOR_PART_UNDER; +} + +int rectsIntersect(int x, int y, int w, int h, + int x2, int y2, int w2, int h2) { + if (x2 >= (x+w)) + return 0; + if (y2 >= (y+h)) + return 0; + if ((x2+w2) <= x) + return 0; + if ((y2+h2) <= y) + return 0; + return 1; +} + +int rectContains(int outX, int outY, int outW, int outH, + int inX, int inY, int inW, int inH) { + if (inX < outX) + return 0; + if (inY < outY) + return 0; + if ((inX+inW) > (outX+outW)) + return 0; + if ((inY+inH) > (outY+outH)) + return 0; + return 1; +} + +void rectsJoin(int *nx1, int *ny1, int *nw1, int *nh1, + int x2, int y2, int w2, int h2) { + int ox, oy, ow, oh; + ox = *nx1; + oy = *ny1; + ow = *nw1; + oh = *nh1; + + if (x2 < ox) { + ow += ox - x2; + ox = x2; + } + if (y2 < oy) { + oh += oy - y2; + oy = y2; + } + if ((x2+w2) > (ox+ow)) + ow = (x2+w2) - ox; + if ((y2+h2) > (oy+oh)) + oh = (y2+h2) - oy; + + *nx1 = ox; + *ny1 = oy; + *nw1 = ow; + *nh1 = oh; +} + +XImage * +CreateShmZoomImage() +{ + XImage *_image; + XErrorHandler oldXErrorHandler; + + if (!XShmQueryExtension(dpy)) + return NULL; + + _image = XShmCreateImage(dpy, vis, visdepth, ZPixmap, NULL, &zoomshminfo, + si.framebufferWidth, si.framebufferHeight); + if (!_image) return NULL; + + zoomshminfo.shmid = shmget(IPC_PRIVATE, + _image->bytes_per_line * _image->height, + IPC_CREAT|0777); + + if (zoomshminfo.shmid == -1) { + XDestroyImage(_image); + return NULL; + } + + zoomshminfo.shmaddr = _image->data = shmat(zoomshminfo.shmid, 0, 0); + + if (zoomshminfo.shmaddr == (char *)-1) { + XDestroyImage(_image); + shmctl(zoomshminfo.shmid, IPC_RMID, 0); + return NULL; + } + + zoomshminfo.readOnly = True; + + oldXErrorHandler = XSetErrorHandler(ShmCreationXErrorHandler); + XShmAttach(dpy, &zoomshminfo); + XSync(dpy, False); + XSetErrorHandler(oldXErrorHandler); + + if (caughtZoomShmError) { + XDestroyImage(_image); + shmdt(zoomshminfo.shmaddr); + shmctl(zoomshminfo.shmid, IPC_RMID, 0); + return NULL; + } + + needZoomShmCleanup = True; + + fprintf(stderr,"Using shared memory PutImage\n"); + + return _image; +} + + +/* + * DrawZoomedScreenRegionX11Thread + * Never call from any other desktop.c function, only for X11 thread + */ + +void +DrawZoomedScreenRegionX11Thread(Window win, int zwidth, int zheight, + int x, int y, int width, int height) { + if (!image) + return; + + if (zwidth > si.framebufferWidth) + zwidth = si.framebufferWidth; + if (zheight > si.framebufferHeight) + zheight = si.framebufferHeight; + + if (!zoomActive) { + ZoomInit(); + zoomActive = True; + } + + if ((zoomWidth != zwidth) || (zoomHeight != zheight)) { + Surface src, dest; + + zoomWidth = zwidth; + zoomHeight = zheight; + + src.w = si.framebufferWidth; + src.h = si.framebufferHeight; + src.pitch = image->bytes_per_line; + src.pixels = image->data; + src.BytesPerPixel = visbpp / 8; + dest.w = zwidth; + dest.h = zheight; + dest.pitch = zoomImage->bytes_per_line; + dest.pixels = zoomImage->data; + dest.BytesPerPixel = visbpp / 8; + sge_transform(&src, &dest, + (float)dest.w/(float)src.w, (float)dest.h/(float)src.h, + 0, 0); + + if (useZoomShm) + XShmPutImage(dpy, win, gc, zoomImage, 0, 0, 0, 0, zwidth, zheight, False); + else + XPutImage(dpy, win, gc, zoomImage, 0, 0, 0, 0, zwidth, zheight); + return; + } + + if (useZoomShm) + XShmPutImage(dpy, win, gc, zoomImage, x, y, x, y, width, height, False); + else + XPutImage(dpy, win, gc, zoomImage, x, y, x, y, width, height); +} + + +static void +ZoomInit() +{ + if (zoomImage) + return; + + zoomImage = CreateShmZoomImage(); + + if (!zoomImage) { + useZoomShm = False; + zoomImage = XCreateImage(dpy, vis, visdepth, ZPixmap, 0, NULL, + si.framebufferWidth, si.framebufferHeight, + BitmapPad(dpy), 0); + + zoomImage->data = calloc(zoomImage->bytes_per_line * zoomImage->height, 1); + if (!zoomImage->data) { + fprintf(stderr,"malloc failed\n"); + exit(1); + } + } +} + +static void transformZoomSrc(int six, int siy, int siw, int sih, + int *dix, int *diy, int *diw, int *dih, + int srcW, int dstW, int srcH, int dstH) { + double sx, sy, sw, sh; + double dx, dy, dw, dh; + double wq, hq; + + sx = six; sy = siy; + sw = siw; sh = sih; + + wq = ((double)dstW) / (double) srcW; + hq = ((double)dstH) / (double) srcH; + + dx = sx * wq; + dy = sy * hq; + dw = sw * wq; + dh = sh * hq; + + *dix = dx; + *diy = dy; + *diw = dw+(dx-(int)dx)+0.5; + *dih = dh+(dy-(int)dy)+0.5; +} + +static void transformZoomDst(int *six, int *siy, int *siw, int *sih, + int dix, int diy, int diw, int dih, + int srcW, int dstW, int srcH, int dstH) { + double sx, sy, sw, sh; + double dx, dy, dw, dh; + double wq, hq; + + dx = dix; dy = diy; + dw = diw; dh = dih; + + wq = ((double)dstW) / (double) srcW; + hq = ((double)dstH) / (double) srcH; + + sx = dx / wq; + sy = dy / hq; + sw = dw / wq; + sh = dh / hq; + + *six = sx; + *siy = sy; + *siw = sw+(sx-(int)sx)+0.5; + *sih = sh+(sy-(int)sy)+0.5; +} + + +static void ZoomSurfaceSrcCoords(int six, int siy, int siw, int sih, + int *dix, int *diy, int *diw, int *dih, + Surface * src, Surface * dst) +{ + int dx, dy, dw, dh; + int sx, sy, sw, sh; + + transformZoomSrc(six, siy, siw, sih, + &dx, &dy, &dw, &dh, + src->w, dst->w, src->h, dst->h); + dx-=2; + dy-=2; + dw+=4; + dh+=4; + + if (dx < 0) + dx = 0; + if (dy < 0) + dy = 0; + if (dx+dw > dst->w) + dw = dst->w - dx; + if (dy+dh > dst->h) + dh = dst->h - dy; + + transformZoomDst(&sx, &sy, &sw, &sh, + dx, dy, dw, dh, + src->w, dst->w, src->h, dst->h); + + if (sx+sw > src->w) + sw = src->w - sx; + if (sy+sh > src->h) + sh = src->h - sy; + + ZoomSurfaceCoords32(sx, sy, sw, sh, dx, dy, src, dst); + + *dix = dx; + *diy = dy; + *diw = dw; + *dih = dh; +} + +static void ZoomSurfaceCoords32(int sx, int sy, int sw, int sh, + int dx, int dy, + Surface * src, Surface * dst) +{ + Surface s2; + + s2 = *src; + s2.pixels = ((char*)s2.pixels) + (sx * s2.BytesPerPixel) + (sy * src->pitch); + s2.w = sw; + s2.h = sh; + sge_transform(&s2, dst, + (float)dst->w/(float)src->w, (float)dst->h/(float)src->h, + dx, dy); +} + + +#define sge_clip_xmin(pnt) 0 +#define sge_clip_xmax(pnt) pnt->w +#define sge_clip_ymin(pnt) 0 +#define sge_clip_ymax(pnt) pnt->h + +/*================================================================================== +// Helper function to sge_transform() +// Returns the bounding box +//================================================================================== +*/ +static void _calcRect(Surface *src, Surface *dst, float xscale, float yscale, + Uint16 qx, Uint16 qy, + Sint16 *xmin, Sint16 *ymin, Sint16 *xmax, Sint16 *ymax) +{ + Sint16 x, y, rx, ry; + int i; + + /* Clip to src surface */ + Sint16 sxmin = sge_clip_xmin(src); + Sint16 sxmax = sge_clip_xmax(src); + Sint16 symin = sge_clip_ymin(src); + Sint16 symax = sge_clip_ymax(src); + Sint16 sx[5]; + Sint16 sy[4]; + + /* We don't really need fixed-point here + * but why not? */ + Sint32 ictx = (Sint32) (xscale * 8192.0); + Sint32 icty = (Sint32) (yscale * 8192.0); + + sx[0] = sxmin; + sx[1] = sxmax; + sx[2] = sxmin; + sx[3] = sxmax; + sy[0] = symin; + sy[1] = symax; + sy[2] = symax; + sy[3] = symin; + + /* Calculate the four corner points */ + for(i=0; i<4; i++){ + rx = sx[i]; + ry = sy[i]; + + x = (Sint16)(((ictx*rx) >> 13) + qx); + y = (Sint16)(((icty*ry) >> 13) + qy); + + + if(i==0){ + *xmax = *xmin = x; + *ymax = *ymin = y; + }else{ + if(x>*xmax) + *xmax=x; + else if(x<*xmin) + *xmin=x; + + if(y>*ymax) + *ymax=y; + else if(y<*ymin) + *ymin=y; + } + } + + /* Better safe than sorry...*/ + *xmin -= 1; + *ymin -= 1; + *xmax += 1; + *ymax += 1; + + /* Clip to dst surface */ + if( !dst ) + return; + if( *xmin < sge_clip_xmin(dst) ) + *xmin = sge_clip_xmin(dst); + if( *xmax > sge_clip_xmax(dst) ) + *xmax = sge_clip_xmax(dst); + if( *ymin < sge_clip_ymin(dst) ) + *ymin = sge_clip_ymin(dst); + if( *ymax > sge_clip_ymax(dst) ) + *ymax = sge_clip_ymax(dst); +} + + +/*================================================================================== +** Scale by scale and place at position (qx,qy). +** +** +** Developed with the help from Terry Hancock (hancock@earthlink.net) +** +**==================================================================================*/ +/* First we need some macros to handle different bpp + * I'm sorry about this... + */ +#define TRANSFORM(UintXX, DIV) \ + Sint32 src_pitch=src->pitch/DIV; \ + Sint32 dst_pitch=dst->pitch/DIV; \ + UintXX *src_row = (UintXX *)src->pixels; \ + UintXX *dst_row; \ +\ + for (y=ymin; y<ymax; y++){ \ + dy = y - qy; \ +\ + sx = (Sint32)ctdx; /* Compute source anchor points */ \ + sy = (Sint32)(cty*dy); \ +\ + /* Calculate pointer to dst surface */ \ + dst_row = (UintXX *)dst->pixels + y*dst_pitch; \ +\ + for (x=xmin; x<xmax; x++){ \ + rx=(Sint16)(sx >> 13); /* Convert from fixed-point */ \ + ry=(Sint16)(sy >> 13); \ +\ + /* Make sure the source pixel is actually in the source image. */ \ + if( (rx>=sxmin) && (rx<sxmax) && (ry>=symin) && (ry<symax) ) \ + *(dst_row + x) = *(src_row + ry*src_pitch + rx); \ +\ + sx += ctx; /* Incremental transformations */ \ + } \ + } + + +/* Interpolated transform */ +#define TRANSFORM_AA(UintXX, DIV) \ + Sint32 src_pitch=src->pitch/DIV; \ + Sint32 dst_pitch=dst->pitch/DIV; \ + UintXX *src_row = (UintXX *)src->pixels; \ + UintXX *dst_row; \ + UintXX c1, c2, c3, c4;\ + Uint32 R, G, B, A=0; \ + UintXX Rmask = image->red_mask;\ + UintXX Gmask = image->green_mask;\ + UintXX Bmask = image->blue_mask;\ + UintXX Amask = 0;\ + Uint32 wx, wy;\ + Uint32 p1, p2, p3, p4;\ +\ + /* + * Interpolation: + * We calculate the distances from our point to the four nearest pixels, d1..d4. + * d(a,b) = sqrt(a²+b²) ~= 0.707(a+b) (Pythagoras (Taylor) expanded around (0.5;0.5)) + * + * 1 wx 2 + * *-|-* (+ = our point at (x,y)) + * | | | (* = the four nearest pixels) + * wy --+ | wx = float(x) - int(x) + * | | wy = float(y) - int(y) + * *---* + * 3 4 + * d1 = d(wx,wy) d2 = d(1-wx,wy) d3 = d(wx,1-wy) d4 = d(1-wx,1-wy) + * We now want to weight each pixels importance - it's vicinity to our point: + * w1=d4 w2=d3 w3=d2 w4=d1 (Yes it works... just think a bit about it) + * + * If the pixels have the colors c1..c4 then our point should have the color + * c = (w1*c1 + w2*c2 + w3*c3 + w4*c4)/(w1+w2+w3+w4) (the weighted average) + * but w1+w2+w3+w4 = 4*0.707 so we might as well write it as + * c = p1*c1 + p2*c2 + p3*c3 + p4*c4 where p1..p4 = (w1..w4)/(4*0.707) + * + * But p1..p4 are fixed point so we can just divide the fixed point constant! + * 8192/(4*0.71) = 2897 and we can skip 0.71 too (the division will cancel it everywhere) + * 8192/4 = 2048 + * + * 020102: I changed the fixed-point representation for the variables in the weighted average + * to 24.7 to avoid problems with 32bit colors. Everything else is still 18.13. This + * does however not solve the problem with 32bit RGBA colors... + */\ +\ + Sint32 one = 2048>>6; /* 1 in Fixed-point */ \ + Sint32 two = 2*2048>>6; /* 2 in Fixed-point */ \ +\ + for (y=ymin; y<ymax; y++){ \ + dy = y - qy; \ +\ + sx = (Sint32)(ctdx); /* Compute source anchor points */ \ + sy = (Sint32)(cty*dy); \ +\ + /* Calculate pointer to dst surface */ \ + dst_row = (UintXX *)dst->pixels + y*dst_pitch; \ +\ + for (x=xmin; x<xmax; x++){ \ + rx=(Sint16)(sx >> 13); /* Convert from fixed-point */ \ + ry=(Sint16)(sy >> 13); \ +\ + /* Make sure the source pixel is actually in the source image. */ \ + if( (rx>=sxmin) && (rx+1<sxmax) && (ry>=symin) && (ry+1<symax) ){ \ + wx = (sx & 0x00001FFF) >>8; /* (float(x) - int(x)) / 4 */ \ + wy = (sy & 0x00001FFF) >>8;\ +\ + p4 = wx+wy;\ + p3 = one-wx+wy;\ + p2 = wx+one-wy;\ + p1 = two-wx-wy;\ +\ + c1 = *(src_row + ry*src_pitch + rx);\ + c2 = *(src_row + ry*src_pitch + rx+1);\ + c3 = *(src_row + (ry+1)*src_pitch + rx);\ + c4 = *(src_row + (ry+1)*src_pitch + rx+1);\ +\ + /* Calculate the average */\ + R = ((p1*(c1 & Rmask) + p2*(c2 & Rmask) + p3*(c3 & Rmask) + p4*(c4 & Rmask))>>7) & Rmask;\ + G = ((p1*(c1 & Gmask) + p2*(c2 & Gmask) + p3*(c3 & Gmask) + p4*(c4 & Gmask))>>7) & Gmask;\ + B = ((p1*(c1 & Bmask) + p2*(c2 & Bmask) + p3*(c3 & Bmask) + p4*(c4 & Bmask))>>7) & Bmask;\ + if(Amask)\ + A = ((p1*(c1 & Amask) + p2*(c2 & Amask) + p3*(c3 & Amask) + p4*(c4 & Amask))>>7) & Amask;\ + \ + *(dst_row + x) = R | G | B | A;\ + } \ + sx += ctx; /* Incremental transformations */ \ + } \ + } + +void sge_transform(Surface *src, Surface *dst, float xscale, float yscale, Uint16 qx, Uint16 qy) +{ + Sint32 dy, sx, sy; + Sint16 x, y, rx, ry; + Rect r; + + Sint32 ctx, cty; + Sint16 xmin, xmax, ymin, ymax; + Sint16 sxmin, sxmax, symin, symax; + Sint32 dx, ctdx; + + + /* Here we use 18.13 fixed point integer math + // Sint32 should have 31 usable bits and one for sign + // 2^13 = 8192 + */ + + /* Check scales */ + Sint32 maxint = (Sint32)(pow(2, sizeof(Sint32)*8 - 1 - 13)); /* 2^(31-13) */ + + r.x = r.y = r.w = r.h = 0; + + if( xscale == 0 || yscale == 0) + return; + + if( 8192.0/xscale > maxint ) + xscale = (float)(8192.0/maxint); + else if( 8192.0/xscale < -maxint ) + xscale = (float)(-8192.0/maxint); + + if( 8192.0/yscale > maxint ) + yscale = (float)(8192.0/maxint); + else if( 8192.0/yscale < -maxint ) + yscale = (float)(-8192.0/maxint); + + + /* Fixed-point equivalents */ + ctx = (Sint32)(8192.0/xscale); + cty = (Sint32)(8192.0/yscale); + + /* Compute a bounding rectangle */ + xmin=0; xmax=dst->w; ymin=0; ymax=dst->h; + _calcRect(src, dst, xscale, yscale, + qx, qy, &xmin, &ymin, &xmax, &ymax); + + /* Clip to src surface */ + sxmin = sge_clip_xmin(src); + sxmax = sge_clip_xmax(src); + symin = sge_clip_ymin(src); + symax = sge_clip_ymax(src); + + /* Some terms in the transform are constant */ + dx = xmin - qx; + ctdx = ctx*dx; + + /* Use the correct bpp */ + if( src->BytesPerPixel == dst->BytesPerPixel){ + switch( src->BytesPerPixel ){ + case 1: { /* Assuming 8-bpp */ + TRANSFORM(Uint8, 1) + } + break; + case 2: { /* Probably 15-bpp or 16-bpp */ + TRANSFORM_AA(Uint16, 2) + } + break; + case 4: { /* Probably 32-bpp */ + TRANSFORM_AA(Uint32, 4) + } + break; + } + } +} + +void freeDesktopResources() { + Cleanup(); + if (image) { + XDestroyImage(image); + } + if (zoomImage) { + XDestroyImage(zoomImage); + } + if (savedArea) + free(savedArea); + + if (gcInited) { + XFreeGC(dpy, gc); + XFreeGC(dpy, srcGC); + XFreeGC(dpy, dstGC); + } + + caughtShmError = False; + needShmCleanup = False; + caughtZoomShmError = False; + needZoomShmCleanup = False; + gcInited = False; + image = NULL; + useShm = True; + zoomActive = False; + zoomImage = NULL; + useZoomShm = True; + savedArea = NULL; +} + + +/* + * ColorRectangle32 + * Only used for debugging / visualizing output + */ +/* +static void +ColorRectangle32(XImage *img, CARD32 fg, int x, int y, int width, int height) +{ + int i, h; + int scrWidthInBytes = img->bytes_per_line; + char *scr; + CARD32 *scr32; + + if ((!img) || (!img->data)) + return; + + scr = (img->data + y * scrWidthInBytes + x * 4); + + if (!CheckRectangle(x, y, width, height)) + return; + + for (h = 0; h < height; h++) { + scr32 = (CARD32*) scr; + for (i = 0; i < width; i++) { + CARD32 n = 0; + CARD32 p = scr32[i]; + if (0xff & fg) + n |= ((( 0xff & p)+( 0xff & fg)) >> 2) & 0xff; + else + n |= (0xff & p); + if (0xff00 & fg) + n |= ((( 0xff00 & p)+( 0xff00 & fg)) >> 2) & 0xff00; + else + n |= (0xff00 & p); + if (0xff0000 & fg) + n |= (((0xff0000 & p)+(0xff0000 & fg)) >> 2) & 0xff0000; + else + n |= (0xff0000 & p); + scr32[i] = n; + } + scr += scrWidthInBytes; + } +} +*/ + diff --git a/krdc/vnc/hextile.c b/krdc/vnc/hextile.c new file mode 100644 index 00000000..002880af --- /dev/null +++ b/krdc/vnc/hextile.c @@ -0,0 +1,129 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * hextile.c - handle hextile encoding. + * + * This file shouldn't be compiled directly. It is included multiple times by + * rfbproto.c, each time with a different definition of the macro BPP. For + * each value of BPP, this file defines a function which handles a hextile + * encoded rectangle with BPP bits per pixel. + */ + +#define HandleHextileBPP CONCAT2E(HandleHextile,BPP) +#define CARDBPP CONCAT2E(CARD,BPP) +#define GET_PIXEL CONCAT2E(GET_PIXEL,BPP) +#define FillRectangleBPP CONCAT2E(FillRectangle,BPP) + +static Bool +HandleHextileBPP (int rx, int ry, int rw, int rh) +{ + CARDBPP bg, fg; + int i; + CARD8 *ptr; + int x, y, w, h; + int sx, sy, sw, sh; + CARD8 subencoding; + CARD8 nSubrects; + + for (y = ry; y < ry+rh; y += 16) { + for (x = rx; x < rx+rw; x += 16) { + w = h = 16; + if (rx+rw - x < 16) + w = rx+rw - x; + if (ry+rh - y < 16) + h = ry+rh - y; + + if (!ReadFromRFBServer((char *)&subencoding, 1)) + return False; + + if (subencoding & rfbHextileRaw) { + if (!ReadFromRFBServer(buffer, w * h * (BPP / 8))) + return False; + + CopyDataToScreen(buffer, x, y, w, h); + continue; + } + + if (subencoding & rfbHextileBackgroundSpecified) + if (!ReadFromRFBServer((char *)&bg, sizeof(bg))) + return False; + + LockFramebuffer(); + FillRectangleBPP(bg, x, y, w, h); + + if (subencoding & rfbHextileForegroundSpecified) + if (!ReadFromRFBServer((char *)&fg, sizeof(fg))) { + UnlockFramebuffer(); + return False; + } + + if (!(subencoding & rfbHextileAnySubrects)) { + UnlockFramebuffer(); + SyncScreenRegion(x, y, w, h); + continue; + } + + if (!ReadFromRFBServer((char *)&nSubrects, 1)) { + UnlockFramebuffer(); + return False; + } + + ptr = (CARD8 *)buffer; + + if (subencoding & rfbHextileSubrectsColoured) { + if (!ReadFromRFBServer(buffer, nSubrects * (2 + (BPP / 8)))) { + UnlockFramebuffer(); + return False; + } + + for (i = 0; i < nSubrects; i++) { + GET_PIXEL(fg, ptr); + sx = rfbHextileExtractX(*ptr); + sy = rfbHextileExtractY(*ptr); + ptr++; + sw = rfbHextileExtractW(*ptr); + sh = rfbHextileExtractH(*ptr); + ptr++; + FillRectangleBPP(fg, x+sx, y+sy, sw, sh); + } + + } else { + if (!ReadFromRFBServer(buffer, nSubrects * 2)) { + UnlockFramebuffer(); + return False; + } + + for (i = 0; i < nSubrects; i++) { + sx = rfbHextileExtractX(*ptr); + sy = rfbHextileExtractY(*ptr); + ptr++; + sw = rfbHextileExtractW(*ptr); + sh = rfbHextileExtractH(*ptr); + ptr++; + FillRectangleBPP(fg, x+sx, y+sy, sw, sh); + } + } + UnlockFramebuffer(); + SyncScreenRegion(x, y, w, h); + } + } + + return True; +} diff --git a/krdc/vnc/kvncview.cpp b/krdc/vnc/kvncview.cpp new file mode 100644 index 00000000..1b6a8de2 --- /dev/null +++ b/krdc/vnc/kvncview.cpp @@ -0,0 +1,828 @@ +/*************************************************************************** + kvncview.cpp - main widget + ------------------- + begin : Thu Dec 20 15:11:42 CET 2001 + copyright : (C) 2001-2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kvncview.h" +#include "vncprefs.h" +#include "vnchostpref.h" + +#include <kapplication.h> +#include <kdebug.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kstandarddirs.h> +#include <kpassdlg.h> +#include <kdialogbase.h> +#include <kwallet.h> + +#include <qdatastream.h> +#include <dcopclient.h> +#include <qclipboard.h> +#include <qbitmap.h> +#include <qmutex.h> +#include <qvbox.h> +#include <qwaitcondition.h> + +#include "vncviewer.h" + +#include <X11/Xlib.h> + +/* + * appData is our application-specific data which can be set by the user with + * application resource specs. The AppData structure is defined in the header + * file. + */ +AppData appData; +bool appDataConfigured = false; + +Display* dpy; + +static KVncView *kvncview; + +//Passwords and KWallet data +extern KWallet::Wallet *wallet; +bool useKWallet = false; +static QCString password; +static QMutex passwordLock; +static QWaitCondition passwordWaiter; + +const unsigned int MAX_SELECTION_LENGTH = 4096; + + +KVncView::KVncView(QWidget *parent, + const char *name, + const QString &_host, + int _port, + const QString &_password, + Quality quality, + DotCursorState dotCursorState, + const QString &encodings) : + KRemoteView(parent, name, Qt::WResizeNoErase | Qt::WRepaintNoErase | Qt::WStaticContents), + m_cthread(this, m_wthread, m_quitFlag), + m_wthread(this, m_quitFlag), + m_quitFlag(false), + m_enableFramebufferLocking(false), + m_scaling(false), + m_remoteMouseTracking(false), + m_viewOnly(false), + m_buttonMask(0), + m_host(_host), + m_port(_port), + m_dontSendCb(false), + m_cursorState(dotCursorState) +{ + kvncview = this; + password = _password.latin1(); + dpy = qt_xdisplay(); + setFixedSize(16,16); + setFocusPolicy(QWidget::StrongFocus); + + m_cb = QApplication::clipboard(); + connect(m_cb, SIGNAL(selectionChanged()), this, SLOT(selectionChanged())); + connect(m_cb, SIGNAL(dataChanged()), this, SLOT(clipboardChanged())); + + KStandardDirs *dirs = KGlobal::dirs(); + QBitmap cursorBitmap(dirs->findResource("appdata", + "pics/pointcursor.png")); + QBitmap cursorMask(dirs->findResource("appdata", + "pics/pointcursormask.png")); + m_cursor = QCursor(cursorBitmap, cursorMask); + + if ((quality != QUALITY_UNKNOWN) || + !encodings.isNull()) + configureApp(quality, encodings); +} + +void KVncView::showDotCursor(DotCursorState state) { + if (state == m_cursorState) + return; + + m_cursorState = state; + showDotCursorInternal(); +} + +DotCursorState KVncView::dotCursorState() const { + return m_cursorState; +} + +void KVncView::showDotCursorInternal() { + switch (m_cursorState) { + case DOT_CURSOR_ON: + setCursor(m_cursor); + break; + case DOT_CURSOR_OFF: + setCursor(QCursor(Qt::BlankCursor)); + break; + case DOT_CURSOR_AUTO: + if (m_enableClientCursor) + setCursor(QCursor(Qt::BlankCursor)); + else + setCursor(m_cursor); + break; + } +} + +QString KVncView::host() { + return m_host; +} + +int KVncView::port() { + return m_port; +} + +void KVncView::startQuitting() { + m_quitFlag = true; + m_wthread.kick(); + m_cthread.kick(); +} + +bool KVncView::isQuitting() { + return m_quitFlag; +} + +void KVncView::configureApp(Quality q, const QString specialEncodings) { + appDataConfigured = true; + appData.shareDesktop = 1; + appData.viewOnly = 0; + + if (q == QUALITY_LOW) { + appData.useBGR233 = 1; + appData.encodingsString = "background copyrect softcursor tight zlib hextile raw"; + appData.compressLevel = -1; + appData.qualityLevel = 1; + appData.dotCursor = 1; + } + else if (q == QUALITY_MEDIUM) { + appData.useBGR233 = 0; + appData.encodingsString = "background copyrect softcursor tight zlib hextile raw"; + appData.compressLevel = -1; + appData.qualityLevel = 7; + appData.dotCursor = 1; + } + else if ((q == QUALITY_HIGH) || (q == QUALITY_UNKNOWN)) { + appData.useBGR233 = 0; + appData.encodingsString = "copyrect softcursor hextile raw"; + appData.compressLevel = -1; + appData.qualityLevel = 9; + appData.dotCursor = 1; + } + + if (!specialEncodings.isNull()) + appData.encodingsString = specialEncodings.latin1(); + + appData.nColours = 256; + appData.useSharedColours = 1; + appData.requestedDepth = 0; + + appData.rawDelay = 0; + appData.copyRectDelay = 0; + + if (!appData.dotCursor) + m_cursorState = DOT_CURSOR_OFF; + showDotCursorInternal(); +} + +bool KVncView::checkLocalKRfb() { + if ( m_host != "localhost" && !m_host.isEmpty() ) + return true; + DCOPClient *d = KApplication::dcopClient(); + + int portNum; + QByteArray sdata, rdata; + QCString replyType; + QDataStream arg(sdata, IO_WriteOnly); + arg << QString("krfb"); + if (!d->call ("kded", "kinetd", "port(QString)", sdata, replyType, rdata)) + return true; + + if (replyType != "int") + return true; + + QDataStream answer(rdata, IO_ReadOnly); + answer >> portNum; + + if (m_port != portNum) + return true; + + setStatus(REMOTE_VIEW_DISCONNECTED); + KMessageBox::error(0, + i18n("It is not possible to connect to a local desktop sharing service."), + i18n("Connection Failure")); + emit disconnectedError(); + return false; +} + +bool KVncView::editPreferences( HostPrefPtr host ) +{ + SmartPtr<VncHostPref> hp( host ); + + int ci = hp->quality(); + bool kwallet = hp->useKWallet(); + + // show preferences dialog + KDialogBase *dlg = new KDialogBase( 0L, "dlg", true, + i18n( "VNC Host Preferences for %1" ).arg( host->host() ), + KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true ); + + QVBox *vbox = dlg->makeVBoxMainWidget(); + VncPrefs *prefs = new VncPrefs( vbox ); + QWidget *spacer = new QWidget( vbox ); + vbox->setStretchFactor( spacer, 10 ); + + prefs->setQuality( ci ); + prefs->setShowPrefs(true); + prefs->setUseKWallet(kwallet); + + if ( dlg->exec() == QDialog::Rejected ) + return false; + + ci = prefs->quality(); + hp->setAskOnConnect(prefs->showPrefs()); + hp->setQuality(ci); + hp->setUseKWallet(prefs->useKWallet()); + + delete dlg; + return true; +} + +bool KVncView::start() { + + if (!checkLocalKRfb()) + return false; + + if (!appDataConfigured) { + + HostPreferences *hps = HostPreferences::instance(); + SmartPtr<VncHostPref> hp = + SmartPtr<VncHostPref>(hps->createHostPref(m_host, + VncHostPref::VncType)); + if (hp->askOnConnect()) { + if (!editPreferences(hp)) + return false; + hps->sync(); + } + + int ci = hp->quality(); + + Quality quality; + if (ci == 0) + quality = QUALITY_HIGH; + else if (ci == 1) + quality = QUALITY_MEDIUM; + else if (ci == 2) + quality = QUALITY_LOW; + else { + kdDebug() << "Unknown quality"; + return false; + } + + configureApp(quality); + useKWallet = hp->useKWallet(); + } + + setStatus(REMOTE_VIEW_CONNECTING); + + m_cthread.start(); + setBackgroundMode(Qt::NoBackground); + return true; +} + +KVncView::~KVncView() +{ + startQuitting(); + m_cthread.wait(); + m_wthread.wait(); + freeResources(); +} + +bool KVncView::supportsLocalCursor() const { + return true; +} + +bool KVncView::supportsScaling() const { + return true; +} + +bool KVncView::scaling() const { + return m_scaling; +} + +bool KVncView::viewOnly() { + return m_viewOnly; +} + +QSize KVncView::framebufferSize() { + return m_framebufferSize; +} + +void KVncView::setViewOnly(bool s) { + m_viewOnly = s; + + if (s) + setCursor(Qt::ArrowCursor); + else + showDotCursorInternal(); +} + +void KVncView::enableScaling(bool s) { + bool os = m_scaling; + m_scaling = s; + if (s != os) { + if (s) { + setMaximumSize(m_framebufferSize); + setMinimumSize(m_framebufferSize.width()/16, + m_framebufferSize.height()/16); + } + else + setFixedSize(m_framebufferSize); + } +} + +void KVncView::paintEvent(QPaintEvent *e) { + drawRegion(e->rect().x(), + e->rect().y(), + e->rect().width(), + e->rect().height()); +} + +void KVncView::drawRegion(int x, int y, int w, int h) { + if (m_scaling) + DrawZoomedScreenRegionX11Thread(winId(), width(), height(), + x, y, w, h); + else + DrawScreenRegionX11Thread(winId(), x, y, w, h); +} + +void KVncView::customEvent(QCustomEvent *e) +{ + if (e->type() == ScreenRepaintEventType) { + ScreenRepaintEvent *sre = (ScreenRepaintEvent*) e; + drawRegion(sre->x(), sre->y(),sre->width(), sre->height()); + } + else if (e->type() == ScreenResizeEventType) { + ScreenResizeEvent *sre = (ScreenResizeEvent*) e; + m_framebufferSize = QSize(sre->width(), sre->height()); + setFixedSize(m_framebufferSize); + emit changeSize(sre->width(), sre->height()); + } + else if (e->type() == DesktopInitEventType) { + m_cthread.desktopInit(); + } + else if (e->type() == StatusChangeEventType) { + StatusChangeEvent *sce = (StatusChangeEvent*) e; + setStatus(sce->status()); + if (m_status == REMOTE_VIEW_CONNECTED) { + emit connected(); + setFocus(); + setMouseTracking(true); + } + else if (m_status == REMOTE_VIEW_DISCONNECTED) { + setMouseTracking(false); + emit disconnected(); + } + else if (m_status == REMOTE_VIEW_PREPARING) { + //Login was successfull: Write KWallet password if necessary. + if ( useKWallet && !password.isNull() && wallet && wallet->isOpen() && !wallet->hasEntry(host())) { + wallet->writePassword(host(), password); + } + delete wallet; wallet=0; + } + } + else if (e->type() == PasswordRequiredEventType) { + emit showingPasswordDialog(true); + + if (KPasswordDialog::getPassword(password, i18n("Access to the system requires a password.")) != KPasswordDialog::Accepted) + password = QCString(); + + emit showingPasswordDialog(false); + + passwordLock.lock(); // to guarantee that thread is waiting + passwordWaiter.wakeAll(); + passwordLock.unlock(); + } + else if (e->type() == WalletOpenEventType) { + QString krdc_folder = "KRDC-VNC"; + emit showingPasswordDialog(true); //Bad things happen if you don't do this. + + // Bugfix: Check if wallet has been closed by an outside source + if ( wallet && !wallet->isOpen() ) { + delete wallet; wallet=0; + } + + // Do we need to open the wallet? + if ( !wallet ) { + QString walletName = KWallet::Wallet::NetworkWallet(); + wallet = KWallet::Wallet::openWallet(walletName); + } + + if (wallet && wallet->isOpen()) { + bool walletOK = wallet->hasFolder(krdc_folder); + if (walletOK == false) { + walletOK = wallet->createFolder(krdc_folder); + } + + if (walletOK == true) { + wallet->setFolder(krdc_folder); + QString newPass; + if ( wallet->hasEntry(kvncview->host()) && !wallet->readPassword(kvncview->host(), newPass) ) { + password=newPass.latin1(); + } + } + } + + passwordLock.lock(); // to guarantee that thread is waiting + passwordWaiter.wakeAll(); + passwordLock.unlock(); + + emit showingPasswordDialog(false); + } + else if (e->type() == FatalErrorEventType) { + FatalErrorEvent *fee = (FatalErrorEvent*) e; + setStatus(REMOTE_VIEW_DISCONNECTED); + switch (fee->errorCode()) { + case ERROR_CONNECTION: + KMessageBox::error(0, + i18n("Connection attempt to host failed."), + i18n("Connection Failure")); + break; + case ERROR_PROTOCOL: + KMessageBox::error(0, + i18n("Remote host is using an incompatible protocol."), + i18n("Connection Failure")); + break; + case ERROR_IO: + KMessageBox::error(0, + i18n("The connection to the host has been interrupted."), + i18n("Connection Failure")); + break; + case ERROR_SERVER_BLOCKED: + KMessageBox::error(0, + i18n("Connection failed. The server does not accept new connections."), + i18n("Connection Failure")); + break; + case ERROR_NAME: + KMessageBox::error(0, + i18n("Connection failed. A server with the given name cannot be found."), + i18n("Connection Failure")); + break; + case ERROR_NO_SERVER: + KMessageBox::error(0, + i18n("Connection failed. No server running at the given address and port."), + i18n("Connection Failure")); + break; + case ERROR_AUTHENTICATION: + //Login failed: Remove wallet entry if there is one. + if ( useKWallet && wallet && wallet->isOpen() && wallet->hasEntry(host()) ) { + wallet->removeEntry(host()); + } + KMessageBox::error(0, + i18n("Authentication failed. Connection aborted."), + i18n("Authentication Failure")); + break; + default: + KMessageBox::error(0, + i18n("Unknown error."), + i18n("Unknown Error")); + break; + } + emit disconnectedError(); + } + else if (e->type() == BeepEventType) { + QApplication::beep(); + } + else if (e->type() == ServerCutEventType) { + ServerCutEvent *sce = (ServerCutEvent*) e; + QString ctext = QString::fromUtf8(sce->bytes(), sce->length()); + m_dontSendCb = true; + m_cb->setText(ctext, QClipboard::Clipboard); + m_cb->setText(ctext, QClipboard::Selection); + m_dontSendCb = false; + } + else if (e->type() == MouseStateEventType) { + MouseStateEvent *mse = (MouseStateEvent*) e; + emit mouseStateChanged(mse->x(), mse->y(), mse->buttonMask()); + bool show = m_plom.handlePointerEvent(mse->x(), mse->y()); + if (m_cursorState != DOT_CURSOR_ON) + showDotCursor(show ? DOT_CURSOR_AUTO : DOT_CURSOR_OFF); + } +} + +void KVncView::mouseEvent(QMouseEvent *e) { + if (m_status != REMOTE_VIEW_CONNECTED) + return; + if (m_viewOnly) + return; + + if ( e->type() != QEvent::MouseMove ) { + if ( (e->type() == QEvent::MouseButtonPress) || + (e->type() == QEvent::MouseButtonDblClick)) { + if ( e->button() & LeftButton ) + m_buttonMask |= 0x01; + if ( e->button() & MidButton ) + m_buttonMask |= 0x02; + if ( e->button() & RightButton ) + m_buttonMask |= 0x04; + } + else if ( e->type() == QEvent::MouseButtonRelease ) { + if ( e->button() & LeftButton ) + m_buttonMask &= 0xfe; + if ( e->button() & MidButton ) + m_buttonMask &= 0xfd; + if ( e->button() & RightButton ) + m_buttonMask &= 0xfb; + } + } + + int x = e->x(); + int y = e->y(); + m_plom.registerPointerState(x, y); + if (m_scaling) { + x = (x * m_framebufferSize.width()) / width(); + y = (y * m_framebufferSize.height()) / height(); + } + m_wthread.queueMouseEvent(x, y, m_buttonMask); + + if (m_enableClientCursor) + DrawCursorX11Thread(x, y); // in rfbproto.c +} + +void KVncView::mousePressEvent(QMouseEvent *e) { + mouseEvent(e); + e->accept(); +} + +void KVncView::mouseDoubleClickEvent(QMouseEvent *e) { + mouseEvent(e); + e->accept(); +} + +void KVncView::mouseReleaseEvent(QMouseEvent *e) { + mouseEvent(e); + e->accept(); +} + +void KVncView::mouseMoveEvent(QMouseEvent *e) { + mouseEvent(e); + e->ignore(); +} + +void KVncView::wheelEvent(QWheelEvent *e) { + if (m_status != REMOTE_VIEW_CONNECTED) + return; + if (m_viewOnly) + return; + + int eb = 0; + if ( e->delta() < 0 ) + eb |= 0x10; + else + eb |= 0x8; + + int x = e->pos().x(); + int y = e->pos().y(); + if (m_scaling) { + x = (x * m_framebufferSize.width()) / width(); + y = (y * m_framebufferSize.height()) / height(); + } + m_wthread.queueMouseEvent(x, y, eb|m_buttonMask); + m_wthread.queueMouseEvent(x, y, m_buttonMask); + e->accept(); +} + +void KVncView::pressKey(XEvent *xe) { + KKeyNative k(xe); + uint mod = k.mod(); + if (mod & KKeyNative::modX(KKey::SHIFT)) + m_wthread.queueKeyEvent(XK_Shift_L, true); + if (mod & KKeyNative::modX(KKey::CTRL)) + m_wthread.queueKeyEvent(XK_Control_L, true); + if (mod & KKeyNative::modX(KKey::ALT)) + m_wthread.queueKeyEvent(XK_Alt_L, true); + if (mod & KKeyNative::modX(KKey::WIN)) + m_wthread.queueKeyEvent(XK_Meta_L, true); + + m_wthread.queueKeyEvent(k.sym(), true); + m_wthread.queueKeyEvent(k.sym(), false); + + if (mod & KKeyNative::modX(KKey::WIN)) + m_wthread.queueKeyEvent(XK_Meta_L, false); + if (mod & KKeyNative::modX(KKey::ALT)) + m_wthread.queueKeyEvent(XK_Alt_L, false); + if (mod & KKeyNative::modX(KKey::CTRL)) + m_wthread.queueKeyEvent(XK_Control_L, false); + if (mod & KKeyNative::modX(KKey::SHIFT)) + m_wthread.queueKeyEvent(XK_Shift_L, false); + + m_mods.clear(); +} + +bool KVncView::x11Event(XEvent *e) { + bool pressed; + if (e->type == KeyPress) + pressed = true; + else if (e->type == KeyRelease) + pressed = false; + else + return QWidget::x11Event(e); + + if (!m_viewOnly) { + unsigned int s = KKeyNative(e).sym(); + + switch (s) { + case XK_Meta_L: + case XK_Alt_L: + case XK_Control_L: + case XK_Shift_L: + case XK_Meta_R: + case XK_Alt_R: + case XK_Control_R: + case XK_Shift_R: + if (pressed) + m_mods[s] = true; + else if (m_mods.contains(s)) + m_mods.remove(s); + else + unpressModifiers(); + } + m_wthread.queueKeyEvent(s, pressed); + } + return true; +} + +void KVncView::unpressModifiers() { + QValueList<unsigned int> keys = m_mods.keys(); + QValueList<unsigned int>::const_iterator it = keys.begin(); + while (it != keys.end()) { + m_wthread.queueKeyEvent(*it, false); + it++; + } + m_mods.clear(); +} + +void KVncView::focusOutEvent(QFocusEvent *) { + unpressModifiers(); +} + +QSize KVncView::sizeHint() { + return maximumSize(); +} + +void KVncView::setRemoteMouseTracking(bool s) { + m_remoteMouseTracking = s; +} + +bool KVncView::remoteMouseTracking() { + return m_remoteMouseTracking; +} + +void KVncView::clipboardChanged() { + if (m_status != REMOTE_VIEW_CONNECTED) + return; + + if (m_cb->ownsClipboard() || m_dontSendCb) + return; + + QString text = m_cb->text(QClipboard::Clipboard); + if (text.length() > MAX_SELECTION_LENGTH) + return; + + m_wthread.queueClientCut(text); +} + +void KVncView::selectionChanged() { + if (m_status != REMOTE_VIEW_CONNECTED) + return; + + if (m_cb->ownsSelection() || m_dontSendCb) + return; + + QString text = m_cb->text(QClipboard::Selection); + if (text.length() > MAX_SELECTION_LENGTH) + return; + + m_wthread.queueClientCut(text); +} + + +void KVncView::lockFramebuffer() { + if (m_enableFramebufferLocking) + m_framebufferLock.lock(); +} + +void KVncView::unlockFramebuffer() { + if (m_enableFramebufferLocking) + m_framebufferLock.unlock(); +} + +void KVncView::enableClientCursor(bool enable) { + if (enable) { + m_enableFramebufferLocking = true; // cant be turned off + } + m_enableClientCursor = enable; + lockFramebuffer(); + showDotCursorInternal(); + unlockFramebuffer(); +} + +/*! + \brief Get a password for this host. + Tries to get a password from the url or wallet if at all possible. If + both of these fail, it then asks the user to enter a password. + \note Lots of dialogs can be popped up during this process. The thread + locks and signals are there to protect against deadlocks and other + horribleness. Be careful making changes here. +*/ +int getPassword(char *passwd, int pwlen) { + int retV = 0; + + //Prepare the system + passwordLock.lock(); + + //Try #1: Did the user give a password in the URL? + if (!password.isNull()) { + retV = 1; //got it! + } + + //Try #2: Is there something in the wallet? + if ( !retV && useKWallet ) { + QApplication::postEvent(kvncview, new WalletOpenEvent()); + passwordWaiter.wait(&passwordLock); //block + if (!password.isNull()) retV = 1; //got it! + } + + //Last try: Ask the user + if (!retV) { + QApplication::postEvent(kvncview, new PasswordRequiredEvent()); + passwordWaiter.wait(&passwordLock); //block + if (!password.isNull()) retV = 1; //got it! + } + + //Process the password if we got it, clear it if we didn't + if (retV) { + strncpy(passwd, (const char*)password, pwlen); + } else { + passwd[0] = 0; + } + + //Pack up and go home + passwordLock.unlock(); + if (!retV) kvncview->startQuitting(); + + return retV; +} + +extern int isQuitFlagSet() { + return kvncview->isQuitting() ? 1 : 0; +} + +extern void DrawScreenRegion(int x, int y, int width, int height) { +/* KApplication::kApplication()->lock(); + kvncview->drawRegion(x, y, width, height); + KApplication::kApplication()->unlock(); +*/ + QApplication::postEvent(kvncview, new ScreenRepaintEvent(x, y, width, height)); +} + +// call only from x11 thread! +extern void DrawAnyScreenRegionX11Thread(int x, int y, int width, int height) { + kvncview->drawRegion(x, y, width, height); +} + +extern void EnableClientCursor(int enable) { + kvncview->enableClientCursor(enable); +} + +extern void LockFramebuffer() { + kvncview->lockFramebuffer(); +} + +extern void UnlockFramebuffer() { + kvncview->unlockFramebuffer(); +} + +extern void beep() { + QApplication::postEvent(kvncview, new BeepEvent()); +} + +extern void newServerCut(char *bytes, int length) { + QApplication::postEvent(kvncview, new ServerCutEvent(bytes, length)); +} + +extern void postMouseEvent(int x, int y, int buttonMask) { + QApplication::postEvent(kvncview, new MouseStateEvent(x, y, buttonMask)); +} + +#include "kvncview.moc" diff --git a/krdc/vnc/kvncview.h b/krdc/vnc/kvncview.h new file mode 100644 index 00000000..1b961f3d --- /dev/null +++ b/krdc/vnc/kvncview.h @@ -0,0 +1,121 @@ +/*************************************************************************** + kvncview.h - widget that shows the vnc client + ------------------- + begin : Thu Dec 20 15:11:42 CET 2001 + copyright : (C) 2001-2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef KVNCVIEW_H +#define KVNCVIEW_H + +#include "kremoteview.h" +#include <qcursor.h> +#include <qmap.h> + +#include "pointerlatencyometer.h" +#include "hostpreferences.h" +#include "vnctypes.h" +#include "threads.h" + +class QClipBoard; + +class KVncView : public KRemoteView +{ + Q_OBJECT +private: + ControllerThread m_cthread; + WriterThread m_wthread; + volatile bool m_quitFlag; // if set: all threads should die ASAP + QMutex m_framebufferLock; + bool m_enableFramebufferLocking; + bool m_enableClientCursor; + + QSize m_framebufferSize; + bool m_scaling; + bool m_remoteMouseTracking; + bool m_viewOnly; + + int m_buttonMask; + QMap<unsigned int,bool> m_mods; + + QString m_host; + int m_port; + + QClipboard *m_cb; + bool m_dontSendCb; + QCursor m_cursor; + DotCursorState m_cursorState; + PointerLatencyOMeter m_plom; + + void mouseEvent(QMouseEvent*); + unsigned long toKeySym(QKeyEvent *k); + bool checkLocalKRfb(); + void paintMessage(const QString &msg); + void showDotCursorInternal(); + void unpressModifiers(); + +protected: + void paintEvent(QPaintEvent*); + void customEvent(QCustomEvent*); + void mousePressEvent(QMouseEvent*); + void mouseDoubleClickEvent(QMouseEvent*); + void mouseReleaseEvent(QMouseEvent*); + void mouseMoveEvent(QMouseEvent*); + void wheelEvent(QWheelEvent *); + void focusOutEvent(QFocusEvent *); + bool x11Event(XEvent*); + +public: + KVncView(QWidget* parent=0, const char *name=0, + const QString &host = QString(""), int port = 5900, + const QString &password = QString::null, + Quality quality = QUALITY_UNKNOWN, + DotCursorState dotCursorState = DOT_CURSOR_AUTO, + const QString &encodings = QString::null); + ~KVncView(); + QSize sizeHint(); + void drawRegion(int x, int y, int w, int h); + void lockFramebuffer(); + void unlockFramebuffer(); + void enableClientCursor(bool enable); + virtual bool scaling() const; + virtual bool supportsScaling() const; + virtual bool supportsLocalCursor() const; + virtual QSize framebufferSize(); + void setRemoteMouseTracking(bool s); + bool remoteMouseTracking(); + void configureApp(Quality q, const QString specialEncodings = QString::null); + void showDotCursor(DotCursorState state); + DotCursorState dotCursorState() const; + virtual void startQuitting(); + virtual bool isQuitting(); + virtual QString host(); + virtual int port(); + virtual bool start(); + + virtual bool viewOnly(); + + static bool editPreferences( HostPrefPtr ); + +public slots: + virtual void enableScaling(bool s); + virtual void setViewOnly(bool s); + virtual void pressKey(XEvent *k); + + +private slots: + void clipboardChanged(); + void selectionChanged(); +}; + +#endif diff --git a/krdc/vnc/pointerlatencyometer.h b/krdc/vnc/pointerlatencyometer.h new file mode 100644 index 00000000..559536b2 --- /dev/null +++ b/krdc/vnc/pointerlatencyometer.h @@ -0,0 +1,83 @@ +/*************************************************************************** + pointerlatencyometer.h - measuring pointer latency + ------------------- + begin : Wed Jun 30 12:04:44 CET 2002 + copyright : (C) 2002-2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <qdatetime.h> +#include <kdebug.h> + +struct PointerState { + int x, y; + QTime timestamp; +}; + +class PointerLatencyOMeter { +private: + enum { stateCapacity = 30, maximumLatency = 1000 }; + PointerState states[stateCapacity]; + int firstState, stateNum; + float last3Latency, last20Latency; + +public: + PointerLatencyOMeter() : + firstState(0), + stateNum(0), + last3Latency(125), + last20Latency(25) { + } + + // registers a client pointer state + void registerPointerState(int x, int y) { + if (stateNum == stateCapacity) + stateNum--; + if (firstState == 0) + firstState = stateCapacity-1; + else + firstState--; + states[firstState].x = x; + states[firstState].y = y; + states[firstState].timestamp.start(); + stateNum++; + } + + /* Returns true if pointer should be visible */ + bool registerLatency(int msecs) { + last3Latency = ((last3Latency * 2.0) + msecs) / 3.0; + last20Latency = ((last20Latency * 19.0) + msecs) / 20.0; + + if (msecs >= maximumLatency) + return true; + if (last3Latency > (1000/4)) + return true; + return last20Latency > (1000/12); + } + + // Called with server-side coordinates. + // Returns true if pointer should be visible + bool handlePointerEvent(int x, int y) { + for (int i = stateNum-1; i >= 0; i--) { + int idx = (i+firstState) % stateCapacity; + if ((states[idx].x != x) || + (states[idx].y != y)) + continue; + + stateNum = i; + int l = states[idx].timestamp.elapsed(); + return registerLatency((l > maximumLatency) ? maximumLatency : l); + } + return true; + } + +}; diff --git a/krdc/vnc/rfbproto.c b/krdc/vnc/rfbproto.c new file mode 100644 index 00000000..e9ed5764 --- /dev/null +++ b/krdc/vnc/rfbproto.c @@ -0,0 +1,1335 @@ +/* + * Copyright (C) 2002, Tim Jansen. All Rights Reserved. + * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved. + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * rfbproto.c - functions to deal with client side of RFB protocol. + * tim@tjansen.de: - added softcursor encoding + * - changed various things for krdc + */ + +#include <unistd.h> +#include <errno.h> +#include <pwd.h> +#include "vncviewer.h" +#include "vncauth.h" +#include <zlib.h> +#include <jpeglib.h> + +static Bool HandleHextile8(int rx, int ry, int rw, int rh); +static Bool HandleHextile16(int rx, int ry, int rw, int rh); +static Bool HandleHextile32(int rx, int ry, int rw, int rh); +static Bool HandleZlib8(int rx, int ry, int rw, int rh); +static Bool HandleZlib16(int rx, int ry, int rw, int rh); +static Bool HandleZlib32(int rx, int ry, int rw, int rh); +static Bool HandleTight8(int rx, int ry, int rw, int rh); +static Bool HandleTight16(int rx, int ry, int rw, int rh); +static Bool HandleTight32(int rx, int ry, int rw, int rh); + +static long ReadCompactLen (void); + +static void JpegInitSource(j_decompress_ptr cinfo); +static boolean JpegFillInputBuffer(j_decompress_ptr cinfo); +static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes); +static void JpegTermSource(j_decompress_ptr cinfo); +static void JpegSetSrcManager(j_decompress_ptr cinfo, CARD8 *compressedData, + int compressedLen); + + +#define RGB24_TO_PIXEL(bpp,r,g,b) \ + ((((CARD##bpp)(r) & 0xFF) * myFormat.redMax + 127) / 255 \ + << myFormat.redShift | \ + (((CARD##bpp)(g) & 0xFF) * myFormat.greenMax + 127) / 255 \ + << myFormat.greenShift | \ + (((CARD##bpp)(b) & 0xFF) * myFormat.blueMax + 127) / 255 \ + << myFormat.blueShift) + +int rfbsock; +char *desktopName; +rfbPixelFormat myFormat; +rfbServerInitMsg si; + +int endianTest = 1; + +/* + * Softcursor variables + */ + +int cursorX, cursorY; +int imageIndex = -1; + +PointerImage pointerImages[rfbSoftCursorMaxImages]; + + +/* Hextile assumes it is big enough to hold 16 * 16 * 32 bits. + Tight encoding assumes BUFFER_SIZE is at least 16384 bytes. */ + +#define BUFFER_SIZE (16384) +static char buffer[BUFFER_SIZE]; + + +/* The zlib encoding requires expansion/decompression/deflation of the + compressed data in the "buffer" above into another, result buffer. + However, the size of the result buffer can be determined precisely + based on the bitsPerPixel, height and width of the rectangle. We + allocate this buffer one time to be the full size of the buffer. */ + +static int raw_buffer_size = -1; +static char *raw_buffer = NULL; + +static z_stream decompStream; +static Bool decompStreamInited = False; + + +/* + * Variables for the ``tight'' encoding implementation. + */ + +/* Separate buffer for compressed data. */ +#define ZLIB_BUFFER_SIZE 512 +static char zlib_buffer[ZLIB_BUFFER_SIZE]; + +/* Four independent compression streams for zlib library. */ +static z_stream zlibStream[4]; +static Bool zlibStreamActive[4] = { + False, False, False, False +}; + +/* Filter stuff. Should be initialized by filter initialization code. */ +static Bool cutZeros; +static int rectWidth, rectColors; +static char tightPalette[256*4]; +static CARD8 tightPrevRow[2048*3*sizeof(CARD16)]; + +/* JPEG decoder state. */ +static Bool jpegError; + +/* Maximum length for the cut buffer (16 MB)*/ +#define MAX_CUTBUFFER (1024*1024*16) + +/* Maximum length for the strings (64 kB)*/ +#define MAX_STRING (1024*64) + +/* Maximum length for the strings (32 MB)*/ +#define MAX_JPEG_SIZE (1024*1024*32) + + +/* + * ConnectToRFBServer. + */ + +int +ConnectToRFBServer(const char *hostname, int port) +{ + unsigned int host; + + if (!StringToIPAddr(hostname, &host)) { + fprintf(stderr,"Couldn't convert '%s' to host address\n", hostname); + return -(int)INIT_NAME_RESOLUTION_FAILURE; + } + + rfbsock = ConnectToTcpAddr(host, port); + if (rfbsock < 0) { + fprintf(stderr,"Unable to connect to VNC server\n"); + } + + return rfbsock; +} + + +/* + * InitialiseRFBConnection. + */ + +enum InitStatus +InitialiseRFBConnection() +{ + rfbProtocolVersionMsg pv; + int major,minor; + CARD32 authScheme, reasonLen, authResult; + char *reason; + CARD8 challenge[CHALLENGESIZE]; + char passwd[9]; + int i; + rfbClientInitMsg ci; + + /* if the connection is immediately closed, don't report anything, so + that pmw's monitor can make test connections */ + + if (!ReadFromRFBServer(pv, sz_rfbProtocolVersionMsg)) return INIT_SERVER_BLOCKED; + + errorMessageOnReadFailure = True; + + pv[sz_rfbProtocolVersionMsg] = 0; + + if (sscanf(pv,rfbProtocolVersionFormat,&major,&minor) != 2) { + fprintf(stderr,"Not a valid VNC server\n"); + return INIT_PROTOCOL_FAILURE; + } + + fprintf(stderr,"VNC server supports protocol version %d.%d (viewer %d.%d)\n", + major, minor, rfbProtocolMajorVersion, rfbProtocolMinorVersion); + + major = rfbProtocolMajorVersion; + minor = rfbProtocolMinorVersion; + + sprintf(pv,rfbProtocolVersionFormat,major,minor); + + if (!WriteExact(rfbsock, pv, sz_rfbProtocolVersionMsg)) return INIT_CONNECTION_FAILED; + + if (!ReadFromRFBServer((char *)&authScheme, 4)) return INIT_CONNECTION_FAILED; + + authScheme = Swap32IfLE(authScheme); + + switch (authScheme) { + + case rfbConnFailed: + if (!ReadFromRFBServer((char *)&reasonLen, 4)) return INIT_CONNECTION_FAILED; + reasonLen = Swap32IfLE(reasonLen); + + if (reasonLen > MAX_STRING) { + fprintf(stderr, "Connection failure reason too long.\n"); + return INIT_CONNECTION_FAILED; + } + + reason = malloc(reasonLen); + if (!reason) + return INIT_CONNECTION_FAILED; + + if (!ReadFromRFBServer(reason, reasonLen)) return INIT_CONNECTION_FAILED; + + fprintf(stderr,"VNC connection failed: %.*s\n",(int)reasonLen, reason); + free(reason); + return INIT_CONNECTION_FAILED; + + case rfbNoAuth: + fprintf(stderr,"No authentication needed\n"); + break; + + case rfbVncAuth: + if (!ReadFromRFBServer((char *)challenge, CHALLENGESIZE)) return INIT_CONNECTION_FAILED; + + if (!getPassword(passwd, 8)) + return INIT_ABORTED; + + passwd[8] = '\0'; + + vncEncryptBytes(challenge, passwd); + + /* Lose the password from memory */ + for (i = strlen(passwd); i >= 0; i--) { + passwd[i] = '\0'; + } + + if (!WriteExact(rfbsock, (char *)challenge, CHALLENGESIZE)) return INIT_CONNECTION_FAILED; + + if (!ReadFromRFBServer((char *)&authResult, 4)) return INIT_CONNECTION_FAILED; + + authResult = Swap32IfLE(authResult); + + switch (authResult) { + case rfbVncAuthOK: + fprintf(stderr,"VNC authentication succeeded\n"); + break; + case rfbVncAuthFailed: + fprintf(stderr,"VNC authentication failed\n"); + return INIT_AUTHENTICATION_FAILED; + case rfbVncAuthTooMany: + fprintf(stderr,"VNC authentication failed - too many tries\n"); + return INIT_AUTHENTICATION_FAILED; + default: + fprintf(stderr,"Unknown VNC authentication result: %d\n", + (int)authResult); + return INIT_CONNECTION_FAILED; + } + break; + + default: + fprintf(stderr,"Unknown authentication scheme from VNC server: %d\n", + (int)authScheme); + return INIT_CONNECTION_FAILED; + } + + ci.shared = (appData.shareDesktop ? 1 : 0); + + if (!WriteExact(rfbsock, (char *)&ci, sz_rfbClientInitMsg)) return INIT_CONNECTION_FAILED; + + if (!ReadFromRFBServer((char *)&si, sz_rfbServerInitMsg)) return INIT_CONNECTION_FAILED; + + si.framebufferWidth = Swap16IfLE(si.framebufferWidth); + si.framebufferHeight = Swap16IfLE(si.framebufferHeight); + si.format.redMax = Swap16IfLE(si.format.redMax); + si.format.greenMax = Swap16IfLE(si.format.greenMax); + si.format.blueMax = Swap16IfLE(si.format.blueMax); + si.nameLength = Swap32IfLE(si.nameLength); + + if ((si.framebufferWidth*si.framebufferHeight) > (4096*4096)) + return INIT_CONNECTION_FAILED; + + if (si.nameLength > MAX_STRING) { + fprintf(stderr, "Display name too long.\n"); + return INIT_CONNECTION_FAILED; + } + + desktopName = malloc(si.nameLength + 1); + if (!desktopName) { + fprintf(stderr, "Error allocating memory for desktop name, %lu bytes\n", + (unsigned long)si.nameLength); + return INIT_CONNECTION_FAILED; + } + + if (!ReadFromRFBServer(desktopName, si.nameLength)) return INIT_CONNECTION_FAILED; + + desktopName[si.nameLength] = 0; + + fprintf(stderr,"Desktop name \"%s\"\n",desktopName); + + fprintf(stderr,"Connected to VNC server, using protocol version %d.%d\n", + rfbProtocolMajorVersion, rfbProtocolMinorVersion); + + fprintf(stderr,"VNC server default format:\n"); + PrintPixelFormat(&si.format); + + return INIT_OK; +} + + +/* + * SetFormatAndEncodings. + */ + +Bool +SetFormatAndEncodings() +{ + rfbSetPixelFormatMsg spf; + char buf[sz_rfbSetEncodingsMsg + MAX_ENCODINGS * 4]; + rfbSetEncodingsMsg *se = (rfbSetEncodingsMsg *)buf; + CARD32 *encs = (CARD32 *)(&buf[sz_rfbSetEncodingsMsg]); + int len = 0; + Bool requestCompressLevel = False; + Bool requestQualityLevel = False; + Bool requestLastRectEncoding = False; + + spf.type = rfbSetPixelFormat; + spf.pad1 = 0; + spf.pad2 = 0; + spf.format = myFormat; + spf.format.redMax = Swap16IfLE(spf.format.redMax); + spf.format.greenMax = Swap16IfLE(spf.format.greenMax); + spf.format.blueMax = Swap16IfLE(spf.format.blueMax); + + if (!WriteExact(rfbsock, (char *)&spf, sz_rfbSetPixelFormatMsg)) + return False; + + se->type = rfbSetEncodings; + se->pad = 0; + se->nEncodings = 0; + + if (appData.encodingsString) { + const char *encStr = appData.encodingsString; + int encStrLen; + do { + char *nextEncStr = strchr(encStr, ' '); + if (nextEncStr) { + encStrLen = nextEncStr - encStr; + nextEncStr++; + } else { + encStrLen = strlen(encStr); + } + + if (strncasecmp(encStr,"raw",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw); + } else if (strncasecmp(encStr,"copyrect",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect); + } else if (strncasecmp(encStr,"softcursor",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingSoftCursor); + /* if server supports SoftCursor, it will ignore X/RichCursor + * and PointerPos */ + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingXCursor); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRichCursor); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingPointerPos); + } else if (strncasecmp(encStr,"background",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingBackground); + } else if (strncasecmp(encStr,"tight",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight); + requestLastRectEncoding = True; + if (appData.compressLevel >= 0 && appData.compressLevel <= 9) + requestCompressLevel = True; + if (appData.qualityLevel >= 0 && appData.qualityLevel <= 9) + requestQualityLevel = True; + } else if (strncasecmp(encStr,"hextile",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile); + } else if (strncasecmp(encStr,"zlib",encStrLen) == 0) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZlib); + if (appData.compressLevel >= 0 && appData.compressLevel <= 9) + requestCompressLevel = True; + } else { + fprintf(stderr,"Unknown encoding '%.*s'\n",encStrLen,encStr); + } + + encStr = nextEncStr; + } while (encStr && se->nEncodings < MAX_ENCODINGS); + + if (se->nEncodings < MAX_ENCODINGS && requestCompressLevel) { + encs[se->nEncodings++] = Swap32IfLE(appData.compressLevel + + rfbEncodingCompressLevel0); + } + + if (se->nEncodings < MAX_ENCODINGS && requestQualityLevel) { + encs[se->nEncodings++] = Swap32IfLE(appData.qualityLevel + + rfbEncodingQualityLevel0); + } + + if (se->nEncodings < MAX_ENCODINGS && requestLastRectEncoding) { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingLastRect); + } + } + else { + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingCopyRect); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingTight); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingHextile); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingZlib); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingRaw); + + if (appData.compressLevel >= 0 && appData.compressLevel <= 9) { + encs[se->nEncodings++] = Swap32IfLE(appData.compressLevel + + rfbEncodingCompressLevel0); + } + + if (appData.qualityLevel >= 0 && appData.qualityLevel <= 9) { + encs[se->nEncodings++] = Swap32IfLE(appData.qualityLevel + + rfbEncodingQualityLevel0); + } + + if (si.format.depth >= 8) + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingSoftCursor); + encs[se->nEncodings++] = Swap32IfLE(rfbEncodingLastRect); + } + + len = sz_rfbSetEncodingsMsg + se->nEncodings * 4; + + se->nEncodings = Swap16IfLE(se->nEncodings); + + if (!WriteExact(rfbsock, buf, len)) return False; + + return True; +} + + +/* + * SendIncrementalFramebufferUpdateRequest. + * Note: this should only be called by the WriterThread + */ + +Bool +SendIncrementalFramebufferUpdateRequest() +{ + return SendFramebufferUpdateRequest(0, 0, si.framebufferWidth, + si.framebufferHeight, True); +} + + +/* + * SendFramebufferUpdateRequest. + * Note: this should only be called by the WriterThread + */ + +Bool +SendFramebufferUpdateRequest(int x, int y, int w, int h, Bool incremental) +{ + rfbFramebufferUpdateRequestMsg fur; + + fur.type = rfbFramebufferUpdateRequest; + fur.incremental = incremental ? 1 : 0; + fur.x = Swap16IfLE(x); + fur.y = Swap16IfLE(y); + fur.w = Swap16IfLE(w); + fur.h = Swap16IfLE(h); + + if (!WriteExact(rfbsock, (char *)&fur, sz_rfbFramebufferUpdateRequestMsg)) + return False; + + return True; +} + + +/* + * SendPointerEvent. + * Note: this should only be called by the WriterThread + */ + +Bool +SendPointerEvent(int x, int y, int buttonMask) +{ + rfbPointerEventMsg pe; + + pe.type = rfbPointerEvent; + pe.buttonMask = buttonMask; + if (x < 0) x = 0; + if (y < 0) y = 0; + pe.x = Swap16IfLE(x); + pe.y = Swap16IfLE(y); + return WriteExact(rfbsock, (char *)&pe, sz_rfbPointerEventMsg); +} + + +/* + * SendKeyEvent. + * Note: this should only be called by the WriterThread + */ + +Bool +SendKeyEvent(CARD32 key, Bool down) +{ + rfbKeyEventMsg ke; + + ke.type = rfbKeyEvent; + ke.down = down ? 1 : 0; + ke.key = Swap32IfLE(key); + return WriteExact(rfbsock, (char *)&ke, sz_rfbKeyEventMsg); +} + + +/* + * SendClientCutText. + * Note: this should only be called by the WriterThread + */ + +Bool +SendClientCutText(const char *str, int len) +{ + rfbClientCutTextMsg cct; + + cct.type = rfbClientCutText; + cct.length = Swap32IfLE((unsigned int)len); + return (WriteExact(rfbsock, (char *)&cct, sz_rfbClientCutTextMsg) && + WriteExact(rfbsock, str, len)); +} + + +static Bool +HandleSoftCursorSetImage(rfbSoftCursorSetImage *msg, rfbRectangle *rect) +{ + int iindex = msg->imageIndex - rfbSoftCursorSetIconOffset; + PointerImage *pi = &pointerImages[iindex]; + if (iindex >= rfbSoftCursorMaxImages) { + fprintf(stderr, "Received invalid soft cursor image index %d for SetImage\n", iindex); + return False; + } + EnableClientCursor(0); + + if (pi->set && pi->image) + free(pi->image); + + pi->w = rect->w; + pi->h = rect->h; + pi->hotX = rect->x; + pi->hotY = rect->y; + pi->len = Swap16IfLE(msg->imageLength); + pi->image = malloc(pi->len); + if (!pi->image) { + fprintf(stderr, "out of memory (size=%d)\n", pi->len); + return False; + } + + if (!ReadFromRFBServer(pi->image, pi->len)) + return False; + pi->set = 1; + return True; +} + +/* framebuffer must be locked when calling this!!! */ +static Bool +PointerMove(unsigned int x, unsigned int y, unsigned int mask, + int ox, int oy, int ow, int oh) +{ + int nx, ny, nw, nh; + + if (x >= si.framebufferWidth) + x = si.framebufferWidth - 1; + if (y >= si.framebufferHeight) + y = si.framebufferHeight - 1; + + cursorX = x; + cursorY = y; + drawCursor(); + UnlockFramebuffer(); + + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &nx, &ny, &nw, &nh); + + if (rectsIntersect(ox, oy, ow, oh, nx, ny, nw, nh)) { + rectsJoin(&ox, &oy, &ow, &oh, nx, ny, nw, nh); + SyncScreenRegion(ox, oy, ow, oh); + } + else { + SyncScreenRegion(ox, oy, ow, oh); + SyncScreenRegion(nx, ny, nw, nh); + } + + postMouseEvent(cursorX, cursorY, mask); + + return True; +} + +static Bool +HandleSoftCursorMove(rfbSoftCursorMove *msg, rfbRectangle *rect) +{ + int ii, ox, oy, ow, oh; + + /* get old cursor rect to know what to update */ + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &ox, &oy, &ow, &oh); + + ii = msg->imageIndex; + if (ii >= rfbSoftCursorMaxImages) { + fprintf(stderr, "Received invalid soft cursor image index %d for Move\n", ii); + return False; + } + + if (!pointerImages[ii].set) + return True; + + LockFramebuffer(); + undrawCursor(); + imageIndex = ii; + + return PointerMove(rect->w, rect->h, msg->buttonMask, ox, oy, ow, oh); +} + +static Bool +HandleCursorPos(unsigned int x, unsigned int y) +{ + int ox, oy, ow, oh; + + /* get old cursor rect to know what to update */ + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &ox, &oy, &ow, &oh); + if (!pointerImages[0].set) + return True; + + LockFramebuffer(); + undrawCursor(); + imageIndex = 0; + return PointerMove(x, y, 0, ox, oy, ow, oh); +} + +/* call only from X11 thread. Only updates framebuffer, does not sync! */ +void DrawCursorX11Thread(int x, int y) { + int ox, oy, ow, oh, nx, ny, nw, nh; + if (!pointerImages[0].set) + return True; + imageIndex = 0; + + if (x >= si.framebufferWidth) + x = si.framebufferWidth - 1; + if (y >= si.framebufferHeight) + y = si.framebufferHeight - 1; + + LockFramebuffer(); + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &ox, &oy, &ow, &oh); + undrawCursor(); + cursorX = x; + cursorY = y; + drawCursor(); + UnlockFramebuffer(); + + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &nx, &ny, &nw, &nh); + if (rectsIntersect(ox, oy, ow, oh, nx, ny, nw, nh)) { + rectsJoin(&ox, &oy, &ow, &oh, nx, ny, nw, nh); + SyncScreenRegionX11Thread(ox, oy, ow, oh); + } + else { + SyncScreenRegionX11Thread(ox, oy, ow, oh); + SyncScreenRegionX11Thread(nx, ny, nw, nh); + } +} + +/** + * Create a softcursor in the "compressed alpha" format. + * Returns the softcursor, caller owns the object + */ +static void *MakeSoftCursor(int bpp, int cursorWidth, int cursorHeight, + CARD8 *cursorData, CARD8 *cursorMask, short *imageLen) +{ + int w = (cursorWidth+7)/8; + unsigned char *cp, *sp, *dstData; + int state; /* 0 = transparent, 1 otherwise */ + CARD8 *counter; + unsigned char bit; + int i,j; + + sp = (unsigned char*)cursorData; + dstData = cp = (unsigned char*)calloc(cursorWidth*(bpp+2),cursorHeight); + if (!dstData) + return 0; + + state = 0; + counter = cp++; + *counter = 0; + + for(j=0;j<cursorHeight;j++) + for(i=0,bit=0x80;i<cursorWidth;i++,bit=(bit&1)?0x80:bit>>1) + if(cursorMask[j*w+i/8]&bit) { + if (state) { + memcpy(cp,sp,bpp); + cp += bpp; + sp += bpp; + (*counter)++; + if (*counter == 255) { + state = 0; + counter = cp++; + *counter = 0; + } + } + else { + state = 1; + counter = cp++; + *counter = 1; + memcpy(cp,sp,bpp); + cp += bpp; + sp += bpp; + } + } + else { + if (!state) { + (*counter)++; + if (*counter == 255) { + state = 1; + counter = cp++; + *counter = 0; + } + } + else { + state = 0; + counter = cp++; + *counter = 1; + } + sp += bpp; + } + + *imageLen = cp - dstData; + return (void*) dstData; +} + + +/********************************************************************* + * HandleCursorShape(). Support for XCursor and RichCursor shape + * updates. We emulate cursor operating on the frame buffer (that is + * why we call it "software cursor"). + ********************************************************************/ + +static Bool HandleCursorShape(int xhot, int yhot, int width, int height, CARD32 enc) +{ + int bytesPerPixel; + size_t bytesPerRow, bytesMaskData; + rfbXCursorColors rgb; + CARD32 colors[2]; + CARD8 *ptr, *rcSource, *rcMask; + void *softCursor; + int x, y, b; + int ox, oy, ow, oh; + PointerImage *pi; + short imageLen; + + bytesPerPixel = myFormat.bitsPerPixel / 8; + bytesPerRow = (width + 7) / 8; + bytesMaskData = bytesPerRow * height; + + if (width * height == 0) + return True; + + /* Allocate memory for pixel data and temporary mask data. */ + + rcSource = malloc(width * height * bytesPerPixel); + if (rcSource == NULL) + return False; + + rcMask = malloc(bytesMaskData); + if (rcMask == NULL) { + free(rcSource); + return False; + } + + /* Read and decode cursor pixel data, depending on the encoding type. */ + + if (enc == rfbEncodingXCursor) { + /* Read and convert background and foreground colors. */ + if (!ReadFromRFBServer((char *)&rgb, sz_rfbXCursorColors)) { + free(rcSource); + free(rcMask); + return False; + } + colors[0] = RGB24_TO_PIXEL(32, rgb.backRed, rgb.backGreen, rgb.backBlue); + colors[1] = RGB24_TO_PIXEL(32, rgb.foreRed, rgb.foreGreen, rgb.foreBlue); + + /* Read 1bpp pixel data into a temporary buffer. */ + if (!ReadFromRFBServer((char*)rcMask, bytesMaskData)) { + free(rcSource); + free(rcMask); + return False; + } + + /* Convert 1bpp data to byte-wide color indices. */ + ptr = rcSource; + for (y = 0; y < height; y++) { + for (x = 0; x < width / 8; x++) { + for (b = 7; b >= 0; b--) { + *ptr = rcMask[y * bytesPerRow + x] >> b & 1; + ptr += bytesPerPixel; + } + } + for (b = 7; b > 7 - width % 8; b--) { + *ptr = rcMask[y * bytesPerRow + x] >> b & 1; + ptr += bytesPerPixel; + } + } + + /* Convert indices into the actual pixel values. */ + switch (bytesPerPixel) { + case 1: + for (x = 0; x < width * height; x++) + rcSource[x] = (CARD8)colors[rcSource[x]]; + break; + case 2: + for (x = 0; x < width * height; x++) + ((CARD16 *)rcSource)[x] = (CARD16)colors[rcSource[x * 2]]; + break; + case 4: + for (x = 0; x < width * height; x++) + ((CARD32 *)rcSource)[x] = colors[rcSource[x * 4]]; + break; + } + + + } else { + if (!ReadFromRFBServer((char *)rcSource, width * height * bytesPerPixel)) { + free(rcSource); + free(rcMask); + return False; + } + } + + /* Read mask data. */ + + if (!ReadFromRFBServer((char*)rcMask, bytesMaskData)) { + free(rcSource); + free(rcMask); + return False; + } + + + /* Set the soft cursor. */ + softCursor = MakeSoftCursor(bytesPerPixel, width, height, rcSource, rcMask, &imageLen); + if (!softCursor) { + free(rcMask); + free(rcSource); + return False; + } + + /* get old cursor rect to know what to update */ + EnableClientCursor(1); + LockFramebuffer(); + getBoundingRectCursor(cursorX, cursorY, imageIndex, + &ox, &oy, &ow, &oh); + undrawCursor(); + + pi = &pointerImages[0]; + if (pi->set && pi->image) + free(pi->image); + pi->w = width; + pi->h = height; + pi->hotX = xhot; + pi->hotY = yhot; + pi->len = imageLen; + pi->image = softCursor; + pi->set = 1; + + imageIndex = 0; + + free(rcMask); + free(rcSource); + + return PointerMove(cursorX, cursorY, 0, ox, oy, ow, oh); +} + + + +/* + * HandleRFBServerMessage. + */ + +Bool +HandleRFBServerMessage() +{ + rfbServerToClientMsg msg; + if (!ReadFromRFBServer((char *)&msg, 1)) + return False; + + switch (msg.type) { + + case rfbSetColourMapEntries: + { + int i; + CARD16 rgb[3]; + XColor xc; + + if (!ReadFromRFBServer(((char *)&msg) + 1, + sz_rfbSetColourMapEntriesMsg - 1)) + return False; + + msg.scme.firstColour = Swap16IfLE(msg.scme.firstColour); + msg.scme.nColours = Swap16IfLE(msg.scme.nColours); + + for (i = 0; i < msg.scme.nColours; i++) { + if (!ReadFromRFBServer((char *)rgb, 6)) + return False; + xc.pixel = msg.scme.firstColour + i; + xc.red = Swap16IfLE(rgb[0]); + xc.green = Swap16IfLE(rgb[1]); + xc.blue = Swap16IfLE(rgb[2]); + xc.flags = DoRed|DoGreen|DoBlue; + /* Disable colormaps + lockQt(); + XStoreColor(dpy, cmap, &xc); + unlockQt(); + */ + } + + break; + } + + case rfbFramebufferUpdate: + { + rfbFramebufferUpdateRectHeader rect; + int linesToRead; + int bytesPerLine; + int i; + + announceIncrementalUpdateRequest(); + + if (!ReadFromRFBServer(((char *)&msg.fu) + 1, + sz_rfbFramebufferUpdateMsg - 1)) + return False; + + msg.fu.nRects = Swap16IfLE(msg.fu.nRects); + + for (i = 0; i < msg.fu.nRects; i++) { + if (!ReadFromRFBServer((char *)&rect, sz_rfbFramebufferUpdateRectHeader)) + return False; + + rect.encoding = Swap32IfLE(rect.encoding); + if (rect.encoding == rfbEncodingLastRect) + break; + + rect.r.x = Swap16IfLE(rect.r.x); + rect.r.y = Swap16IfLE(rect.r.y); + rect.r.w = Swap16IfLE(rect.r.w); + rect.r.h = Swap16IfLE(rect.r.h); + + if (rect.encoding == rfbEncodingPointerPos) { + if (!HandleCursorPos(rect.r.x, rect.r.y)) { + return False; + } + continue; + } + + if (rect.encoding == rfbEncodingXCursor || + rect.encoding == rfbEncodingRichCursor) { + if (!HandleCursorShape(rect.r.x, rect.r.y, rect.r.w, rect.r.h, + rect.encoding)) { + return False; + } + continue; + } + + if ((rect.r.x + rect.r.w > si.framebufferWidth) || + (rect.r.y + rect.r.h > si.framebufferHeight)) + { + fprintf(stderr,"Rect too large: %dx%d at (%d, %d)\n", + rect.r.w, rect.r.h, rect.r.x, rect.r.y); + return False; + } + + if ((rect.r.h * rect.r.w == 0) && + (rect.encoding != rfbEncodingSoftCursor)) { + fprintf(stderr,"Zero size rect - ignoring\n"); + continue; + } + + switch (rect.encoding) { + + case rfbEncodingRaw: + + bytesPerLine = rect.r.w * myFormat.bitsPerPixel / 8; + linesToRead = BUFFER_SIZE / bytesPerLine; + + while (rect.r.h > 0) { + if (linesToRead > rect.r.h) + linesToRead = rect.r.h; + + if (!ReadFromRFBServer(buffer,bytesPerLine * linesToRead)) + return False; + + CopyDataToScreen(buffer, rect.r.x, rect.r.y, rect.r.w, + linesToRead); + + rect.r.h -= linesToRead; + rect.r.y += linesToRead; + + } + break; + + case rfbEncodingCopyRect: + { + rfbCopyRect cr; + + if (!ReadFromRFBServer((char *)&cr, sz_rfbCopyRect)) + return False; + + cr.srcX = Swap16IfLE(cr.srcX); + cr.srcY = Swap16IfLE(cr.srcY); + + CopyArea(cr.srcX, cr.srcY, rect.r.w, rect.r.h, rect.r.x, rect.r.y); + + break; + } + + case rfbEncodingHextile: + { + switch (myFormat.bitsPerPixel) { + case 8: + if (!HandleHextile8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + case 16: + if (!HandleHextile16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + case 32: + if (!HandleHextile32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + } + break; + } + + case rfbEncodingZlib: + { + switch (myFormat.bitsPerPixel) { + case 8: + if (!HandleZlib8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + case 16: + if (!HandleZlib16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + case 32: + if (!HandleZlib32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + } + break; + } + + case rfbEncodingTight: + { + switch (myFormat.bitsPerPixel) { + case 8: + if (!HandleTight8(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + case 16: + if (!HandleTight16(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + case 32: + if (!HandleTight32(rect.r.x,rect.r.y,rect.r.w,rect.r.h)) + return False; + break; + } + break; + } + + case rfbEncodingSoftCursor: + { + rfbSoftCursorMsg scmsg; + if (!ReadFromRFBServer((char *)&scmsg, 1)) + return False; + if (scmsg.type < rfbSoftCursorMaxImages) { + if (!ReadFromRFBServer(((char *)&scmsg)+1, + sizeof(rfbSoftCursorMove)- 1)) + return False; + if (!HandleSoftCursorMove(&scmsg.move, &rect.r)) + return False; + } + else if((scmsg.type >= rfbSoftCursorSetIconOffset) && + (scmsg.type < rfbSoftCursorSetIconOffset+rfbSoftCursorMaxImages)) { + if (!ReadFromRFBServer(((char *)&scmsg)+1, + sizeof(rfbSoftCursorSetImage)- 1)) + return False; + if (!HandleSoftCursorSetImage(&scmsg.setImage, &rect.r)) + return False; + } + else { + fprintf(stderr,"Unknown soft cursor image index %d\n", + (int)scmsg.type); + return False; + } + break; + } + + default: + fprintf(stderr,"Unknown rect encoding %d\n", + (int)rect.encoding); + return False; + } + + } + + queueIncrementalUpdateRequest(); + + break; + } + + case rfbBell: + { + beep(); + break; + } + + case rfbServerCutText: + { + char *serverCutText; + if (!ReadFromRFBServer(((char *)&msg) + 1, + sz_rfbServerCutTextMsg - 1)) + return False; + + msg.sct.length = Swap32IfLE(msg.sct.length); + + if (msg.sct.length > MAX_CUTBUFFER) { + fprintf(stderr, "Cutbuffer too long.\n"); + return False; + } + + serverCutText = malloc(msg.sct.length+1); + + if (!serverCutText) { + fprintf(stderr, "Out-of-memory, cutbuffer too long.\n"); + return False; + } + + if (!ReadFromRFBServer(serverCutText, msg.sct.length)) + return False; + + serverCutText[msg.sct.length] = 0; + newServerCut(serverCutText, msg.sct.length); /* takes ownership of serverCutText */ + + break; + } + + default: + fprintf(stderr,"Unknown message type %d from VNC server\n",msg.type); + return False; + } + + return True; +} + + +#define GET_PIXEL8(pix, ptr) ((pix) = *(ptr)++) + +#define GET_PIXEL16(pix, ptr) (((CARD8*)&(pix))[0] = *(ptr)++, \ + ((CARD8*)&(pix))[1] = *(ptr)++) + +#define GET_PIXEL32(pix, ptr) (((CARD8*)&(pix))[0] = *(ptr)++, \ + ((CARD8*)&(pix))[1] = *(ptr)++, \ + ((CARD8*)&(pix))[2] = *(ptr)++, \ + ((CARD8*)&(pix))[3] = *(ptr)++) + +/* CONCAT2 concatenates its two arguments. CONCAT2E does the same but also + expands its arguments if they are macros */ + +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) + +#define BPP 8 +#include "hextile.c" +#include "zlib.c" +#include "tight.c" +#undef BPP +#define BPP 16 +#include "hextile.c" +#include "zlib.c" +#include "tight.c" +#undef BPP +#define BPP 32 +#include "hextile.c" +#include "zlib.c" +#include "tight.c" +#undef BPP + + +/* + * PrintPixelFormat. + */ + +void +PrintPixelFormat(format) + rfbPixelFormat *format; +{ + if (format->bitsPerPixel == 1) { + fprintf(stderr," Single bit per pixel.\n"); + fprintf(stderr, + " %s significant bit in each byte is leftmost on the screen.\n", + (format->bigEndian ? "Most" : "Least")); + } else { + fprintf(stderr," %d bits per pixel.\n",format->bitsPerPixel); + if (format->bitsPerPixel != 8) { + fprintf(stderr," %s significant byte first in each pixel.\n", + (format->bigEndian ? "Most" : "Least")); + } + if (format->trueColour) { + fprintf(stderr," True colour: max red %d green %d blue %d", + format->redMax, format->greenMax, format->blueMax); + fprintf(stderr,", shift red %d green %d blue %d\n", + format->redShift, format->greenShift, format->blueShift); + } else { + fprintf(stderr," Colour map (not true colour).\n"); + } + } +} + +static long +ReadCompactLen (void) +{ + long len; + CARD8 b; + + if (!ReadFromRFBServer((char *)&b, 1)) + return -1; + len = (int)b & 0x7F; + if (b & 0x80) { + if (!ReadFromRFBServer((char *)&b, 1)) + return -1; + len |= ((int)b & 0x7F) << 7; + if (b & 0x80) { + if (!ReadFromRFBServer((char *)&b, 1)) + return -1; + len |= ((int)b & 0xFF) << 14; + } + } + return len; +} + +void freeRFBProtoResources() { + int i; + + if (desktopName) + free(desktopName); + if (raw_buffer) + free(raw_buffer); + for (i = 0; i < rfbSoftCursorMaxImages; i++) + if (pointerImages[i].set && pointerImages[i].image) + free(pointerImages[i].image); + + raw_buffer_size = -1; + raw_buffer = NULL; + decompStreamInited = False; + zlibStreamActive[0] = False; + zlibStreamActive[1] = False; + zlibStreamActive[2] = False; + zlibStreamActive[3] = False; + for (i = 0; i < rfbSoftCursorMaxImages; i++) + pointerImages[i].set = 0; + imageIndex = -1; +} + +void freeResources() { + freeSocketsResources(); + freeDesktopResources(); + freeRFBProtoResources(); +} + +/* + * JPEG source manager functions for JPEG decompression in Tight decoder. + */ + +static struct jpeg_source_mgr jpegSrcManager; +static JOCTET *jpegBufferPtr; +static size_t jpegBufferLen; + +static void +JpegInitSource(j_decompress_ptr cinfo) +{ + jpegError = False; +} + +static boolean +JpegFillInputBuffer(j_decompress_ptr cinfo) +{ + jpegError = True; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; + + return TRUE; +} + +static void +JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes) +{ + if (num_bytes < 0 || num_bytes > jpegSrcManager.bytes_in_buffer) { + jpegError = True; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + jpegSrcManager.next_input_byte = (JOCTET *)jpegBufferPtr; + } else { + jpegSrcManager.next_input_byte += (size_t) num_bytes; + jpegSrcManager.bytes_in_buffer -= (size_t) num_bytes; + } +} + +static void +JpegTermSource(j_decompress_ptr cinfo) +{ + /* No work necessary here. */ +} + +static void +JpegSetSrcManager(j_decompress_ptr cinfo, CARD8 *compressedData, + int compressedLen) +{ + jpegBufferPtr = (JOCTET *)compressedData; + jpegBufferLen = (size_t)compressedLen; + + jpegSrcManager.init_source = JpegInitSource; + jpegSrcManager.fill_input_buffer = JpegFillInputBuffer; + jpegSrcManager.skip_input_data = JpegSkipInputData; + jpegSrcManager.resync_to_restart = jpeg_resync_to_restart; + jpegSrcManager.term_source = JpegTermSource; + jpegSrcManager.next_input_byte = jpegBufferPtr; + jpegSrcManager.bytes_in_buffer = jpegBufferLen; + + cinfo->src = &jpegSrcManager; +} + diff --git a/krdc/vnc/rfbproto.h b/krdc/vnc/rfbproto.h new file mode 100644 index 00000000..61ceb903 --- /dev/null +++ b/krdc/vnc/rfbproto.h @@ -0,0 +1,957 @@ +/* + * Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved. + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * rfbproto.h - header file for the RFB protocol version 3.3 + * + * Uses types CARD<n> for an n-bit unsigned integer, INT<n> for an n-bit signed + * integer (for n = 8, 16 and 32). + * + * All multiple byte integers are in big endian (network) order (most + * significant byte first). Unless noted otherwise there is no special + * alignment of protocol structures. + * + * + * Once the initial handshaking is done, all messages start with a type byte, + * (usually) followed by message-specific data. The order of definitions in + * this file is as follows: + * + * (1) Structures used in several types of message. + * (2) Structures used in the initial handshaking. + * (3) Message types. + * (4) Encoding types. + * (5) For each message type, the form of the data following the type byte. + * Sometimes this is defined by a single structure but the more complex + * messages have to be explained by comments. + */ + +#include "vnctypes.h" + +/***************************************************************************** + * + * Structures used in several messages + * + *****************************************************************************/ + +/*----------------------------------------------------------------------------- + * Structure used to specify a rectangle. This structure is a multiple of 4 + * bytes so that it can be interspersed with 32-bit pixel data without + * affecting alignment. + */ + +typedef struct { + CARD16 x; + CARD16 y; + CARD16 w; + CARD16 h; +} rfbRectangle; + +#define sz_rfbRectangle 8 + + +/*----------------------------------------------------------------------------- + * Structure used to specify pixel format. + */ + +typedef struct { + + CARD8 bitsPerPixel; /* 8,16,32 only */ + + CARD8 depth; /* 8 to 32 */ + + CARD8 bigEndian; /* True if multi-byte pixels are interpreted + as big endian, or if single-bit-per-pixel + has most significant bit of the byte + corresponding to first (leftmost) pixel. Of + course this is meaningless for 8 bits/pix */ + + CARD8 trueColour; /* If false then we need a "colour map" to + convert pixels to RGB. If true, xxxMax and + xxxShift specify bits used for red, green + and blue */ + + /* the following fields are only meaningful if trueColour is true */ + + CARD16 redMax; /* maximum red value (= 2^n - 1 where n is the + number of bits used for red). Note this + value is always in big endian order. */ + + CARD16 greenMax; /* similar for green */ + + CARD16 blueMax; /* and blue */ + + CARD8 redShift; /* number of shifts needed to get the red + value in a pixel to the least significant + bit. To find the red value from a given + pixel, do the following: + 1) Swap pixel value according to bigEndian + (e.g. if bigEndian is false and host byte + order is big endian, then swap). + 2) Shift right by redShift. + 3) AND with redMax (in host byte order). + 4) You now have the red value between 0 and + redMax. */ + + CARD8 greenShift; /* similar for green */ + + CARD8 blueShift; /* and blue */ + + CARD8 pad1; + CARD16 pad2; + +} rfbPixelFormat; + +#define sz_rfbPixelFormat 16 + + + +/***************************************************************************** + * + * Initial handshaking messages + * + *****************************************************************************/ + +/*----------------------------------------------------------------------------- + * Protocol Version + * + * The server always sends 12 bytes to start which identifies the latest RFB + * protocol version number which it supports. These bytes are interpreted + * as a string of 12 ASCII characters in the format "RFB xxx.yyy\n" where + * xxx and yyy are the major and minor version numbers (for version 3.3 + * this is "RFB 003.003\n"). + * + * The client then replies with a similar 12-byte message giving the version + * number of the protocol which should actually be used (which may be different + * to that quoted by the server). + * + * It is intended that both clients and servers may provide some level of + * backwards compatibility by this mechanism. Servers in particular should + * attempt to provide backwards compatibility, and even forwards compatibility + * to some extent. For example if a client demands version 3.1 of the + * protocol, a 3.0 server can probably assume that by ignoring requests for + * encoding types it doesn't understand, everything will still work OK. This + * will probably not be the case for changes in the major version number. + * + * The format string below can be used in sprintf or sscanf to generate or + * decode the version string respectively. + */ + +#define rfbProtocolVersionFormat "RFB %03d.%03d\n" +#define rfbProtocolMajorVersion 3 +#define rfbProtocolMinorVersion 3 + +typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */ + +#define sz_rfbProtocolVersionMsg 12 + + +/*----------------------------------------------------------------------------- + * Authentication + * + * Once the protocol version has been decided, the server then sends a 32-bit + * word indicating whether any authentication is needed on the connection. + * The value of this word determines the authentication scheme in use. For + * version 3.0 of the protocol this may have one of the following values: + */ + +#define rfbConnFailed 0 +#define rfbNoAuth 1 +#define rfbVncAuth 2 + +/* + * rfbConnFailed: For some reason the connection failed (e.g. the server + * cannot support the desired protocol version). This is + * followed by a string describing the reason (where a + * string is specified as a 32-bit length followed by that + * many ASCII characters). + * + * rfbNoAuth: No authentication is needed. + * + * rfbVncAuth: The VNC authentication scheme is to be used. A 16-byte + * challenge follows, which the client encrypts as + * appropriate using the password and sends the resulting + * 16-byte response. If the response is correct, the + * server sends the 32-bit word rfbVncAuthOK. If a simple + * failure happens, the server sends rfbVncAuthFailed and + * closes the connection. If the server decides that too + * many failures have occurred, it sends rfbVncAuthTooMany + * and closes the connection. In the latter case, the + * server should not allow an immediate reconnection by + * the client. + */ + +#define rfbVncAuthOK 0 +#define rfbVncAuthFailed 1 +#define rfbVncAuthTooMany 2 + + +/*----------------------------------------------------------------------------- + * Client Initialisation Message + * + * Once the client and server are sure that they're happy to talk to one + * another, the client sends an initialisation message. At present this + * message only consists of a boolean indicating whether the server should try + * to share the desktop by leaving other clients connected, or give exclusive + * access to this client by disconnecting all other clients. + */ + +typedef struct { + CARD8 shared; +} rfbClientInitMsg; + +#define sz_rfbClientInitMsg 1 + + +/*----------------------------------------------------------------------------- + * Server Initialisation Message + * + * After the client initialisation message, the server sends one of its own. + * This tells the client the width and height of the server's framebuffer, + * its pixel format and the name associated with the desktop. + */ + +typedef struct { + CARD16 framebufferWidth; + CARD16 framebufferHeight; + rfbPixelFormat format; /* the server's preferred pixel format */ + CARD32 nameLength; + /* followed by char name[nameLength] */ +} rfbServerInitMsg; + +#define sz_rfbServerInitMsg (8 + sz_rfbPixelFormat) + + +/* + * Following the server initialisation message it's up to the client to send + * whichever protocol messages it wants. Typically it will send a + * SetPixelFormat message and a SetEncodings message, followed by a + * FramebufferUpdateRequest. From then on the server will send + * FramebufferUpdate messages in response to the client's + * FramebufferUpdateRequest messages. The client should send + * FramebufferUpdateRequest messages with incremental set to true when it has + * finished processing one FramebufferUpdate and is ready to process another. + * With a fast client, the rate at which FramebufferUpdateRequests are sent + * should be regulated to avoid hogging the network. + */ + + + +/***************************************************************************** + * + * Message types + * + *****************************************************************************/ + +/* server -> client */ + +#define rfbFramebufferUpdate 0 +#define rfbSetColourMapEntries 1 +#define rfbBell 2 +#define rfbServerCutText 3 + + +/* client -> server */ + +#define rfbSetPixelFormat 0 +#define rfbFixColourMapEntries 1 /* not currently supported */ +#define rfbSetEncodings 2 +#define rfbFramebufferUpdateRequest 3 +#define rfbKeyEvent 4 +#define rfbPointerEvent 5 +#define rfbClientCutText 6 + + + + +/***************************************************************************** + * + * Encoding types + * + *****************************************************************************/ + +#define rfbEncodingRaw 0 +#define rfbEncodingCopyRect 1 +#define rfbEncodingRRE 2 +#define rfbEncodingCoRRE 4 +#define rfbEncodingHextile 5 +#define rfbEncodingZlib 6 +#define rfbEncodingTight 7 +#define rfbEncodingZlibHex 8 + +/* + * Special encoding numbers: + * 0xFFFFFF00 .. 0xFFFFFF0F -- encoding-specific compression levels; + * 0xFFFFFF10 .. 0xFFFFFF1F -- mouse cursor shape data; + * 0xFFFFFF20 .. 0xFFFFFF2F -- various protocol extensions; + * 0xFFFFFF30 .. 0xFFFFFFDF -- not allocated yet; + * 0xFFFFFFE0 .. 0xFFFFFFEF -- quality level for JPEG compressor; + * 0xFFFFFFF0 .. 0xFFFFFFFF -- cross-encoding compression levels. + */ + +#define rfbEncodingCompressLevel0 0xFFFFFF00 +#define rfbEncodingCompressLevel1 0xFFFFFF01 +#define rfbEncodingCompressLevel2 0xFFFFFF02 +#define rfbEncodingCompressLevel3 0xFFFFFF03 +#define rfbEncodingCompressLevel4 0xFFFFFF04 +#define rfbEncodingCompressLevel5 0xFFFFFF05 +#define rfbEncodingCompressLevel6 0xFFFFFF06 +#define rfbEncodingCompressLevel7 0xFFFFFF07 +#define rfbEncodingCompressLevel8 0xFFFFFF08 +#define rfbEncodingCompressLevel9 0xFFFFFF09 + +#define rfbEncodingXCursor 0xFFFFFF10 +#define rfbEncodingRichCursor 0xFFFFFF11 +#define rfbEncodingSoftCursor 0xFFFFFF12 +#define rfbEncodingPointerPos 0xFFFFFF18 + +#define rfbEncodingLastRect 0xFFFFFF20 +#define rfbEncodingBackground 0xFFFFFF25 + +#define rfbEncodingQualityLevel0 0xFFFFFFE0 +#define rfbEncodingQualityLevel1 0xFFFFFFE1 +#define rfbEncodingQualityLevel2 0xFFFFFFE2 +#define rfbEncodingQualityLevel3 0xFFFFFFE3 +#define rfbEncodingQualityLevel4 0xFFFFFFE4 +#define rfbEncodingQualityLevel5 0xFFFFFFE5 +#define rfbEncodingQualityLevel6 0xFFFFFFE6 +#define rfbEncodingQualityLevel7 0xFFFFFFE7 +#define rfbEncodingQualityLevel8 0xFFFFFFE8 +#define rfbEncodingQualityLevel9 0xFFFFFFE9 + + +/***************************************************************************** + * + * Server -> client message definitions + * + *****************************************************************************/ + + +/*----------------------------------------------------------------------------- + * FramebufferUpdate - a block of rectangles to be copied to the framebuffer. + * + * This message consists of a header giving the number of rectangles of pixel + * data followed by the rectangles themselves. The header is padded so that + * together with the type byte it is an exact multiple of 4 bytes (to help + * with alignment of 32-bit pixels): + */ + +typedef struct { + CARD8 type; /* always rfbFramebufferUpdate */ + CARD8 pad; + CARD16 nRects; + /* followed by nRects rectangles */ +} rfbFramebufferUpdateMsg; + +#define sz_rfbFramebufferUpdateMsg 4 + +/* + * Each rectangle of pixel data consists of a header describing the position + * and size of the rectangle and a type word describing the encoding of the + * pixel data, followed finally by the pixel data. Note that if the client has + * not sent a SetEncodings message then it will only receive raw pixel data. + * Also note again that this structure is a multiple of 4 bytes. + */ + +typedef struct { + rfbRectangle r; + CARD32 encoding; /* one of the encoding types rfbEncoding... */ +} rfbFramebufferUpdateRectHeader; + +#define sz_rfbFramebufferUpdateRectHeader (sz_rfbRectangle + 4) + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Raw Encoding. Pixels are sent in top-to-bottom scanline order, + * left-to-right within a scanline with no padding in between. + */ + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * CopyRect Encoding. The pixels are specified simply by the x and y position + * of the source rectangle. + */ + +typedef struct { + CARD16 srcX; + CARD16 srcY; +} rfbCopyRect; + +#define sz_rfbCopyRect 4 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * RRE - Rise-and-Run-length Encoding. We have an rfbRREHeader structure + * giving the number of subrectangles following. Finally the data follows in + * the form [<bgpixel><subrect><subrect>...] where each <subrect> is + * [<pixel><rfbRectangle>]. + */ + +typedef struct { + CARD32 nSubrects; +} rfbRREHeader; + +#define sz_rfbRREHeader 4 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * CoRRE - Compact RRE Encoding. We have an rfbRREHeader structure giving + * the number of subrectangles following. Finally the data follows in the form + * [<bgpixel><subrect><subrect>...] where each <subrect> is + * [<pixel><rfbCoRRERectangle>]. This means that + * the whole rectangle must be at most 255x255 pixels. + */ + +typedef struct { + CARD8 x; + CARD8 y; + CARD8 w; + CARD8 h; +} rfbCoRRERectangle; + +#define sz_rfbCoRRERectangle 4 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Hextile Encoding. The rectangle is divided up into "tiles" of 16x16 pixels, + * starting at the top left going in left-to-right, top-to-bottom order. If + * the width of the rectangle is not an exact multiple of 16 then the width of + * the last tile in each row will be correspondingly smaller. Similarly if the + * height is not an exact multiple of 16 then the height of each tile in the + * final row will also be smaller. Each tile begins with a "subencoding" type + * byte, which is a mask made up of a number of bits. If the Raw bit is set + * then the other bits are irrelevant; w*h pixel values follow (where w and h + * are the width and height of the tile). Otherwise the tile is encoded in a + * similar way to RRE, except that the position and size of each subrectangle + * can be specified in just two bytes. The other bits in the mask are as + * follows: + * + * BackgroundSpecified - if set, a pixel value follows which specifies + * the background colour for this tile. The first non-raw tile in a + * rectangle must have this bit set. If this bit isn't set then the + * background is the same as the last tile. + * + * ForegroundSpecified - if set, a pixel value follows which specifies + * the foreground colour to be used for all subrectangles in this tile. + * If this bit is set then the SubrectsColoured bit must be zero. + * + * AnySubrects - if set, a single byte follows giving the number of + * subrectangles following. If not set, there are no subrectangles (i.e. + * the whole tile is just solid background colour). + * + * SubrectsColoured - if set then each subrectangle is preceded by a pixel + * value giving the colour of that subrectangle. If not set, all + * subrectangles are the same colour, the foreground colour; if the + * ForegroundSpecified bit wasn't set then the foreground is the same as + * the last tile. + * + * The position and size of each subrectangle is specified in two bytes. The + * Pack macros below can be used to generate the two bytes from x, y, w, h, + * and the Extract macros can be used to extract the x, y, w, h values from + * the two bytes. + */ + +#define rfbHextileRaw (1 << 0) +#define rfbHextileBackgroundSpecified (1 << 1) +#define rfbHextileForegroundSpecified (1 << 2) +#define rfbHextileAnySubrects (1 << 3) +#define rfbHextileSubrectsColoured (1 << 4) + +#define rfbHextilePackXY(x,y) (((x) << 4) | (y)) +#define rfbHextilePackWH(w,h) ((((w)-1) << 4) | ((h)-1)) +#define rfbHextileExtractX(byte) ((byte) >> 4) +#define rfbHextileExtractY(byte) ((byte) & 0xf) +#define rfbHextileExtractW(byte) (((byte) >> 4) + 1) +#define rfbHextileExtractH(byte) (((byte) & 0xf) + 1) + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * zlib - zlib compressed Encoding. We have an rfbZlibHeader structure + * giving the number of bytes following. Finally the data follows is + * zlib compressed version of the raw pixel data as negotiated. + */ + +typedef struct { + CARD32 nBytes; +} rfbZlibHeader; + +#define sz_rfbZlibHeader 4 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * Tight Encoding. + * + *-- The first byte of each Tight-encoded rectangle is a "compression control + * byte". Its format is as follows (bit 0 is the least significant one): + * + * bit 0: if 1, then compression stream 0 should be reset; + * bit 1: if 1, then compression stream 1 should be reset; + * bit 2: if 1, then compression stream 2 should be reset; + * bit 3: if 1, then compression stream 3 should be reset; + * bits 7-4: if 1000 (0x08), then the compression type is "fill", + * if 1001 (0x09), then the compression type is "jpeg", + * if 0xxx, then the compression type is "basic", + * values greater than 1001 are not valid. + * + * If the compression type is "basic", then bits 6..4 of the + * compression control byte (those xxx in 0xxx) specify the following: + * + * bits 5-4: decimal representation is the index of a particular zlib + * stream which should be used for decompressing the data; + * bit 6: if 1, then a "filter id" byte is following this byte. + * + *-- The data that follows after the compression control byte described + * above depends on the compression type ("fill", "jpeg" or "basic"). + * + *-- If the compression type is "fill", then the only pixel value follows, in + * client pixel format (see NOTE 1). This value applies to all pixels of the + * rectangle. + * + *-- If the compression type is "jpeg", the following data stream looks like + * this: + * + * 1..3 bytes: data size (N) in compact representation; + * N bytes: JPEG image. + * + * Data size is compactly represented in one, two or three bytes, according + * to the following scheme: + * + * 0xxxxxxx (for values 0..127) + * 1xxxxxxx 0yyyyyyy (for values 128..16383) + * 1xxxxxxx 1yyyyyyy zzzzzzzz (for values 16384..4194303) + * + * Here each character denotes one bit, xxxxxxx are the least significant 7 + * bits of the value (bits 0-6), yyyyyyy are bits 7-13, and zzzzzzzz are the + * most significant 8 bits (bits 14-21). For example, decimal value 10000 + * should be represented as two bytes: binary 10010000 01001110, or + * hexadecimal 90 4E. + * + *-- If the compression type is "basic" and bit 6 of the compression control + * byte was set to 1, then the next (second) byte specifies "filter id" which + * tells the decoder what filter type was used by the encoder to pre-process + * pixel data before the compression. The "filter id" byte can be one of the + * following: + * + * 0: no filter ("copy" filter); + * 1: "palette" filter; + * 2: "gradient" filter. + * + *-- If bit 6 of the compression control byte is set to 0 (no "filter id" + * byte), or if the filter id is 0, then raw pixel values in the client + * format (see NOTE 1) will be compressed. See below details on the + * compression. + * + *-- The "gradient" filter pre-processes pixel data with a simple algorithm + * which converts each color component to a difference between a "predicted" + * intensity and the actual intensity. Such a technique does not affect + * uncompressed data size, but helps to compress photo-like images better. + * Pseudo-code for converting intensities to differences is the following: + * + * P[i,j] := V[i-1,j] + V[i,j-1] - V[i-1,j-1]; + * if (P[i,j] < 0) then P[i,j] := 0; + * if (P[i,j] > MAX) then P[i,j] := MAX; + * D[i,j] := V[i,j] - P[i,j]; + * + * Here V[i,j] is the intensity of a color component for a pixel at + * coordinates (i,j). MAX is the maximum value of intensity for a color + * component. + * + *-- The "palette" filter converts true-color pixel data to indexed colors + * and a palette which can consist of 2..256 colors. If the number of colors + * is 2, then each pixel is encoded in 1 bit, otherwise 8 bits is used to + * encode one pixel. 1-bit encoding is performed such way that the most + * significant bits correspond to the leftmost pixels, and each raw of pixels + * is aligned to the byte boundary. When "palette" filter is used, the + * palette is sent before the pixel data. The palette begins with an unsigned + * byte which value is the number of colors in the palette minus 1 (i.e. 1 + * means 2 colors, 255 means 256 colors in the palette). Then follows the + * palette itself which consist of pixel values in client pixel format (see + * NOTE 1). + * + *-- The pixel data is compressed using the zlib library. But if the data + * size after applying the filter but before the compression is less then 12, + * then the data is sent as is, uncompressed. Four separate zlib streams + * (0..3) can be used and the decoder should read the actual stream id from + * the compression control byte (see NOTE 2). + * + * If the compression is not used, then the pixel data is sent as is, + * otherwise the data stream looks like this: + * + * 1..3 bytes: data size (N) in compact representation; + * N bytes: zlib-compressed data. + * + * Data size is compactly represented in one, two or three bytes, just like + * in the "jpeg" compression method (see above). + * + *-- NOTE 1. If the color depth is 24, and all three color components are + * 8-bit wide, then one pixel in Tight encoding is always represented by + * three bytes, where the first byte is red component, the second byte is + * green component, and the third byte is blue component of the pixel color + * value. This applies to colors in palettes as well. + * + *-- NOTE 2. The decoder must reset compression streams' states before + * decoding the rectangle, if some of bits 0,1,2,3 in the compression control + * byte are set to 1. Note that the decoder must reset zlib streams even if + * the compression type is "fill" or "jpeg". + * + *-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only + * when bits-per-pixel value is either 16 or 32, not 8. + * + *-- NOTE 4. The width of any Tight-encoded rectangle cannot exceed 2048 + * pixels. If a rectangle is wider, it must be split into several rectangles + * and each one should be encoded separately. + * + */ + +#define rfbTightExplicitFilter 0x04 +#define rfbTightFill 0x08 +#define rfbTightJpeg 0x09 +#define rfbTightMaxSubencoding 0x09 + +/* Filters to improve compression efficiency */ +#define rfbTightFilterCopy 0x00 +#define rfbTightFilterPalette 0x01 +#define rfbTightFilterGradient 0x02 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * XCursor encoding. This is a special encoding used to transmit X-style + * cursor shapes from server to clients. Note that for this encoding, + * coordinates in rfbFramebufferUpdateRectHeader structure hold hotspot + * position (r.x, r.y) and cursor size (r.w, r.h). If (w * h != 0), two RGB + * samples are sent after header in the rfbXCursorColors structure. They + * denote foreground and background colors of the cursor. If a client + * supports only black-and-white cursors, it should ignore these colors and + * assume that foreground is black and background is white. Next, two bitmaps + * (1 bits per pixel) follow: first one with actual data (value 0 denotes + * background color, value 1 denotes foreground color), second one with + * transparency data (bits with zero value mean that these pixels are + * transparent). Both bitmaps represent cursor data in a byte stream, from + * left to right, from top to bottom, and each row is byte-aligned. Most + * significant bits correspond to leftmost pixels. The number of bytes in + * each row can be calculated as ((w + 7) / 8). If (w * h == 0), cursor + * should be hidden (or default local cursor should be set by the client). + */ + +typedef struct { + CARD8 foreRed; + CARD8 foreGreen; + CARD8 foreBlue; + CARD8 backRed; + CARD8 backGreen; + CARD8 backBlue; +} rfbXCursorColors; + +#define sz_rfbXCursorColors 6 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * RichCursor encoding. This is a special encoding used to transmit cursor + * shapes from server to clients. It is similar to the XCursor encoding but + * uses client pixel format instead of two RGB colors to represent cursor + * image. For this encoding, coordinates in rfbFramebufferUpdateRectHeader + * structure hold hotspot position (r.x, r.y) and cursor size (r.w, r.h). + * After header, two pixmaps follow: first one with cursor image in current + * client pixel format (like in raw encoding), second with transparency data + * (1 bit per pixel, exactly the same format as used for transparency bitmap + * in the XCursor encoding). If (w * h == 0), cursor should be hidden (or + * default local cursor should be set by the client). + */ + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * SoftCursor encoding. This encoding is used to transmit image and position + * of the remote cursor. It has two sub-messages: SetImage is used to upload + * one of 16 images, and Move selects the image and sets the position of the + * cursor. + * Each SoftCursor message starts with a CARD8. If it is in the 0-15 range + * it specifies the number of the cursor image and is followed by the + * rfbSoftCursorMove message. If the given cursor has not been set yet the + * message will be ignored. If the first CARD8 is in the 128-143 range it + * specifies the cursor that will be set in the following + * rfbSoftCursorSetImage message. To hide the cursor send a SetImage + * message with width and height 0 and imageLength 0. + * SetImage transmits the hotspot coordinates in the x/y fields of the + * rfbFramebufferUpdateRectHeader, width and height of the image are in the + * header's width and height fields. + * Move transmits the pointer coordinates in the w/h fields of the + * header, x/y are always 0. + */ + +typedef struct { + CARD8 imageIndex; + CARD8 buttonMask; /* bits 0-7 are buttons 1-8, 0=up, 1=down */ +} rfbSoftCursorMove; + +typedef struct { + CARD8 imageIndex; + CARD8 padding; + CARD16 imageLength; + /* + * Followed by an image of the cursor in the client's image format + * with the following RLE mask compression. It begins with CARD8 that + * specifies the number of mask'ed pixels that will be NOT transmitted. + * Then follows a CARD8 that specified by the number of unmask'd pixels + * that will be transmitted next. Then a CARD8 with the number of mask'd + * pixels and so on. + */ +} rfbSoftCursorSetImage; + +typedef union { + CARD8 type; + rfbSoftCursorMove move; + rfbSoftCursorSetImage setImage; +} rfbSoftCursorMsg; + +#define rfbSoftCursorMaxImages 16 +#define rfbSoftCursorSetIconOffset 128 + +/*----------------------------------------------------------------------------- + * SetColourMapEntries - these messages are only sent if the pixel + * format uses a "colour map" (i.e. trueColour false) and the client has not + * fixed the entire colour map using FixColourMapEntries. In addition they + * will only start being sent after the client has sent its first + * FramebufferUpdateRequest. So if the client always tells the server to use + * trueColour then it never needs to process this type of message. + */ + +typedef struct { + CARD8 type; /* always rfbSetColourMapEntries */ + CARD8 pad; + CARD16 firstColour; + CARD16 nColours; + + /* Followed by nColours * 3 * CARD16 + r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */ + +} rfbSetColourMapEntriesMsg; + +#define sz_rfbSetColourMapEntriesMsg 6 + + + +/*----------------------------------------------------------------------------- + * Bell - ring a bell on the client if it has one. + */ + +typedef struct { + CARD8 type; /* always rfbBell */ +} rfbBellMsg; + +#define sz_rfbBellMsg 1 + + + +/*----------------------------------------------------------------------------- + * ServerCutText - the server has new text in its cut buffer. + */ + +typedef struct { + CARD8 type; /* always rfbServerCutText */ + CARD8 pad1; + CARD16 pad2; + CARD32 length; + /* followed by char text[length] */ +} rfbServerCutTextMsg; + +#define sz_rfbServerCutTextMsg 8 + + +/*----------------------------------------------------------------------------- + * Union of all server->client messages. + */ + +typedef union { + CARD8 type; + rfbFramebufferUpdateMsg fu; + rfbSetColourMapEntriesMsg scme; + rfbBellMsg b; + rfbServerCutTextMsg sct; +} rfbServerToClientMsg; + + + +/***************************************************************************** + * + * Message definitions (client -> server) + * + *****************************************************************************/ + + +/*----------------------------------------------------------------------------- + * SetPixelFormat - tell the RFB server the format in which the client wants + * pixels sent. + */ + +typedef struct { + CARD8 type; /* always rfbSetPixelFormat */ + CARD8 pad1; + CARD16 pad2; + rfbPixelFormat format; +} rfbSetPixelFormatMsg; + +#define sz_rfbSetPixelFormatMsg (sz_rfbPixelFormat + 4) + + +/*----------------------------------------------------------------------------- + * FixColourMapEntries - when the pixel format uses a "colour map", fix + * read-only colour map entries. + * + * ***************** NOT CURRENTLY SUPPORTED ***************** + */ + +typedef struct { + CARD8 type; /* always rfbFixColourMapEntries */ + CARD8 pad; + CARD16 firstColour; + CARD16 nColours; + + /* Followed by nColours * 3 * CARD16 + r1, g1, b1, r2, g2, b2, r3, g3, b3, ..., rn, bn, gn */ + +} rfbFixColourMapEntriesMsg; + +#define sz_rfbFixColourMapEntriesMsg 6 + + +/*----------------------------------------------------------------------------- + * SetEncodings - tell the RFB server which encoding types we accept. Put them + * in order of preference, if we have any. We may always receive raw + * encoding, even if we don't specify it here. + */ + +typedef struct { + CARD8 type; /* always rfbSetEncodings */ + CARD8 pad; + CARD16 nEncodings; + /* followed by nEncodings * CARD32 encoding types */ +} rfbSetEncodingsMsg; + +#define sz_rfbSetEncodingsMsg 4 + + +/*----------------------------------------------------------------------------- + * FramebufferUpdateRequest - request for a framebuffer update. If incremental + * is true then the client just wants the changes since the last update. If + * false then it wants the whole of the specified rectangle. + */ + +typedef struct { + CARD8 type; /* always rfbFramebufferUpdateRequest */ + CARD8 incremental; + CARD16 x; + CARD16 y; + CARD16 w; + CARD16 h; +} rfbFramebufferUpdateRequestMsg; + +#define sz_rfbFramebufferUpdateRequestMsg 10 + + +/*----------------------------------------------------------------------------- + * KeyEvent - key press or release + * + * Keys are specified using the "keysym" values defined by the X Window System. + * For most ordinary keys, the keysym is the same as the corresponding ASCII + * value. Other common keys are: + * + * BackSpace 0xff08 + * Tab 0xff09 + * Return or Enter 0xff0d + * Escape 0xff1b + * Insert 0xff63 + * Delete 0xffff + * Home 0xff50 + * End 0xff57 + * Page Up 0xff55 + * Page Down 0xff56 + * Left 0xff51 + * Up 0xff52 + * Right 0xff53 + * Down 0xff54 + * F1 0xffbe + * F2 0xffbf + * ... ... + * F12 0xffc9 + * Shift 0xffe1 + * Control 0xffe3 + * Meta 0xffe7 + * Alt 0xffe9 + */ + +typedef struct { + CARD8 type; /* always rfbKeyEvent */ + CARD8 down; /* true if down (press), false if up */ + CARD16 pad; + CARD32 key; /* key is specified as an X keysym */ +} rfbKeyEventMsg; + +#define sz_rfbKeyEventMsg 8 + + +/*----------------------------------------------------------------------------- + * PointerEvent - mouse/pen move and/or button press. + */ + +typedef struct { + CARD8 type; /* always rfbPointerEvent */ + CARD8 buttonMask; /* bits 0-7 are buttons 1-8, 0=up, 1=down */ + CARD16 x; + CARD16 y; +} rfbPointerEventMsg; + +#define rfbButton1Mask 1 +#define rfbButton2Mask 2 +#define rfbButton3Mask 4 + +#define sz_rfbPointerEventMsg 6 + + + +/*----------------------------------------------------------------------------- + * ClientCutText - the client has new text in its cut buffer. + */ + +typedef struct { + CARD8 type; /* always rfbClientCutText */ + CARD8 pad1; + CARD16 pad2; + CARD32 length; + /* followed by char text[length] */ +} rfbClientCutTextMsg; + +#define sz_rfbClientCutTextMsg 8 + + + +/*----------------------------------------------------------------------------- + * Union of all client->server messages. + */ + +typedef union { + CARD8 type; + rfbSetPixelFormatMsg spf; + rfbFixColourMapEntriesMsg fcme; + rfbSetEncodingsMsg se; + rfbFramebufferUpdateRequestMsg fur; + rfbKeyEventMsg ke; + rfbPointerEventMsg pe; + rfbClientCutTextMsg cct; +} rfbClientToServerMsg; diff --git a/krdc/vnc/sockets.c b/krdc/vnc/sockets.c new file mode 100644 index 00000000..797dd22d --- /dev/null +++ b/krdc/vnc/sockets.c @@ -0,0 +1,325 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * 03-05-2002 tim@tjansen.de: removed Xt event processing for krdc + */ + +/* + * sockets.c - functions to deal with sockets. + */ + +#include <unistd.h> +#include <sys/socket.h> +#include <errno.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <fcntl.h> +#include <assert.h> +#include "vncviewer.h" + +void PrintInHex(char *buf, int len); + +Bool errorMessageOnReadFailure = True; + +#define BUF_SIZE 8192 +static char buf[BUF_SIZE]; +static char *bufoutptr = buf; +static unsigned int buffered = 0; + +/* Wait duration of select in seconds */ +#define SELECT_PERIOD 3 + + +/* + * ReadFromRFBServer is called whenever we want to read some data from the RFB + * server. + */ +Bool +ReadFromRFBServer(char *out, unsigned int n) +{ + fd_set fds; + int e; + struct timeval tx; + + if (isQuitFlagSet()) + return False; + + if (n <= buffered) { + memcpy(out, bufoutptr, n); + bufoutptr += n; + buffered -= n; + return True; + } + + memcpy(out, bufoutptr, buffered); + + out += buffered; + n -= buffered; + + bufoutptr = buf; + buffered = 0; + + if (n <= BUF_SIZE) { + + while (buffered < n) { + int i; + if (isQuitFlagSet()) + return False; + i = read(rfbsock, buf + buffered, BUF_SIZE - buffered); + + if (i <= 0) { + if (i < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + FD_ZERO(&fds); + FD_SET(rfbsock,&fds); + + tx.tv_sec = SELECT_PERIOD; + tx.tv_usec = 0; + if ((e=select(rfbsock+1, &fds, NULL, &fds, &tx)) < 0) { + perror("krdc: select read"); + return False; + } + i = 0; + } else { + perror("krdc: read"); + return False; + } + } else { + fprintf(stderr,"VNC server closed connection\n"); + return False; + } + } + buffered += i; + } + + memcpy(out, bufoutptr, n); + bufoutptr += n; + buffered -= n; + return isQuitFlagSet() ? False : True; + + } else { + + while (n > 0) { + int i; + if (isQuitFlagSet()) + return False; + i = read(rfbsock, out, n); + if (i <= 0) { + if (i < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + FD_ZERO(&fds); + FD_SET(rfbsock,&fds); + + tx.tv_sec = SELECT_PERIOD; + tx.tv_usec = 0; + if ((e=select(rfbsock+1, &fds, NULL, &fds, &tx)) < 0) { + perror("krdc: select"); + return False; + } + i = 0; + } else { + perror("krdc: read"); + return False; + } + } else { + fprintf(stderr,"VNC server closed connection\n"); + return False; + } + } + out += i; + n -= i; + } + + return isQuitFlagSet() ? False : True; + } +} + + +/* + * Write an exact number of bytes, and don't return until you've sent them. + * Note: this should only be called by the WriterThread + */ + +Bool +WriteExact(int sock, const char *_buf, int n) +{ + fd_set fds; + int i = 0; + int j; + int e; + struct timeval tx; + + while (i < n) { + if (isQuitFlagSet()) + return False; + j = write(sock, _buf + i, (n - i)); + if (j <= 0) { + if (j < 0) { + if (errno == EWOULDBLOCK || errno == EAGAIN) { + FD_ZERO(&fds); + FD_SET(rfbsock,&fds); + + tx.tv_sec = SELECT_PERIOD; + tx.tv_usec = 0; + if ((e=select(rfbsock+1, NULL, &fds, NULL, &tx)) < 0) { + perror("krdc: select write"); + return False; + } + j = 0; + } else { + perror("krdc: write"); + return False; + } + } else { + fprintf(stderr,"write failed\n"); + return False; + } + } + i += j; + } + return True; +} + + +/* + * ConnectToTcpAddr connects to the given TCP port. + */ + +int +ConnectToTcpAddr(unsigned int host, int port) +{ + int sock; + struct sockaddr_in addr; + int one = 1; + + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = host; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + perror("krdc: ConnectToTcpAddr: socket"); + return -(int)INIT_CONNECTION_FAILED; + } + + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("krdc: ConnectToTcpAddr: connect"); + close(sock); + return -(int)INIT_NO_SERVER; + } + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + perror("krdc: ConnectToTcpAddr: setsockopt"); + close(sock); + return -(int)INIT_CONNECTION_FAILED; + } + + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { + perror(": AcceptTcpConnection: fcntl"); + close(sock); + return -(int)INIT_CONNECTION_FAILED; + } + + return sock; +} + + +/* + * StringToIPAddr - convert a host string to an IP address. + */ + +Bool +StringToIPAddr(const char *str, unsigned int *addr) +{ + struct hostent *hp; + + if (strcmp(str,"") == 0) { + *addr = 0; /* local */ + return True; + } + + *addr = inet_addr(str); + + if (*addr != -1) + return True; + + hp = gethostbyname(str); + + if (hp) { + *addr = *(unsigned int *)hp->h_addr; + return True; + } + + return False; +} + + +/* + * Print out the contents of a packet for debugging. + */ + +void +PrintInHex(char *_buf, int len) +{ + int i, j; + char c, str[17]; + + str[16] = 0; + + fprintf(stderr,"ReadExact: "); + + for (i = 0; i < len; i++) + { + if ((i % 16 == 0) && (i != 0)) { + fprintf(stderr," "); + } + c = _buf[i]; + str[i % 16] = (((c > 31) && (c < 127)) ? c : '.'); + fprintf(stderr,"%02x ",(unsigned char)c); + if ((i % 4) == 3) + fprintf(stderr," "); + if ((i % 16) == 15) + { + fprintf(stderr,"%s\n",str); + } + } + if ((i % 16) != 0) + { + for (j = i % 16; j < 16; j++) + { + fprintf(stderr," "); + if ((j % 4) == 3) fprintf(stderr," "); + } + str[i % 16] = 0; + fprintf(stderr,"%s\n",str); + } + + fflush(stderr); +} + +void freeSocketsResources() { + close(rfbsock); + + errorMessageOnReadFailure = True; + bufoutptr = buf; + buffered = 0; +} + diff --git a/krdc/vnc/threads.cpp b/krdc/vnc/threads.cpp new file mode 100644 index 00000000..fe5a1d62 --- /dev/null +++ b/krdc/vnc/threads.cpp @@ -0,0 +1,392 @@ +/*************************************************************************** + threads.cpp - threads + ------------------- + begin : Thu May 09 17:01:44 CET 2002 + copyright : (C) 2002 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kvncview.h" + +#include <kdebug.h> +#include <kapplication.h> + +#include "vncviewer.h" +#include "threads.h" + +#include <qcstring.h> + +// Maximum idle time for writer thread in ms. When it timeouts, it will request +// another incremental update. Must be smaller than the timeout of the server +// (krfb's is 20s). +static const int MAXIMUM_WAIT_PERIOD = 8000; + +// time to postpone incremental updates that have not been requested explicitly +static const int POSTPONED_INCRRQ_WAIT_PERIOD = 110; + +static const int MOUSEPRESS_QUEUE_SIZE = 5; +static const int MOUSEMOVE_QUEUE_SIZE = 3; +static const int KEY_QUEUE_SIZE = 8192; + + +ControllerThread::ControllerThread(KVncView *v, WriterThread &wt, volatile bool &quitFlag) : + m_view(v), + m_status(REMOTE_VIEW_CONNECTING), + m_wthread(wt), + m_quitFlag(quitFlag), + m_desktopInitialized(false) +{ +} + +void ControllerThread::changeStatus(RemoteViewStatus s) { + m_status = s; + QApplication::postEvent(m_view, new StatusChangeEvent(s)); +} + +void ControllerThread::sendFatalError(ErrorCode s) { + m_quitFlag = true; + QApplication::postEvent(m_view, new FatalErrorEvent(s)); + m_wthread.kick(); +} + +/* + * Calls this from the X11 thread + */ +void ControllerThread::desktopInit() { + SetVisualAndCmap(); + ToplevelInit(); + DesktopInit(m_view->winId()); + m_desktopInitialized = true; + m_waiter.wakeAll(); +} + +void ControllerThread::kick() { + m_waiter.wakeAll(); +} + +void ControllerThread::run() { + int fd; + fd = ConnectToRFBServer(m_view->host().latin1(), m_view->port()); + if (fd < 0) { + if (fd == -(int)INIT_NO_SERVER) + sendFatalError(ERROR_NO_SERVER); + else if (fd == -(int)INIT_NAME_RESOLUTION_FAILURE) + sendFatalError(ERROR_NAME); + else + sendFatalError(ERROR_CONNECTION); + return; + } + if (m_quitFlag) { + changeStatus(REMOTE_VIEW_DISCONNECTED); + return; + } + + changeStatus(REMOTE_VIEW_AUTHENTICATING); + + enum InitStatus s = InitialiseRFBConnection(); + if (s != INIT_OK) { + if (s == INIT_CONNECTION_FAILED) + sendFatalError(ERROR_IO); + else if (s == INIT_SERVER_BLOCKED) + sendFatalError(ERROR_SERVER_BLOCKED); + else if (s == INIT_PROTOCOL_FAILURE) + sendFatalError(ERROR_PROTOCOL); + else if (s == INIT_AUTHENTICATION_FAILED) + sendFatalError(ERROR_AUTHENTICATION); + else if (s == INIT_ABORTED) + changeStatus(REMOTE_VIEW_DISCONNECTED); + else + sendFatalError(ERROR_INTERNAL); + return; + } + + QApplication::postEvent(m_view, + new ScreenResizeEvent(si.framebufferWidth, + si.framebufferHeight)); + m_wthread.queueUpdateRequest(QRegion(QRect(0,0,si.framebufferWidth, + si.framebufferHeight))); + + QApplication::postEvent(m_view, new DesktopInitEvent()); + while ((!m_quitFlag) && (!m_desktopInitialized)) + m_waiter.wait(1000); + + if (m_quitFlag) { + changeStatus(REMOTE_VIEW_DISCONNECTED); + return; + } + + changeStatus(REMOTE_VIEW_PREPARING); + + if (!SetFormatAndEncodings()) { + sendFatalError(ERROR_INTERNAL); + return; + } + + changeStatus(REMOTE_VIEW_CONNECTED); + + m_wthread.start(); + + while (!m_quitFlag) { + if ((!HandleRFBServerMessage()) && (!m_quitFlag)) { + sendFatalError(ERROR_IO); + return; + } + } + + m_quitFlag = true; + changeStatus(REMOTE_VIEW_DISCONNECTED); + m_wthread.kick(); +} + +enum RemoteViewStatus ControllerThread::status() { + return m_status; +} + + + + + +static WriterThread *writerThread; +void queueIncrementalUpdateRequest() { + writerThread->queueIncrementalUpdateRequest(); +} + +void announceIncrementalUpdateRequest() { + writerThread->announceIncrementalUpdateRequest(); +} + + +WriterThread::WriterThread(KVncView *v, volatile bool &quitFlag) : + m_quitFlag(quitFlag), + m_view(v), + m_lastIncrUpdatePostponed(false), + m_incrementalUpdateRQ(false), + m_incrementalUpdateAnnounced(false), + m_mouseEventNum(0), + m_keyEventNum(0), + m_clientCut(QString::null) +{ + writerThread = this; + m_lastIncrUpdate.start(); +} + +bool WriterThread::sendIncrementalUpdateRequest() { + m_lastIncrUpdate.restart(); + return SendIncrementalFramebufferUpdateRequest(); +} + +bool WriterThread::sendUpdateRequest(const QRegion ®ion) { + QMemArray<QRect> r = region.rects(); + for (unsigned int i = 0; i < r.size(); i++) + if (!SendFramebufferUpdateRequest(r[i].x(), + r[i].y(), + r[i].width(), + r[i].height(), False)) + return false; + return true; +} + +bool WriterThread::sendInputEvents(const QValueList<InputEvent> &events) { + QValueList<InputEvent>::const_iterator it = events.begin(); + while (it != events.end()) { + if ((*it).type == KeyEventType) { + if (!SendKeyEvent((*it).e.k.k, (*it).e.k.down ? True : False)) + return false; + } + else + if (!SendPointerEvent((*it).e.m.x, (*it).e.m.y, (*it).e.m.buttons)) + return false; + it++; + } + return true; +} + +void WriterThread::queueIncrementalUpdateRequest() { + m_lock.lock(); + m_incrementalUpdateRQ = true; + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::announceIncrementalUpdateRequest() { + m_lock.lock(); + m_incrementalUpdateAnnounced = true; + m_lock.unlock(); +} + + +void WriterThread::queueUpdateRequest(const QRegion &r) { + m_lock.lock(); + m_updateRegionRQ += r; + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::queueMouseEvent(int x, int y, int buttonMask) { + InputEvent e; + e.type = MouseEventType; + e.e.m.x = x; + e.e.m.y = y; + e.e.m.buttons = buttonMask; + + m_lock.lock(); + if (m_mouseEventNum > 0) { + if ((e.e.m.x == m_lastMouseEvent.x) && + (e.e.m.y == m_lastMouseEvent.y) && + (e.e.m.buttons == m_lastMouseEvent.buttons)) { + m_lock.unlock(); + return; + } + if (m_mouseEventNum >= MOUSEPRESS_QUEUE_SIZE) { + m_lock.unlock(); + return; + } + if ((m_lastMouseEvent.buttons == buttonMask) && + (m_mouseEventNum >= MOUSEMOVE_QUEUE_SIZE)) { + m_lock.unlock(); + return; + } + } + + m_mouseEventNum++; + m_lastMouseEvent = e.e.m; + + m_inputEvents.push_back(e); + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::queueKeyEvent(unsigned int k, bool down) { + InputEvent e; + e.type = KeyEventType; + e.e.k.k = k; + e.e.k.down = down; + + m_lock.lock(); + if (m_keyEventNum >= KEY_QUEUE_SIZE) { + m_lock.unlock(); + return; + } + + m_keyEventNum++; + m_inputEvents.push_back(e); + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::queueClientCut(const QString &text) { + m_lock.lock(); + + m_clientCut = text; + + m_waiter.wakeAll(); + m_lock.unlock(); +} + +void WriterThread::kick() { + m_waiter.wakeAll(); +} + +void WriterThread::run() { + bool incrementalUpdateRQ = false; + bool incrementalUpdateAnnounced = false; + QRegion updateRegionRQ; + QValueList<InputEvent> inputEvents; + QString clientCut; + + while (!m_quitFlag) { + m_lock.lock(); + incrementalUpdateRQ = m_incrementalUpdateRQ; + incrementalUpdateAnnounced = m_incrementalUpdateAnnounced; + updateRegionRQ = m_updateRegionRQ; + inputEvents = m_inputEvents; + clientCut = m_clientCut; + + if ((!incrementalUpdateRQ) && + (updateRegionRQ.isNull()) && + (inputEvents.size() == 0) && + (clientCut.isNull())) { + if (!m_waiter.wait(&m_lock, + m_lastIncrUpdatePostponed ? + POSTPONED_INCRRQ_WAIT_PERIOD : MAXIMUM_WAIT_PERIOD)) + m_incrementalUpdateRQ = true; + m_lock.unlock(); + } + else { + m_incrementalUpdateRQ = false; + m_incrementalUpdateAnnounced = false; + m_updateRegionRQ = QRegion(); + m_inputEvents.clear(); + m_keyEventNum = 0; + m_mouseEventNum = 0; + m_clientCut = QString::null; + m_lock.unlock(); + + // always send incremental update, unless + // a) a framebuffer update is done ATM and will do the request later, or + // b) the last unrequested update has been done less than 0.1s ago + // + // if the update has not been done because of b, postpone it. + if (incrementalUpdateRQ || !incrementalUpdateAnnounced) { + bool sendUpdate; + if (incrementalUpdateRQ) { + sendUpdate = true; + m_lastIncrUpdatePostponed = false; + } + else { + if (m_lastIncrUpdate.elapsed() < 100) { + sendUpdate = false; + m_lastIncrUpdatePostponed = true; + } + else { + sendUpdate = true; + m_lastIncrUpdatePostponed = false; + } + } + + if (sendUpdate) + if (!sendIncrementalUpdateRequest()) { + sendFatalError(ERROR_IO); + break; + } + } + else + m_lastIncrUpdatePostponed = false; + + if (!updateRegionRQ.isNull()) + if (!sendUpdateRequest(updateRegionRQ)) { + sendFatalError(ERROR_IO); + break; + } + if (inputEvents.size() != 0) + if (!sendInputEvents(inputEvents)) { + sendFatalError(ERROR_IO); + break; + } + if (!clientCut.isNull()) { + QCString cutTextUtf8(clientCut.utf8()); + if (!SendClientCutText(cutTextUtf8.data(), + (int)cutTextUtf8.length())) { + sendFatalError(ERROR_IO); + break; + } + } + } + } + m_quitFlag = true; +} + +void WriterThread::sendFatalError(ErrorCode s) { + m_quitFlag = true; + QApplication::postEvent(m_view, new FatalErrorEvent(s)); +} + diff --git a/krdc/vnc/threads.h b/krdc/vnc/threads.h new file mode 100644 index 00000000..5f38b71f --- /dev/null +++ b/krdc/vnc/threads.h @@ -0,0 +1,126 @@ +/*************************************************************************** + threads.h - threads for kvncview + ------------------- + begin : Thu May 09 16:01:42 CET 2002 + copyright : (C) 2002 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef THREADS_H +#define THREADS_H + +#include <qthread.h> +#include <qregion.h> +#include <qrect.h> +#include <qmutex.h> +#include <qwaitcondition.h> +#include <qevent.h> +#include <qvaluelist.h> +#include <qdatetime.h> + +#include <stdlib.h> + +#include "events.h" +#include "vnctypes.h" + +class KVncView; + +enum EventType { + MouseEventType, + KeyEventType +}; + + +struct MouseEvent { + int x, y, buttons; +}; + +struct KeyEvent { + unsigned int k; + bool down; +}; + +struct InputEvent { + EventType type; + union { + MouseEvent m; + KeyEvent k; + } e; +}; + + +class WriterThread : public QThread { +private: + QMutex m_lock; + QWaitCondition m_waiter; + volatile bool &m_quitFlag; + KVncView *m_view; + + QTime m_lastIncrUpdate; // start()ed when a incr update is sent + bool m_lastIncrUpdatePostponed; + + // all things that can be send follow: + bool m_incrementalUpdateRQ; // for sending an incremental request + bool m_incrementalUpdateAnnounced; // set when a RQ will come soon + QRegion m_updateRegionRQ; // for sending updates, null if it is done + QValueList<InputEvent> m_inputEvents; // list of unsent input events + MouseEvent m_lastMouseEvent; + int m_mouseEventNum, m_keyEventNum; + QString m_clientCut; + + void sendFatalError(ErrorCode s); + +public: + WriterThread(KVncView *v, volatile bool &quitFlag); + + void queueIncrementalUpdateRequest(); + void announceIncrementalUpdateRequest(); + void queueUpdateRequest(const QRegion &r); + void queueMouseEvent(int x, int y, int buttonMask); + void queueKeyEvent(unsigned int k, bool down); + void queueClientCut(const QString &text); + void kick(); + +protected: + void run(); + bool sendIncrementalUpdateRequest(); + bool sendUpdateRequest(const QRegion &r); + bool sendInputEvents(const QValueList<InputEvent> &events); +}; + + + +class ControllerThread : public QThread { +private: + KVncView *m_view; + enum RemoteViewStatus m_status; + WriterThread &m_wthread; + volatile bool &m_quitFlag; + volatile bool m_desktopInitialized; + QWaitCondition m_waiter; + + void changeStatus(RemoteViewStatus s); + void sendFatalError(ErrorCode s); + +public: + ControllerThread(KVncView *v, WriterThread &wt, volatile bool &quitFlag); + enum RemoteViewStatus status(); + void desktopInit(); + void kick(); + +protected: + void run(); +}; + + + +#endif diff --git a/krdc/vnc/tight.c b/krdc/vnc/tight.c new file mode 100644 index 00000000..2f08cfb0 --- /dev/null +++ b/krdc/vnc/tight.c @@ -0,0 +1,610 @@ +/* + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * tight.c - handle ``tight'' encoding. + * + * This file shouldn't be compiled directly. It is included multiple + * times by rfbproto.c, each time with a different definition of the + * macro BPP. For each value of BPP, this file defines a function + * which handles a tight-encoded rectangle with BPP bits per pixel. + * + */ + +#define TIGHT_MIN_TO_COMPRESS 12 + +#define CARDBPP CONCAT2E(CARD,BPP) +#define filterPtrBPP CONCAT2E(filterPtr,BPP) + +#define HandleTightBPP CONCAT2E(HandleTight,BPP) +#define InitFilterCopyBPP CONCAT2E(InitFilterCopy,BPP) +#define InitFilterPaletteBPP CONCAT2E(InitFilterPalette,BPP) +#define InitFilterGradientBPP CONCAT2E(InitFilterGradient,BPP) +#define FilterCopyBPP CONCAT2E(FilterCopy,BPP) +#define FilterPaletteBPP CONCAT2E(FilterPalette,BPP) +#define FilterGradientBPP CONCAT2E(FilterGradient,BPP) +#define FillRectangleBPP CONCAT2E(FillRectangle,BPP) + +#if BPP != 8 +#define DecompressJpegRectBPP CONCAT2E(DecompressJpegRect,BPP) +#endif + +#ifndef RGB_TO_PIXEL + +#define RGB_TO_PIXEL(bpp,r,g,b) \ + (((CARD##bpp)(r) & myFormat.redMax) << myFormat.redShift | \ + ((CARD##bpp)(g) & myFormat.greenMax) << myFormat.greenShift | \ + ((CARD##bpp)(b) & myFormat.blueMax) << myFormat.blueShift) + +#define RGB24_TO_PIXEL(bpp,r,g,b) \ + ((((CARD##bpp)(r) & 0xFF) * myFormat.redMax + 127) / 255 \ + << myFormat.redShift | \ + (((CARD##bpp)(g) & 0xFF) * myFormat.greenMax + 127) / 255 \ + << myFormat.greenShift | \ + (((CARD##bpp)(b) & 0xFF) * myFormat.blueMax + 127) / 255 \ + << myFormat.blueShift) + +#define RGB24_TO_PIXEL32(r,g,b) \ + (((CARD32)(r) & 0xFF) << myFormat.redShift | \ + ((CARD32)(g) & 0xFF) << myFormat.greenShift | \ + ((CARD32)(b) & 0xFF) << myFormat.blueShift) + +#endif + +/* Type declarations */ + +typedef void (*filterPtrBPP)(int, CARDBPP *); + +/* Prototypes */ + +static int InitFilterCopyBPP (int rw, int rh); +static int InitFilterPaletteBPP (int rw, int rh); +static int InitFilterGradientBPP (int rw, int rh); +static void FilterCopyBPP (int numRows, CARDBPP *destBuffer); +static void FilterPaletteBPP (int numRows, CARDBPP *destBuffer); +static void FilterGradientBPP (int numRows, CARDBPP *destBuffer); + +static Bool DecompressJpegRectBPP(int x, int y, int w, int h); + +/* Definitions */ + +static Bool +HandleTightBPP (int rx, int ry, int rw, int rh) +{ + CARDBPP fill_colour; + XGCValues gcv; + CARD8 comp_ctl; + CARD8 filter_id; + filterPtrBPP filterFn; + z_streamp zs; + char *buffer2; + int err, stream_id, compressedLen, bitsPixel; + int bufferSize, rowSize, numRows, portionLen, rowsProcessed, extraBytes; + CARDBPP *rawData; + + if (!ReadFromRFBServer((char *)&comp_ctl, 1)) + return False; + + /* Flush zlib streams if we are told by the server to do so. */ + for (stream_id = 0; stream_id < 4; stream_id++) { + if ((comp_ctl & 1) && zlibStreamActive[stream_id]) { + if (inflateEnd (&zlibStream[stream_id]) != Z_OK && + zlibStream[stream_id].msg != NULL) + fprintf(stderr, "inflateEnd: %s\n", zlibStream[stream_id].msg); + zlibStreamActive[stream_id] = False; + } + comp_ctl >>= 1; + } + + /* Handle solid rectangles. */ + if (comp_ctl == rfbTightFill) { +#if BPP == 32 + if (myFormat.depth == 24 && myFormat.redMax == 0xFF && + myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) { + if (!ReadFromRFBServer(buffer, 3)) + return False; + fill_colour = RGB24_TO_PIXEL32(buffer[0], buffer[1], buffer[2]); + } else { + if (!ReadFromRFBServer((char*)&fill_colour, sizeof(fill_colour))) + return False; + } +#else + if (!ReadFromRFBServer((char*)&fill_colour, sizeof(fill_colour))) + return False; +#endif + + LockFramebuffer(); + FillRectangleBPP(fill_colour, rx, ry, rw, rh); + UnlockFramebuffer(); + SyncScreenRegion(rx, ry, rw, rh); + return True; + } + +#if BPP == 8 + if (comp_ctl == rfbTightJpeg) { + fprintf(stderr, "Tight encoding: JPEG is not supported in 8 bpp mode.\n"); + return False; + } +#else + if (comp_ctl == rfbTightJpeg) { + return DecompressJpegRectBPP(rx, ry, rw, rh); + } +#endif + + /* Quit on unsupported subencoding value. */ + if (comp_ctl > rfbTightMaxSubencoding) { + fprintf(stderr, "Tight encoding: bad subencoding value received.\n"); + return False; + } + + /* + * Here primary compression mode handling begins. + * Data was processed with optional filter + zlib compression. + */ + + /* First, we should identify a filter to use. */ + if ((comp_ctl & rfbTightExplicitFilter) != 0) { + if (!ReadFromRFBServer((char*)&filter_id, 1)) + return False; + + switch (filter_id) { + case rfbTightFilterCopy: + filterFn = FilterCopyBPP; + bitsPixel = InitFilterCopyBPP(rw, rh); + break; + case rfbTightFilterPalette: + filterFn = FilterPaletteBPP; + bitsPixel = InitFilterPaletteBPP(rw, rh); + break; + case rfbTightFilterGradient: + filterFn = FilterGradientBPP; + bitsPixel = InitFilterGradientBPP(rw, rh); + break; + default: + fprintf(stderr, "Tight encoding: unknown filter code received.\n"); + return False; + } + } else { + filterFn = FilterCopyBPP; + bitsPixel = InitFilterCopyBPP(rw, rh); + } + if (bitsPixel == 0) { + fprintf(stderr, "Tight encoding: error receiving palette.\n"); + return False; + } + + /* Determine if the data should be decompressed or just copied. */ + rowSize = (rw * bitsPixel + 7) / 8; + if (rh * rowSize < TIGHT_MIN_TO_COMPRESS) { + if (!ReadFromRFBServer((char*)buffer, rh * rowSize)) + return False; + + buffer2 = &buffer[TIGHT_MIN_TO_COMPRESS * 4]; + filterFn(rh, (CARDBPP *)buffer2); + CopyDataToScreen(buffer2, rx, ry, rw, rh); + + return True; + } + + /* Read the length (1..3 bytes) of compressed data following. */ + compressedLen = (int)ReadCompactLen(); + if (compressedLen <= 0) { + fprintf(stderr, "Incorrect data received from the server.\n"); + return False; + } + + /* Now let's initialize compression stream if needed. */ + stream_id = comp_ctl & 0x03; + zs = &zlibStream[stream_id]; + if (!zlibStreamActive[stream_id]) { + zs->zalloc = Z_NULL; + zs->zfree = Z_NULL; + zs->opaque = Z_NULL; + err = inflateInit(zs); + if (err != Z_OK) { + if (zs->msg != NULL) + fprintf(stderr, "InflateInit error: %s.\n", zs->msg); + return False; + } + zlibStreamActive[stream_id] = True; + } + + /* Read, decode and draw actual pixel data in a loop. */ + + bufferSize = BUFFER_SIZE * bitsPixel / (bitsPixel + BPP) & 0xFFFFFFFC; + buffer2 = &buffer[bufferSize]; + if (rowSize > bufferSize) { + /* Should be impossible when BUFFER_SIZE >= 16384 */ + fprintf(stderr, "Internal error: incorrect buffer size.\n"); + return False; + } + + rowsProcessed = 0; + extraBytes = 0; + + while (compressedLen > 0) { + if (compressedLen > ZLIB_BUFFER_SIZE) + portionLen = ZLIB_BUFFER_SIZE; + else + portionLen = compressedLen; + + if (!ReadFromRFBServer((char*)zlib_buffer, portionLen)) + return False; + + compressedLen -= portionLen; + + zs->next_in = (Bytef *)zlib_buffer; + zs->avail_in = portionLen; + + do { + zs->next_out = (Bytef *)&buffer[extraBytes]; + zs->avail_out = bufferSize - extraBytes; + + err = inflate(zs, Z_SYNC_FLUSH); + if (err == Z_BUF_ERROR) /* Input exhausted -- no problem. */ + break; + if (err != Z_OK && err != Z_STREAM_END) { + if (zs->msg != NULL) { + fprintf(stderr, "Inflate error: %s.\n", zs->msg); + } else { + fprintf(stderr, "Inflate error: %d.\n", err); + } + return False; + } + + numRows = (bufferSize - zs->avail_out) / rowSize; + + filterFn(numRows, (CARDBPP *)buffer2); + + extraBytes = bufferSize - zs->avail_out - numRows * rowSize; + if (extraBytes > 0) + memcpy(buffer, &buffer[numRows * rowSize], extraBytes); + + CopyDataToScreen(buffer2, rx, ry + rowsProcessed, rw, numRows); + rowsProcessed += numRows; + } + while (zs->avail_out == 0); + } + + if (rowsProcessed != rh) { + fprintf(stderr, "Incorrect number of scan lines after decompression.\n"); + return False; + } + + return True; +} + +/*---------------------------------------------------------------------------- + * + * Filter stuff. + * + */ + +/* + The following variables are defined in rfbproto.c: + static Bool cutZeros; + static int rectWidth, rectColors; + static CARD8 tightPalette[256*4]; + static CARD8 tightPrevRow[2048*3*sizeof(CARD16)]; +*/ + +static int +InitFilterCopyBPP (int rw, int rh) +{ + rectWidth = rw; + +#if BPP == 32 + if (myFormat.depth == 24 && myFormat.redMax == 0xFF && + myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) { + cutZeros = True; + return 24; + } else { + cutZeros = False; + } +#endif + + return BPP; +} + +static void +FilterCopyBPP (int numRows, CARDBPP *dst) +{ + +#if BPP == 32 + int x, y; + + if (cutZeros) { + for (y = 0; y < numRows; y++) { + for (x = 0; x < rectWidth; x++) { + dst[y*rectWidth+x] = + RGB24_TO_PIXEL32(buffer[(y*rectWidth+x)*3], + buffer[(y*rectWidth+x)*3+1], + buffer[(y*rectWidth+x)*3+2]); + } + } + return; + } +#endif + + memcpy (dst, buffer, numRows * rectWidth * (BPP / 8)); +} + +static int +InitFilterGradientBPP (int rw, int rh) +{ + int bits; + + bits = InitFilterCopyBPP(rw, rh); + if (cutZeros) + memset(tightPrevRow, 0, rw * 3); + else + memset(tightPrevRow, 0, rw * 3 * sizeof(CARD16)); + + return bits; +} + +#if BPP == 32 + +static void +FilterGradient24 (int numRows, CARD32 *dst) +{ + int x, y, c; + CARD8 thisRow[2048*3]; + CARD8 pix[3]; + int est[3]; + + for (y = 0; y < numRows; y++) { + + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = tightPrevRow[c] + buffer[y*rectWidth*3+c]; + thisRow[c] = pix[c]; + } + dst[y*rectWidth] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + + /* Remaining pixels of a row */ + for (x = 1; x < rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = (int)tightPrevRow[x*3+c] + (int)pix[c] - + (int)tightPrevRow[(x-1)*3+c]; + if (est[c] > 0xFF) { + est[c] = 0xFF; + } else if (est[c] < 0x00) { + est[c] = 0x00; + } + pix[c] = (CARD8)est[c] + buffer[(y*rectWidth+x)*3+c]; + thisRow[x*3+c] = pix[c]; + } + dst[y*rectWidth+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]); + } + + memcpy(tightPrevRow, thisRow, rectWidth * 3); + } +} + +#endif + +static void +FilterGradientBPP (int numRows, CARDBPP *dst) +{ + int x, y, c; + CARDBPP *src = (CARDBPP *)buffer; + CARD16 *thatRow = (CARD16 *)tightPrevRow; + CARD16 thisRow[2048*3]; + CARD16 pix[3]; + CARD16 max[3]; + int shift[3]; + int est[3]; + +#if BPP == 32 + if (cutZeros) { + FilterGradient24(numRows, dst); + return; + } +#endif + + max[0] = myFormat.redMax; + max[1] = myFormat.greenMax; + max[2] = myFormat.blueMax; + + shift[0] = myFormat.redShift; + shift[1] = myFormat.greenShift; + shift[2] = myFormat.blueShift; + + for (y = 0; y < numRows; y++) { + + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = (CARD16)((src[y*rectWidth] >> shift[c]) + thatRow[c] & max[c]); + thisRow[c] = pix[c]; + } + dst[y*rectWidth] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); + + /* Remaining pixels of a row */ + for (x = 1; x < rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = (int)thatRow[x*3+c] + (int)pix[c] - (int)thatRow[(x-1)*3+c]; + if (est[c] > (int)max[c]) { + est[c] = (int)max[c]; + } else if (est[c] < 0) { + est[c] = 0; + } + pix[c] = (CARD16)((src[y*rectWidth+x] >> shift[c]) + est[c] & max[c]); + thisRow[x*3+c] = pix[c]; + } + dst[y*rectWidth+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]); + } + memcpy(thatRow, thisRow, rectWidth * 3 * sizeof(CARD16)); + } +} + +static int +InitFilterPaletteBPP (int rw, int rh) +{ + int i; + CARD8 numColors; + CARDBPP *palette = (CARDBPP *)tightPalette; + + rectWidth = rw; + + if (!ReadFromRFBServer((char*)&numColors, 1)) + return 0; + + rectColors = (int)numColors; + if (++rectColors < 2) + return 0; + +#if BPP == 32 + if (myFormat.depth == 24 && myFormat.redMax == 0xFF && + myFormat.greenMax == 0xFF && myFormat.blueMax == 0xFF) { + if (!ReadFromRFBServer((char*)&tightPalette, rectColors * 3)) + return 0; + for (i = rectColors - 1; i >= 0; i--) { + palette[i] = RGB24_TO_PIXEL32(tightPalette[i*3], + tightPalette[i*3+1], + tightPalette[i*3+2]); + } + return (rectColors == 2) ? 1 : 8; + } +#endif + + if (!ReadFromRFBServer((char*)&tightPalette, rectColors * (BPP / 8))) + return 0; + + return (rectColors == 2) ? 1 : 8; +} + +static void +FilterPaletteBPP (int numRows, CARDBPP *dst) +{ + int x, y, b, w; + CARD8 *src = (CARD8 *)buffer; + CARDBPP *palette = (CARDBPP *)tightPalette; + + if (rectColors == 2) { + w = (rectWidth + 7) / 8; + for (y = 0; y < numRows; y++) { + for (x = 0; x < rectWidth / 8; x++) { + for (b = 7; b >= 0; b--) + dst[y*rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1]; + } + for (b = 7; b >= 8 - rectWidth % 8; b--) { + dst[y*rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1]; + } + } + } else { + for (y = 0; y < numRows; y++) + for (x = 0; x < rectWidth; x++) + dst[y*rectWidth+x] = palette[(int)src[y*rectWidth+x]]; + } +} + +#if BPP != 8 + +/*---------------------------------------------------------------------------- + * + * JPEG decompression. + * + */ + +/* + The following variables are defined in rfbproto.c: + static Bool jpegError; + static struct jpeg_source_mgr jpegSrcManager; + static JOCTET *jpegBufferPtr; + static size_t *jpegBufferLen; +*/ + +static Bool +DecompressJpegRectBPP(int x, int y, int w, int h) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + int compressedLen; + CARD8 *compressedData; + CARDBPP *pixelPtr; + JSAMPROW rowPointer[1]; + int dx, dy; + + compressedLen = (int)ReadCompactLen(); + if (compressedLen <= 0) { + fprintf(stderr, "Incorrect data received from the server.\n"); + return False; + } + + if (compressedLen > MAX_JPEG_SIZE) { + fprintf(stderr, "To large data announced by the server.\n"); + return False; + } + + compressedData = malloc(compressedLen); + if (compressedData == NULL) { + fprintf(stderr, "Memory allocation error.\n"); + return False; + } + + if (!ReadFromRFBServer((char*)compressedData, compressedLen)) { + free(compressedData); + return False; + } + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + + JpegSetSrcManager(&cinfo, compressedData, compressedLen); + + jpeg_read_header(&cinfo, TRUE); + cinfo.out_color_space = JCS_RGB; + + jpeg_start_decompress(&cinfo); + if (cinfo.output_width != w || cinfo.output_height != h || + cinfo.output_components != 3) { + fprintf(stderr, "Tight Encoding: Wrong JPEG data received.\n"); + jpeg_destroy_decompress(&cinfo); + free(compressedData); + return False; + } + + rowPointer[0] = (JSAMPROW)buffer; + dy = 0; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, rowPointer, 1); + if (jpegError) { + break; + } + pixelPtr = (CARDBPP *)&buffer[BUFFER_SIZE / 2]; + for (dx = 0; dx < w; dx++) { + *pixelPtr++ = + RGB24_TO_PIXEL(BPP, buffer[dx*3], buffer[dx*3+1], buffer[dx*3+2]); + } + CopyDataToScreen(&buffer[BUFFER_SIZE / 2], x, y + dy, w, 1); + dy++; + } + + if (!jpegError) + jpeg_finish_decompress(&cinfo); + + jpeg_destroy_decompress(&cinfo); + free(compressedData); + + return !jpegError; +} + +#endif + diff --git a/krdc/vnc/vncauth.c b/krdc/vnc/vncauth.c new file mode 100644 index 00000000..b5d42836 --- /dev/null +++ b/krdc/vnc/vncauth.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * vncauth.c - Functions for VNC password management and authentication. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <vncauth.h> +#include <d3des.h> + + +/* + * We use a fixed key to store passwords, since we assume that our local + * file system is secure but nonetheless don't want to store passwords + * as plaintext. + */ + +unsigned char fixedkey[8] = {23,82,107,6,35,78,88,7}; + + +/* + * Encrypt a password and store it in a file. Returns 0 if successful, + * 1 if the file could not be written. + */ + +int +vncEncryptAndStorePasswd(char *passwd, char *fname) +{ + FILE *fp; + int i; + unsigned char encryptedPasswd[8]; + + if ((fp = fopen(fname,"w")) == NULL) return 1; + + chmod(fname, S_IRUSR|S_IWUSR); + + /* pad password with nulls */ + + for (i = 0; i < 8; i++) { + if (i < strlen(passwd)) { + encryptedPasswd[i] = passwd[i]; + } else { + encryptedPasswd[i] = 0; + } + } + + /* Do encryption in-place - this way we overwrite our copy of the plaintext + password */ + + deskey(fixedkey, EN0); + des(encryptedPasswd, encryptedPasswd); + + for (i = 0; i < 8; i++) { + putc(encryptedPasswd[i], fp); + } + + fclose(fp); + return 0; +} + + +/* + * Decrypt a password from a file. Returns a pointer to a newly allocated + * string containing the password or a null pointer if the password could + * not be retrieved for some reason. + */ + +char * +vncDecryptPasswdFromFile(char *fname) +{ + FILE *fp; + int i, ch; + unsigned char *passwd = (unsigned char *)malloc(9); + + if ((fp = fopen(fname,"r")) == NULL) return NULL; + + for (i = 0; i < 8; i++) { + ch = getc(fp); + if (ch == EOF) { + fclose(fp); + return NULL; + } + passwd[i] = ch; + } + + fclose(fp); + + deskey(fixedkey, DE1); + des(passwd, passwd); + + passwd[8] = 0; + + return (char *)passwd; +} + + +/* + * Generate CHALLENGESIZE random bytes for use in challenge-response + * authentication. + */ + +void +vncRandomBytes(unsigned char *bytes) +{ + int i; + unsigned int seed = (unsigned int) time(0); + + srandom(seed); + for (i = 0; i < CHALLENGESIZE; i++) { + bytes[i] = (unsigned char)(random() & 255); + } +} + + +/* + * Encrypt CHALLENGESIZE bytes in memory using a password. + */ + +void +vncEncryptBytes(unsigned char *bytes, char *passwd) +{ + unsigned char key[8]; + int i; + + /* key is simply password padded with nulls */ + + for (i = 0; i < 8; i++) { + if (i < strlen(passwd)) { + key[i] = passwd[i]; + } else { + key[i] = 0; + } + } + + deskey(key, EN0); + + for (i = 0; i < CHALLENGESIZE; i += 8) { + des(bytes+i, bytes+i); + } +} diff --git a/krdc/vnc/vncauth.h b/krdc/vnc/vncauth.h new file mode 100644 index 00000000..86dc455c --- /dev/null +++ b/krdc/vnc/vncauth.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * vncauth.h - describes the functions provided by the vncauth library. + */ + +#define MAXPWLEN 8 +#define CHALLENGESIZE 16 + +extern int vncEncryptAndStorePasswd(char *passwd, char *fname); +extern char *vncDecryptPasswdFromFile(char *fname); +extern void vncRandomBytes(unsigned char *bytes); +extern void vncEncryptBytes(unsigned char *bytes, char *passwd); diff --git a/krdc/vnc/vnchostpref.cpp b/krdc/vnc/vnchostpref.cpp new file mode 100644 index 00000000..a17f8ea1 --- /dev/null +++ b/krdc/vnc/vnchostpref.cpp @@ -0,0 +1,127 @@ +/*************************************************************************** + vnchostprefs.cpp - vnc host preferences + ------------------- + begin : Fri May 09 22:32 CET 2003 + copyright : (C) 2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "vnchostpref.h" +#include <kconfig.h> +#include <klocale.h> + +const QString VncHostPref::VncType = "VNC"; + +VncHostPref::VncHostPref(KConfig *conf, const QString &host, const QString &type) : + HostPref(conf, host, type), + m_quality(0), + m_useKWallet(true), + m_askOnConnect(true) { +} + +VncHostPref::~VncHostPref() { +} + +void VncHostPref::save() { + if ( !m_host.isEmpty() && !m_type.isEmpty() ) + { + m_config->setGroup("PerHostSettings"); + QString p = prefix(); + m_config->writeEntry(p+"exists", true); + m_config->writeEntry(p+"quality", m_quality); + m_config->writeEntry(p+"askOnConnect", m_askOnConnect); + m_config->writeEntry(p+"useKWallet", m_useKWallet); + } + else + { + m_config->setGroup( "VncDefaultSettings" ); + m_config->writeEntry( "vncQuality", m_quality ); + m_config->writeEntry( "vncShowHostPreferences", m_askOnConnect ); + m_config->writeEntry( "vncUseKWallet", m_useKWallet ); + } +} + +void VncHostPref::load() { + if ( !m_host.isEmpty() && !m_type.isEmpty() ) + { + m_config->setGroup("PerHostSettings"); + QString p = prefix(); + m_quality = m_config->readNumEntry(p+"quality", 0); + m_askOnConnect = m_config->readBoolEntry(p+"askOnConnect", true); + m_useKWallet = m_config->readBoolEntry(p+"useKWallet", true); + } + else + { + setDefaults(); + } +} + +void VncHostPref::remove() { + m_config->setGroup("PerHostSettings"); + QString p = prefix(); + m_config->deleteEntry(p+"exists"); + m_config->deleteEntry(p+"quality"); + m_config->deleteEntry(p+"askOnConnect"); +} + +void VncHostPref::setDefaults() { + m_config->setGroup("VncDefaultSettings"); + m_quality = m_config->readNumEntry("vncQuality", 0); + m_askOnConnect = m_config->readBoolEntry("vncShowHostPreferences", true); + m_useKWallet = m_config->readBoolEntry("vncUseKWallet", true); +} + +QString VncHostPref::prefDescription() const { + QString q; + switch(m_quality) { + case 0: + q = i18n("High"); + break; + case 1: + q = i18n("Medium"); + break; + case 2: + q = i18n("Low"); + break; + default: + Q_ASSERT(true); + } + return i18n("Show Preferences: %1, Quality: %2, KWallet: %3") + .arg(m_askOnConnect ? i18n("yes") : i18n("no")).arg(q).arg(m_useKWallet ? i18n("yes") : i18n("no")); +} + +void VncHostPref::setQuality(int q) { + m_quality = q; + save(); +} + +int VncHostPref::quality() const { + return m_quality; +} + +void VncHostPref::setAskOnConnect(bool ask) { + m_askOnConnect = ask; + save(); +} + +bool VncHostPref::askOnConnect() const { + return m_askOnConnect; +} + +void VncHostPref::setUseKWallet(bool use) { + m_useKWallet = use; + save(); +} + +bool VncHostPref::useKWallet() const { + return m_useKWallet; +} diff --git a/krdc/vnc/vnchostpref.h b/krdc/vnc/vnchostpref.h new file mode 100644 index 00000000..33a20600 --- /dev/null +++ b/krdc/vnc/vnchostpref.h @@ -0,0 +1,52 @@ +/*************************************************************************** + vnchostprefs.h - vnc host preferences + ------------------- + begin : Fri May 09 22:32 CET 2003 + copyright : (C) 2003 by Tim Jansen + email : tim@tjansen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef VNCHOSTPREF_H +#define VNCHOSTPREF_H + +#include "hostpreferences.h" + +class VncHostPref : public HostPref { +protected: + friend class HostPreferences; + + int m_quality; + bool m_askOnConnect; + bool m_useKWallet; + + virtual void load(); + virtual void setDefaults(); + virtual void save(); + virtual void remove(); + +public: + static const QString VncType; + + VncHostPref(KConfig *conf, const QString &host=QString::null, + const QString &type=QString::null); + virtual ~VncHostPref(); + + virtual QString prefDescription() const; + void setQuality(int q); + int quality() const; + void setAskOnConnect(bool ask); + bool askOnConnect() const; + void setUseKWallet(bool); + bool useKWallet() const; +}; + +#endif diff --git a/krdc/vnc/vncprefs.ui b/krdc/vnc/vncprefs.ui new file mode 100644 index 00000000..034b2b1a --- /dev/null +++ b/krdc/vnc/vncprefs.ui @@ -0,0 +1,165 @@ +<!DOCTYPE UI><UI version="3.2" stdsetdef="1"> +<class>VncPrefs</class> +<widget class="QWidget"> + <property name="name"> + <cstring>VncPrefs</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>472</width> + <height>126</height> + </rect> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox</cstring> + </property> + <property name="title"> + <string>Connection</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>cbUseEncryption</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Enable encryption (secure, but slow and not always possible)</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Enable this option to encrypt the connection. Only newer servers support this option. Encrypting prevents others from eavesdropping, but can slow down the connection considerably.</string> + </property> + </widget> + <widget class="QCheckBox" row="2" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>cbUseKWallet</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Use K&Wallet for passwords</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Enable this option to store your passwords with KWallet.</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>connectionTypeLabel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>1</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Connection &type:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>cmbQuality</cstring> + </property> + </widget> + <widget class="QComboBox" row="0" column="1"> + <item> + <property name="text"> + <string>High Quality (LAN, direct connection)</string> + </property> + </item> + <item> + <property name="text"> + <string>Medium Quality (DSL, Cable, fast Internet)</string> + </property> + </item> + <item> + <property name="text"> + <string>Low Quality (Modem, ISDN, slow Internet)</string> + </property> + </item> + <property name="name"> + <cstring>cmbQuality</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>280</width> + <height>0</height> + </size> + </property> + <property name="whatsThis" stdset="0"> + <string>Use this to specify the performance of your connection. Note that you should select the speed of the weakest link - even if you have a high speed connection, it will not help you if the remote computer uses a slow modem. Choosing a level of quality that is too high on a slow link will cause slower response times. Choosing a lower quality will increase latencies in high speed connections and results in lower image quality, especially in 'Low Quality' mode.</string> + </property> + </widget> + <spacer row="0" column="2"> + <property name="name"> + <cstring>Spacer</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>84</width> + <height>16</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>cbShowPrefs</cstring> + </property> + <property name="text"> + <string>&Show this dialog again for this host</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Select this option if you do not want to be asked for the settings when connecting to a host. For hosts with existing profiles these profiles will be taken. New hosts will be configured with the defaults.</string> + </property> + </widget> + </vbox> +</widget> +<includes> + <include location="global" impldecl="in implementation">kdialog.h</include> + <include location="local" impldecl="in implementation">vncprefs.ui.h</include> +</includes> +<functions> + <function specifier="non virtual">setQuality( int quality )</function> + <function specifier="non virtual" returnType="int">quality()</function> + <function specifier="non virtual">setShowPrefs( bool b )</function> + <function specifier="non virtual" returnType="int">showPrefs()</function> + <function specifier="non virtual">setUseEncryption( bool b )</function> + <function specifier="non virtual" returnType="bool">useEncryption()</function> + <function specifier="non virtual">setUseKWallet( bool b )</function> + <function specifier="non virtual" returnType="bool">useKWallet()</function> +</functions> +<layoutdefaults spacing="6" margin="11"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +</UI> diff --git a/krdc/vnc/vncprefs.ui.h b/krdc/vnc/vncprefs.ui.h new file mode 100644 index 00000000..a3438f05 --- /dev/null +++ b/krdc/vnc/vncprefs.ui.h @@ -0,0 +1,53 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you wish to add, delete or rename functions or slots use +** Qt Designer which will update this file, preserving your code. Create an +** init() function in place of a constructor, and a destroy() function in +** place of a destructor. +*****************************************************************************/ + +void VncPrefs::setQuality( int quality ) +{ + cmbQuality->setCurrentItem(quality); +} + + +int VncPrefs::quality() +{ + return cmbQuality->currentItem(); +} + + +void VncPrefs::setShowPrefs( bool b ) +{ + cbShowPrefs->setChecked(b); +} + + +int VncPrefs::showPrefs() +{ + return cbShowPrefs->isChecked(); +} + + +void VncPrefs::setUseEncryption( bool b ) +{ + cbUseEncryption->setChecked(b); +} + + +bool VncPrefs::useEncryption() +{ + return cbUseEncryption->isChecked(); +} + +void VncPrefs::setUseKWallet( bool b ) +{ + cbUseKWallet->setChecked(b); +} + +bool VncPrefs::useKWallet() +{ + return cbUseKWallet->isChecked(); +} diff --git a/krdc/vnc/vnctypes.h b/krdc/vnc/vnctypes.h new file mode 100644 index 00000000..15329ee2 --- /dev/null +++ b/krdc/vnc/vnctypes.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2002 Tim Jansen. All Rights Reserved. + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef VNCTYPES_H +#define VNCTYPES_H + +#if(defined __cplusplus) +extern "C" +{ +#endif + +#include <X11/Xmd.h> + + +typedef struct { + int shareDesktop; /* bool */ + int viewOnly; /* bool */ + + const char* encodingsString; + + int useBGR233; /* bool */ + int nColours; + int useSharedColours; /* bool */ + int requestedDepth; + + int rawDelay; + int copyRectDelay; + + int debug; /* bool */ + + int compressLevel; + int qualityLevel; + int dotCursor; /* bool */ + +} AppData; + + +enum InitStatus { + INIT_OK = 0, + INIT_NAME_RESOLUTION_FAILURE = 1, + INIT_PROTOCOL_FAILURE = 2, + INIT_CONNECTION_FAILED = 3, + INIT_AUTHENTICATION_FAILED = 4, + INIT_NO_SERVER = 5, + INIT_SERVER_BLOCKED = 6, + INIT_ABORTED = 7 +}; + + +#if(defined __cplusplus) +} +#endif + +#endif diff --git a/krdc/vnc/vncviewer.h b/krdc/vnc/vncviewer.h new file mode 100644 index 00000000..dcc66cde --- /dev/null +++ b/krdc/vnc/vncviewer.h @@ -0,0 +1,186 @@ +#ifndef VNCVIEWER_H +#define VNCVIEWER_H +/* + * Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved. + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * vncviewer.h + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/time.h> +#include <unistd.h> +#include <X11/IntrinsicP.h> +#include <X11/StringDefs.h> +#include <X11/Shell.h> +#include <X11/keysym.h> +#include <X11/Xatom.h> +#include <X11/Xmu/StdSel.h> +#include "vnctypes.h" + +#if(defined __cplusplus) +extern "C" +{ +#endif + +#include "rfbproto.h" + +extern int endianTest; + +#define Swap16IfLE(s) \ + (*(char *)&endianTest ? ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) : (s)) + +#define Swap32IfLE(l) \ + (*(char *)&endianTest ? ((((l) & 0xff000000) >> 24) | \ + (((l) & 0x00ff0000) >> 8) | \ + (((l) & 0x0000ff00) << 8) | \ + (((l) & 0x000000ff) << 24)) : (l)) + +#define MAX_ENCODINGS 20 + + +/** kvncview.cpp **/ + +extern AppData appData; + +extern Display* dpy; +extern const char *vncServerHost; +extern int vncServerPort; + +extern int isQuitFlagSet(); +extern int getPassword(char *passwd, int pwlen); +extern void DrawScreenRegion(int x, int y, int width, int height); +extern void DrawAnyScreenRegionX11Thread(int x, int y, int width, int height); +extern void LockFramebuffer(); +extern void UnlockFramebuffer(); +extern void EnableClientCursor(int state); +extern void beep(); +extern void newServerCut(char *bytes, int len); +extern void postMouseEvent(int x, int y, int buttonMask); + +/** threads.cpp **/ + +extern void queueIncrementalUpdateRequest(); +extern void announceIncrementalUpdateRequest(); + +/* colour.c */ + +extern unsigned long BGR233ToPixel[]; + +extern Colormap cmap; +extern Visual *vis; +extern unsigned int visdepth, visbpp; + +extern void SetVisualAndCmap(void); + +/* desktop.c */ + +extern Widget form, viewport, desktop; +extern GC gc; +extern GC srcGC, dstGC; +extern Dimension dpyWidth, dpyHeight; + +extern void DesktopInit(Window win); +extern void ToplevelInit(void); +extern void SendRFBEvent(XEvent *event, String *params, Cardinal *num_params); +extern void CopyDataToScreen(char *buf, int x, int y, int width, int height); +extern void CopyDataFromScreen(char *buf, int x, int y, int width, int height); +extern void FillRectangle8(CARD8, int x, int y, int width, int height); +extern void FillRectangle16(CARD16, int x, int y, int width, int height); +extern void FillRectangle32(CARD32, int x, int y, int width, int height); +extern void CopyArea(int srcX, int srcY, int width, int height, int x, int y); +extern void SyncScreenRegion(int x, int y, int width, int height); +extern void SyncScreenRegionX11Thread(int x, int y, int width, int height); +extern void drawCursor(void); +extern void DrawCursorX11Thread(int x, int y); +extern void undrawCursor(void); +extern void getBoundingRectCursor(int cx, int cy, int _imageIndex, + int *x, int *y, int *w, int *h); +extern int rectsIntersect(int x, int y, int w, int h, + int x2, int y2, int w2, int h2); +extern int rectContains(int outX, int outY, int outW, int outH, + int inX, int inY, int inW, int inH); +extern void rectsJoin(int *nx1, int *ny1, int *nw1, int *nh1, + int x2, int y2, int w2, int h2); +extern void DrawZoomedScreenRegionX11Thread(Window win, int zwidth, + int zheight, + int x, int y, + int width, int height); +extern void DrawScreenRegionX11Thread(Window win, int x, int y, + int width, int height); +extern void Cleanup(void); +extern XImage *CreateShmZoomImage(void); +extern XImage *CreateShmImage(void); +extern void ShmCleanup(void); +extern void freeDesktopResources(void); + +/* rfbproto.c */ + +extern int rfbsock; +extern Bool canUseCoRRE; +extern Bool canUseHextile; +extern char *desktopName; +extern rfbPixelFormat myFormat; +extern rfbServerInitMsg si; + +extern int cursorX, cursorY; +extern int imageIndex; +typedef struct { + int set; + int w, h; + int hotX, hotY; + int len; + char *image; +} PointerImage; +extern PointerImage pointerImages[]; + +extern int ConnectToRFBServer(const char *hostname, int port); +extern enum InitStatus InitialiseRFBConnection(void); +extern Bool SetFormatAndEncodings(void); +extern Bool SendIncrementalFramebufferUpdateRequest(void); +extern Bool SendFramebufferUpdateRequest(int x, int y, int w, int h, + Bool incremental); +extern Bool SendPointerEvent(int x, int y, int buttonMask); +extern Bool SendKeyEvent(CARD32 key, Bool down); +extern Bool SendClientCutText(const char *str, int len); +extern Bool HandleRFBServerMessage(void); + +extern void PrintPixelFormat(rfbPixelFormat *format); +extern void freeRFBProtoResources(void); +extern void freeResources(void); + +/* sockets.c */ + +extern Bool errorMessageOnReadFailure; + +extern Bool ReadFromRFBServer(char *out, unsigned int n); +extern Bool WriteExact(int sock, const char *buf, int n); +extern int ConnectToTcpAddr(unsigned int host, int port); + +extern int StringToIPAddr(const char *str, unsigned int *addr); +extern void freeSocketsResources(void); + +#if(defined __cplusplus) +} +#endif +#endif diff --git a/krdc/vnc/zlib.c b/krdc/vnc/zlib.c new file mode 100644 index 00000000..80c4eeea --- /dev/null +++ b/krdc/vnc/zlib.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2000 Tridia Corporation. All Rights Reserved. + * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/* + * zlib.c - handle zlib encoding. + * + * This file shouldn't be compiled directly. It is included multiple times by + * rfbproto.c, each time with a different definition of the macro BPP. For + * each value of BPP, this file defines a function which handles an zlib + * encoded rectangle with BPP bits per pixel. + */ + +#define HandleZlibBPP CONCAT2E(HandleZlib,BPP) +#define CARDBPP CONCAT2E(CARD,BPP) + +static Bool +HandleZlibBPP (int rx, int ry, int rw, int rh) +{ + rfbZlibHeader hdr; + int remaining; + int inflateResult; + int toRead; + + /* First make sure we have a large enough raw buffer to hold the + * decompressed data. In practice, with a fixed BPP, fixed frame + * buffer size and the first update containing the entire frame + * buffer, this buffer allocation should only happen once, on the + * first update. + */ + if ( raw_buffer_size < (( rw * rh ) * ( BPP / 8 ))) { + + if ( raw_buffer != NULL ) { + + free( raw_buffer ); + + } + + raw_buffer_size = (( rw * rh ) * ( BPP / 8 )); + raw_buffer = (char*) malloc( raw_buffer_size ); + + } + + if (!ReadFromRFBServer((char *)&hdr, sz_rfbZlibHeader)) + return False; + + remaining = Swap32IfLE(hdr.nBytes); + + /* Need to initialize the decompressor state. */ + decompStream.next_in = ( Bytef * )buffer; + decompStream.avail_in = 0; + decompStream.next_out = ( Bytef * )raw_buffer; + decompStream.avail_out = raw_buffer_size; + decompStream.data_type = Z_BINARY; + + /* Initialize the decompression stream structures on the first invocation. */ + if ( decompStreamInited == False ) { + + inflateResult = inflateInit( &decompStream ); + + if ( inflateResult != Z_OK ) { + fprintf(stderr, + "inflateInit returned error: %d, msg: %s\n", + inflateResult, + decompStream.msg); + return False; + } + + decompStreamInited = True; + + } + + inflateResult = Z_OK; + + /* Process buffer full of data until no more to process, or + * some type of inflater error, or Z_STREAM_END. + */ + while (( remaining > 0 ) && + ( inflateResult == Z_OK )) { + + if ( remaining > BUFFER_SIZE ) { + toRead = BUFFER_SIZE; + } + else { + toRead = remaining; + } + + /* Fill the buffer, obtaining data from the server. */ + if (!ReadFromRFBServer(buffer,toRead)) + return False; + + decompStream.next_in = ( Bytef * )buffer; + decompStream.avail_in = toRead; + + /* Need to uncompress buffer full. */ + inflateResult = inflate( &decompStream, Z_SYNC_FLUSH ); + + /* We never supply a dictionary for compression. */ + if ( inflateResult == Z_NEED_DICT ) { + fprintf(stderr,"zlib inflate needs a dictionary!\n"); + return False; + } + if ( inflateResult < 0 ) { + fprintf(stderr, + "zlib inflate returned error: %d, msg: %s\n", + inflateResult, + decompStream.msg); + return False; + } + + /* Result buffer allocated to be at least large enough. We should + * never run out of space! + */ + if (( decompStream.avail_in > 0 ) && + ( decompStream.avail_out <= 0 )) { + fprintf(stderr,"zlib inflate ran out of space!\n"); + return False; + } + + remaining -= toRead; + + } /* while ( remaining > 0 ) */ + + if ( inflateResult == Z_OK ) { + + /* Put the uncompressed contents of the update on the screen. */ + CopyDataToScreen(raw_buffer, rx, ry, rw, rh); + + } + else { + + fprintf(stderr, + "zlib inflate returned error: %d, msg: %s\n", + inflateResult, + decompStream.msg); + return False; + + } + + return True; +} |