diff --git a/Makefile.am b/Makefile.am index e382d58..e655a2b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -30,6 +30,10 @@ if DBE DBE_DIR=dbe endif +if XCLIPLIST +XCLIPLIST_DIR=xcliplist +endif + SUBDIRS = \ doc \ include \ @@ -54,6 +58,7 @@ SUBDIRS = \ $(XTRAP_DIR) \ $(COMPOSITE_DIR) \ $(GLX_DIR) \ + $(XCLIPLIST_DIR) \ exa \ config \ hw diff --git a/configure.ac b/configure.ac index 5417bbb..5679cba 100644 diff --git a/hw/Makefile.am b/hw/Makefile.am index c2b9571..7ad240e 100644 diff --git a/hw/dmx/Makefile.am b/hw/dmx/Makefile.am index 8457aea..e7cf89f 100644 --- a/hw/dmx/Makefile.am +++ b/hw/dmx/Makefile.am @@ -1,6 +1,6 @@ -DIST_SUBDIRS = input config glxProxy examples doc +DIST_SUBDIRS = input vnc config glxProxy examples doc -SUBDIRS = input config examples +SUBDIRS = input vnc config examples bin_PROGRAMS = Xdmx if XINERAMA diff --git a/hw/dmx/dmx-config.h b/hw/dmx/dmx-config.h index 343fdab..9f7ca47 100644 --- a/hw/dmx/dmx-config.h +++ b/hw/dmx/dmx-config.h @@ -72,6 +72,9 @@ /* Enable the DMX extension */ #define DMXEXT +/* Enable VNC ability */ +#define DMXVNC 1 + /* Disable the extensions that are not currently supported */ #undef BEZIER #undef PEXEXT diff --git a/hw/dmx/dmxinit.c b/hw/dmx/dmxinit.c index 29dc005..f313e7e 100644 --- a/hw/dmx/dmxinit.c +++ b/hw/dmx/dmxinit.c @@ -77,6 +77,10 @@ extern void GlxSetVisualConfigs( ); #endif /* GLXEXT */ +#ifdef DMXVNC +extern void VNCInitForDMX(void); +#endif + /* Global variables available to all Xserver/hw/dmx routines. */ int dmxNumScreens; DMXScreenInfo *dmxScreens; @@ -812,6 +816,9 @@ void InitOutput(ScreenInfo *pScreenInfo, int argc, char *argv[]) dmxLog(dmxInfo, "Shadow framebuffer support %s\n", dmxShadowFB ? "enabled" : "disabled"); +#ifdef DMXVNC + VNCInitForDMX(); +#endif } /* RATS: Assuming the fp string (which comes from the command-line argv @@ -1045,3 +1052,14 @@ void ddxUseMsg(void) ErrorF(" Ctrl-Alt-q Quit (core devices only)\n"); ErrorF(" Ctrl-Alt-F* Switch to VC (local only)\n"); } + +#ifdef DDXTIME +/** Return wall-clock time in milliseconds. */ +CARD32 GetTimeInMillis(void) +{ + struct timeval tp; + + gettimeofday(&tp, 0); + return tp.tv_sec * 1000 + tp.tv_usec / 1000; +} +#endif diff --git a/hw/dmx/dmxsync.c b/hw/dmx/dmxsync.c index c1aa431..2498a38 100644 --- a/hw/dmx/dmxsync.c +++ b/hw/dmx/dmxsync.c @@ -99,9 +99,16 @@ static void dmxSyncBlockHandler(pointer blockData, OSTimePtr pTimeout, TimerForce(dmxSyncTimer); } +#ifdef DMXVNC +extern void rfbWakeupHandlerDMX(void); +#endif + static void dmxSyncWakeupHandler(pointer blockData, int result, pointer pReadMask) { +#ifdef DMXVNC + rfbWakeupHandlerDMX(); +#endif } /** Request the XSync() batching optimization with the specified \a diff --git a/hw/dmx/input/Makefile.am b/hw/dmx/input/Makefile.am index da8de05..1969a0c 100644 --- a/hw/dmx/input/Makefile.am +++ b/hw/dmx/input/Makefile.am @@ -65,6 +65,7 @@ AM_CFLAGS = $(DIX_CFLAGS) \ -I$(top_srcdir)/hw/xfree86/common \ $(GLX_INCS) \ -DHAVE_DMX_CONFIG_H \ + -DDMXVNC=1 \ $(GLX_DEFS) \ @DMXMODULES_CFLAGS@ diff --git a/hw/dmx/input/dmxcommon.c b/hw/dmx/input/dmxcommon.c index e77bb79..0866c84 100644 --- a/hw/dmx/input/dmxcommon.c +++ b/hw/dmx/input/dmxcommon.c @@ -655,7 +655,6 @@ void dmxCommonRestoreState(pointer private) dmxLogInput(dmxInput, "Keyboard busy, waiting\n"); else dmxLogInput(dmxInput, "Keyboard error, waiting\n"); - /* Don't generate X11 protocol for a bit */ for (tmp = GetTimeInMillis(); GetTimeInMillis() - tmp < 250;) { usleep(250); /* This ends up sleeping only until diff --git a/hw/dmx/input/dmxinputinit.c b/hw/dmx/input/dmxinputinit.c index fd4eeaa..7de9546 100644 diff --git a/hw/dmx/input/dmxinputinit.h b/hw/dmx/input/dmxinputinit.h index 6f491ed..e6297f0 100644 --- a/hw/dmx/input/dmxinputinit.h +++ b/hw/dmx/input/dmxinputinit.h @@ -290,4 +290,9 @@ extern int dmxInputAttachConsole(const char *name, int isCore, extern int dmxInputAttachBackend(int physicalScreen, int isCore, int *id); +#ifdef DMXVNC +extern void vncSetKeyboardDevice(DeviceIntPtr kbd); +extern void vncSetPointerDevice(DeviceIntPtr ptr); +#endif + #endif diff --git a/hw/dmx/vnc/.gitignore b/hw/dmx/vnc/.gitignore new file mode 100644 index 0000000..d14621f --- /dev/null +++ b/hw/dmx/vnc/.gitignore @@ -0,0 +1,36 @@ +auth.c +cmap.c +corre.c +cursor.c +cutpaste.c +d3des.c +d3des.h +dispcur.c +draw.c +hextile.c +httpd.c +kbdptr.c +keyboard.h +loginauth.c +rdp.c +rfb.h +rfbkeyb.c +rfbmouse.c +rfbproto.h +rfbserver.c +rre.c +sockets.c +sprite.c +sprite.h +spritest.h +stats.c +tableinitcmtemplate.c +tableinittctemplate.c +tabletranstemplate.c +tight.c +translate.c +vncauth.c +vncauth.h +vncext.c +xistubs.c +zlib.c diff --git a/hw/dmx/vnc/Makefile.am b/hw/dmx/vnc/Makefile.am new file mode 100644 index 0000000..48c07bd --- /dev/null +++ b/hw/dmx/vnc/Makefile.am @@ -0,0 +1,43 @@ +noinst_LIBRARIES = libdmxvnc.a + +SRCS = \ + auth.c \ + cmap.c \ + corre.c \ + cursor.c \ + cutpaste.c \ + d3des.c \ + dispcur.c \ + draw.c \ + hextile.c \ + httpd.c \ + kbdptr.c \ + loginauth.c \ + rdp.c \ + rfbkeyb.c \ + rfbmouse.c \ + rfbserver.c \ + rre.c \ + sockets.c \ + sprite.c \ + stats.c \ + tight.c \ + translate.c \ + vncauth.c \ + vncext.c \ + vncInit.c \ + xistubs.c \ + zlib.c + + +libdmxvnc_a_SOURCES = $(SRCS) + +AM_CFLAGS = \ + -I$(top_srcdir)/hw/dmx \ + -I$(top_srcdir)/hw/xfree86/common \ + -DHAVE_DMX_CONFIG_H \ + $(DIX_CFLAGS) \ + -DDMXVNC=1 \ + @DMXMODULES_CFLAGS@ + +###EXTRA_DIST = dmxdetach.c diff --git a/hw/dmx/vnc/vncInit.c b/hw/dmx/vnc/vncInit.c new file mode 100644 index 0000000..5fdf647 --- /dev/null +++ b/hw/dmx/vnc/vncInit.c @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2002 Alan Hourihane. 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. + * + * Author: Alan Hourihane + */ + +#include "rfb.h" + +#ifdef HAVE_DMX_CONFIG_H +#include "dmx-config.h" +#endif + +#ifdef DMXVNC +#include "../dmx.h" +#include "../dmxcb.h" +#endif + +#include +#include "fb.h" + +#include "dixstruct.h" +#include "compiler.h" + +#include "mipointer.h" +#include "mibstore.h" + +#include "globals.h" +#define DPMS_SERVER +#include + +#include + +int vncScreenPrivateIndex = -1; +int rfbGCIndex = -1; +int inetdSock = -1; +Atom VNC_LAST_CLIENT_ID = 0; +Atom VNC_CONNECT = 0; +char *desktopName = "x11"; +char rfbThisHost[256]; + +extern void VncExtensionInit(void); + +extern void vncInitMouse(void); +extern void vncInitKeyb(void); +Bool VNCInit(ScreenPtr pScreen, DMXScreenInfo *dmxScreen); + +#ifndef XFree86LOADER +static unsigned long VNCGeneration = 0; +#endif +static void rfbWakeupHandler (int i, pointer blockData, unsigned long err, pointer pReadmask); + +/* + * rfbLog prints a time-stamped message to the log file (stderr). + */ + +void rfbLog(char *format, ...) +{ + va_list args; + char buf[256]; + time_t clock; + + va_start(args, format); + + time(&clock); + strftime(buf, 255, "%d/%m/%Y %H:%M:%S ", localtime(&clock)); + fprintf(stderr, buf); + + vfprintf(stderr, format, args); + fflush(stderr); + + va_end(args); +} + +void rfbLogPerror(char *str) +{ + rfbLog(""); + perror(str); +} + + +static ScreenPtr TheVNCScreen = NULL; + + +static void +nopGetImage(DrawablePtr pDrawable, int sx, int sy, + int w, int h, unsigned int format, + unsigned long planemask, char *pdstLine) +{ + ScreenPtr pScreen = pDrawable->pScreen; + + (*pScreen->GetImage)(pDrawable,sx,sy,w,h,format,planemask,pdstLine); +} + + +static void +SetupVNCScreen(void) +{ + vncScreenPtr pScreenPriv; + VisualPtr v = NULL; + int i; + + pScreenPriv = xalloc(sizeof(vncScreenRec)); + pScreenPriv->width = dmxGlobalWidth; + pScreenPriv->height = dmxGlobalHeight; + pScreenPriv->rfbAuthTries = 0; + pScreenPriv->rfbAuthTooManyTries = FALSE; + pScreenPriv->timer = NULL; + pScreenPriv->udpPort = 0; + pScreenPriv->rfbListenSock = -1; + pScreenPriv->udpSock = -1; + pScreenPriv->udpSockConnected = FALSE; + pScreenPriv->httpListenSock = -1; + pScreenPriv->httpSock = -1; + pScreenPriv->maxFd = 0; + pScreenPriv->rfbAuthPasswdFile = NULL; + pScreenPriv->httpDir = NULL; + pScreenPriv->rfbInstalledColormap = NULL; + pScreenPriv->interface.s_addr = htonl (INADDR_ANY); + + pScreenPriv->rfbPort = 0; + pScreenPriv->httpPort = 0; + pScreenPriv->rfbAuthPasswdFile = NULL; + pScreenPriv->httpDir = NULL; + pScreenPriv->rfbAlwaysShared = FALSE; + pScreenPriv->rfbNeverShared = FALSE; + pScreenPriv->rfbUserAccept = FALSE; + pScreenPriv->rfbViewOnly = FALSE; + pScreenPriv->rfbDontDisconnect = FALSE; + pScreenPriv->loginAuthEnabled = FALSE; + + /* Find the root window's visual. + * XXX this might be the wrong info to use below. + */ + for (i = 0; i < screenInfo.screens[0]->numVisuals; i++) { + v = screenInfo.screens[0]->visuals + i; + if (v->vid == screenInfo.screens[0]->rootVisual) { + break; + } + } + + /* XXX is this the right depth and bpp? */ + /* With DMX, screenInfo.screens[0]->rootDepth is 24 and that doesn't + * work in translate.c! We're just using 32. + */ + pScreenPriv->rfbServerFormat.bitsPerPixel = 32; + pScreenPriv->rfbServerFormat.depth = 32; + pScreenPriv->rfbServerFormat.bigEndian = 0; + pScreenPriv->rfbServerFormat.trueColour = (v->class == TrueColor); + if (pScreenPriv->rfbServerFormat.trueColour) { + pScreenPriv->rfbServerFormat.redMax = v->redMask >> v->offsetRed; + pScreenPriv->rfbServerFormat.greenMax = v->greenMask >> v->offsetGreen; + pScreenPriv->rfbServerFormat.blueMax = v->blueMask >> v->offsetBlue; + pScreenPriv->rfbServerFormat.redShift = v->offsetRed; + pScreenPriv->rfbServerFormat.greenShift = v->offsetGreen; + pScreenPriv->rfbServerFormat.blueShift = v->offsetBlue; + } else { + pScreenPriv->rfbServerFormat.redMax + = pScreenPriv->rfbServerFormat.greenMax + = pScreenPriv->rfbServerFormat.blueMax = 0; + pScreenPriv->rfbServerFormat.redShift + = pScreenPriv->rfbServerFormat.greenShift + = pScreenPriv->rfbServerFormat.blueShift = 0; + } + + /* Allocate/init TheVNCScreen object */ + vncScreenPrivateIndex = 0; + + TheVNCScreen = (ScreenPtr) Xcalloc(1, sizeof(struct _Screen)); + TheVNCScreen->devPrivates = (DevUnion *) Xcalloc(1, sizeof(DevUnion)); + TheVNCScreen->devPrivates[vncScreenPrivateIndex].ptr = pScreenPriv; + TheVNCScreen->GetImage = nopGetImage; + + rfbInitSockets(TheVNCScreen); + if (inetdSock == -1) + httpInitSockets(TheVNCScreen); +} + + +/* + * Called by DMX's InitOutput() + * Will be called for each X server generation. + */ +void +VNCInitForDMX(void) +{ + rfbLog("DMXVNC: Initializing VNC for DMX\n"); + ErrorF("DMXVNC: Initializing VNC for DMX\n"); + + if (TheVNCScreen) { + rfbLog("DMXVNC: New server generation\n"); + } + else { + rfbLog("DMXVNC: Set up VNC screen\n"); + SetupVNCScreen(); + } + + /* Reset the input device pointers. They'll get set later. + * This allows successful server regeneration. + */ + vncSetKeyboardDevice(NULL); + vncSetPointerDevice(NULL); +} + + +Bool +VNCInit(ScreenPtr pScreen, DMXScreenInfo *pScrn) +{ + VisualPtr visual; + vncScreenPtr pScreenPriv; +#if 0 + char *interface_str = NULL; +#endif +#ifdef RENDER + PictureScreenPtr ps; +#endif + +#ifndef XFree86LOADER + if (VNCGeneration != serverGeneration) { + VNCGeneration = serverGeneration; + if ( ((vncScreenPrivateIndex = AllocateScreenPrivateIndex()) < 0) || + ((rfbGCIndex = AllocateGCPrivateIndex()) < 0) ) + return FALSE; + } +#endif + + if (!AllocateGCPrivate(pScreen, rfbGCIndex, sizeof(rfbGCRec))) { + ErrorF("VNCInit(): failed to allocate GCIndex\n"); + return FALSE; + } + + if (!(pScreenPriv = xalloc(sizeof(vncScreenRec)))) + return FALSE; + + pScreen->devPrivates[vncScreenPrivateIndex].ptr = (pointer)pScreenPriv; + + pScreenPriv->rfbAuthTries = 0; + pScreenPriv->rfbAuthTooManyTries = FALSE; + pScreenPriv->timer = NULL; + pScreenPriv->udpPort = 0; + pScreenPriv->rfbListenSock = -1; + pScreenPriv->udpSock = -1; + pScreenPriv->udpSockConnected = FALSE; + pScreenPriv->httpListenSock = -1; + pScreenPriv->httpSock = -1; + pScreenPriv->maxFd = 0; + pScreenPriv->rfbAuthPasswdFile = NULL; + pScreenPriv->httpDir = NULL; + pScreenPriv->rfbInstalledColormap = NULL; + pScreenPriv->interface.s_addr = htonl (INADDR_ANY); + + pScreenPriv->rfbPort = 0; + pScreenPriv->httpPort = 0; + pScreenPriv->rfbAuthPasswdFile = NULL; + pScreenPriv->httpDir = NULL; + pScreenPriv->rfbAlwaysShared = FALSE; + pScreenPriv->rfbNeverShared = FALSE; + pScreenPriv->rfbUserAccept = FALSE; + pScreenPriv->rfbViewOnly = FALSE; + pScreenPriv->rfbDontDisconnect = FALSE; + pScreenPriv->loginAuthEnabled = FALSE; + +#if 0 + if (xf86ReturnOptValBool(options, OPTION_LOCALHOST, FALSE)) + pScreenPriv->interface.s_addr = htonl (INADDR_LOOPBACK); + + interface_str = xf86GetOptValString(options, OPTION_INTERFACE); + + if (interface_str && pScreenPriv->interface.s_addr == htonl(INADDR_ANY)) { + Bool failed = FALSE; + struct in_addr got; + unsigned long octet; + char *p = interface_str, *end; + int q; + + for (q = 0; q < 4; q++) { + octet = strtoul (p, &end, 10); + + if (p == end || octet > 255) + failed = TRUE; + + if ((q < 3 && *end != '.') || + (q == 3 && *end != '\0')) + failed = TRUE; + + got.s_addr = (got.s_addr << 8) | octet; + p = end + 1; + } + + if (!failed) + pScreenPriv->interface.s_addr = htonl (got.s_addr); + else + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "VNC interface option malformed, not using.\n"); + } +#endif + + if (!VNC_LAST_CLIENT_ID) + VNC_LAST_CLIENT_ID = MakeAtom("VNC_LAST_CLIENT_ID", + strlen("VNC_LAST_CLIENT_ID"), TRUE); + if (!VNC_CONNECT) + VNC_CONNECT = MakeAtom("VNC_CONNECT", strlen("VNC_CONNECT"), TRUE); + + rfbInitSockets(pScreen); + if (inetdSock == -1) + httpInitSockets(pScreen); + +#ifdef CORBA + initialiseCORBA(argc, argv, desktopName); +#endif + + pScreenPriv->width = pScrn->scrnWidth; + pScreenPriv->height = pScrn->scrnHeight; + pScreenPriv->depth = pScrn->beDepth; + pScreenPriv->paddedWidthInBytes = PixmapBytePad(pScrn->scrnWidth, pScrn->beDepth); + pScreenPriv->bitsPerPixel = rfbBitsPerPixel(pScrn->beDepth); + pScreenPriv->pfbMemory = NULL; + pScreenPriv->oldpfbMemory = NULL; + + pScreenPriv->cursorIsDrawn = TRUE; + pScreenPriv->dontSendFramebufferUpdate = FALSE; + + pScreenPriv->CloseScreen = pScreen->CloseScreen; + pScreenPriv->CreateGC = pScreen->CreateGC; + pScreenPriv->PaintWindowBackground = pScreen->PaintWindowBackground; + pScreenPriv->PaintWindowBorder = pScreen->PaintWindowBorder; + pScreenPriv->CopyWindow = pScreen->CopyWindow; + pScreenPriv->ClearToBackground = pScreen->ClearToBackground; + pScreenPriv->RestoreAreas = pScreen->RestoreAreas; + pScreenPriv->WakeupHandler = pScreen->WakeupHandler; + pScreenPriv->InstallColormap = pScreen->InstallColormap; + pScreenPriv->UninstallColormap = pScreen->UninstallColormap; + pScreenPriv->ListInstalledColormaps = pScreen->ListInstalledColormaps; + pScreenPriv->StoreColors = pScreen->StoreColors; +#ifdef CHROMIUM + pScreenPriv->RealizeWindow = pScreen->RealizeWindow; + pScreenPriv->UnrealizeWindow = pScreen->UnrealizeWindow; + pScreenPriv->DestroyWindow = pScreen->DestroyWindow; + pScreenPriv->PositionWindow = pScreen->PositionWindow; + pScreenPriv->ResizeWindow = pScreen->ResizeWindow; + pScreenPriv->ClipNotify = pScreen->ClipNotify; +#endif +#ifdef RENDER + ps = GetPictureScreenIfSet(pScreen); + if (ps) + pScreenPriv->Composite = ps->Composite; +#endif + pScreen->CloseScreen = rfbCloseScreen; + pScreen->CreateGC = rfbCreateGC; + pScreen->PaintWindowBackground = rfbPaintWindowBackground; + pScreen->PaintWindowBorder = rfbPaintWindowBorder; + pScreen->CopyWindow = rfbCopyWindow; + pScreen->ClearToBackground = rfbClearToBackground; + pScreen->RestoreAreas = rfbRestoreAreas; + pScreen->WakeupHandler = rfbWakeupHandler; + pScreen->InstallColormap = rfbInstallColormap; + pScreen->UninstallColormap = rfbUninstallColormap; + pScreen->ListInstalledColormaps = rfbListInstalledColormaps; + pScreen->StoreColors = rfbStoreColors; + +#ifdef CHROMIUM + pScreen->RealizeWindow = rfbRealizeWindow; + pScreen->UnrealizeWindow = rfbUnrealizeWindow; + pScreen->DestroyWindow = rfbDestroyWindow; + pScreen->PositionWindow = rfbPositionWindow; + pScreen->ResizeWindow = rfbResizeWindow; + pScreen->ClipNotify = rfbClipNotify; +#endif +#ifdef RENDER + if (ps) + ps->Composite = rfbComposite; +#endif + + for (visual = pScreen->visuals; visual->vid != pScreen->rootVisual; visual++) + ; + + if (!visual) { + ErrorF("rfbScreenInit: couldn't find root visual\n"); + return FALSE; + } + + pScreenPriv->rfbServerFormat.bitsPerPixel = pScrn->beBPP; + pScreenPriv->rfbServerFormat.depth = pScrn->beDepth; + pScreenPriv->rfbServerFormat.bigEndian = !(*(char *)&rfbEndianTest); + pScreenPriv->rfbServerFormat.trueColour = (visual->class == TrueColor); + if (pScreenPriv->rfbServerFormat.trueColour) { + pScreenPriv->rfbServerFormat.redMax = visual->redMask >> visual->offsetRed; + pScreenPriv->rfbServerFormat.greenMax = visual->greenMask >> visual->offsetGreen; + pScreenPriv->rfbServerFormat.blueMax = visual->blueMask >> visual->offsetBlue; + pScreenPriv->rfbServerFormat.redShift = visual->offsetRed; + pScreenPriv->rfbServerFormat.greenShift = visual->offsetGreen; + pScreenPriv->rfbServerFormat.blueShift = visual->offsetBlue; + } else { + pScreenPriv->rfbServerFormat.redMax + = pScreenPriv->rfbServerFormat.greenMax + = pScreenPriv->rfbServerFormat.blueMax = 0; + pScreenPriv->rfbServerFormat.redShift + = pScreenPriv->rfbServerFormat.greenShift + = pScreenPriv->rfbServerFormat.blueShift = 0; + } + + return TRUE; +} + +static void +rfbWakeupHandler ( + int i, + pointer blockData, + unsigned long err, + pointer pReadmask +){ + ScreenPtr pScreen = screenInfo.screens[i]; + vncScreenPtr pScreenPriv = VNCPTR(pScreen); + /*DMXScreenInfo *pScrn = &dmxScreens[pScreen->myNum];*/ + + rfbRootPropertyChange(pScreen); /* Check clipboard */ + + rfbCheckFds(pScreen); + httpCheckFds(pScreen); +#ifdef CORBA + corbaCheckFds(); +#endif + + pScreen->WakeupHandler = pScreenPriv->WakeupHandler; + (*pScreen->WakeupHandler) (i, blockData, err, pReadmask); + pScreen->WakeupHandler = rfbWakeupHandler; +} + + +/** + * The rfb wake-up handler used when we're compiled into the DMX server. + */ +void +rfbWakeupHandlerDMX(void) +{ + ScreenPtr pScreen = TheVNCScreen; + rfbRootPropertyChange(pScreen); /* Check clipboard */ + rfbCheckFds(pScreen); + httpCheckFds(pScreen); +} diff --git a/hw/dmx/vnc/vncint.h b/hw/dmx/vnc/vncint.h new file mode 100644 index 0000000..afa84f8 --- /dev/null +++ b/hw/dmx/vnc/vncint.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2002 Alan Hourihane. 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. + * + * Author: Alan Hourihane + */ + +#ifndef _VNCINT_H_ +#define _VNCINT_H_ + +#include + +extern int vncScreenPrivateIndex; + +#define VNCPTR(pScreen)\ + (vncScreenPtr)((pScreen)->devPrivates[vncScreenPrivateIndex].ptr) + +typedef struct { + int rfbPort; + int rdpPort; + int udpPort; + int rfbListenSock; + int rdpListenSock; + int udpSock; + int httpPort; + int httpListenSock; + int httpSock; + char * httpDir; + char buf[HTTP_BUF_SIZE]; + Bool udpSockConnected; + char * rfbAuthPasswdFile; + size_t buf_filled; + int maxFd; + fd_set allFds; + unsigned char * oldpfbMemory; + Bool rfbAlwaysShared; + Bool rfbNeverShared; + Bool rfbDontDisconnect; + Bool rfbUserAccept; + Bool rfbViewOnly; + unsigned char * pfbMemory; + int paddedWidthInBytes; + ColormapPtr rfbInstalledColormap; + ColormapPtr savedColormap; + rfbPixelFormat rfbServerFormat; + Bool rfbAuthTooManyTries; + int rfbAuthTries; + Bool loginAuthEnabled; + struct in_addr interface; + OsTimerPtr timer; + unsigned char updateBuf[UPDATE_BUF_SIZE]; + int ublen; + int width; + int height; + int depth; + int bitsPerPixel; + + /* The following two members are used to minimise the amount of unnecessary + drawing caused by cursor movement. Whenever any drawing affects the + part of the screen where the cursor is, the cursor is removed first and + then the drawing is done (this is what the sprite routines test for). + Afterwards, however, we do not replace the cursor, even when the cursor + is logically being moved across the screen. We only draw the cursor + again just as we are about to send the client a framebuffer update. + + We need to be careful when removing and drawing the cursor because of + their relationship with the normal drawing routines. The drawing + routines can invoke the cursor routines, but also the cursor routines + themselves end up invoking drawing routines. + + Removing the cursor (rfbSpriteRemoveCursor) is eventually achieved by + doing a CopyArea from a pixmap to the screen, where the pixmap contains + the saved contents of the screen under the cursor. Before doing this, + however, we set cursorIsDrawn to FALSE. Then, when CopyArea is called, + it sees that cursorIsDrawn is FALSE and so doesn't feel the need to + (recursively!) remove the cursor before doing it. + + Putting up the cursor (rfbSpriteRestoreCursor) involves a call to + PushPixels. While this is happening, cursorIsDrawn must be FALSE so + that PushPixels doesn't think it has to remove the cursor first. + Obviously cursorIsDrawn is set to TRUE afterwards. + + Another problem we face is that drawing routines sometimes cause a + framebuffer update to be sent to the RFB client. When the RFB client is + already waiting for a framebuffer update and some drawing to the + framebuffer then happens, the drawing routine sees that the client is + ready, so it calls rfbSendFramebufferUpdate. If the cursor is not drawn + at this stage, it must be put up, and so rfbSpriteRestoreCursor is + called. However, if the original drawing routine was actually called + from within rfbSpriteRestoreCursor or rfbSpriteRemoveCursor we don't + want this to happen. So both the cursor routines set + dontSendFramebufferUpdate to TRUE, and all the drawing routines check + this before calling rfbSendFramebufferUpdate. */ + + Bool cursorIsDrawn; /* TRUE if the cursor is currently drawn */ + Bool dontSendFramebufferUpdate; /* TRUE while removing or drawing the + cursor */ + + /* wrapped screen functions */ + + CloseScreenProcPtr CloseScreen; + CreateGCProcPtr CreateGC; + PaintWindowBackgroundProcPtr PaintWindowBackground; + PaintWindowBorderProcPtr PaintWindowBorder; + CopyWindowProcPtr CopyWindow; + ClearToBackgroundProcPtr ClearToBackground; + RestoreAreasProcPtr RestoreAreas; + ScreenWakeupHandlerProcPtr WakeupHandler; + InstallColormapProcPtr InstallColormap; + UninstallColormapProcPtr UninstallColormap; + ListInstalledColormapsProcPtr ListInstalledColormaps; + StoreColorsProcPtr StoreColors; + DisplayCursorProcPtr DisplayCursor; + CursorPtr pCurs; + Bool (*UseHWCursor)(ScreenPtr, CursorPtr); + Bool (*UseHWCursorARGB)(ScreenPtr, CursorPtr); + Bool *SWCursor; +#ifdef CHROMIUM + RealizeWindowProcPtr RealizeWindow; + UnrealizeWindowProcPtr UnrealizeWindow; + DestroyWindowProcPtr DestroyWindow; + ResizeWindowProcPtr ResizeWindow; + PositionWindowProcPtr PositionWindow; + ClipNotifyProcPtr ClipNotify; +#endif +#ifdef RENDER + CompositeProcPtr Composite; +#endif + +} vncScreenRec, *vncScreenPtr; + +extern Bool vncUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs); +extern Bool vncUseHWCursorARGB(ScreenPtr pScreen, CursorPtr pCurs); + +extern void +rfbSendChromiumStart(unsigned int ipaddress, unsigned int port); + +#endif /* _VNCINT_H_ */ + diff --git a/hw/vnc/LICENCE.TXT b/hw/vnc/LICENCE.TXT new file mode 100644 index 0000000..ae3b531 --- /dev/null +++ b/hw/vnc/LICENCE.TXT @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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. + + This program 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. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/hw/vnc/Makefile.am b/hw/vnc/Makefile.am new file mode 100644 index 0000000..9bf90ec --- /dev/null +++ b/hw/vnc/Makefile.am @@ -0,0 +1,54 @@ +# XXX This Makefile.am probably needs some work. + +bin_PROGRAMS = Xvnc + +Xvnc_SOURCES = \ + $(top_srcdir)/fb/fbcmap_mi.c \ + $(top_srcdir)/mi/miinitext.c \ + auth.c \ + cmap.c \ + corre.c \ + cursor.c \ + cutpaste.c \ + d3des.c \ + dispcur.c \ + dpmsstubs.c \ + draw.c \ + hextile.c \ + httpd.c \ + init.c \ + kbdptr.c \ + loginauth.c \ + rfbkeyb.c \ + rfbmouse.c \ + rfbserver.c \ + rre.c \ + sprite.c \ + sockets.c \ + stats.c \ + tight.c \ + translate.c \ + vncauth.c \ + vncext.c \ + xistubs.c \ + zlib.c + + +JPEG_LIBS = -ljpeg +CRYPT_LIBS = -lcrypt +FB_LIBS = ../../fb/.libs/libfb.a + +AM_CFLAGS = $(DIX_CFLAGS) $(XVNC_CFLAGS) -I$(top_srcdir)/hw/dmx/vnc -DCHROMIUM=1 + +Xvnc_LDFLAGS = $(LD_EXPORT_SYMBOLS_FLAG) +Xvnc_LDADD = \ + $(XORG_CORE_LIBS) \ + $(XVNC_LIBS) \ + $(JPEG_LIBS) \ + $(CRYPT_LIBS) \ + $(XSERVER_SYS_LIBS) \ + $(VNCMODULES_LIBS) + + +relink: + rm -f Xvnc && $(MAKE) Xvnc diff --git a/hw/vnc/README b/hw/vnc/README new file mode 100644 index 0000000..d65d560 --- /dev/null +++ b/hw/vnc/README @@ -0,0 +1,14 @@ + +This is the directory containing the code specific to the TightVNC X server (Xvnc). +Note that within this directory the name RFB is still used instead of VNC. + +NOTE: + +The is the new XFree86 v4 architecture VNC server code. + +Modified entirely by Alan Hourihane + +For information please visit http://xf4vnc.sourceforge.net + +Moved to X.org modular tree by Brian Paul. + diff --git a/hw/vnc/auth.c b/hw/vnc/auth.c new file mode 100644 index 0000000..f91a1ed --- /dev/null +++ b/hw/vnc/auth.c @@ -0,0 +1,566 @@ +/* + * auth.c - deal with authentication. + * + * This file implements authentication when setting up an RFB connection. + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * Copyright (C) 2003-2004 Constantin Kaplinsky. 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include "rfb.h" +#include "windowstr.h" + +static void rfbSendSecurityType(rfbClientPtr cl, int securityType); +static void rfbSendSecurityTypeList(rfbClientPtr cl, int primaryType); +static void rfbSendTunnelingCaps(rfbClientPtr cl); +static void rfbSendAuthCaps(rfbClientPtr cl); +static void rfbVncAuthSendChallenge(rfbClientPtr cl); + +/* + * rfbAuthNewClient is called right after negotiating the protocol + * version. Depending on the protocol version, we send either a code + * for authentication scheme to be used (protocol 3.3), or a list of + * possible "security types" (protocol 3.7). + */ + +void +rfbAuthNewClient(cl) + rfbClientPtr cl; +{ + VNCSCREENPTR(cl->pScreen); + int securityType = rfbSecTypeInvalid; + + if ((!pVNC->rfbAuthPasswdFile && !pVNC->loginAuthEnabled) || cl->reverseConnection) { + securityType = rfbSecTypeNone; + } else { + if (rfbAuthIsBlocked(cl)) { + rfbLog("Too many authentication failures - client rejected\n"); + rfbClientConnFailed(cl, "Too many authentication failures"); + return; + } + if (pVNC->rfbAuthPasswdFile) + securityType = rfbSecTypeVncAuth; + } + + if (cl->protocol_minor_ver < 7) { + /* Make sure we use only RFB 3.3 compatible security types. */ + if (securityType == rfbSecTypeInvalid) { + rfbLog("VNC authentication disabled - RFB 3.3 client rejected\n"); + rfbClientConnFailed(cl, "Your viewer cannot handle required " + "authentication methods"); + return; + } + rfbSendSecurityType(cl, securityType); + } else { + /* Here it's ok when securityType is set to rfbSecTypeInvalid. */ + rfbSendSecurityTypeList(cl, securityType); + } +} + + +/* + * Tell the client what security type will be used (protocol 3.3). + */ + +static void +rfbSendSecurityType(cl, securityType) + rfbClientPtr cl; + int securityType; +{ + CARD32 value32; + + /* Send the value. */ + value32 = Swap32IfLE(securityType); + if (WriteExact(cl->sock, (char *)&value32, 4) < 0) { + rfbLogPerror("rfbSendSecurityType: write"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + /* Decide what to do next. */ + switch (securityType) { + case rfbSecTypeNone: + /* Dispatch client input to rfbProcessClientInitMessage. */ + cl->state = RFB_INITIALISATION; + break; + case rfbSecTypeVncAuth: + /* Begin the standard VNC authentication procedure. */ + rfbVncAuthSendChallenge(cl); + break; + default: + /* Impossible case (hopefully). */ + rfbLogPerror("rfbSendSecurityType: assertion failed"); + rfbCloseSock(cl->pScreen, cl->sock); + } +} + + +/* + * Advertise our supported security types (protocol 3.7). The list + * will include one standard security type (if primaryType is not set + * to rfbSecTypeInvalid), and then one more value telling the client + * that we support TightVNC protocol extensions. Thus, currently, + * there will be either 1 or 2 items in the list. + */ + +static void +rfbSendSecurityTypeList(cl, primaryType) + rfbClientPtr cl; + int primaryType; +{ + int count = 1; + + /* Fill in the list of security types in the client structure. */ + if (primaryType != rfbSecTypeInvalid) { + cl->securityTypes[count++] = (CARD8)primaryType; + } + cl->securityTypes[count] = (CARD8)rfbSecTypeTight; + cl->securityTypes[0] = (CARD8)count++; + + /* Send the list. */ + if (WriteExact(cl->sock, (char *)cl->securityTypes, count) < 0) { + rfbLogPerror("rfbSendSecurityTypeList: write"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + /* Dispatch client input to rfbProcessClientSecurityType. */ + cl->state = RFB_SECURITY_TYPE; +} + + +/* + * Read the security type chosen by the client (protocol 3.7). + */ + +void +rfbProcessClientSecurityType(cl) + rfbClientPtr cl; +{ + int n, count, i; + CARD8 chosenType; + + /* Read the security type. */ + n = ReadExact(cl->sock, (char *)&chosenType, 1); + if (n <= 0) { + if (n == 0) + rfbLog("rfbProcessClientSecurityType: client gone\n"); + else + rfbLogPerror("rfbProcessClientSecurityType: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + /* Make sure it was present in the list sent by the server. */ + count = (int)cl->securityTypes[0]; + for (i = 1; i <= count; i++) { + if (chosenType == cl->securityTypes[i]) + break; + } + if (i > count) { + rfbLog("rfbProcessClientSecurityType: " + "wrong security type requested\n"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + /* Now go to the proper authentication procedure. */ + switch (chosenType) { + case rfbSecTypeNone: + /* Dispatch client input to rfbProcessClientInitMessage. */ + cl->state = RFB_INITIALISATION; + break; + case rfbSecTypeVncAuth: + /* Begin the standard VNC authentication procedure. */ + rfbVncAuthSendChallenge(cl); + break; + case rfbSecTypeTight: + /* We are lucky: the viewer supports TightVNC extensions. */ + rfbLog("Enabling TightVNC protocol extensions\n"); + /* Switch to the protocol 3.7t. */ + cl->protocol_tightvnc = TRUE; + /* Advertise our tunneling capabilities. */ + rfbSendTunnelingCaps(cl); + break; + default: + /* Impossible case (hopefully). */ + rfbLog("rfbProcessClientSecurityType: " + "unknown authentication scheme\n"); + rfbCloseSock(cl->pScreen, cl->sock); + } +} + + +/* + * Send the list of our tunneling capabilities (protocol 3.7t). + */ + +static void +rfbSendTunnelingCaps(cl) + rfbClientPtr cl; +{ + rfbTunnelingCapsMsg caps; + CARD32 nTypes = 0; /* we don't support tunneling yet */ + + caps.nTunnelTypes = Swap32IfLE(nTypes); + if (WriteExact(cl->sock, (char *)&caps, sz_rfbTunnelingCapsMsg) < 0) { + rfbLogPerror("rfbSendTunnelingCaps: write"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + if (nTypes) { + /* Dispatch client input to rfbProcessClientTunnelingType(). */ + cl->state = RFB_TUNNELING_TYPE; + } else { + rfbSendAuthCaps(cl); + } +} + + +/* + * Read tunneling type requested by the client (protocol 3.7t). + * NOTE: Currently, we don't support tunneling, and this function + * can never be called. + */ + +void +rfbProcessClientTunnelingType(cl) + rfbClientPtr cl; +{ + /* If we were called, then something's really wrong. */ + rfbLog("rfbProcessClientTunnelingType: not implemented\n"); + rfbCloseSock(cl->pScreen, cl->sock); + return; +} + + +/* + * Send the list of our authentication capabilities to the client + * (protocol 3.7t). + */ + +static void +rfbSendAuthCaps(cl) + rfbClientPtr cl; +{ + VNCSCREENPTR(cl->pScreen); + Bool authRequired; + rfbAuthenticationCapsMsg caps; + rfbCapabilityInfo caplist[MAX_AUTH_CAPS]; + int count = 0; + + authRequired = ((pVNC->rfbAuthPasswdFile != NULL || pVNC->loginAuthEnabled) && + !cl->reverseConnection); + + if (authRequired) { + if (pVNC->loginAuthEnabled) { + SetCapInfo(&caplist[count], rfbAuthUnixLogin, rfbTightVncVendor); + cl->authCaps[count++] = rfbAuthUnixLogin; + } + if (pVNC->rfbAuthPasswdFile != NULL) { + SetCapInfo(&caplist[count], rfbAuthVNC, rfbStandardVendor); + cl->authCaps[count++] = rfbAuthVNC; + } + if (count == 0) { + /* Should never happen. */ + rfbLog("rfbSendAuthCaps: assertion failed\n"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + } + + cl->nAuthCaps = count; + caps.nAuthTypes = Swap32IfLE((CARD32)count); + if (WriteExact(cl->sock, (char *)&caps, sz_rfbAuthenticationCapsMsg) < 0) { + rfbLogPerror("rfbSendAuthCaps: write"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + if (count) { + if (WriteExact(cl->sock, (char *)&caplist[0], + count * sz_rfbCapabilityInfo) < 0) { + rfbLogPerror("rfbSendAuthCaps: write"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + /* Dispatch client input to rfbProcessClientAuthType. */ + cl->state = RFB_AUTH_TYPE; + } else { + /* Dispatch client input to rfbProcessClientInitMessage. */ + cl->state = RFB_INITIALISATION; + } +} + + +/* + * Read client's preferred authentication type (protocol 3.7t). + */ + +void +rfbProcessClientAuthType(cl) + rfbClientPtr cl; +{ + CARD32 auth_type; + int n, i; + + /* Read authentication type selected by the client. */ + n = ReadExact(cl->sock, (char *)&auth_type, sizeof(auth_type)); + if (n <= 0) { + if (n == 0) + rfbLog("rfbProcessClientAuthType: client gone\n"); + else + rfbLogPerror("rfbProcessClientAuthType: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + auth_type = Swap32IfLE(auth_type); + + /* Make sure it was present in the list sent by the server. */ + for (i = 0; i < cl->nAuthCaps; i++) { + if (auth_type == cl->authCaps[i]) + break; + } + if (i >= cl->nAuthCaps) { + rfbLog("rfbProcessClientAuthType: " + "wrong authentication type requested\n"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + switch (auth_type) { + case rfbAuthNone: + /* Dispatch client input to rfbProcessClientInitMessage. */ + cl->state = RFB_INITIALISATION; + break; + case rfbAuthVNC: + rfbVncAuthSendChallenge(cl); + break; + case rfbAuthUnixLogin: + /* FIXME: Do (cl->state = RFB_LOGIN_AUTH) instead? */ + rfbLoginAuthProcessClientMessage(cl); + break; + default: + rfbLog("rfbProcessClientAuthType: unknown authentication scheme\n"); + rfbCloseSock(cl->pScreen, cl->sock); + } +} + + +/* + * Send the authentication challenge. + */ + +static void +rfbVncAuthSendChallenge(cl) + rfbClientPtr cl; +{ + vncRandomBytes(cl->authChallenge); + if (WriteExact(cl->sock, (char *)cl->authChallenge, CHALLENGESIZE) < 0) { + rfbLogPerror("rfbVncAuthSendChallenge: write"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + /* Dispatch client input to rfbVncAuthProcessResponse. */ + cl->state = RFB_AUTHENTICATION; +} + +/* + * rfbVncAuthProcessResponse is called when the client sends its + * authentication response. + */ + +void +rfbVncAuthProcessResponse(cl) + rfbClientPtr cl; +{ + VNCSCREENPTR(cl->pScreen); + char passwdFullControl[9]; + char passwdViewOnly[9]; + int numPasswords; + Bool ok; + int n; + CARD8 encryptedChallenge1[CHALLENGESIZE]; + CARD8 encryptedChallenge2[CHALLENGESIZE]; + CARD8 response[CHALLENGESIZE]; + CARD32 authResult; + + n = ReadExact(cl->sock, (char *)response, CHALLENGESIZE); + if (n <= 0) { + if (n != 0) + rfbLogPerror("rfbVncAuthProcessResponse: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + numPasswords = vncDecryptPasswdFromFile2(pVNC->rfbAuthPasswdFile, + passwdFullControl, + passwdViewOnly); + if (numPasswords == 0) { + rfbLog("rfbVncAuthProcessResponse: could not get password from %s\n", + pVNC->rfbAuthPasswdFile); + + authResult = Swap32IfLE(rfbVncAuthFailed); + + if (WriteExact(cl->sock, (char *)&authResult, 4) < 0) { + rfbLogPerror("rfbVncAuthProcessResponse: write"); + } + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + memcpy(encryptedChallenge1, cl->authChallenge, CHALLENGESIZE); + vncEncryptBytes(encryptedChallenge1, passwdFullControl); + memcpy(encryptedChallenge2, cl->authChallenge, CHALLENGESIZE); + vncEncryptBytes(encryptedChallenge2, + (numPasswords == 2) ? passwdViewOnly : passwdFullControl); + + /* Lose the passwords from memory */ + memset(passwdFullControl, 0, 9); + memset(passwdViewOnly, 0, 9); + + ok = FALSE; + if (memcmp(encryptedChallenge1, response, CHALLENGESIZE) == 0) { + rfbLog("Full-control authentication passed by %s\n", cl->host); + ok = TRUE; + cl->viewOnly = FALSE; + } else if (memcmp(encryptedChallenge2, response, CHALLENGESIZE) == 0) { + rfbLog("View-only authentication passed by %s\n", cl->host); + ok = TRUE; + cl->viewOnly = TRUE; + } + + if (!ok) { + rfbLog("rfbVncAuthProcessResponse: authentication failed from %s\n", + cl->host); + + if (rfbAuthConsiderBlocking(cl)) { + authResult = Swap32IfLE(rfbVncAuthTooMany); + } else { + authResult = Swap32IfLE(rfbVncAuthFailed); + } + + if (WriteExact(cl->sock, (char *)&authResult, 4) < 0) { + rfbLogPerror("rfbVncAuthProcessResponse: write"); + } + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + rfbAuthUnblock(cl); + + authResult = Swap32IfLE(rfbVncAuthOK); + + if (WriteExact(cl->sock, (char *)&authResult, 4) < 0) { + rfbLogPerror("rfbVncAuthProcessResponse: write"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + /* Dispatch client input to rfbProcessClientInitMessage(). */ + cl->state = RFB_INITIALISATION; +} + + +/* + * Functions to prevent too many successive authentication failures. + * FIXME: This should be performed separately per each client IP. + */ + +/* Maximum authentication failures before blocking connections */ +#define MAX_AUTH_TRIES 5 + +/* Delay in ms, doubles for each failure over MAX_AUTH_TRIES */ +#define AUTH_TOO_MANY_BASE_DELAY 10 * 1000 + +/* + * This function should not be called directly, it is called by + * setting a timer in rfbAuthConsiderBlocking(). + */ + +static CARD32 +rfbAuthReenable(OsTimerPtr timer, CARD32 now, pointer arg) +{ + rfbClientPtr cl = (rfbClientPtr) arg; + VNCSCREENPTR(cl->pScreen); + (void)cl; + pVNC->rfbAuthTooManyTries = FALSE; + return 0; +} + +/* + * This function should be called after each authentication failure. + * The return value will be true if there was too many failures. + */ + +Bool +rfbAuthConsiderBlocking(rfbClientPtr cl) +{ + VNCSCREENPTR(cl->pScreen); + int i; + + pVNC->rfbAuthTries++; + + if (pVNC->rfbAuthTries >= MAX_AUTH_TRIES) { + CARD32 delay = AUTH_TOO_MANY_BASE_DELAY; + for (i = MAX_AUTH_TRIES; i < pVNC->rfbAuthTries; i++) + delay *= 2; + pVNC->timer = TimerSet(pVNC->timer, 0, delay, rfbAuthReenable, cl); + pVNC->rfbAuthTooManyTries = TRUE; + return TRUE; + } + + return FALSE; +} + +/* + * This function should be called after successful authentication. + * It resets the counter of authentication failures. Note that it's + * not necessary to clear the rfbAuthTooManyTries flag as it will be + * reset by the timer function. + */ + +void +rfbAuthUnblock(rfbClientPtr cl) +{ + VNCSCREENPTR(cl->pScreen); + pVNC->rfbAuthTries = 0; +} + +/* + * This function should be called before authentication process. + * The return value will be true if there was too many authentication + * failures, and the server should not allow another try. + */ + +Bool +rfbAuthIsBlocked(rfbClientPtr cl) +{ + VNCSCREENPTR(cl->pScreen); + return pVNC->rfbAuthTooManyTries; +} + diff --git a/hw/vnc/cmap.c b/hw/vnc/cmap.c new file mode 100644 index 0000000..2e4514a --- /dev/null +++ b/hw/vnc/cmap.c @@ -0,0 +1,166 @@ +/* + * cmap.c + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ + +/* + +Copyright (c) 1993 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the X Consortium. + +*/ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include "rfb.h" + +int +rfbListInstalledColormaps(pScreen, pmaps) + ScreenPtr pScreen; + Colormap *pmaps; +{ + VNCSCREENPTR(pScreen); + /* By the time we are processing requests, we can guarantee that there + * is always a colormap installed */ + if (pVNC->rfbInstalledColormap) + *pmaps = pVNC->rfbInstalledColormap->mid; + +#if XFREE86VNC + pScreen->ListInstalledColormaps = pVNC->ListInstalledColormaps; + (*pScreen->ListInstalledColormaps)(pScreen, pmaps); + pScreen->ListInstalledColormaps = rfbListInstalledColormaps; +#endif + + return (1); +} + + +void +rfbInstallColormap(pmap) + ColormapPtr pmap; +{ + VNCSCREENPTR(pmap->pScreen); + + if (pmap != pVNC->rfbInstalledColormap) { + + if(pVNC->rfbInstalledColormap != (ColormapPtr)None) + WalkTree(pmap->pScreen, TellLostMap, + (char *)&pVNC->rfbInstalledColormap->mid); + /* Install pmap */ + pVNC->rfbInstalledColormap = pmap; + WalkTree(pmap->pScreen, TellGainedMap, (char *)&pmap->mid); + + rfbSetClientColourMaps(0, 0); + } +#if XFREE86VNC + pmap->pScreen->InstallColormap = pVNC->InstallColormap; + (*pmap->pScreen->InstallColormap)(pmap); + pmap->pScreen->InstallColormap = rfbInstallColormap; +#endif +} + +void +rfbUninstallColormap(pmap) + ColormapPtr pmap; +{ + VNCSCREENPTR(pmap->pScreen); + + if(pmap == pVNC->rfbInstalledColormap) + { + if (pmap->mid != pmap->pScreen->defColormap) + { + pVNC->rfbInstalledColormap = + (ColormapPtr) LookupIDByType(pmap->pScreen->defColormap, + RT_COLORMAP); + (*pmap->pScreen->InstallColormap)(pVNC->rfbInstalledColormap); + } + } +#if XFREE86VNC + pmap->pScreen->UninstallColormap = pVNC->UninstallColormap; + (*pmap->pScreen->UninstallColormap)(pmap); + pmap->pScreen->UninstallColormap = rfbUninstallColormap; +#endif +} + + +/* + * rfbStoreColors. We have a set of pixels but they may be in any order. + * If some of them happen to be in continuous ascending order then we can + * group them together into a single call to rfbSetClientColourMaps. + */ + +void +rfbStoreColors(pmap, ndef, pdefs) + ColormapPtr pmap; + int ndef; + xColorItem *pdefs; +{ + VNCSCREENPTR(pmap->pScreen); + int i; + int first = -1; + int n = 0; + + if (pmap == pVNC->rfbInstalledColormap) { + for (i = 0; i < ndef; i++) { + if ((first != -1) && (first + n == pdefs[i].pixel)) { + n++; + } else { + if (first != -1) { + rfbSetClientColourMaps(first, n); + } + first = pdefs[i].pixel; + n = 1; + } + } + rfbSetClientColourMaps(first, n); + } +#if XFREE86VNC + pmap->pScreen->StoreColors = pVNC->StoreColors; + (*pmap->pScreen->StoreColors)(pmap, ndef, pdefs); + pmap->pScreen->StoreColors = rfbStoreColors; +#endif +} diff --git a/hw/vnc/corre.c b/hw/vnc/corre.c new file mode 100644 index 0000000..1a9b4d5 --- /dev/null +++ b/hw/vnc/corre.c @@ -0,0 +1,353 @@ +/* + * corre.c + * + * Routines to implement Compact Rise-and-Run-length Encoding (CoRRE). This + * code is based on krw's original javatel rfbserver. + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include +#include "rfb.h" + +/* + * rreBeforeBuf contains pixel data in the client's format. + * rreAfterBuf contains the RRE encoded version. If the RRE encoded version is + * larger than the raw data or if it exceeds rreAfterBufSize then + * raw encoding is used instead. + */ + +static int rreBeforeBufSize = 0; +static unsigned char *rreBeforeBuf = NULL; + +static int rreAfterBufSize = 0; +static unsigned char *rreAfterBuf = NULL; +static int rreAfterBufLen; + +static int subrectEncode8(CARD8 *data, int w, int h); +static int subrectEncode16(CARD16 *data, int w, int h); +static int subrectEncode32(CARD32 *data, int w, int h); +static CARD32 getBgColour(char *data, int size, int bpp); +static Bool rfbSendSmallRectEncodingCoRRE(rfbClientPtr cl, int x, int y, + int w, int h); + + +/* + * rfbSendRectEncodingCoRRE - send an arbitrary size rectangle using CoRRE + * encoding. + */ + +Bool +rfbSendRectEncodingCoRRE(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + if (h > cl->correMaxHeight) { + return (rfbSendRectEncodingCoRRE(cl, x, y, w, cl->correMaxHeight) && + rfbSendRectEncodingCoRRE(cl, x, y + cl->correMaxHeight, + w, h - cl->correMaxHeight)); + } + + if (w > cl->correMaxWidth) { + return (rfbSendRectEncodingCoRRE(cl, x, y, cl->correMaxWidth, h) && + rfbSendRectEncodingCoRRE(cl, x + cl->correMaxWidth, y, + w - cl->correMaxWidth, h)); + } + + return rfbSendSmallRectEncodingCoRRE(cl, x, y, w, h); +} + + + +/* + * rfbSendSmallRectEncodingCoRRE - send a small (guaranteed < 256x256) + * rectangle using CoRRE encoding. + */ + +static Bool +rfbSendSmallRectEncodingCoRRE(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + VNCSCREENPTR(cl->pScreen); + rfbFramebufferUpdateRectHeader rect; + rfbRREHeader hdr; + int nSubrects; + int i; + int maxRawSize = (pVNC->width * pVNC->height + * (cl->format.bitsPerPixel / 8)); + + if (rreBeforeBufSize < maxRawSize) { + rreBeforeBufSize = maxRawSize; + if (rreBeforeBuf == NULL) + rreBeforeBuf = (unsigned char *)xalloc(rreBeforeBufSize); + else + rreBeforeBuf = (unsigned char *)xrealloc(rreBeforeBuf, rreBeforeBufSize); + } + + if (rreAfterBufSize < maxRawSize) { + rreAfterBufSize = maxRawSize; + if (rreAfterBuf == NULL) + rreAfterBuf = (unsigned char *)xalloc(rreAfterBufSize); + else + rreAfterBuf = (unsigned char *)xrealloc(rreAfterBuf, rreAfterBufSize); + } + + (*cl->translateFn)(cl->pScreen, cl->translateLookupTable, + &pVNC->rfbServerFormat, + &cl->format, rreBeforeBuf, + pVNC->paddedWidthInBytes, w, h, x, y); + + switch (cl->format.bitsPerPixel) { + case 8: + nSubrects = subrectEncode8((CARD8 *)rreBeforeBuf, w, h); + break; + case 16: + nSubrects = subrectEncode16((CARD16 *)rreBeforeBuf, w, h); + break; + case 32: + nSubrects = subrectEncode32((CARD32 *)rreBeforeBuf, w, h); + break; + default: + rfbLog("getBgColour: bpp %d?\n",cl->format.bitsPerPixel); + exit(1); + } + + if (nSubrects < 0) { + + /* RRE encoding was too large, use raw */ + + return rfbSendRectEncodingRaw(cl, x, y, w, h); + } + + cl->rfbRectanglesSent[rfbEncodingCoRRE]++; + cl->rfbBytesSent[rfbEncodingCoRRE] += (sz_rfbFramebufferUpdateRectHeader + + sz_rfbRREHeader + rreAfterBufLen); + + if (pVNC->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + > UPDATE_BUF_SIZE) + { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingCoRRE); + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + pVNC->ublen += sz_rfbFramebufferUpdateRectHeader; + + hdr.nSubrects = Swap32IfLE(nSubrects); + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&hdr, sz_rfbRREHeader); + pVNC->ublen += sz_rfbRREHeader; + + for (i = 0; i < rreAfterBufLen;) { + + int bytesToCopy = UPDATE_BUF_SIZE - pVNC->ublen; + + if (i + bytesToCopy > rreAfterBufLen) { + bytesToCopy = rreAfterBufLen - i; + } + + memcpy(&pVNC->updateBuf[pVNC->ublen], &rreAfterBuf[i], bytesToCopy); + + pVNC->ublen += bytesToCopy; + i += bytesToCopy; + + if (pVNC->ublen == UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + } + + return TRUE; +} + + + +/* + * subrectEncode() encodes the given multicoloured rectangle as a background + * colour overwritten by single-coloured rectangles. It returns the number + * of subrectangles in the encoded buffer, or -1 if subrect encoding won't + * fit in the buffer. It puts the encoded rectangles in rreAfterBuf. The + * single-colour rectangle partition is not optimal, but does find the biggest + * horizontal or vertical rectangle top-left anchored to each consecutive + * coordinate position. + * + * The coding scheme is simply [...] where each + * is []. + */ + +#define DEFINE_SUBRECT_ENCODE(bpp) \ +static int \ +subrectEncode##bpp(data,w,h) \ + CARD##bpp *data; \ + int w; \ + int h; \ +{ \ + CARD##bpp cl; \ + rfbCoRRERectangle subrect; \ + int x,y; \ + int i,j; \ + int hx=0,hy,vx=0,vy; \ + int hyflag; \ + CARD##bpp *seg; \ + CARD##bpp *line; \ + int hw,hh,vw,vh; \ + int thex,they,thew,theh; \ + int numsubs = 0; \ + int newLen; \ + CARD##bpp bg = (CARD##bpp)getBgColour((char*)data,w*h,bpp); \ + \ + *((CARD##bpp*)rreAfterBuf) = bg; \ + \ + rreAfterBufLen = (bpp/8); \ + \ + for (y=0; y 0) && (i >= hx)) {hy += 1;} else {hyflag = 0;} \ + } \ + vy = j-1; \ + \ + /* We now have two possible subrects: (x,y,hx,hy) and (x,y,vx,vy) \ + * We'll choose the bigger of the two. \ + */ \ + hw = hx-x+1; \ + hh = hy-y+1; \ + vw = vx-x+1; \ + vh = vy-y+1; \ + \ + thex = x; \ + they = y; \ + \ + if ((hw*hh) > (vw*vh)) { \ + thew = hw; \ + theh = hh; \ + } else { \ + thew = vw; \ + theh = vh; \ + } \ + \ + subrect.x = thex; \ + subrect.y = they; \ + subrect.w = thew; \ + subrect.h = theh; \ + \ + newLen = rreAfterBufLen + (bpp/8) + sz_rfbCoRRERectangle; \ + if ((newLen > (w * h * (bpp/8))) || (newLen > rreAfterBufSize)) \ + return -1; \ + \ + numsubs += 1; \ + *((CARD##bpp*)(rreAfterBuf + rreAfterBufLen)) = cl; \ + rreAfterBufLen += (bpp/8); \ + memcpy(&rreAfterBuf[rreAfterBufLen],&subrect,sz_rfbCoRRERectangle); \ + rreAfterBufLen += sz_rfbCoRRERectangle; \ + \ + /* \ + * Now mark the subrect as done. \ + */ \ + for (j=they; j < (they+theh); j++) { \ + for (i=thex; i < (thex+thew); i++) { \ + data[j*w+i] = bg; \ + } \ + } \ + } \ + } \ + } \ + \ + return numsubs; \ +} + +DEFINE_SUBRECT_ENCODE(8) +DEFINE_SUBRECT_ENCODE(16) +DEFINE_SUBRECT_ENCODE(32) + + +/* + * getBgColour() gets the most prevalent colour in a byte array. + */ +static CARD32 +getBgColour(data,size,bpp) + char *data; + int size; + int bpp; +{ + +#define NUMCLRS 256 + + static int counts[NUMCLRS]; + int i,j,k; + + int maxcount = 0; + CARD8 maxclr = 0; + + if (bpp != 8) { + if (bpp == 16) { + return ((CARD16 *)data)[0]; + } else if (bpp == 32) { + return ((CARD32 *)data)[0]; + } else { + rfbLog("getBgColour: bpp %d?\n",bpp); + exit(1); + } + } + + for (i=0; i= NUMCLRS) { + rfbLog("getBgColour: unusual colour = %d\n", k); + exit(1); + } + counts[k] += 1; + if (counts[k] > maxcount) { + maxcount = counts[k]; + maxclr = ((CARD8 *)data)[j]; + } + } + + return maxclr; +} diff --git a/hw/vnc/cursor.c b/hw/vnc/cursor.c new file mode 100644 index 0000000..d355e03 --- /dev/null +++ b/hw/vnc/cursor.c @@ -0,0 +1,407 @@ +/* + * cursor.c - support for cursor shape updates. + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * Copyright (C) 2000, 2001 Const Kaplinsky. 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include +#include "rfb.h" +#include "mipointer.h" +#include "sprite.h" +#include "cursorstr.h" +#include "servermd.h" + + +/* Copied from Xvnc/lib/font/util/utilbitmap.c */ +static unsigned char _reverse_byte[0x100] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + + +static int EncodeRichCursorData8 (unsigned char *buf, rfbPixelFormat *fmt, + CursorPtr pCursor); +static int EncodeRichCursorData16 (ScreenPtr pScreen, + unsigned char *buf, rfbPixelFormat *fmt, + CursorPtr pCursor); +static int EncodeRichCursorData32 (ScreenPtr pScreen, + unsigned char *buf, rfbPixelFormat *fmt, + CursorPtr pCursor); + + +/* + * Send cursor shape either in X-style format or in client pixel format. + */ + +Bool +rfbSendCursorShape(cl, pScreen) + rfbClientPtr cl; + ScreenPtr pScreen; +{ + VNCSCREENPTR(pScreen); + CursorPtr pCursor; + rfbFramebufferUpdateRectHeader rect; + rfbXCursorColors colors; + int saved_ublen; + int bitmapRowBytes, paddedRowBytes, maskBytes, dataBytes; + int i, j; + CARD8 *bitmapData; + CARD8 bitmapByte; + + if (cl->useRichCursorEncoding) { + rect.encoding = Swap32IfLE(rfbEncodingRichCursor); + } else { + rect.encoding = Swap32IfLE(rfbEncodingXCursor); + } + +#if XFREE86VNC + pCursor = pVNC->pCurs; +#else + pCursor = rfbSpriteGetCursorPtr(pScreen); +#endif + + /* If there is no cursor, send update with empty cursor data. */ + + if ( pCursor != NULL && + pCursor->bits->width == 1 && + pCursor->bits->height == 1 && + pCursor->bits->mask[0] == 0 ) { + pCursor = NULL; + } + + if (pCursor == NULL) { + if (pVNC->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE ) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + rect.r.x = rect.r.y = 0; + rect.r.w = rect.r.h = 0; + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + pVNC->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbCursorShapeBytesSent += sz_rfbFramebufferUpdateRectHeader; + cl->rfbCursorShapeUpdatesSent++; + + return TRUE; + } + + /* Calculate data sizes. */ + + bitmapRowBytes = (pCursor->bits->width + 7) / 8; + paddedRowBytes = PixmapBytePad(pCursor->bits->width, 1); + maskBytes = bitmapRowBytes * pCursor->bits->height; + dataBytes = (cl->useRichCursorEncoding) ? + (pCursor->bits->width * pCursor->bits->height * + (cl->format.bitsPerPixel / 8)) : maskBytes; + + /* Send buffer contents if needed. */ + + if ( pVNC->ublen + sz_rfbFramebufferUpdateRectHeader + + sz_rfbXCursorColors + maskBytes + dataBytes > UPDATE_BUF_SIZE ) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + if ( pVNC->ublen + sz_rfbFramebufferUpdateRectHeader + + sz_rfbXCursorColors + maskBytes + dataBytes > UPDATE_BUF_SIZE ) { + return FALSE; /* FIXME. */ + } + + saved_ublen = pVNC->ublen; + + /* Prepare rectangle header. */ + + rect.r.x = Swap16IfLE(pCursor->bits->xhot); + rect.r.y = Swap16IfLE(pCursor->bits->yhot); + rect.r.w = Swap16IfLE(pCursor->bits->width); + rect.r.h = Swap16IfLE(pCursor->bits->height); + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); + pVNC->ublen += sz_rfbFramebufferUpdateRectHeader; + + /* Prepare actual cursor data (depends on encoding used). */ + + if (!cl->useRichCursorEncoding) { + /* XCursor encoding. */ + colors.foreRed = (char)(pCursor->foreRed >> 8); + colors.foreGreen = (char)(pCursor->foreGreen >> 8); + colors.foreBlue = (char)(pCursor->foreBlue >> 8); + colors.backRed = (char)(pCursor->backRed >> 8); + colors.backGreen = (char)(pCursor->backGreen >> 8); + colors.backBlue = (char)(pCursor->backBlue >> 8); + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&colors, sz_rfbXCursorColors); + pVNC->ublen += sz_rfbXCursorColors; + + bitmapData = (CARD8 *)pCursor->bits->source; + + for (i = 0; i < pCursor->bits->height; i++) { + for (j = 0; j < bitmapRowBytes; j++) { + bitmapByte = bitmapData[i * paddedRowBytes + j]; + if (screenInfo.bitmapBitOrder == LSBFirst) { + bitmapByte = _reverse_byte[bitmapByte]; + } + pVNC->updateBuf[pVNC->ublen++] = (char)bitmapByte; + } + } + } else { + /* RichCursor encoding. */ + switch (cl->format.bitsPerPixel) { + case 8: + pVNC->ublen += EncodeRichCursorData8(&pVNC->updateBuf[pVNC->ublen], + &cl->format, pCursor); + break; + case 16: + pVNC->ublen += EncodeRichCursorData16(pScreen, &pVNC->updateBuf[pVNC->ublen], + &cl->format, pCursor); + break; + case 32: + pVNC->ublen += EncodeRichCursorData32(pScreen, &pVNC->updateBuf[pVNC->ublen], + &cl->format, pCursor); + break; + default: + return FALSE; + } + } + + /* Prepare transparency mask. */ + + bitmapData = (CARD8 *)pCursor->bits->mask; + + for (i = 0; i < pCursor->bits->height; i++) { + for (j = 0; j < bitmapRowBytes; j++) { + bitmapByte = bitmapData[i * paddedRowBytes + j]; + if (screenInfo.bitmapBitOrder == LSBFirst) { + bitmapByte = _reverse_byte[bitmapByte]; + } + pVNC->updateBuf[pVNC->ublen++] = (char)bitmapByte; + } + } + + /* Update statistics. */ + + cl->rfbCursorShapeBytesSent += (pVNC->ublen - saved_ublen); + cl->rfbCursorShapeUpdatesSent++; + + return TRUE; +} + +/* + * Send cursor position (PointerPos pseudo-encoding). + */ +Bool +rfbSendCursorPos(cl, pScreen) + rfbClientPtr cl; + ScreenPtr pScreen; +{ + VNCSCREENPTR(pScreen); + rfbFramebufferUpdateRectHeader rect; +#if XFREE86VNC + ScreenPtr pCursorScreen = miPointerCurrentScreen(); /*XXX deprecated*/ +#endif + int x, y; + + if (pVNC->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + +#if XFREE86VNC + if (pScreen == pCursorScreen) + miPointerPosition(&x, &y); +#else + rfbSpriteGetCursorPos(pScreen, &x, &y); +#endif + + rect.encoding = Swap32IfLE(rfbEncodingPointerPos); + rect.r.x = Swap16IfLE((CARD16)x); + rect.r.y = Swap16IfLE((CARD16)y); + rect.r.w = 0; + rect.r.h = 0; + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + pVNC->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbCursorPosBytesSent += sz_rfbFramebufferUpdateRectHeader; + cl->rfbCursorPosUpdatesSent++; + + if (!rfbSendUpdateBuf(cl)) + return FALSE; + + cl->cursorX = x; + cl->cursorY = y; + + return TRUE; +} + +/* + * Code to convert cursor source bitmap to the desired pixel format. + */ + +#define RGB48_TO_PIXEL(fmt,r,g,b) \ + (((CARD32)(r) * ((fmt)->redMax + 1) >> 16) << (fmt)->redShift | \ + ((CARD32)(g) * ((fmt)->greenMax + 1) >> 16) << (fmt)->greenShift | \ + ((CARD32)(b) * ((fmt)->blueMax + 1) >> 16) << (fmt)->blueShift) + +static int +EncodeRichCursorData8(buf, fmt, pCursor) + unsigned char *buf; + rfbPixelFormat *fmt; + CursorPtr pCursor; +{ + int widthPixels, widthBytes; + int x, y, b; + CARD8 *src; + char pix[2]; + CARD8 bitmapByte; + + pix[0] = (char)RGB48_TO_PIXEL(fmt, pCursor->backRed, pCursor->backGreen, + pCursor->backBlue); + pix[1] = (char)RGB48_TO_PIXEL(fmt, pCursor->foreRed, pCursor->foreGreen, + pCursor->foreBlue); + + src = (CARD8 *)pCursor->bits->source; + widthPixels = pCursor->bits->width; + widthBytes = PixmapBytePad(widthPixels, 1); + + for (y = 0; y < pCursor->bits->height; y++) { + for (x = 0; x < widthPixels / 8; x++) { + bitmapByte = src[y * widthBytes + x]; + if (screenInfo.bitmapBitOrder == LSBFirst) { + bitmapByte = _reverse_byte[bitmapByte]; + } + for (b = 7; b >= 0; b--) { + *buf++ = pix[bitmapByte >> b & 1]; + } + } + if (widthPixels % 8) { + bitmapByte = src[y * widthBytes + x]; + if (screenInfo.bitmapBitOrder == LSBFirst) { + bitmapByte = _reverse_byte[bitmapByte]; + } + for (b = 7; b > 7 - widthPixels % 8; b--) { + *buf++ = pix[bitmapByte >> b & 1]; + } + } + } + + return (widthPixels * pCursor->bits->height); +} + +#define DEFINE_RICH_ENCODE(bpp) \ + \ +static int \ +EncodeRichCursorData##bpp(pScreen, buf, fmt, pCursor) \ + ScreenPtr pScreen; \ + unsigned char *buf; \ + rfbPixelFormat *fmt; \ + CursorPtr pCursor; \ +{ \ + VNCSCREENPTR(pScreen); \ + int widthPixels, widthBytes; \ + int x, y, b; \ + CARD8 *src; \ + CARD##bpp pix[2]; \ + CARD8 bitmapByte; \ + \ + pix[0] = (CARD##bpp)RGB48_TO_PIXEL(fmt, pCursor->backRed, \ + pCursor->backGreen, \ + pCursor->backBlue); \ + pix[1] = (CARD##bpp)RGB48_TO_PIXEL(fmt, pCursor->foreRed, \ + pCursor->foreGreen, \ + pCursor->foreBlue); \ + if (!pVNC->rfbServerFormat.bigEndian != !fmt->bigEndian) { \ + pix[0] = Swap##bpp(pix[0]); \ + pix[1] = Swap##bpp(pix[1]); \ + } \ + \ + src = (CARD8 *)pCursor->bits->source; \ + widthPixels = pCursor->bits->width; \ + widthBytes = PixmapBytePad(widthPixels, 1); \ + \ + for (y = 0; y < pCursor->bits->height; y++) { \ + for (x = 0; x < widthPixels / 8; x++) { \ + bitmapByte = src[y * widthBytes + x]; \ + if (screenInfo.bitmapBitOrder == LSBFirst) { \ + bitmapByte = _reverse_byte[bitmapByte]; \ + } \ + for (b = 7; b >= 0; b--) { \ + memcpy (buf, (char *)&pix[bitmapByte >> b & 1], \ + sizeof(CARD##bpp)); \ + buf += sizeof(CARD##bpp); \ + } \ + } \ + if (widthPixels % 8) { \ + bitmapByte = src[y * widthBytes + x]; \ + if (screenInfo.bitmapBitOrder == LSBFirst) { \ + bitmapByte = _reverse_byte[bitmapByte]; \ + } \ + for (b = 7; b > 7 - widthPixels % 8; b--) { \ + memcpy (buf, (char *)&pix[bitmapByte >> b & 1], \ + sizeof(CARD##bpp)); \ + buf += sizeof(CARD##bpp); \ + } \ + } \ + } \ + \ + return (widthPixels * pCursor->bits->height * (bpp / 8)); \ +} + +DEFINE_RICH_ENCODE(16) +DEFINE_RICH_ENCODE(32) + diff --git a/hw/vnc/cutpaste.c b/hw/vnc/cutpaste.c new file mode 100644 index 0000000..6f57aca --- /dev/null +++ b/hw/vnc/cutpaste.c @@ -0,0 +1,87 @@ +/* + * cutpaste.c - routines to deal with cut & paste buffers / selection. + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include +#define NEED_EVENTS +#include +#include +#include "rfb.h" +#include "selection.h" +#include "input.h" +#include + +extern Selection *CurrentSelections; +extern int NumCurrentSelections; + + +static Bool inSetXCutText = FALSE; + +/* + * rfbSetXCutText sets the cut buffer to be the given string. We also clear + * the primary selection. Ideally we'd like to set it to the same thing, but I + * can't work out how to do that without some kind of helper X client. + */ + +void +rfbSetXCutText(char *str, int len) +{ + int i = 0; + Selection *pSel; + + inSetXCutText = TRUE; + ChangeWindowProperty(WindowTable[0], XA_CUT_BUFFER0, XA_STRING, + 8, PropModeReplace, len, + (pointer)str, TRUE); + + if (dixLookupSelection(&pSel, XA_PRIMARY, serverClient, DixSetAttrAccess)) { + xEvent event; + + if (pSel->client) { + event.u.u.type = SelectionClear; + event.u.selectionClear.time = GetTimeInMillis(); + event.u.selectionClear.window = pSel->window; + event.u.selectionClear.atom = pSel->selection; + (void) TryClientEvents (pSel->client, &event, 1, + NoEventMask, NoEventMask /* CantBeFiltered */, + NullGrab); + } + + pSel->window = None; + pSel->pWin = NULL; + pSel->client = NullClient; + } + + inSetXCutText = FALSE; +} + + +void rfbGotXCutText(char *str, int len) +{ + if (!inSetXCutText) + rfbSendServerCutText(str, len); +} diff --git a/hw/vnc/d3des.c b/hw/vnc/d3des.c new file mode 100644 index 0000000..a4e145f --- /dev/null +++ b/hw/vnc/d3des.c @@ -0,0 +1,437 @@ +/* + * 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#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 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/hw/vnc/d3des.h b/hw/vnc/d3des.h new file mode 100644 index 0000000..ea3da44 --- /dev/null +++ b/hw/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/hw/vnc/dispcur.c b/hw/vnc/dispcur.c new file mode 100644 index 0000000..0ccf0a7 --- /dev/null +++ b/hw/vnc/dispcur.c @@ -0,0 +1,792 @@ +/* + * dispcur.c + * + * cursor display routines - based on midispcur.c + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ + +/* + +Copyright (c) 1989 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. +*/ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#define NEED_EVENTS +# include +# include "rfb.h" +# include "misc.h" +# include "input.h" +# include "cursorstr.h" +# include "windowstr.h" +# include "regionstr.h" +# include "dixstruct.h" +# include "scrnintstr.h" +# include "servermd.h" +# include "sprite.h" +# include "gcstruct.h" + +#ifdef ARGB_CURSOR +# include "picturestr.h" +#endif + +/* per-screen private data */ + +static DevPrivateKey rfbDCScreenKey = &rfbDCScreenKey; + +static Bool rfbDCCloseScreen(int index, ScreenPtr pScreen); + +typedef struct { + GCPtr pSourceGC, pMaskGC; + GCPtr pSaveGC, pRestoreGC; + GCPtr pMoveGC; + GCPtr pPixSourceGC, pPixMaskGC; + CloseScreenProcPtr CloseScreen; + PixmapPtr pSave, pTemp; +#ifdef ARGB_CURSOR + PicturePtr pRootPicture; + PicturePtr pTempPicture; +#endif +} rfbDCScreenRec, *rfbDCScreenPtr; + +/* per-cursor per-screen private data */ +typedef struct { + PixmapPtr sourceBits; /* source bits */ + PixmapPtr maskBits; /* mask bits */ +#ifdef ARGB_CURSOR + PicturePtr pPicture; +#endif +} rfbDCCursorRec, *rfbDCCursorPtr; + +/* + * sprite/cursor method table + */ + +static Bool rfbDCRealizeCursor(ScreenPtr pScreen, CursorPtr pCursor); +static Bool rfbDCUnrealizeCursor(ScreenPtr pScreen, CursorPtr pCursor); +static Bool rfbDCPutUpCursor(ScreenPtr pScreen, CursorPtr pCursor, + int x, int y, unsigned long source, + unsigned long mask); +static Bool rfbDCSaveUnderCursor(ScreenPtr pScreen, int x, int y, + int w, int h); +static Bool rfbDCRestoreUnderCursor(ScreenPtr pScreen, int x, int y, + int w, int h); +static Bool rfbDCMoveCursor(ScreenPtr pScreen, CursorPtr pCursor, + int x, int y, int w, int h, int dx, int dy, + unsigned long source, unsigned long mask); +static Bool rfbDCChangeSave(ScreenPtr pScreen, int x, int y, int w, int h, + int dx, int dy); + +static rfbSpriteCursorFuncRec rfbDCFuncs = { + rfbDCRealizeCursor, + rfbDCUnrealizeCursor, + rfbDCPutUpCursor, + rfbDCSaveUnderCursor, + rfbDCRestoreUnderCursor, + rfbDCMoveCursor, + rfbDCChangeSave, +}; + +Bool +rfbDCInitialize (ScreenPtr pScreen, miPointerScreenFuncPtr screenFuncs) +{ + rfbDCScreenPtr pScreenPriv; + + pScreenPriv = (rfbDCScreenPtr) xalloc (sizeof (rfbDCScreenRec)); + if (!pScreenPriv) + return FALSE; + + /* + * initialize the entire private structure to zeros + */ + + pScreenPriv->pSourceGC = + pScreenPriv->pMaskGC = + pScreenPriv->pSaveGC = + pScreenPriv->pRestoreGC = + pScreenPriv->pMoveGC = + pScreenPriv->pPixSourceGC = + pScreenPriv->pPixMaskGC = NULL; +#ifdef ARGB_CURSOR + pScreenPriv->pRootPicture = NULL; + pScreenPriv->pTempPicture = NULL; +#endif + + pScreenPriv->pSave = pScreenPriv->pTemp = NULL; + + pScreenPriv->CloseScreen = pScreen->CloseScreen; + pScreen->CloseScreen = rfbDCCloseScreen; + + dixSetPrivate(&pScreen->devPrivates, rfbDCScreenKey, pScreenPriv); + + if (!rfbSpriteInitialize (pScreen, &rfbDCFuncs, screenFuncs)) + { + xfree ((pointer) pScreenPriv); + return FALSE; + } + return TRUE; +} + +#define tossGC(gc) (gc ? FreeGC (gc, (GContext) 0) : 0) +#define tossPix(pix) (pix ? (*pScreen->DestroyPixmap) (pix) : TRUE) +#define tossPict(pict) (pict ? FreePicture (pict, 0) : 0) + +static Bool +rfbDCCloseScreen (index, pScreen) + ScreenPtr pScreen; +{ + rfbDCScreenPtr pScreenPriv; + + pScreenPriv = (rfbDCScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbDCScreenKey); + pScreen->CloseScreen = pScreenPriv->CloseScreen; + tossGC (pScreenPriv->pSourceGC); + tossGC (pScreenPriv->pMaskGC); + tossGC (pScreenPriv->pSaveGC); + tossGC (pScreenPriv->pRestoreGC); + tossGC (pScreenPriv->pMoveGC); + tossGC (pScreenPriv->pPixSourceGC); + tossGC (pScreenPriv->pPixMaskGC); + tossPix (pScreenPriv->pSave); + tossPix (pScreenPriv->pTemp); +#ifdef ARGB_CURSOR + tossPict (pScreenPriv->pRootPicture); + tossPict (pScreenPriv->pTempPicture); +#endif + xfree ((pointer) pScreenPriv); + return (*pScreen->CloseScreen) (index, pScreen); +} + +static Bool +rfbDCRealizeCursor (pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + if (pCursor->bits->refcnt <= 1) + dixSetPrivate(&pCursor->bits->devPrivates, pScreen, NULL); + return TRUE; +} + +#ifdef ARGB_CURSOR +#define EnsurePicture(picture,draw,win) (picture || rfbDCMakePicture(&picture,draw,win)) + +static VisualPtr +rfbDCGetWindowVisual (WindowPtr pWin) +{ + ScreenPtr pScreen = pWin->drawable.pScreen; + VisualID vid = wVisual (pWin); + int i; + + for (i = 0; i < pScreen->numVisuals; i++) + if (pScreen->visuals[i].vid == vid) + return &pScreen->visuals[i]; + return 0; +} + +static PicturePtr +rfbDCMakePicture (PicturePtr *ppPicture, DrawablePtr pDraw, WindowPtr pWin) +{ + ScreenPtr pScreen = pDraw->pScreen; + VisualPtr pVisual; + PictFormatPtr pFormat; + XID subwindow_mode = IncludeInferiors; + PicturePtr pPicture; + int error; + + pVisual = rfbDCGetWindowVisual (pWin); + if (!pVisual) + return 0; + pFormat = PictureMatchVisual (pScreen, pDraw->depth, pVisual); + if (!pFormat) + return 0; + pPicture = CreatePicture (0, pDraw, pFormat, + CPSubwindowMode, &subwindow_mode, + serverClient, &error); + *ppPicture = pPicture; + return pPicture; +} +#endif + +static rfbDCCursorPtr +rfbDCRealize (ScreenPtr pScreen, CursorPtr pCursor) +{ + rfbDCCursorPtr pPriv; + GCPtr pGC; + XID gcvals[3]; + + pPriv = (rfbDCCursorPtr) xalloc (sizeof (rfbDCCursorRec)); + if (!pPriv) + return (rfbDCCursorPtr)NULL; +#ifdef ARGB_CURSOR + if (pCursor->bits->argb) + { + PixmapPtr pPixmap; + PictFormatPtr pFormat; + int error; + + pFormat = PictureMatchFormat (pScreen, 32, PICT_a8r8g8b8); + if (!pFormat) + { + xfree ((pointer) pPriv); + return (rfbDCCursorPtr)NULL; + } + + pPriv->sourceBits = 0; + pPriv->maskBits = 0; + pPixmap = (*pScreen->CreatePixmap) (pScreen, pCursor->bits->width, + pCursor->bits->height, 32, 0); + if (!pPixmap) + { + xfree ((pointer) pPriv); + return (rfbDCCursorPtr)NULL; + } + pGC = GetScratchGC (32, pScreen); + if (!pGC) + { + (*pScreen->DestroyPixmap) (pPixmap); + xfree ((pointer) pPriv); + return (rfbDCCursorPtr)NULL; + } + ValidateGC (&pPixmap->drawable, pGC); + (*pGC->ops->PutImage) (&pPixmap->drawable, pGC, 32, + 0, 0, pCursor->bits->width, + pCursor->bits->height, + 0, ZPixmap, (char *) pCursor->bits->argb); + FreeScratchGC (pGC); + pPriv->pPicture = CreatePicture (0, &pPixmap->drawable, + pFormat, 0, 0, serverClient, &error); + (*pScreen->DestroyPixmap) (pPixmap); + if (!pPriv->pPicture) + { + xfree ((pointer) pPriv); + return (rfbDCCursorPtr)NULL; + } + dixSetPrivate(&pCursor->bits->devPrivates, pScreen, pPriv); + return pPriv; + } + pPriv->pPicture = 0; +#endif + pPriv->sourceBits = (*pScreen->CreatePixmap) (pScreen, pCursor->bits->width, pCursor->bits->height, 1, 0); + if (!pPriv->sourceBits) + { + xfree ((pointer) pPriv); + return (rfbDCCursorPtr)NULL; + } + pPriv->maskBits = (*pScreen->CreatePixmap) (pScreen, pCursor->bits->width, pCursor->bits->height, 1, 0); + if (!pPriv->maskBits) + { + (*pScreen->DestroyPixmap) (pPriv->sourceBits); + xfree ((pointer) pPriv); + return (rfbDCCursorPtr)NULL; + } + dixSetPrivate(&pCursor->bits->devPrivates, pScreen, pPriv); + + /* create the two sets of bits, clipping as appropriate */ + + pGC = GetScratchGC (1, pScreen); + if (!pGC) + { + (void) rfbDCUnrealizeCursor (pScreen, pCursor); + return (rfbDCCursorPtr)NULL; + } + + ValidateGC ((DrawablePtr)pPriv->sourceBits, pGC); + (*pGC->ops->PutImage) ((DrawablePtr)pPriv->sourceBits, pGC, 1, + 0, 0, pCursor->bits->width, pCursor->bits->height, + 0, XYPixmap, (char *)pCursor->bits->source); + gcvals[0] = GXand; + ChangeGC (pGC, GCFunction, gcvals); + ValidateGC ((DrawablePtr)pPriv->sourceBits, pGC); + (*pGC->ops->PutImage) ((DrawablePtr)pPriv->sourceBits, pGC, 1, + 0, 0, pCursor->bits->width, pCursor->bits->height, + 0, XYPixmap, (char *)pCursor->bits->mask); + + /* mask bits -- pCursor->mask & ~pCursor->source */ + gcvals[0] = GXcopy; + ChangeGC (pGC, GCFunction, gcvals); + ValidateGC ((DrawablePtr)pPriv->maskBits, pGC); + (*pGC->ops->PutImage) ((DrawablePtr)pPriv->maskBits, pGC, 1, + 0, 0, pCursor->bits->width, pCursor->bits->height, + 0, XYPixmap, (char *)pCursor->bits->mask); + gcvals[0] = GXandInverted; + ChangeGC (pGC, GCFunction, gcvals); + ValidateGC ((DrawablePtr)pPriv->maskBits, pGC); + (*pGC->ops->PutImage) ((DrawablePtr)pPriv->maskBits, pGC, 1, + 0, 0, pCursor->bits->width, pCursor->bits->height, + 0, XYPixmap, (char *)pCursor->bits->source); + FreeScratchGC (pGC); + return pPriv; +} + +static Bool +rfbDCUnrealizeCursor (pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + rfbDCCursorPtr pPriv; + + pPriv = (rfbDCCursorPtr)dixLookupPrivate(&pCursor->bits->devPrivates, + pScreen); + if (pPriv && (pCursor->bits->refcnt <= 1)) + { + if (pPriv->sourceBits) + (*pScreen->DestroyPixmap) (pPriv->sourceBits); + if (pPriv->maskBits) + (*pScreen->DestroyPixmap) (pPriv->maskBits); +#ifdef ARGB_CURSOR + if (pPriv->pPicture) + FreePicture (pPriv->pPicture, 0); +#endif + xfree ((pointer) pPriv); + dixSetPrivate(&pCursor->bits->devPrivates, pScreen, NULL); + } + return TRUE; +} + +static void +rfbDCPutBits (DrawablePtr pDrawable, rfbDCCursorPtr pPriv, GCPtr sourceGC, GCPtr maskGC, int x, int y, unsigned w, unsigned h, unsigned long source, unsigned long mask) +{ + XID gcvals[1]; + + if (sourceGC->fgPixel != source) + { + gcvals[0] = source; + DoChangeGC (sourceGC, GCForeground, gcvals, 0); + } + if (sourceGC->serialNumber != pDrawable->serialNumber) + ValidateGC (pDrawable, sourceGC); + (*sourceGC->ops->PushPixels) (sourceGC, pPriv->sourceBits, pDrawable, w, h, x, y); + if (maskGC->fgPixel != mask) + { + gcvals[0] = mask; + DoChangeGC (maskGC, GCForeground, gcvals, 0); + } + if (maskGC->serialNumber != pDrawable->serialNumber) + ValidateGC (pDrawable, maskGC); + (*maskGC->ops->PushPixels) (maskGC, pPriv->maskBits, pDrawable, w, h, x, y); +} + +#define EnsureGC(gc,win) (gc || rfbDCMakeGC(&gc, win)) + +static GCPtr +rfbDCMakeGC(GCPtr *ppGC, WindowPtr pWin) +{ + GCPtr pGC; + int status; + XID gcvals[2]; + + gcvals[0] = IncludeInferiors; + gcvals[1] = FALSE; + pGC = CreateGC((DrawablePtr)pWin, + GCSubwindowMode|GCGraphicsExposures, gcvals, &status, (XID)0, serverClient); + if (pGC && pWin->drawable.pScreen->DrawGuarantee) + (*pWin->drawable.pScreen->DrawGuarantee) (pWin, pGC, GuaranteeVisBack); + *ppGC = pGC; + return pGC; +} + +static Bool +rfbDCPutUpCursor (pScreen, pCursor, x, y, source, mask) + ScreenPtr pScreen; + CursorPtr pCursor; + int x, y; + unsigned long source, mask; +{ + rfbDCScreenPtr pScreenPriv; + rfbDCCursorPtr pPriv; + WindowPtr pWin; + + pPriv = (rfbDCCursorPtr)dixLookupPrivate(&pCursor->bits->devPrivates, + pScreen); + if (!pPriv) + { + pPriv = rfbDCRealize(pScreen, pCursor); + if (!pPriv) + return FALSE; + } + pScreenPriv = (rfbDCScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbDCScreenKey); + pWin = WindowTable[pScreen->myNum]; +#ifdef ARGB_CURSOR + if (pPriv->pPicture) + { + if (!EnsurePicture(pScreenPriv->pRootPicture, &pWin->drawable, pWin)) + return FALSE; + CompositePicture (PictOpOver, + pPriv->pPicture, + NULL, + pScreenPriv->pRootPicture, + 0, 0, 0, 0, + x, y, + pCursor->bits->width, + pCursor->bits->height); + } + else +#endif + { + if (!EnsureGC(pScreenPriv->pSourceGC, pWin)) + return FALSE; + if (!EnsureGC(pScreenPriv->pMaskGC, pWin)) + { + FreeGC (pScreenPriv->pSourceGC, (GContext) 0); + pScreenPriv->pSourceGC = 0; + return FALSE; + } + rfbDCPutBits ((DrawablePtr)pWin, pPriv, + pScreenPriv->pSourceGC, pScreenPriv->pMaskGC, + x, y, pCursor->bits->width, pCursor->bits->height, + source, mask); + } + return TRUE; +} + +static Bool +rfbDCSaveUnderCursor (pScreen, x, y, w, h) + ScreenPtr pScreen; + int x, y, w, h; +{ + rfbDCScreenPtr pScreenPriv; + PixmapPtr pSave; + WindowPtr pWin; + GCPtr pGC; + + pScreenPriv = (rfbDCScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbDCScreenKey); + pSave = pScreenPriv->pSave; + pWin = WindowTable[pScreen->myNum]; + if (!pSave || pSave->drawable.width < w || pSave->drawable.height < h) + { + if (pSave) + (*pScreen->DestroyPixmap) (pSave); + pScreenPriv->pSave = pSave = + (*pScreen->CreatePixmap) (pScreen, w, h, pScreen->rootDepth, 0); + if (!pSave) + return FALSE; + } + if (!EnsureGC(pScreenPriv->pSaveGC, pWin)) + return FALSE; + pGC = pScreenPriv->pSaveGC; + if (pSave->drawable.serialNumber != pGC->serialNumber) + ValidateGC ((DrawablePtr) pSave, pGC); + (*pGC->ops->CopyArea) ((DrawablePtr) pWin, (DrawablePtr) pSave, pGC, + x, y, w, h, 0, 0); + return TRUE; +} + +static Bool +rfbDCRestoreUnderCursor (pScreen, x, y, w, h) + ScreenPtr pScreen; + int x, y, w, h; +{ + rfbDCScreenPtr pScreenPriv; + PixmapPtr pSave; + WindowPtr pWin; + GCPtr pGC; + + pScreenPriv = (rfbDCScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbDCScreenKey); + pSave = pScreenPriv->pSave; + pWin = WindowTable[pScreen->myNum]; + if (!pSave) + return FALSE; + if (!EnsureGC(pScreenPriv->pRestoreGC, pWin)) + return FALSE; + pGC = pScreenPriv->pRestoreGC; + if (pWin->drawable.serialNumber != pGC->serialNumber) + ValidateGC ((DrawablePtr) pWin, pGC); + (*pGC->ops->CopyArea) ((DrawablePtr) pSave, (DrawablePtr) pWin, pGC, + 0, 0, w, h, x, y); + return TRUE; +} + +static Bool +rfbDCChangeSave (pScreen, x, y, w, h, dx, dy) + ScreenPtr pScreen; + int x, y, w, h, dx, dy; +{ + rfbDCScreenPtr pScreenPriv; + PixmapPtr pSave; + WindowPtr pWin; + GCPtr pGC; + int sourcex, sourcey, destx, desty, copyw, copyh; + + pScreenPriv = (rfbDCScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbDCScreenKey); + pSave = pScreenPriv->pSave; + pWin = WindowTable[pScreen->myNum]; + /* + * restore the bits which are about to get trashed + */ + if (!pSave) + return FALSE; + if (!EnsureGC(pScreenPriv->pRestoreGC, pWin)) + return FALSE; + pGC = pScreenPriv->pRestoreGC; + if (pWin->drawable.serialNumber != pGC->serialNumber) + ValidateGC ((DrawablePtr) pWin, pGC); + /* + * copy the old bits to the screen. + */ + if (dy > 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pSave, (DrawablePtr) pWin, pGC, + 0, h - dy, w, dy, x + dx, y + h); + } + else if (dy < 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pSave, (DrawablePtr) pWin, pGC, + 0, 0, w, -dy, x + dx, y + dy); + } + if (dy >= 0) + { + desty = y + dy; + sourcey = 0; + copyh = h - dy; + } + else + { + desty = y; + sourcey = - dy; + copyh = h + dy; + } + if (dx > 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pSave, (DrawablePtr) pWin, pGC, + w - dx, sourcey, dx, copyh, x + w, desty); + } + else if (dx < 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pSave, (DrawablePtr) pWin, pGC, + 0, sourcey, -dx, copyh, x + dx, desty); + } + if (!EnsureGC(pScreenPriv->pSaveGC, pWin)) + return FALSE; + pGC = pScreenPriv->pSaveGC; + if (pSave->drawable.serialNumber != pGC->serialNumber) + ValidateGC ((DrawablePtr) pSave, pGC); + /* + * move the bits that are still valid within the pixmap + */ + if (dx >= 0) + { + sourcex = 0; + destx = dx; + copyw = w - dx; + } + else + { + destx = 0; + sourcex = - dx; + copyw = w + dx; + } + if (dy >= 0) + { + sourcey = 0; + desty = dy; + copyh = h - dy; + } + else + { + desty = 0; + sourcey = -dy; + copyh = h + dy; + } + (*pGC->ops->CopyArea) ((DrawablePtr) pSave, (DrawablePtr) pSave, pGC, + sourcex, sourcey, copyw, copyh, destx, desty); + /* + * copy the new bits from the screen into the remaining areas of the + * pixmap + */ + if (dy > 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pWin, (DrawablePtr) pSave, pGC, + x, y, w, dy, 0, 0); + } + else if (dy < 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pWin, (DrawablePtr) pSave, pGC, + x, y + h + dy, w, -dy, 0, h + dy); + } + if (dy >= 0) + { + desty = dy; + sourcey = y + dy; + copyh = h - dy; + } + else + { + desty = 0; + sourcey = y; + copyh = h + dy; + } + if (dx > 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pWin, (DrawablePtr) pSave, pGC, + x, sourcey, dx, copyh, 0, desty); + } + else if (dx < 0) + { + (*pGC->ops->CopyArea) ((DrawablePtr) pWin, (DrawablePtr) pSave, pGC, + x + w + dx, sourcey, -dx, copyh, w + dx, desty); + } + return TRUE; +} + +static Bool +rfbDCMoveCursor (pScreen, pCursor, x, y, w, h, dx, dy, source, mask) + ScreenPtr pScreen; + CursorPtr pCursor; + int x, y, w, h, dx, dy; + unsigned long source, mask; +{ + rfbDCCursorPtr pPriv; + rfbDCScreenPtr pScreenPriv; + int status; + WindowPtr pWin; + GCPtr pGC; + XID gcval = FALSE; + PixmapPtr pTemp; + + pPriv = (rfbDCCursorPtr)dixLookupPrivate(&pCursor->bits->devPrivates, + pScreen); + if (!pPriv) + { + pPriv = rfbDCRealize(pScreen, pCursor); + if (!pPriv) + return FALSE; + } + pScreenPriv = (rfbDCScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbDCScreenKey); + pWin = WindowTable[pScreen->myNum]; + pTemp = pScreenPriv->pTemp; + if (!pTemp || + pTemp->drawable.width != pScreenPriv->pSave->drawable.width || + pTemp->drawable.height != pScreenPriv->pSave->drawable.height) + { + if (pTemp) + (*pScreen->DestroyPixmap) (pTemp); +#ifdef ARGB_CURSOR + if (pScreenPriv->pTempPicture) + { + FreePicture (pScreenPriv->pTempPicture, 0); + pScreenPriv->pTempPicture = 0; + } +#endif + pScreenPriv->pTemp = pTemp = (*pScreen->CreatePixmap) + (pScreen, w, h, pScreenPriv->pSave->drawable.depth, 0); + if (!pTemp) + return FALSE; + } + if (!pScreenPriv->pMoveGC) + { + pScreenPriv->pMoveGC = CreateGC ((DrawablePtr)pTemp, + GCGraphicsExposures, &gcval, &status, (XID)0, serverClient); + if (!pScreenPriv->pMoveGC) + return FALSE; + } + /* + * copy the saved area to a temporary pixmap + */ + pGC = pScreenPriv->pMoveGC; + if (pGC->serialNumber != pTemp->drawable.serialNumber) + ValidateGC ((DrawablePtr) pTemp, pGC); + (*pGC->ops->CopyArea)((DrawablePtr)pScreenPriv->pSave, + (DrawablePtr)pTemp, pGC, 0, 0, w, h, 0, 0); + + /* + * draw the cursor in the temporary pixmap + */ +#ifdef ARGB_CURSOR + if (pPriv->pPicture) + { + if (!EnsurePicture(pScreenPriv->pTempPicture, &pTemp->drawable, pWin)) + return FALSE; + CompositePicture (PictOpOver, + pPriv->pPicture, + NULL, + pScreenPriv->pTempPicture, + 0, 0, 0, 0, + dx, dy, + pCursor->bits->width, + pCursor->bits->height); + } + else +#endif + { + if (!pScreenPriv->pPixSourceGC) + { + pScreenPriv->pPixSourceGC = CreateGC ((DrawablePtr)pTemp, + GCGraphicsExposures, &gcval, &status, (XID)0, serverClient); + if (!pScreenPriv->pPixSourceGC) + return FALSE; + } + if (!pScreenPriv->pPixMaskGC) + { + pScreenPriv->pPixMaskGC = CreateGC ((DrawablePtr)pTemp, + GCGraphicsExposures, &gcval, &status, (XID)0, serverClient); + if (!pScreenPriv->pPixMaskGC) + return FALSE; + } + rfbDCPutBits ((DrawablePtr)pTemp, pPriv, + pScreenPriv->pPixSourceGC, pScreenPriv->pPixMaskGC, + dx, dy, pCursor->bits->width, pCursor->bits->height, + source, mask); + } + + /* + * copy the temporary pixmap onto the screen + */ + + if (!EnsureGC(pScreenPriv->pRestoreGC, pWin)) + return FALSE; + pGC = pScreenPriv->pRestoreGC; + if (pWin->drawable.serialNumber != pGC->serialNumber) + ValidateGC ((DrawablePtr) pWin, pGC); + + (*pGC->ops->CopyArea) ((DrawablePtr) pTemp, (DrawablePtr) pWin, + pGC, + 0, 0, w, h, x, y); + return TRUE; +} diff --git a/hw/vnc/dpmsstubs.c b/hw/vnc/dpmsstubs.c new file mode 100644 index 0000000..48ee545 --- /dev/null +++ b/hw/vnc/dpmsstubs.c @@ -0,0 +1,52 @@ +/* $Xorg: dpmsstubs.c,v 1.3 2000/08/17 19:47:56 cpqbld Exp $ */ +/***************************************************************** + +Copyright (c) 1996 Digital Equipment Corporation, Maynard, Massachusetts. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +DIGITAL EQUIPMENT CORPORATION BE LIABLE FOR ANY CLAIM, DAMAGES, INCLUDING, +BUT NOT LIMITED TO CONSEQUENTIAL OR INCIDENTAL DAMAGES, OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of Digital Equipment Corporation +shall not be used in advertising or otherwise to promote the sale, use or other +dealings in this Software without prior written authorization from Digital +Equipment Corporation. + +******************************************************************/ +/* $XFree86: xc/programs/Xserver/Xext/dpmsstubs.c,v 3.3 1999/12/16 02:26:23 robin Exp $ */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include "rfb.h" + +#define FALSE 0 + +Bool DPMSSupported(void) +{ + return FALSE; +} + +int DPSMGet(int *level) +{ + return -1; +} + +void DPMSSet(int level) +{ + +} diff --git a/hw/vnc/draw.c b/hw/vnc/draw.c new file mode 100644 index 0000000..631aa20 --- /dev/null +++ b/hw/vnc/draw.c @@ -0,0 +1,2021 @@ +/* + * draw.c - drawing routines for the RFB X server. This is a set of + * wrappers around the standard MI/MFB/CFB drawing routines which work out + * to a fair approximation the region of the screen being modified by the + * drawing. If the RFB client is ready then the modified region of the screen + * is sent to the client, otherwise the modified region will simply grow with + * each drawing request until the client is ready. + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ + +/* + +Copyright (c) 1989 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. +*/ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include "rfb.h" + +int rfbDeferUpdateTime = 40; /* ms */ + + +/****************************************************************************/ +/* + * Macro definitions + */ +/****************************************************************************/ + +#define TRC(x) /* (rfbLog x) */ + +/* ADD_TO_MODIFIED_REGION adds the given region to the modified region for each + client */ + +#define ADD_TO_MODIFIED_REGION(pScreen,reg) \ + { \ + rfbClientPtr cl; \ + for (cl = rfbClientHead; cl; cl = cl->next) { \ + REGION_UNION((pScreen),&cl->modifiedRegion,&cl->modifiedRegion,reg);\ + } \ + } + +/* SCHEDULE_FB_UPDATE is used at the end of each drawing routine to schedule an + update to be sent to each client if there is one pending and the client is + ready for it. */ + +#define SCHEDULE_FB_UPDATE(pScreen,pVNC) \ + if (!pVNC->dontSendFramebufferUpdate) { \ + rfbClientPtr cl, nextCl; \ + for (cl = rfbClientHead; cl; cl = nextCl) { \ + nextCl = cl->next; \ + if (!cl->deferredUpdateScheduled && FB_UPDATE_PENDING(cl) && \ + REGION_NOTEMPTY(pScreen,&cl->requestedRegion)) \ + { \ + rfbScheduleDeferredUpdate(pScreen, cl); \ + } \ + } \ + } + +/* function prototypes */ + +static void rfbScheduleDeferredUpdate(ScreenPtr pScreen, rfbClientPtr cl); +static void rfbCopyRegion(ScreenPtr pScreen, rfbClientPtr cl, + RegionPtr src, RegionPtr dst, int dx, int dy); +#ifdef DEBUG +static void PrintRegion(ScreenPtr pScreen, RegionPtr reg); +#endif + +/* GC funcs */ + +static void rfbValidateGC(GCPtr, unsigned long /*changes*/, DrawablePtr); +static void rfbChangeGC(GCPtr, unsigned long /*mask*/); +static void rfbCopyGC(GCPtr /*src*/, unsigned long /*mask*/, GCPtr /*dst*/); +static void rfbDestroyGC(GCPtr); +static void rfbChangeClip(GCPtr, int /*type*/, pointer /*pValue*/, + int /*nrects*/); +static void rfbDestroyClip(GCPtr); +static void rfbCopyClip(GCPtr /*dst*/, GCPtr /*src*/); + +/* GC ops */ + +static void rfbFillSpans(DrawablePtr pDrawable, GCPtr pGC, int nInit, DDXPointPtr pptInit, int *pwidthInit, int fSorted); +static void rfbSetSpans(DrawablePtr pDrawable, + GCPtr pGC, + char *psrc, + register DDXPointPtr ppt, + int *pwidth, + int nspans, + int fSorted); +static void rfbPutImage(DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, int w, int h, int leftPad, int format, char *pBits); +static RegionPtr rfbCopyArea (DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, int srcx, int srcy, int w, int h, int dstx, int dsty); +static RegionPtr rfbCopyPlane (DrawablePtr pSrc, DrawablePtr pDst, register GCPtr pGC, int srcx, int srcy, int w, int h, int dstx, int dsty, unsigned long plane); +static void rfbPolyPoint (DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, xPoint *pts); +static void rfbPolylines (DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, DDXPointPtr ppts); +static void rfbPolySegment(DrawablePtr pDrawable, GCPtr pGC, int nseg, xSegment *segs); +static void rfbPolyRectangle(DrawablePtr pDrawable, GCPtr pGC, int nrects, xRectangle *rects); +static void rfbPolyArc(DrawablePtr pDrawable, register GCPtr pGC, int narcs, xArc *arcs); +static void rfbFillPolygon(register DrawablePtr pDrawable, register GCPtr pGC, int shape, int mode, int count, DDXPointPtr pts); +static void rfbPolyFillRect(DrawablePtr pDrawable, GCPtr pGC, int nrects, xRectangle *rects); +static void rfbPolyFillArc(DrawablePtr pDrawable, GCPtr pGC, int narcs, xArc *arcs); +static int rfbPolyText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, char *chars); +static int rfbPolyText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, unsigned short *chars); +static void rfbImageText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, char *chars); +static void rfbImageText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, unsigned short *chars); +static void rfbImageGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr *ppci, pointer pglyphBase); +static void rfbPolyGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr *ppci, pointer pglyphBase); +static void rfbPushPixels(GCPtr pGC, PixmapPtr pBitMap, DrawablePtr pDrawable, int w, int h, int x, int y); + + +static GCFuncs rfbGCFuncs = { + rfbValidateGC, + rfbChangeGC, + rfbCopyGC, + rfbDestroyGC, + rfbChangeClip, + rfbDestroyClip, + rfbCopyClip, +}; + + +static GCOps rfbGCOps = { + rfbFillSpans, rfbSetSpans, rfbPutImage, + rfbCopyArea, rfbCopyPlane, rfbPolyPoint, + rfbPolylines, rfbPolySegment, rfbPolyRectangle, + rfbPolyArc, rfbFillPolygon, rfbPolyFillRect, + rfbPolyFillArc, rfbPolyText8, rfbPolyText16, + rfbImageText8, rfbImageText16, rfbImageGlyphBlt, + rfbPolyGlyphBlt, rfbPushPixels +}; + + + +/****************************************************************************/ +/* + * Screen functions wrapper stuff + */ +/****************************************************************************/ + +#define SCREEN_PROLOGUE(scrn, field) \ + ScreenPtr pScreen = scrn; \ + VNCSCREENPTR(pScreen); \ + pScreen->field = pVNC->field; + +#define SCREEN_EPILOGUE(field, wrapper) \ + pScreen->field = wrapper; + + +/* + * CloseScreen wrapper -- unwrap everything, free the private data + * and call the wrapped CloseScreen function. + */ + +Bool +rfbCloseScreen (int i, ScreenPtr pScreen) +{ + VNCSCREENPTR(pScreen); +#if XFREE86VNC + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; +#endif + int sock; + + for (sock = 0; sock <= pVNC->maxFd; sock++) { + if (FD_ISSET(sock, &pVNC->allFds)) + if (sock != pVNC->rfbListenSock && sock != pVNC->httpListenSock) { + rfbCloseSock(pScreen, sock); + } + } + + if (pVNC->rfbListenSock > 0) + if (close(pVNC->rfbListenSock)) + ErrorF("Close of port %d failed\n",pVNC->rfbPort); + + if (pVNC->httpListenSock > 0) + if (close(pVNC->httpListenSock)) + ErrorF("Close of port %d failed\n",pVNC->httpPort); + + pScreen->CloseScreen = pVNC->CloseScreen; + pScreen->CreateGC = pVNC->CreateGC; + pScreen->PaintWindowBackground = pVNC->PaintWindowBackground; + pScreen->PaintWindowBorder = pVNC->PaintWindowBorder; + pScreen->CopyWindow = pVNC->CopyWindow; + pScreen->ClearToBackground = pVNC->ClearToBackground; + pScreen->RestoreAreas = pVNC->RestoreAreas; + pScreen->WakeupHandler = pVNC->WakeupHandler; + +#if XFREE86VNC + pScreen->InstallColormap = pVNC->InstallColormap; + pScreen->UninstallColormap = pVNC->UninstallColormap; + pScreen->ListInstalledColormaps = pVNC->ListInstalledColormaps; + pScreen->StoreColors = pVNC->StoreColors; + pScrn->EnableDisableFBAccess = pVNC->EnableDisableFBAccess; + + xfree(pVNC); +#endif + + TRC((stderr,"Unwrapped screen functions\n")); + + return (*pScreen->CloseScreen) (i, pScreen); +} + +#if XFREE86VNC +void +rfbEnableDisableFBAccess (int index, Bool enable) +{ + ScrnInfoPtr pScrn = xf86Screens[index]; + VNCSCREENPTR(pScrn->pScreen); + + /* + * Blank the screen for security while inputs are disabled. + * When VT switching is fixed, we might be able to allow + * control even when switched away. + */ + if (!enable) { + WindowPtr pWin = WindowTable[index]; + ScreenPtr pScreen = pWin->drawable.pScreen; + GCPtr pGC; + xRectangle rect; + + rect.x = 0; + rect.y = 0; + rect.width = pScrn->virtualX; + rect.height = pScrn->virtualY; + + if (!(pGC = GetScratchGC(pScreen->rootDepth, pScreen))) { + ErrorF("Couldn't blank screen"); + } else { + CARD32 attributes[2]; + attributes[0] = pScreen->whitePixel; + attributes[1] = pScreen->blackPixel; + (void)ChangeGC(pGC, GCForeground | GCBackground, attributes); + + ValidateGC((DrawablePtr)pWin, pGC); + + (*pGC->ops->PolyFillRect)((DrawablePtr)pWin, pGC, 1, &rect); + + FreeScratchGC(pGC); + + /* Flush pending packets */ + rfbCheckFds(pScreen); + httpCheckFds(pScreen); + } + } + + pScrn->EnableDisableFBAccess = pVNC->EnableDisableFBAccess; + (*pScrn->EnableDisableFBAccess)(index, enable); + pScrn->EnableDisableFBAccess = rfbEnableDisableFBAccess; +} +#endif + +/* + * CreateGC - wrap the GC funcs (the GC ops will be wrapped when the GC + * func "ValidateGC" is called). + */ + +Bool +rfbCreateGC (GCPtr pGC) +{ + Bool ret; + rfbGCPtr pGCPriv; + + SCREEN_PROLOGUE(pGC->pScreen,CreateGC); + + pGCPriv = (rfbGCPtr) + dixLookupPrivate(&(pGC)->devPrivates, rfbGCKey); + + ret = (*pScreen->CreateGC) (pGC); + + TRC((stderr,"rfbCreateGC called\n")); + + pGCPriv->wrapOps = NULL; + pGCPriv->wrapFuncs = pGC->funcs; + pGC->funcs = &rfbGCFuncs; + + SCREEN_EPILOGUE(CreateGC,rfbCreateGC); + + return ret; +} + +/* + * PaintWindowBackground - the region being modified is just the given region. + */ + +void +rfbPaintWindowBackground (WindowPtr pWin, RegionPtr pRegion, int what) +{ + SCREEN_PROLOGUE(pWin->drawable.pScreen,PaintWindowBackground); + + TRC((stderr,"rfbPaintWindowBackground called\n")); + + ADD_TO_MODIFIED_REGION(pScreen,pRegion); + + (*pScreen->PaintWindowBackground) (pWin, pRegion, what); + + SCHEDULE_FB_UPDATE(pScreen, pVNC); + + SCREEN_EPILOGUE(PaintWindowBackground,rfbPaintWindowBackground); +} + +/* + * PaintWindowBorder - the region being modified is just the given region. + */ + +void +rfbPaintWindowBorder (WindowPtr pWin, RegionPtr pRegion, int what) +{ + SCREEN_PROLOGUE(pWin->drawable.pScreen,PaintWindowBorder); + + TRC((stderr,"rfbPaintWindowBorder called\n")); + + ADD_TO_MODIFIED_REGION(pScreen,pRegion); + + (*pScreen->PaintWindowBorder) (pWin, pRegion, what); + + SCHEDULE_FB_UPDATE(pScreen, pVNC); + + SCREEN_EPILOGUE(PaintWindowBorder,rfbPaintWindowBorder); +} + +#ifdef CHROMIUM +Bool +rfbRealizeWindow(WindowPtr pWin) +{ + CRWindowTable *wt = NULL, *nextWt = NULL; + Bool ret; + SCREEN_PROLOGUE(pWin->drawable.pScreen,RealizeWindow); + + for (wt = windowTable; wt; wt = nextWt) { + nextWt = wt->next; + if (wt->XwinId == pWin->drawable.id) { + rfbSendChromiumWindowShow(wt->CRwinId, 1); + } + } + + ret = (*pScreen->RealizeWindow)(pWin); + + SCREEN_EPILOGUE(RealizeWindow,rfbRealizeWindow); + + return ret; +} + +Bool +rfbUnrealizeWindow(WindowPtr pWin) +{ + CRWindowTable *wt = NULL, *nextWt = NULL; + Bool ret; + SCREEN_PROLOGUE(pWin->drawable.pScreen,UnrealizeWindow); + + for (wt = windowTable; wt; wt = nextWt) { + nextWt = wt->next; + if (wt->XwinId == pWin->drawable.id) { + rfbSendChromiumWindowShow(wt->CRwinId, 0); + } + } + + ret = (*pScreen->UnrealizeWindow)(pWin); + + SCREEN_EPILOGUE(UnrealizeWindow,rfbUnrealizeWindow); + + return ret; +} + +Bool +rfbDestroyWindow(WindowPtr pWin) +{ + CRWindowTable *wt = NULL, *nextWt = NULL, *prevWt = NULL; + Bool ret; + SCREEN_PROLOGUE(pWin->drawable.pScreen,DestroyWindow); + + /* loop over monitored windows */ + for (wt = windowTable; wt; wt = nextWt) { + nextWt = wt->next; + if (wt->XwinId == pWin->drawable.id) { + rfbSendChromiumWindowDestroy(wt->CRwinId); + /* also remove from list */ + if (prevWt) + prevWt->next = wt->next; + else + windowTable = wt->next; + xfree(wt); + } + else { + prevWt = wt; + } + } + + ret = (*pScreen->DestroyWindow)(pWin); + + SCREEN_EPILOGUE(DestroyWindow,rfbDestroyWindow); + + return ret; +} + +void +rfbResizeWindow(WindowPtr pWin, int x, int y, unsigned int w, unsigned int h, WindowPtr pSib) +{ + CRWindowTable *wt = NULL, *nextWt = NULL; + SCREEN_PROLOGUE(pWin->drawable.pScreen,ResizeWindow); + + for (wt = windowTable; wt; wt = nextWt) { + nextWt = wt->next; + if (wt->XwinId == pWin->drawable.id) { + rfbSendChromiumMoveResizeWindow(wt->CRwinId, pWin->drawable.x, pWin->drawable.y, w, h); + } + } + + (*pScreen->ResizeWindow)(pWin, x, y, w, h, pSib); + + SCREEN_EPILOGUE(ResizeWindow,rfbResizeWindow); +} + +Bool +rfbPositionWindow(WindowPtr pWin, int x, int y) +{ + Bool ret; + CRWindowTable *wt, *nextWt; + SCREEN_PROLOGUE(pWin->drawable.pScreen,PositionWindow); + + for (wt = windowTable; wt; wt = nextWt) { + nextWt = wt->next; + if (wt->XwinId == pWin->drawable.id) { + rfbSendChromiumMoveResizeWindow(wt->CRwinId, x, y, pWin->drawable.width, pWin->drawable.height); + } + } + + ret = (*pScreen->PositionWindow)(pWin, x, y); + + SCREEN_EPILOGUE(PositionWindow,rfbPositionWindow); + + return ret; +} + +void +rfbClipNotify(WindowPtr pWin, int x, int y) +{ + CRWindowTable *wt, *nextWt; + SCREEN_PROLOGUE(pWin->drawable.pScreen,ClipNotify); + + for (wt = windowTable; wt; wt = nextWt) { + nextWt = wt->next; + if (wt->XwinId == pWin->drawable.id) { + int numClipRects = REGION_NUM_RECTS(&pWin->clipList); + BoxPtr pClipRects = REGION_RECTS(&pWin->clipList); + + /* Possible optimization - has the cliplist really? changed */ + + rfbSendChromiumClipList(wt->CRwinId, pClipRects, numClipRects); + } + } + + if (*pScreen->ClipNotify) + (*pScreen->ClipNotify)(pWin, x, y); + + SCREEN_EPILOGUE(ClipNotify,rfbClipNotify); +} +#endif /* CHROMIUM */ + +/* + * CopyWindow - the region being modified is the translation of the old + * region, clipped to the border clip region of the window. Note that any + * parts of the window which have become newly-visible will not be affected by + * this call - a separate PaintWindowBackground/Border will be called to do + * that. If the client will accept CopyRect messages then use rfbCopyRegion to + * optimise the pending screen changes into a single "copy region" plus the + * ordinary modified region. + */ + +void +rfbCopyWindow (WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr pOldRegion) +{ + rfbClientPtr cl; + RegionRec srcRegion, dstRegion; + SCREEN_PROLOGUE(pWin->drawable.pScreen,CopyWindow); + + TRC((stderr,"rfbCopyWindow called\n")); + + REGION_NULL(pScreen,&dstRegion); + REGION_COPY(pScreen,&dstRegion,pOldRegion); + REGION_TRANSLATE(pWin->drawable.pScreen, &dstRegion, + pWin->drawable.x - ptOldOrg.x, + pWin->drawable.y - ptOldOrg.y); + REGION_INTERSECT(pWin->drawable.pScreen, &dstRegion, &dstRegion, + &pWin->borderClip); + + for (cl = rfbClientHead; cl; cl = cl->next) { + if (cl->useCopyRect) { + REGION_NULL(pScreen,&srcRegion); + REGION_COPY(pScreen,&srcRegion,pOldRegion); + + rfbCopyRegion(pScreen, cl, &srcRegion, &dstRegion, + pWin->drawable.x - ptOldOrg.x, + pWin->drawable.y - ptOldOrg.y); + + REGION_UNINIT(pScreen, &srcRegion); + + } else { + + REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion, + &dstRegion); + } + } + + REGION_UNINIT(pScreen, &dstRegion); + + (*pScreen->CopyWindow) (pWin, ptOldOrg, pOldRegion); + + SCHEDULE_FB_UPDATE(pScreen, pVNC); + + SCREEN_EPILOGUE(CopyWindow,rfbCopyWindow); +} + +/* + * ClearToBackground - when generateExposures is false, the region being + * modified is the given rectangle (clipped to the "window clip region"). + */ + +void +rfbClearToBackground (WindowPtr pWin, int x, int y, int w, int h, + Bool generateExposures) +{ + RegionRec tmpRegion; + BoxRec box; + SCREEN_PROLOGUE(pWin->drawable.pScreen,ClearToBackground); + + TRC((stderr,"rfbClearToBackground called\n")); + + if (!generateExposures) { + box.x1 = x + pWin->drawable.x; + box.y1 = y + pWin->drawable.y; + box.x2 = w ? (box.x1 + w) : (pWin->drawable.x + pWin->drawable.width); + box.y2 = h ? (box.y1 + h) : (pWin->drawable.y + pWin->drawable.height); + + SAFE_REGION_INIT(pScreen, &tmpRegion, &box, 0); + + REGION_INTERSECT(pScreen, &tmpRegion, &tmpRegion, &pWin->clipList); + + ADD_TO_MODIFIED_REGION(pScreen, &tmpRegion); + + REGION_UNINIT(pScreen, &tmpRegion); + } + + (*pScreen->ClearToBackground) (pWin, x, y, w, h, generateExposures); + + if (!generateExposures) { + SCHEDULE_FB_UPDATE(pScreen, pVNC); + } + + SCREEN_EPILOGUE(ClearToBackground,rfbClearToBackground); +} + +/* + * RestoreAreas - just be safe here - the region being modified is the whole + * exposed region. + */ + +RegionPtr +rfbRestoreAreas (WindowPtr pWin, RegionPtr prgnExposed) +{ + RegionPtr result; + SCREEN_PROLOGUE(pWin->drawable.pScreen,RestoreAreas); + + TRC((stderr,"rfbRestoreAreas called\n")); + + ADD_TO_MODIFIED_REGION(pScreen, prgnExposed); + + result = (*pScreen->RestoreAreas) (pWin, prgnExposed); + + SCHEDULE_FB_UPDATE(pScreen, pVNC); + + SCREEN_EPILOGUE(RestoreAreas,rfbRestoreAreas); + + return result; +} + + + +/****************************************************************************/ +/* + * GC funcs wrapper stuff + * + * We only really want to wrap the GC ops, but to do this we need to wrap + * ValidateGC and so all the other GC funcs must be wrapped as well. + */ +/****************************************************************************/ + +#define GC_FUNC_PROLOGUE(pGC) \ + rfbGCPtr pGCPriv = (rfbGCPtr)dixLookupPrivate(&(pGC)->devPrivates, rfbGCKey); \ + (pGC)->funcs = pGCPriv->wrapFuncs; \ + if (pGCPriv->wrapOps) \ + (pGC)->ops = pGCPriv->wrapOps; + +#define GC_FUNC_EPILOGUE(pGC) \ + pGCPriv->wrapFuncs = (pGC)->funcs; \ + (pGC)->funcs = &rfbGCFuncs; \ + if (pGCPriv->wrapOps) { \ + pGCPriv->wrapOps = (pGC)->ops; \ + (pGC)->ops = &rfbGCOps; \ + } + + +/* + * ValidateGC - call the wrapped ValidateGC, then wrap the resulting GC ops if + * the drawing will be to a viewable window. + */ + +static void +rfbValidateGC (GCPtr pGC, unsigned long changes, DrawablePtr pDrawable) +{ + GC_FUNC_PROLOGUE(pGC); + + TRC((stderr,"rfbValidateGC called\n")); + + (*pGC->funcs->ValidateGC) (pGC, changes, pDrawable); + + pGCPriv->wrapOps = NULL; + if (pDrawable->type == DRAWABLE_WINDOW && ((WindowPtr)pDrawable)->viewable) + { + WindowPtr pWin = (WindowPtr) pDrawable; + RegionPtr pRegion = &pWin->clipList; + + if (pGC->subWindowMode == IncludeInferiors) + pRegion = &pWin->borderClip; + if (REGION_NOTEMPTY(pDrawable->pScreen, pRegion)) { + pGCPriv->wrapOps = pGC->ops; + TRC((stderr,"rfbValidateGC: wrapped GC ops\n")); + } + } + + GC_FUNC_EPILOGUE(pGC); +} + +/* + * All other GC funcs simply unwrap the GC funcs and ops, call the wrapped + * function and then rewrap the funcs and ops. + */ + +static void +rfbChangeGC (pGC, mask) + GCPtr pGC; + unsigned long mask; +{ + GC_FUNC_PROLOGUE(pGC); + (*pGC->funcs->ChangeGC) (pGC, mask); + GC_FUNC_EPILOGUE(pGC); +} + +static void +rfbCopyGC (pGCSrc, mask, pGCDst) + GCPtr pGCSrc, pGCDst; + unsigned long mask; +{ + GC_FUNC_PROLOGUE(pGCDst); + (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst); + GC_FUNC_EPILOGUE(pGCDst); +} + +static void +rfbDestroyGC (pGC) + GCPtr pGC; +{ + GC_FUNC_PROLOGUE(pGC); + (*pGC->funcs->DestroyGC) (pGC); + GC_FUNC_EPILOGUE(pGC); +} + +static void +rfbChangeClip (pGC, type, pvalue, nrects) + GCPtr pGC; + int type; + pointer pvalue; + int nrects; +{ + GC_FUNC_PROLOGUE(pGC); + (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects); + GC_FUNC_EPILOGUE(pGC); +} + +static void +rfbDestroyClip(pGC) + GCPtr pGC; +{ + GC_FUNC_PROLOGUE(pGC); + (* pGC->funcs->DestroyClip)(pGC); + GC_FUNC_EPILOGUE(pGC); +} + +static void +rfbCopyClip(pgcDst, pgcSrc) + GCPtr pgcDst, pgcSrc; +{ + GC_FUNC_PROLOGUE(pgcDst); + (* pgcDst->funcs->CopyClip)(pgcDst, pgcSrc); + GC_FUNC_EPILOGUE(pgcDst); +} + + +/****************************************************************************/ +/* + * GC ops wrapper stuff + * + * Note that these routines will only have been wrapped for drawing to + * viewable windows so we don't need to check each time that the drawable + * is a viewable window. + */ +/****************************************************************************/ + +#define GC_OP_PROLOGUE(pDrawable,pGC) \ + ScreenPtr pScreen = pGC->pScreen; \ + VNCSCREENPTR(pScreen); \ + rfbGCPtr pGCPrivate = (rfbGCPtr)dixLookupPrivate(&(pGC)->devPrivates, rfbGCKey); \ + GCFuncs *oldFuncs = pGC->funcs; \ + (void) pScreen; /* silence compiler */ \ + (pGC)->funcs = pGCPrivate->wrapFuncs; \ + (pGC)->ops = pGCPrivate->wrapOps; + +#define GC_OP_EPILOGUE(pGC) \ + pGCPrivate->wrapOps = (pGC)->ops; \ + (pGC)->funcs = oldFuncs; \ + (pGC)->ops = &rfbGCOps; + + +/* + * FillSpans - being very safe - the region being modified is the border clip + * region of the window. + */ + +static void +rfbFillSpans(pDrawable, pGC, nInit, pptInit, pwidthInit, fSorted) + DrawablePtr pDrawable; + GCPtr pGC; + int nInit; /* number of spans to fill */ + DDXPointPtr pptInit; /* pointer to list of start points */ + int *pwidthInit; /* pointer to list of n widths */ + int fSorted; +{ + GC_OP_PROLOGUE(pDrawable,pGC); + + TRC((stderr,"rfbFillSpans called\n")); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, + &((WindowPtr)pDrawable)->borderClip); + + (*pGC->ops->FillSpans) (pDrawable, pGC, nInit, pptInit,pwidthInit,fSorted); + + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + + GC_OP_EPILOGUE(pGC); +} + +/* + * SetSpans - being very safe - the region being modified is the border clip + * region of the window. + */ + +static void +rfbSetSpans(DrawablePtr pDrawable, + GCPtr pGC, + char *psrc, + register DDXPointPtr ppt, + int *pwidth, + int nspans, + int fSorted) +{ + GC_OP_PROLOGUE(pDrawable,pGC); + + TRC((stderr,"rfbSetSpans called\n")); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, + &((WindowPtr)pDrawable)->borderClip); + + (*pGC->ops->SetSpans) (pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted); + + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + + GC_OP_EPILOGUE(pGC); +} + +/* + * PutImage - the region being modified is the rectangle of the + * PutImage (clipped to the window clip region). + */ + +static void +rfbPutImage(DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, int w, int h, int leftPad, int format, char *pBits) +{ + RegionRec tmpRegion; + BoxRec box; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbPutImage called\n")); + + box.x1 = x + pDrawable->x; + box.y1 = y + pDrawable->y; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0); + + REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion); + + REGION_UNINIT(pDrawable->pScreen, &tmpRegion); + + (*pGC->ops->PutImage) (pDrawable, pGC, depth, x, y, w, h, + leftPad, format, pBits); + + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + + GC_OP_EPILOGUE(pGC); +} + +/* + * CopyArea - the region being modified is the destination rectangle (clipped + * to the window clip region). + * If the client will accept CopyRect messages then use rfbCopyRegion + * to optimise the pending screen changes into a single "copy region" plus + * the ordinary modified region. + */ + +static RegionPtr +rfbCopyArea (DrawablePtr pSrc, DrawablePtr pDst, GCPtr pGC, int srcx, int srcy, int w, int h, int dstx, int dsty) +{ + rfbClientPtr cl; + RegionPtr rgn; + RegionRec srcRegion, dstRegion; + BoxRec box; + GC_OP_PROLOGUE(pDst, pGC); + + TRC((stderr,"rfbCopyArea called\n")); + + box.x1 = dstx + pDst->x; + box.y1 = dsty + pDst->y; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + SAFE_REGION_INIT(pDst->pScreen, &dstRegion, &box, 0); + REGION_INTERSECT(pDst->pScreen, &dstRegion, &dstRegion, + pGC->pCompositeClip); + + if ((pSrc->type == DRAWABLE_WINDOW) && (pSrc->pScreen == pDst->pScreen)) { + box.x1 = srcx + pSrc->x; + box.y1 = srcy + pSrc->y; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + for (cl = rfbClientHead; cl; cl = cl->next) { + if (cl->useCopyRect) { + SAFE_REGION_INIT(pSrc->pScreen, &srcRegion, &box, 0); + REGION_INTERSECT(pSrc->pScreen, &srcRegion, &srcRegion, + &((WindowPtr)pSrc)->clipList); + + rfbCopyRegion(pSrc->pScreen, cl, &srcRegion, &dstRegion, + dstx + pDst->x - srcx - pSrc->x, + dsty + pDst->y - srcy - pSrc->y); + + REGION_UNINIT(pSrc->pScreen, &srcRegion); + + } else { + + REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion, + &dstRegion); + } + } + + } else { + + ADD_TO_MODIFIED_REGION(pDst->pScreen, &dstRegion); + } + + REGION_UNINIT(pDst->pScreen, &dstRegion); + + rgn = (*pGC->ops->CopyArea) (pSrc, pDst, pGC, srcx, srcy, w, h, + dstx, dsty); + + SCHEDULE_FB_UPDATE(pDst->pScreen, pVNC); + + GC_OP_EPILOGUE(pGC); + + return rgn; +} + + +/* + * CopyPlane - the region being modified is the destination rectangle (clipped + * to the window clip region). + */ + +static RegionPtr +rfbCopyPlane (DrawablePtr pSrc, DrawablePtr pDst, register GCPtr pGC, int srcx, int srcy, int w, int h, int dstx, int dsty, unsigned long plane) +{ + RegionPtr rgn; + RegionRec tmpRegion; + BoxRec box; + GC_OP_PROLOGUE(pDst, pGC); + + TRC((stderr,"rfbCopyPlane called\n")); + + box.x1 = dstx + pDst->x; + box.y1 = dsty + pDst->y; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + SAFE_REGION_INIT(pDst->pScreen, &tmpRegion, &box, 0); + + REGION_INTERSECT(pDst->pScreen, &tmpRegion, &tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDst->pScreen, &tmpRegion); + + REGION_UNINIT(pDst->pScreen, &tmpRegion); + + rgn = (*pGC->ops->CopyPlane) (pSrc, pDst, pGC, srcx, srcy, w, h, + dstx, dsty, plane); + + SCHEDULE_FB_UPDATE(pDst->pScreen, pVNC); + + GC_OP_EPILOGUE(pGC); + + return rgn; +} + +/* + * PolyPoint - find the smallest rectangle which encloses the points drawn + * (and clip). + */ + +static void +rfbPolyPoint (DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, xPoint *pts) +{ + int i; + RegionRec tmpRegion; + BoxRec box; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbPolyPoint called\n")); + + if (npt) { + int minX = pts[0].x, maxX = pts[0].x; + int minY = pts[0].y, maxY = pts[0].y; + + if (mode == CoordModePrevious) + { + int x = pts[0].x, y = pts[0].y; + + for (i = 1; i < npt; i++) { + x += pts[i].x; + y += pts[i].y; + if (x < minX) minX = x; + if (x > maxX) maxX = x; + if (y < minY) minY = y; + if (y > maxY) maxY = y; + } + } + else + { + for (i = 1; i < npt; i++) { + if (pts[i].x < minX) minX = pts[i].x; + if (pts[i].x > maxX) maxX = pts[i].x; + if (pts[i].y < minY) minY = pts[i].y; + if (pts[i].y > maxY) maxY = pts[i].y; + } + } + + box.x1 = minX + pDrawable->x; + box.y1 = minY + pDrawable->y; + box.x2 = maxX + 1 + pDrawable->x; + box.y2 = maxY + 1 + pDrawable->y; + + SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0); + + REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion); + + REGION_UNINIT(pDrawable->pScreen, &tmpRegion); + } + + (*pGC->ops->PolyPoint) (pDrawable, pGC, mode, npt, pts); + + if (npt) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); +} + +/* + * PolyLines - take the union of bounding boxes around each line (and clip). + */ + +static void +rfbPolylines (DrawablePtr pDrawable, GCPtr pGC, int mode, int npt, DDXPointPtr ppts) +{ + RegionPtr tmpRegion; + xRectangle *rects; + int i, extra, nlines, lw; + int x1, x2, y1, y2; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbPolylines called\n")); + + if (npt) { + lw = pGC->lineWidth; + if (lw == 0) + lw = 1; + + if (npt == 1) + { + nlines = 1; + rects = (xRectangle *)xalloc(sizeof(xRectangle)); + if (!rects) { + FatalError("rfbPolylines: xalloc failed\n"); + } + + rects[0].x = ppts[0].x - lw + pDrawable->x; /* being safe here */ + rects[0].y = ppts[0].y - lw + pDrawable->y; + rects[0].width = 2*lw; + rects[0].height = 2*lw; + } + else + { + nlines = npt - 1; + rects = (xRectangle *)xalloc(nlines*sizeof(xRectangle)); + if (!rects) { + FatalError("rfbPolylines: xalloc failed\n"); + } + + /* + * mitered joins can project quite a way from + * the line end; the 11 degree miter limit limits + * this extension to lw / (2 * tan(11/2)), rounded up + * and converted to int yields 6 * lw + */ + + if (pGC->joinStyle == JoinMiter) { + extra = 6 * lw; + } else { + extra = lw / 2; + } + + x1 = ppts[0].x + pDrawable->x; + y1 = ppts[0].y + pDrawable->y; + + for (i = 0; i < nlines; i++) { + if (mode == CoordModeOrigin) { + x2 = pDrawable->x + ppts[i+1].x; + y2 = pDrawable->y + ppts[i+1].y; + } else { + x2 = x1 + ppts[i+1].x; + y2 = y1 + ppts[i+1].y; + } + + if (x1 > x2) { + rects[i].x = x2 - extra; + rects[i].width = x1 - x2 + 1 + 2 * extra; + } else { + rects[i].x = x1 - extra; + rects[i].width = x2 - x1 + 1 + 2 * extra; + } + + if (y1 > y2) { + rects[i].y = y2 - extra; + rects[i].height = y1 - y2 + 1 + 2 * extra; + } else { + rects[i].y = y1 - extra; + rects[i].height = y2 - y1 + 1 + 2 * extra; + } + + x1 = x2; + y1 = y2; + } + } + tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nlines, rects,CT_NONE); + REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion); + + REGION_DESTROY(pDrawable->pScreen, tmpRegion); + xfree((char *)rects); + } + + (*pGC->ops->Polylines) (pDrawable, pGC, mode, npt, ppts); + + if (npt) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); +} + +/* + * PolySegment - take the union of bounding boxes around each segment (and + * clip). + */ + +static void +rfbPolySegment(DrawablePtr pDrawable, GCPtr pGC, int nseg, xSegment *segs) +{ + RegionPtr tmpRegion; + xRectangle *rects; + int i, extra, lw; + + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbPolySegment called\n")); + + if (nseg) { + rects = (xRectangle *)xalloc(nseg*sizeof(xRectangle)); + if (!rects) { + FatalError("rfbPolySegment: xalloc failed\n"); + } + + lw = pGC->lineWidth; + if (lw == 0) + lw = 1; + + extra = lw / 2; + + for (i = 0; i < nseg; i++) + { + if (segs[i].x1 > segs[i].x2) { + rects[i].x = segs[i].x2 - extra + pDrawable->x; + rects[i].width = segs[i].x1 - segs[i].x2 + 1 + 2 * extra; + } else { + rects[i].x = segs[i].x1 - extra + pDrawable->x; + rects[i].width = segs[i].x2 - segs[i].x1 + 1 + 2 * extra; + } + + if (segs[i].y1 > segs[i].y2) { + rects[i].y = segs[i].y2 - extra + pDrawable->y; + rects[i].height = segs[i].y1 - segs[i].y2 + 1 + 2 * extra; + } else { + rects[i].y = segs[i].y1 - extra + pDrawable->y; + rects[i].height = segs[i].y2 - segs[i].y1 + 1 + 2 * extra; + } + } + + tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nseg, rects, CT_NONE); + REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion); + + REGION_DESTROY(pDrawable->pScreen, tmpRegion); + xfree((char *)rects); + } + + (*pGC->ops->PolySegment) (pDrawable, pGC, nseg, segs); + + if (nseg) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); +} + +/* + * PolyRectangle (rectangle outlines) - take the union of bounding boxes + * around each line (and clip). + */ + +static void +rfbPolyRectangle(DrawablePtr pDrawable, GCPtr pGC, int nrects, xRectangle *rects) +{ + int i, extra, lw; + RegionPtr tmpRegion; + xRectangle *regRects; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbPolyRectangle called\n")); + + if (nrects) { + regRects = (xRectangle *)xalloc(nrects*4*sizeof(xRectangle)); + if (!regRects) { + FatalError("rfbPolyRectangle: xalloc failed\n"); + } + + lw = pGC->lineWidth; + if (lw == 0) + lw = 1; + + extra = lw / 2; + + for (i = 0; i < nrects; i++) + { + regRects[i*4].x = rects[i].x - extra + pDrawable->x; + regRects[i*4].y = rects[i].y - extra + pDrawable->y; + regRects[i*4].width = rects[i].width + 1 + 2 * extra; + regRects[i*4].height = 1 + 2 * extra; + + regRects[i*4+1].x = rects[i].x - extra + pDrawable->x; + regRects[i*4+1].y = rects[i].y - extra + pDrawable->y; + regRects[i*4+1].width = 1 + 2 * extra; + regRects[i*4+1].height = rects[i].height + 1 + 2 * extra; + + regRects[i*4+2].x + = rects[i].x + rects[i].width - extra + pDrawable->x; + regRects[i*4+2].y = rects[i].y - extra + pDrawable->y; + regRects[i*4+2].width = 1 + 2 * extra; + regRects[i*4+2].height = rects[i].height + 1 + 2 * extra; + + regRects[i*4+3].x = rects[i].x - extra + pDrawable->x; + regRects[i*4+3].y + = rects[i].y + rects[i].height - extra + pDrawable->y; + regRects[i*4+3].width = rects[i].width + 1 + 2 * extra; + regRects[i*4+3].height = 1 + 2 * extra; + } + + tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nrects*4, + regRects, CT_NONE); + REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion); + + REGION_DESTROY(pDrawable->pScreen, tmpRegion); + xfree((char *)regRects); + } + + (*pGC->ops->PolyRectangle) (pDrawable, pGC, nrects, rects); + + if (nrects) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); +} + +/* + * PolyArc - take the union of bounding boxes around each arc (and clip). + * Bounding boxes assume each is a full circle / ellipse. + */ + +static void +rfbPolyArc(DrawablePtr pDrawable, register GCPtr pGC, int narcs, xArc *arcs) +{ + int i, extra, lw; + RegionPtr tmpRegion; + xRectangle *rects; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbPolyArc called\n")); + + if (narcs) { + rects = (xRectangle *)xalloc(narcs*sizeof(xRectangle)); + if (!rects) { + FatalError("rfbPolyArc: xalloc failed\n"); + } + + lw = pGC->lineWidth; + if (lw == 0) + lw = 1; + + extra = lw / 2; + + for (i = 0; i < narcs; i++) + { + rects[i].x = arcs[i].x - extra + pDrawable->x; + rects[i].y = arcs[i].y - extra + pDrawable->y; + rects[i].width = arcs[i].width + lw; + rects[i].height = arcs[i].height + lw; + } + + tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, narcs, rects, CT_NONE); + REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion); + + REGION_DESTROY(pDrawable->pScreen, tmpRegion); + xfree((char *)rects); + } + + (*pGC->ops->PolyArc) (pDrawable, pGC, narcs, arcs); + + if (narcs) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); +} + +/* + * FillPolygon - take bounding box around polygon (and clip). + */ + +static void +rfbFillPolygon(register DrawablePtr pDrawable, register GCPtr pGC, int shape, int mode, int count, DDXPointPtr pts) +{ + int i; + RegionRec tmpRegion; + BoxRec box; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbFillPolygon called\n")); + + if (count) { + int minX = pts[0].x, maxX = pts[0].x; + int minY = pts[0].y, maxY = pts[0].y; + + if (mode == CoordModePrevious) + { + int x = pts[0].x, y = pts[0].y; + + for (i = 1; i < count; i++) { + x += pts[i].x; + y += pts[i].y; + if (x < minX) minX = x; + if (x > maxX) maxX = x; + if (y < minY) minY = y; + if (y > maxY) maxY = y; + } + } + else + { + for (i = 1; i < count; i++) { + if (pts[i].x < minX) minX = pts[i].x; + if (pts[i].x > maxX) maxX = pts[i].x; + if (pts[i].y < minY) minY = pts[i].y; + if (pts[i].y > maxY) maxY = pts[i].y; + } + } + + box.x1 = minX + pDrawable->x; + box.y1 = minY + pDrawable->y; + box.x2 = maxX + 1 + pDrawable->x; + box.y2 = maxY + 1 + pDrawable->y; + + SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0); + + REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion); + + REGION_UNINIT(pDrawable->pScreen, &tmpRegion); + } + + (*pGC->ops->FillPolygon) (pDrawable, pGC, shape, mode, count, pts); + + if (count) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); +} + +/* + * PolyFillRect - take the union of the given rectangles (and clip). + */ + +static void +rfbPolyFillRect(DrawablePtr pDrawable, GCPtr pGC, int nrects, xRectangle *rects) +{ + RegionPtr tmpRegion; + xRectangle *regRects; + int i; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbPolyFillRect called\n")); + + if (nrects) { + regRects = (xRectangle *)xalloc(nrects*sizeof(xRectangle)); + if (!regRects) { + FatalError("rfbPolyFillRect: xalloc failed\n"); + } + + for (i = 0; i < nrects; i++) { + regRects[i].x = rects[i].x + pDrawable->x; + regRects[i].y = rects[i].y + pDrawable->y; + regRects[i].width = rects[i].width; + regRects[i].height = rects[i].height; + } + + tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, nrects, regRects, + CT_NONE); + REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion); + + REGION_DESTROY(pDrawable->pScreen, tmpRegion); + xfree((char *)regRects); + } + + (*pGC->ops->PolyFillRect) (pDrawable, pGC, nrects, rects); + + if (nrects) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); +} + +/* + * PolyFillArc - take the union of bounding boxes around each arc (and clip). + * Bounding boxes assume each is a full circle / ellipse. + */ + +static void +rfbPolyFillArc(DrawablePtr pDrawable, GCPtr pGC, int narcs, xArc *arcs) +{ + int i, extra, lw; + RegionPtr tmpRegion; + xRectangle *rects; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbPolyFillArc called\n")); + + if (narcs) { + rects = (xRectangle *)xalloc(narcs*sizeof(xRectangle)); + if (!rects) { + FatalError("rfbPolyFillArc: xalloc failed\n"); + } + + lw = pGC->lineWidth; + if (lw == 0) + lw = 1; + + extra = lw / 2; + + for (i = 0; i < narcs; i++) + { + rects[i].x = arcs[i].x - extra + pDrawable->x; + rects[i].y = arcs[i].y - extra + pDrawable->y; + rects[i].width = arcs[i].width + lw; + rects[i].height = arcs[i].height + lw; + } + + tmpRegion = RECTS_TO_REGION(pDrawable->pScreen, narcs, rects, CT_NONE); + REGION_INTERSECT(pDrawable->pScreen, tmpRegion, tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, tmpRegion); + + REGION_DESTROY(pDrawable->pScreen, tmpRegion); + xfree((char *)rects); + } + + (*pGC->ops->PolyFillArc) (pDrawable, pGC, narcs, arcs); + + if (narcs) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); +} + +/* + * Get a rough bounding box around n characters of the given font. + */ + +static void GetTextBoundingBox(DrawablePtr pDrawable, FontPtr font, int x, int y, int n, BoxPtr pbox) +{ + int maxAscent, maxDescent, maxCharWidth; + + if (FONTASCENT(font) > FONTMAXBOUNDS(font,ascent)) + maxAscent = FONTASCENT(font); + else + maxAscent = FONTMAXBOUNDS(font,ascent); + + if (FONTDESCENT(font) > FONTMAXBOUNDS(font,descent)) + maxDescent = FONTDESCENT(font); + else + maxDescent = FONTMAXBOUNDS(font,descent); + + if (FONTMAXBOUNDS(font,rightSideBearing) > FONTMAXBOUNDS(font,characterWidth)) + maxCharWidth = FONTMAXBOUNDS(font,rightSideBearing); + else + maxCharWidth = FONTMAXBOUNDS(font,characterWidth); + + pbox->x1 = pDrawable->x + x; + pbox->y1 = pDrawable->y + y - maxAscent; + pbox->x2 = pbox->x1 + maxCharWidth * n; + pbox->y2 = pbox->y1 + maxAscent + maxDescent; + + if (FONTMINBOUNDS(font,leftSideBearing) < 0) { + pbox->x1 += FONTMINBOUNDS(font,leftSideBearing); + } +} + + +/* + * PolyText8 - use rough bounding box. + */ + +static int +rfbPolyText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, char *chars) +{ + int ret; + RegionRec tmpRegion; + BoxRec box; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbPolyText8 called '%.*s'\n",count,chars)); + + if (count) { + GetTextBoundingBox(pDrawable, pGC->font, x, y, count, &box); + + SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0); + + REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion); + + REGION_UNINIT(pDrawable->pScreen, &tmpRegion); + } + + ret = (*pGC->ops->PolyText8) (pDrawable, pGC, x, y, count, chars); + + if (count) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); + return ret; +} + +/* + * PolyText16 - use rough bounding box. + */ + +static int +rfbPolyText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, unsigned short *chars) +{ + int ret; + RegionRec tmpRegion; + BoxRec box; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbPolyText16 called\n")); + + if (count) { + GetTextBoundingBox(pDrawable, pGC->font, x, y, count, &box); + + SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0); + + REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion); + + REGION_UNINIT(pDrawable->pScreen, &tmpRegion); + } + + ret = (*pGC->ops->PolyText16) (pDrawable, pGC, x, y, count, chars); + + if (count) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); + return ret; +} + +/* + * ImageText8 - use rough bounding box. + */ + +static void +rfbImageText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, char *chars) +{ + RegionRec tmpRegion; + BoxRec box; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbImageText8 called '%.*s'\n",count,chars)); + + if (count) { + GetTextBoundingBox(pDrawable, pGC->font, x, y, count, &box); + + SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0); + + REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion); + + REGION_UNINIT(pDrawable->pScreen, &tmpRegion); + } + + (*pGC->ops->ImageText8) (pDrawable, pGC, x, y, count, chars); + + if (count) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); +} + +/* + * ImageText16 - use rough bounding box. + */ + +static void +rfbImageText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, unsigned short *chars) +{ + RegionRec tmpRegion; + BoxRec box; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbImageText16 called\n")); + + if (count) { + GetTextBoundingBox(pDrawable, pGC->font, x, y, count, &box); + + SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0); + + REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion); + + REGION_UNINIT(pDrawable->pScreen, &tmpRegion); + } + + (*pGC->ops->ImageText16) (pDrawable, pGC, x, y, count, chars); + + if (count) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); +} + +/* + * ImageGlyphBlt - use rough bounding box. + */ + +static void +rfbImageGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr *ppci, pointer pglyphBase) +{ + RegionRec tmpRegion; + BoxRec box; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbImageGlyphBlt called\n")); + + if (nglyph) { + GetTextBoundingBox(pDrawable, pGC->font, x, y, nglyph, &box); + + SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0); + + REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion); + + REGION_UNINIT(pDrawable->pScreen, &tmpRegion); + } + + (*pGC->ops->ImageGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci,pglyphBase); + + if (nglyph) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); +} + +/* + * PolyGlyphBlt - use rough bounding box. + */ + +static void +rfbPolyGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, int x, int y, unsigned int nglyph, CharInfoPtr *ppci, pointer pglyphBase) +{ + RegionRec tmpRegion; + BoxRec box; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbPolyGlyphBlt called\n")); + + if (nglyph) { + GetTextBoundingBox(pDrawable, pGC->font, x, y, nglyph, &box); + + SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0); + + REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion); + + REGION_UNINIT(pDrawable->pScreen, &tmpRegion); + } + + (*pGC->ops->PolyGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); + + if (nglyph) { + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + } + + GC_OP_EPILOGUE(pGC); +} + +/* + * PushPixels - be fairly safe - region modified is intersection of the given + * rectangle with the window clip region. + */ + +static void +rfbPushPixels(GCPtr pGC, PixmapPtr pBitMap, DrawablePtr pDrawable, int w, int h, int x, int y) +{ + RegionRec tmpRegion; + BoxRec box; + GC_OP_PROLOGUE(pDrawable, pGC); + + TRC((stderr,"rfbPushPixels called\n")); + + box.x1 = x + pDrawable->x; + box.y1 = y + pDrawable->y; + box.x2 = box.x1 + w; + box.y2 = box.y1 + h; + + SAFE_REGION_INIT(pDrawable->pScreen, &tmpRegion, &box, 0); + + REGION_INTERSECT(pDrawable->pScreen, &tmpRegion, &tmpRegion, + pGC->pCompositeClip); + + ADD_TO_MODIFIED_REGION(pDrawable->pScreen, &tmpRegion); + + REGION_UNINIT(pDrawable->pScreen, &tmpRegion); + + (*pGC->ops->PushPixels) (pGC, pBitMap, pDrawable, w, h, x, y); + + SCHEDULE_FB_UPDATE(pDrawable->pScreen, pVNC); + + GC_OP_EPILOGUE(pGC); +} + +#ifdef RENDER +void +rfbComposite( + CARD8 op, + PicturePtr pSrc, + PicturePtr pMask, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + INT16 xMask, + INT16 yMask, + INT16 xDst, + INT16 yDst, + CARD16 width, + CARD16 height +){ + ScreenPtr pScreen = pDst->pDrawable->pScreen; + VNCSCREENPTR(pScreen); + RegionRec tmpRegion; + BoxRec box; + PictureScreenPtr ps = GetPictureScreen(pScreen); + + box.x1 = pDst->pDrawable->x + xDst; + box.y1 = pDst->pDrawable->y + yDst; + box.x2 = box.x1 + width; + box.y2 = box.y1 + height; + + REGION_INIT(pScreen, &tmpRegion, &box, 0); + + ADD_TO_MODIFIED_REGION(pScreen, &tmpRegion); + + ps->Composite = pVNC->Composite; + (*ps->Composite)(op, pSrc, pMask, pDst, xSrc, ySrc, + xMask, yMask, xDst, yDst, width, height); + ps->Composite = rfbComposite; + + SCHEDULE_FB_UPDATE(pScreen, pVNC); + + REGION_UNINIT(pScreen, &tmpRegion); +} +#endif /* RENDER */ + +/****************************************************************************/ +/* + * Other functions + */ +/****************************************************************************/ + +/* + * rfbCopyRegion. Args are src and dst regions plus a translation (dx,dy). + * Takes these args together with the existing modified region and possibly an + * existing copy region and translation. Produces a combined modified region + * plus copy region and translation. Note that the copy region is the + * destination of the copy. + * + * First we trim parts of src which are invalid (ie in the modified region). + * Then we see if there is any overlap between the src and the existing copy + * region. If not then the two copies cannot be combined, so we choose + * whichever is bigger to form the basis of a new copy, while the other copy is + * just done the hard way by being added to the modified region. So if the + * existing copy is bigger then we simply add the destination of the new copy + * to the modified region and we're done. If the new copy is bigger, we add + * the old copy region to the modified region and behave as though there is no + * existing copy region. + * + * At this stage we now know that either the two copies can be combined, or + * that there is no existing copy. We temporarily add both the existing copy + * region and dst to the modified region (this is the entire area of the screen + * affected in any way). Finally we calculate the new copy region, and remove + * it from the modified region. + * + * Note: + * 1. The src region is modified by this routine. + * 2. When the copy region is empty, copyDX and copyDY MUST be set to zero. + */ + +static void +rfbCopyRegion(pScreen, cl, src, dst, dx, dy) + ScreenPtr pScreen; + rfbClientPtr cl; + RegionPtr src; + RegionPtr dst; + int dx, dy; +{ + RegionRec tmp; + + /* src = src - modifiedRegion */ + + REGION_SUBTRACT(pScreen, src, src, &cl->modifiedRegion); + + if (REGION_NOTEMPTY(pScreen, &cl->copyRegion)) { + + REGION_NULL(pScreen, &tmp); + REGION_INTERSECT(pScreen, &tmp, src, &cl->copyRegion); + + if (REGION_NOTEMPTY(pScreen, &tmp)) { + + /* if src and copyRegion overlap: + src = src intersect copyRegion */ + + REGION_COPY(pScreen, src, &tmp); + + } else { + + /* if no overlap, find bigger region */ + + int newArea = (((REGION_EXTENTS(pScreen,src))->x2 + - (REGION_EXTENTS(pScreen,src))->x1) + * ((REGION_EXTENTS(pScreen,src))->y2 + - (REGION_EXTENTS(pScreen,src))->y1)); + + int oldArea = (((REGION_EXTENTS(pScreen,&cl->copyRegion))->x2 + - (REGION_EXTENTS(pScreen,&cl->copyRegion))->x1) + * ((REGION_EXTENTS(pScreen,&cl->copyRegion))->y2 + - (REGION_EXTENTS(pScreen,&cl->copyRegion))->y1)); + + if (oldArea > newArea) { + + /* existing copy is bigger: + modifiedRegion = modifiedRegion union dst + copyRegion = copyRegion - dst + return */ + + REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion, + dst); + REGION_SUBTRACT(pScreen, &cl->copyRegion, &cl->copyRegion, + dst); + if (!REGION_NOTEMPTY(pScreen, &cl->copyRegion)) { + cl->copyDX = 0; + cl->copyDY = 0; + } + return; + } + + /* new copy is bigger: + modifiedRegion = modifiedRegion union copyRegion + copyRegion = empty */ + + REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion, + &cl->copyRegion); + REGION_EMPTY(pScreen, &cl->copyRegion); + cl->copyDX = cl->copyDY = 0; + } + } + + + /* modifiedRegion = modifiedRegion union dst union copyRegion */ + + REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion, dst); + REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion, + &cl->copyRegion); + + /* copyRegion = T(src) intersect dst */ + + REGION_TRANSLATE(pScreen, src, dx, dy); + REGION_INTERSECT(pScreen, &cl->copyRegion, src, dst); + + /* modifiedRegion = modifiedRegion - copyRegion */ + + REGION_SUBTRACT(pScreen, &cl->modifiedRegion, &cl->modifiedRegion, + &cl->copyRegion); + + /* combine new translation T with existing translation */ + + if (REGION_NOTEMPTY(pScreen, &cl->copyRegion)) { + cl->copyDX += dx; + cl->copyDY += dy; + } else { + cl->copyDX = 0; + cl->copyDY = 0; + } +} + + +/* + * rfbDeferredUpdateCallback() is called when a client's deferredUpdateTimer + * goes off. + */ + +static CARD32 +rfbDeferredUpdateCallback(OsTimerPtr timer, CARD32 now, pointer arg) +{ + rfbClientPtr cl = (rfbClientPtr)arg; + + rfbSendFramebufferUpdate(cl->pScreen, cl); + + cl->deferredUpdateScheduled = FALSE; + return 0; +} + + +/* + * rfbScheduleDeferredUpdate() is called from the SCHEDULE_FB_UPDATE macro + * to schedule an update. + */ + +static void +rfbScheduleDeferredUpdate(ScreenPtr pScreen, rfbClientPtr cl) +{ + if (rfbDeferUpdateTime != 0) { + cl->deferredUpdateTimer = TimerSet(cl->deferredUpdateTimer, 0, + rfbDeferUpdateTime, + rfbDeferredUpdateCallback, cl); + cl->deferredUpdateScheduled = TRUE; + } else { + rfbSendFramebufferUpdate(pScreen, cl); + } +} + + +/* + * PrintRegion is useful for debugging. + */ + +#ifdef DEBUG +static void +PrintRegion(ScreenPtr pScreen, RegionPtr reg) +{ + int nrects = REGION_NUM_RECTS(reg); + int i; + + ErrorF("Region num rects %d extents %d,%d %d,%d\n",nrects, + (REGION_EXTENTS(pScreen,reg))->x1, + (REGION_EXTENTS(pScreen,reg))->y1, + (REGION_EXTENTS(pScreen,reg))->x2, + (REGION_EXTENTS(pScreen,reg))->y2); + + for (i = 0; i < nrects; i++) { + ErrorF(" rect %d,%d %dx%d\n", + REGION_RECTS(reg)[i].x1, + REGION_RECTS(reg)[i].y1, + REGION_RECTS(reg)[i].x2-REGION_RECTS(reg)[i].x1, + REGION_RECTS(reg)[i].y2-REGION_RECTS(reg)[i].y1); + } +} +#endif + +/** + * Allow scheduling updates from other functions in other files. + */ +void +rfbScheduleUpdate(ScreenPtr pScreen) +{ + VNCSCREENPTR(pScreen); + SCHEDULE_FB_UPDATE(pScreen, pVNC); +} diff --git a/hw/vnc/hextile.c b/hw/vnc/hextile.c new file mode 100644 index 0000000..eb5e281 --- /dev/null +++ b/hw/vnc/hextile.c @@ -0,0 +1,350 @@ +/* + * hextile.c + * + * Routines to implement Hextile Encoding + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include +#include "rfb.h" + +static Bool sendHextiles8(rfbClientPtr cl, int x, int y, int w, int h); +static Bool sendHextiles16(rfbClientPtr cl, int x, int y, int w, int h); +static Bool sendHextiles32(rfbClientPtr cl, int x, int y, int w, int h); + + +/* + * rfbSendRectEncodingHextile - send a rectangle using hextile encoding. + */ + +Bool +rfbSendRectEncodingHextile(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + VNCSCREENPTR(cl->pScreen); + rfbFramebufferUpdateRectHeader rect; + + if (pVNC->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingHextile); + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + pVNC->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbRectanglesSent[rfbEncodingHextile]++; + cl->rfbBytesSent[rfbEncodingHextile] += sz_rfbFramebufferUpdateRectHeader; + + switch (cl->format.bitsPerPixel) { + case 8: + return sendHextiles8(cl, x, y, w, h); + case 16: + return sendHextiles16(cl, x, y, w, h); + case 32: + return sendHextiles32(cl, x, y, w, h); + } + + rfbLog("rfbSendRectEncodingHextile: bpp %d?\n", cl->format.bitsPerPixel); + return FALSE; +} + + +#define PUT_PIXEL8(pix) (pVNC->updateBuf[pVNC->ublen++] = (pix)) + +#define PUT_PIXEL16(pix) (pVNC->updateBuf[pVNC->ublen++] = ((char*)&(pix))[0], \ + pVNC->updateBuf[pVNC->ublen++] = ((char*)&(pix))[1]) + +#define PUT_PIXEL32(pix) (pVNC->updateBuf[pVNC->ublen++] = ((char*)&(pix))[0], \ + pVNC->updateBuf[pVNC->ublen++] = ((char*)&(pix))[1], \ + pVNC->updateBuf[pVNC->ublen++] = ((char*)&(pix))[2], \ + pVNC->updateBuf[pVNC->ublen++] = ((char*)&(pix))[3]) + + +#define DEFINE_SEND_HEXTILES(bpp) \ + \ + \ +static Bool subrectEncode##bpp(ScreenPtr pScreen, CARD##bpp *data, int w, \ + int h, CARD##bpp bg, \ + CARD##bpp fg, Bool mono); \ +static void testColours##bpp(CARD##bpp *data, int size, Bool *mono, \ + Bool *solid, CARD##bpp *bg, CARD##bpp *fg); \ + \ + \ +/* \ + * rfbSendHextiles \ + */ \ + \ +static Bool \ +sendHextiles##bpp(cl, rx, ry, rw, rh) \ + rfbClientPtr cl; \ + int rx, ry, rw, rh; \ +{ \ + VNCSCREENPTR(cl->pScreen); \ + int x, y, w, h; \ + int startUblen; \ + CARD##bpp bg = 0, fg = 0, newBg, newFg; \ + Bool mono, solid; \ + Bool validBg = FALSE; \ + Bool validFg = FALSE; \ + CARD##bpp clientPixelData[16*16*(bpp/8)]; \ + \ + 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 ((pVNC->ublen + 1 + (2 + 16 * 16) * (bpp/8)) > UPDATE_BUF_SIZE) { \ + if (!rfbSendUpdateBuf(cl)) \ + return FALSE; \ + } \ + \ + (*cl->translateFn)(cl->pScreen, cl->translateLookupTable, \ + &pVNC->rfbServerFormat, \ + &cl->format, (unsigned char *)clientPixelData, \ + pVNC->paddedWidthInBytes, w, h, x, y); \ + \ + startUblen = pVNC->ublen; \ + pVNC->updateBuf[startUblen] = 0; \ + pVNC->ublen++; \ + \ + testColours##bpp(clientPixelData, w * h, \ + &mono, &solid, &newBg, &newFg); \ + \ + if (!validBg || (newBg != bg)) { \ + validBg = TRUE; \ + bg = newBg; \ + pVNC->updateBuf[startUblen] |= rfbHextileBackgroundSpecified; \ + PUT_PIXEL##bpp(bg); \ + } \ + \ + if (solid) { \ + cl->rfbBytesSent[rfbEncodingHextile] += pVNC->ublen - startUblen; \ + continue; \ + } \ + \ + pVNC->updateBuf[startUblen] |= rfbHextileAnySubrects; \ + \ + if (mono) { \ + if (!validFg || (newFg != fg)) { \ + validFg = TRUE; \ + fg = newFg; \ + pVNC->updateBuf[startUblen] |= rfbHextileForegroundSpecified; \ + PUT_PIXEL##bpp(fg); \ + } \ + } else { \ + validFg = FALSE; \ + pVNC->updateBuf[startUblen] |= rfbHextileSubrectsColoured; \ + } \ + \ + if (!subrectEncode##bpp(cl->pScreen, clientPixelData, w, h, bg, fg, mono)) { \ + /* encoding was too large, use raw */ \ + validBg = FALSE; \ + validFg = FALSE; \ + pVNC->ublen = startUblen; \ + pVNC->updateBuf[pVNC->ublen++] = rfbHextileRaw; \ + (*cl->translateFn)(cl->pScreen, cl->translateLookupTable, \ + &pVNC->rfbServerFormat, &cl->format, \ + (unsigned char *)clientPixelData, \ + pVNC->paddedWidthInBytes, w, h, x, y); \ + \ + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)clientPixelData,\ + w * h * (bpp/8)); \ + \ + pVNC->ublen += w * h * (bpp/8); \ + } \ + \ + cl->rfbBytesSent[rfbEncodingHextile] += pVNC->ublen - startUblen; \ + } \ + } \ + \ + return TRUE; \ +} \ + \ + \ +static Bool \ +subrectEncode##bpp(ScreenPtr pScreen, CARD##bpp *data, int w, int h, \ + CARD##bpp bg, CARD##bpp fg, Bool mono) \ +{ \ + VNCSCREENPTR(pScreen); \ + CARD##bpp clientdata; \ + int x,y; \ + int i,j; \ + int hx=0,hy,vx=0,vy; \ + int hyflag; \ + CARD##bpp *seg; \ + CARD##bpp *line; \ + int hw,hh,vw,vh; \ + int thex,they,thew,theh; \ + int numsubs = 0; \ + int newLen; \ + int nSubrectsUblen; \ + \ + nSubrectsUblen = pVNC->ublen; \ + pVNC->ublen++; \ + \ + for (y=0; y 0) && (i >= hx)) { \ + hy += 1; \ + } else { \ + hyflag = 0; \ + } \ + } \ + vy = j-1; \ + \ + /* We now have two possible subrects: (x,y,hx,hy) and \ + * (x,y,vx,vy). We'll choose the bigger of the two. \ + */ \ + hw = hx-x+1; \ + hh = hy-y+1; \ + vw = vx-x+1; \ + vh = vy-y+1; \ + \ + thex = x; \ + they = y; \ + \ + if ((hw*hh) > (vw*vh)) { \ + thew = hw; \ + theh = hh; \ + } else { \ + thew = vw; \ + theh = vh; \ + } \ + \ + if (mono) { \ + newLen = pVNC->ublen - nSubrectsUblen + 2; \ + } else { \ + newLen = pVNC->ublen - nSubrectsUblen + bpp/8 + 2; \ + } \ + \ + if (newLen > (w * h * (bpp/8))) \ + return FALSE; \ + \ + numsubs += 1; \ + \ + if (!mono) PUT_PIXEL##bpp(clientdata); \ + \ + pVNC->updateBuf[pVNC->ublen++] = rfbHextilePackXY(thex,they); \ + pVNC->updateBuf[pVNC->ublen++] = rfbHextilePackWH(thew,theh); \ + \ + /* \ + * Now mark the subrect as done. \ + */ \ + for (j=they; j < (they+theh); j++) { \ + for (i=thex; i < (thex+thew); i++) { \ + data[j*w+i] = bg; \ + } \ + } \ + } \ + } \ + } \ + \ + pVNC->updateBuf[nSubrectsUblen] = numsubs; \ + \ + return TRUE; \ +} \ + \ + \ +/* \ + * testColours() tests if there are one (solid), two (mono) or more \ + * colours in a tile and gets a reasonable guess at the best background \ + * pixel, and the foreground pixel for mono. \ + */ \ + \ +static void \ +testColours##bpp(data,size,mono,solid,bg,fg) \ + CARD##bpp *data; \ + int size; \ + Bool *mono; \ + Bool *solid; \ + CARD##bpp *bg; \ + CARD##bpp *fg; \ +{ \ + CARD##bpp colour1 = 0, colour2 = 0; \ + int n1 = 0, n2 = 0; \ + *mono = TRUE; \ + *solid = TRUE; \ + \ + for (; size > 0; size--, data++) { \ + \ + if (n1 == 0) \ + colour1 = *data; \ + \ + if (*data == colour1) { \ + n1++; \ + continue; \ + } \ + \ + if (n2 == 0) { \ + *solid = FALSE; \ + colour2 = *data; \ + } \ + \ + if (*data == colour2) { \ + n2++; \ + continue; \ + } \ + \ + *mono = FALSE; \ + break; \ + } \ + \ + if (n1 > n2) { \ + *bg = colour1; \ + *fg = colour2; \ + } else { \ + *bg = colour2; \ + *fg = colour1; \ + } \ +} + +DEFINE_SEND_HEXTILES(8) +DEFINE_SEND_HEXTILES(16) +DEFINE_SEND_HEXTILES(32) diff --git a/hw/vnc/httpd.c b/hw/vnc/httpd.c new file mode 100644 index 0000000..5f56ad6 --- /dev/null +++ b/hw/vnc/httpd.c @@ -0,0 +1,519 @@ +/* + * httpd.c - a simple HTTP server + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * Copyright (C) 2002 Constantin Kaplinsky. 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef USE_LIBWRAP +#define USE_LIBWRAP 0 +#endif +#if USE_LIBWRAP +#include +#endif + +#include "rfb.h" + +#define NOT_FOUND_STR "HTTP/1.0 404 Not found\r\n\r\n" \ + "File Not Found\n" \ + "

File Not Found

\n" + +#define OK_STR "HTTP/1.0 200 OK\r\n\r\n" + +static void httpProcessInput(ScreenPtr pScreen); +static Bool compareAndSkip(char **ptr, const char *str); +static Bool parseParams(const char *request, char *result, int max_bytes); +static Bool validateString(char *str); + +/* + * httpInitSockets sets up the TCP socket to listen for HTTP connections. + */ + +Bool +httpInitSockets(ScreenPtr pScreen) +{ + VNCSCREENPTR(pScreen); + + if (!pVNC->httpDir) + return FALSE; + + pVNC->buf_filled = 0; + + if (pVNC->httpPort == 0) { + pVNC->httpPort = 5800 + atoi(display) + pScreen->myNum; + } + + if ((pVNC->httpListenSock = ListenOnTCPPort(pScreen, pVNC->httpPort)) < 0) { + rfbLog("ListenOnTCPPort %d failed\n",pVNC->httpPort); + pVNC->httpPort = 0; + return FALSE; + } + + rfbLog("Listening for HTTP connections on TCP port %d\n", pVNC->httpPort); + rfbLog(" URL http://%s:%d\n",rfbThisHost,pVNC->httpPort); + + AddEnabledDevice(pVNC->httpListenSock); + + return TRUE; +} + + +/* + * httpCheckFds is called from ProcessInputEvents to check for input on the + * HTTP socket(s). If there is input to process, httpProcessInput is called. + */ + +void +httpCheckFds(ScreenPtr pScreen) +{ + VNCSCREENPTR(pScreen); + int nfds; + fd_set fds; + struct timeval tv; + struct sockaddr_in addr; + SOCKLEN_T addrlen = sizeof(addr); + + if (!pVNC->httpDir) + return; + + FD_ZERO(&fds); + FD_SET(pVNC->httpListenSock, &fds); + if (pVNC->httpSock >= 0) { + FD_SET(pVNC->httpSock, &fds); + } + tv.tv_sec = 0; + tv.tv_usec = 0; + nfds = select(max(pVNC->httpSock,pVNC->httpListenSock) + 1, &fds, NULL, NULL, &tv); + if (nfds == 0) { + return; + } + if (nfds < 0) { + if (errno != EINTR) + rfbLogPerror("httpCheckFds: select"); + return; + } + + if ((pVNC->httpSock >= 0) && FD_ISSET(pVNC->httpSock, &fds)) { + httpProcessInput(pScreen); + } + + if (FD_ISSET(pVNC->httpListenSock, &fds)) { + int flags; + + if (pVNC->httpSock >= 0) close(pVNC->httpSock); + + if ((pVNC->httpSock = accept(pVNC->httpListenSock, + (struct sockaddr *)&addr, &addrlen)) < 0) { + rfbLogPerror("httpCheckFds: accept"); + return; + } + +#if USE_LIBWRAP + if (!hosts_ctl("Xvnc", STRING_UNKNOWN, inet_ntoa(addr.sin_addr), + STRING_UNKNOWN)) { + rfbLog("Rejected HTTP connection from client %s\n", + inet_ntoa(addr.sin_addr)); + close(pVNC->httpSock); + pVNC->httpSock = -1; + return; + } +#endif + + flags = fcntl (pVNC->httpSock, F_GETFL); + + if (flags == -1 || + fcntl (pVNC->httpSock, F_SETFL, flags | O_NONBLOCK) == -1) { + rfbLogPerror("httpCheckFds: fcntl"); + close (pVNC->httpSock); + pVNC->httpSock = -1; + return; + } + + AddEnabledDevice(pVNC->httpSock); + } +} + + +static void +httpCloseSock(ScreenPtr pScreen) +{ + VNCSCREENPTR(pScreen); + close(pVNC->httpSock); + RemoveEnabledDevice(pVNC->httpSock); + pVNC->httpSock = -1; + pVNC->buf_filled = 0; +} + + +/* + * httpProcessInput is called when input is received on the HTTP socket. + */ + +static void +httpProcessInput(ScreenPtr pScreen) +{ + VNCSCREENPTR(pScreen); + struct sockaddr_in addr; + SOCKLEN_T addrlen = sizeof(addr); + char fullFname[512]; + char params[1024]; + char *ptr; + char *fname; + int maxFnameLen; + int fd; + Bool performSubstitutions = FALSE; + char str[256]; + struct passwd *user = getpwuid(getuid()); + + if (strlen(pVNC->httpDir) > 255) { + rfbLog("-httpd directory too long\n"); + httpCloseSock(pScreen); + return; + } + strcpy(fullFname, pVNC->httpDir); + fname = &fullFname[strlen(fullFname)]; + maxFnameLen = 511 - strlen(fullFname); + + /* Read data from the HTTP client until we get a complete request. */ + while (1) { + ssize_t got = read (pVNC->httpSock, pVNC->buf + pVNC->buf_filled, + sizeof (pVNC->buf) - pVNC->buf_filled - 1); + + if (got <= 0) { + if (got == 0) { + rfbLog("httpd: premature connection close\n"); + } else { + if (errno == EAGAIN) { + return; + } + rfbLogPerror("httpProcessInput: read"); + } + httpCloseSock(pScreen); + return; + } + + pVNC->buf_filled += got; + pVNC->buf[pVNC->buf_filled] = '\0'; + + /* Is it complete yet (is there a blank line)? */ + if (strstr (pVNC->buf, "\r\r") || strstr (pVNC->buf, "\n\n") || + strstr (pVNC->buf, "\r\n\r\n") || strstr (pVNC->buf, "\n\r\n\r")) + break; + } + + /* Process the request. */ + if (strncmp(pVNC->buf, "GET ", 4)) { + rfbLog("httpd: no GET line\n"); + httpCloseSock(pScreen); + return; + } else { + /* Only use the first line. */ + pVNC->buf[strcspn(pVNC->buf, "\n\r")] = '\0'; + } + + if (strlen(pVNC->buf) > maxFnameLen) { + rfbLog("httpd: GET line too long\n"); + httpCloseSock(pScreen); + return; + } + + if (sscanf(pVNC->buf, "GET %s HTTP/1.0", fname) != 1) { + rfbLog("httpd: couldn't parse GET line\n"); + httpCloseSock(pScreen); + return; + } + + if (fname[0] != '/') { + rfbLog("httpd: filename didn't begin with '/'\n"); + WriteExact(pVNC->httpSock, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); + httpCloseSock(pScreen); + return; + } + + if (strchr(fname+1, '/') != NULL) { + rfbLog("httpd: asking for file in other directory\n"); + WriteExact(pVNC->httpSock, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); + httpCloseSock(pScreen); + return; + } + + getpeername(pVNC->httpSock, (struct sockaddr *)&addr, &addrlen); + rfbLog("httpd: get '%s' for %s\n", fname+1, + inet_ntoa(addr.sin_addr)); + + /* Extract parameters from the URL string if necessary */ + + params[0] = '\0'; + ptr = strchr(fname, '?'); + if (ptr != NULL) { + *ptr = '\0'; + if (!parseParams(&ptr[1], params, 1024)) { + params[0] = '\0'; + rfbLog("httpd: bad parameters in the URL\n"); + } + } + + /* If we were asked for '/', actually read the file index.vnc */ + + if (strcmp(fname, "/") == 0) { + strcpy(fname, "/index.vnc"); + rfbLog("httpd: defaulting to '%s'\n", fname+1); + } + + /* Substitutions are performed on files ending .vnc */ + + if (strlen(fname) >= 4 && strcmp(&fname[strlen(fname)-4], ".vnc") == 0) { + performSubstitutions = TRUE; + } + + /* Open the file */ + + if ((fd = open(fullFname, O_RDONLY)) < 0) { + rfbLogPerror("httpProcessInput: open"); + WriteExact(pVNC->httpSock, NOT_FOUND_STR, strlen(NOT_FOUND_STR)); + httpCloseSock(pScreen); + return; + } + + WriteExact(pVNC->httpSock, OK_STR, strlen(OK_STR)); + + while (1) { + int n = read(fd, pVNC->buf, HTTP_BUF_SIZE-1); + if (n < 0) { + rfbLogPerror("httpProcessInput: read"); + close(fd); + httpCloseSock(pScreen); + return; + } + + if (n == 0) + break; + + if (performSubstitutions) { + + /* Substitute $WIDTH, $HEIGHT, etc with the appropriate values. + This won't quite work properly if the .vnc file is longer than + HTTP_BUF_SIZE, but it's reasonable to assume that .vnc files will + always be short. */ + + char *ptr = pVNC->buf; + char *dollar; + pVNC->buf[n] = 0; /* make sure it's null-terminated */ + + while ((dollar = strchr(ptr, '$'))) { + WriteExact(pVNC->httpSock, ptr, (dollar - ptr)); + + ptr = dollar; + + if (compareAndSkip(&ptr, "$WIDTH")) { + + sprintf(str, "%d", pVNC->width); + WriteExact(pVNC->httpSock, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$HEIGHT")) { + + sprintf(str, "%d", pVNC->height); + WriteExact(pVNC->httpSock, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$APPLETWIDTH")) { + + sprintf(str, "%d", pVNC->width); + WriteExact(pVNC->httpSock, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$APPLETHEIGHT")) { + + sprintf(str, "%d", pVNC->height + 32); + WriteExact(pVNC->httpSock, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$PORT")) { + + sprintf(str, "%d", pVNC->rfbPort); + WriteExact(pVNC->httpSock, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$DESKTOP")) { + + WriteExact(pVNC->httpSock, desktopName, strlen(desktopName)); + + } else if (compareAndSkip(&ptr, "$DISPLAY")) { + + sprintf(str, "%s:%s", rfbThisHost, display); + WriteExact(pVNC->httpSock, str, strlen(str)); + + } else if (compareAndSkip(&ptr, "$USER")) { + + if (user) { + WriteExact(pVNC->httpSock, user->pw_name, + strlen(user->pw_name)); + } else { + WriteExact(pVNC->httpSock, "?", 1); + } + + } else if (compareAndSkip(&ptr, "$PARAMS")) { + + if (params[0] != '\0') + WriteExact(pVNC->httpSock, params, strlen(params)); + + } else { + if (!compareAndSkip(&ptr, "$$")) + ptr++; + + if (WriteExact(pVNC->httpSock, "$", 1) < 0) { + close(fd); + httpCloseSock(pScreen); + return; + } + } + } + if (WriteExact(pVNC->httpSock, ptr, (&pVNC->buf[n] - ptr)) < 0) + break; + + } else { + + /* For files not ending .vnc, just write out the buffer */ + + if (WriteExact(pVNC->httpSock, pVNC->buf, n) < 0) + break; + } + } + + close(fd); + httpCloseSock(pScreen); +} + + +static Bool +compareAndSkip(char **ptr, const char *str) +{ + if (strncmp(*ptr, str, strlen(str)) == 0) { + *ptr += strlen(str); + return TRUE; + } + + return FALSE; +} + +/* + * Parse the request tail after the '?' character, and format a sequence + * of tags for inclusion into an HTML page with embedded applet. + */ + +static Bool +parseParams(const char *request, char *result, int max_bytes) +{ + char param_request[128]; + char param_formatted[196]; + const char *tail; + char *delim_ptr; + char *value_str; + int cur_bytes, len; + + result[0] = '\0'; + cur_bytes = 0; + + tail = request; + for (;;) { + /* Copy individual "name=value" string into a buffer */ + delim_ptr = strchr((char *)tail, '&'); + if (delim_ptr == NULL) { + if (strlen(tail) >= sizeof(param_request)) { + return FALSE; + } + strcpy(param_request, tail); + } else { + len = delim_ptr - tail; + if (len >= sizeof(param_request)) { + return FALSE; + } + memcpy(param_request, tail, len); + param_request[len] = '\0'; + } + + /* Split the request into parameter name and value */ + value_str = strchr(¶m_request[1], '='); + if (value_str == NULL) { + return FALSE; + } + *value_str++ = '\0'; + if (strlen(value_str) == 0) { + return FALSE; + } + + /* Validate both parameter name and value */ + if (!validateString(param_request) || !validateString(value_str)) { + return FALSE; + } + + /* Prepare HTML-formatted representation of the name=value pair */ + len = sprintf(param_formatted, + "\n", + param_request, value_str); + if (cur_bytes + len + 1 > max_bytes) { + return FALSE; + } + strcat(result, param_formatted); + cur_bytes += len; + + /* Go to the next parameter */ + if (delim_ptr == NULL) { + break; + } + tail = delim_ptr + 1; + } + return TRUE; +} + +/* + * Check if the string consists only of alphanumeric characters, '+' + * signs, underscores, and dots. Replace all '+' signs with spaces. + */ + +static Bool +validateString(char *str) +{ + char *ptr; + + for (ptr = str; *ptr != '\0'; ptr++) { + if (!isalnum(*ptr) && *ptr != '_' && *ptr != '.') { + if (*ptr == '+') { + *ptr = ' '; + } else { + return FALSE; + } + } + } + return TRUE; +} + diff --git a/hw/vnc/init.c b/hw/vnc/init.c new file mode 100644 index 0000000..a166358 --- /dev/null +++ b/hw/vnc/init.c @@ -0,0 +1,1078 @@ +/* + * init.c + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ + +/* + +Copyright (c) 1993 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the X Consortium. + +*/ + +/* Use ``#define CORBA'' to enable CORBA control interface */ + +/* XXX this definition should probably go elsewhere */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#ifndef XVNCRELEASE +#define XVNCRELEASE "X.org/xf4vnc custom version" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "X11/X.h" +#define NEED_EVENTS +#include "X11/Xproto.h" +#include "X11/Xos.h" +#include "scrnintstr.h" +#include "servermd.h" +#include "fb.h" +#include "mibstore.h" +#include "colormapst.h" +#include "gcstruct.h" +#include "input.h" +#include "mipointer.h" +#include "dixstruct.h" +#include +#include +#include +#include "dix.h" +#include "micmap.h" +#include "rfb.h" + +#ifdef CORBA +#include +#endif + +#define RFB_DEFAULT_WIDTH 640 +#define RFB_DEFAULT_HEIGHT 480 +#define RFB_DEFAULT_DEPTH 8 +#define RFB_DEFAULT_WHITEPIXEL 0 +#define RFB_DEFAULT_BLACKPIXEL 1 + +static unsigned long VNCGeneration = 0; +rfbScreenInfo rfbScreen; +extern char dispatchExceptionAtReset; + +extern void VncExtensionInit(void); + +static Bool initOutputCalled = FALSE; +static Bool noCursor = FALSE; +char *desktopName = "x11"; + +char rfbThisHost[256]; + +Atom VNC_LAST_CLIENT_ID; +Atom VNC_CONNECT; + +#if 0 +static HWEventQueueType alwaysCheckForInput[2] = { 0, 1 }; +static HWEventQueueType *mieqCheckForInput[2]; +#endif + +static char primaryOrder[4] = ""; +static int redBits, greenBits, blueBits; + +static Bool rfbScreenInit(int index, ScreenPtr pScreen, int argc, + char **argv); +static int rfbKeybdProc(DeviceIntPtr pDevice, int onoff); +static int rfbMouseProc(DeviceIntPtr pDevice, int onoff); +static Bool CheckDisplayNumber(int n); + +static Bool rfbAlwaysTrue(void); +static unsigned char *rfbAllocateFramebufferMemory(rfbScreenInfoPtr prfb); +static Bool rfbCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y); +static void rfbCrossScreen(ScreenPtr pScreen, Bool entering); + + + +static void +PointerWarpCursor(ScreenPtr pScreen, int x, int y) +{ +#if 0 + DeviceIntPtr pDev = NULL; + miPointerSetPosition(pDev, &x, &y, GetTimeInMillis()); +#endif +} + + +static miPointerScreenFuncRec rfbPointerCursorFuncs = { + rfbCursorOffScreen, + rfbCrossScreen, + PointerWarpCursor, + NULL/*dmxeqEnqueue*/, + NULL/*dmxeqSwitchScreen*/ +}; + + +int inetdSock = -1; +static char inetdDisplayNumStr[10]; + + +void +DDXRingBell(int volume, int pitch, int duration) +{ + /* NO-OP - stub to solve link problem */ +} + + +/* + * ddxProcessArgument is our first entry point and will be called at the + * very start for each argument. It is not called again on server reset. + */ + +int +ddxProcessArgument (argc, argv, i) + int argc; + char *argv[]; + int i; +{ + VNCSCREENPTR(screenInfo.screens[i]); + static Bool firstTime = TRUE; + + if (firstTime) + { + pVNC->width = RFB_DEFAULT_WIDTH; + pVNC->height = RFB_DEFAULT_HEIGHT; + pVNC->depth = RFB_DEFAULT_DEPTH; + pVNC->blackPixel = RFB_DEFAULT_BLACKPIXEL; + pVNC->whitePixel = RFB_DEFAULT_WHITEPIXEL; + pVNC->pfbMemory = NULL; + pVNC->httpPort = 0; + pVNC->httpDir = NULL; + pVNC->rfbAuthPasswdFile = NULL; + pVNC->udpPort = 0; + pVNC->rfbPort = 0; + pVNC->rdpPort = 3389; + noCursor = FALSE; + pVNC->loginAuthEnabled = FALSE; + pVNC->rfbAlwaysShared = FALSE; + pVNC->rfbNeverShared = FALSE; + pVNC->rfbDontDisconnect = FALSE; + pVNC->rfbViewOnly = FALSE; + + gethostname(rfbThisHost, 255); + pVNC->interface.s_addr = htonl (INADDR_ANY); + firstTime = FALSE; + } + + if (strcmp (argv[i], "-geometry") == 0) /* -geometry WxH */ + { + if (i + 1 >= argc) UseMsg(); + if (sscanf(argv[i+1],"%dx%d", + &pVNC->width,&pVNC->height) != 2) { + ErrorF("Invalid geometry %s\n", argv[i+1]); + UseMsg(); + } +#ifdef CORBA + screenWidth= pVNC->width; + screenHeight= pVNC->height; +#endif + return 2; + } + + if (strcmp (argv[i], "-depth") == 0) /* -depth D */ + { + if (i + 1 >= argc) UseMsg(); + pVNC->depth = atoi(argv[i+1]); +#ifdef CORBA + screenDepth= pVNC->depth; +#endif + return 2; + } + + if (strcmp (argv[i], "-pixelformat") == 0) { + if (i + 1 >= argc) UseMsg(); + if (sscanf(argv[i+1], "%3s", primaryOrder) < 1) { + ErrorF("Invalid pixel format %s\n", argv[i+1]); + UseMsg(); + } + + return 2; + } + + if (strcmp (argv[i], "-blackpixel") == 0) { /* -blackpixel n */ + if (i + 1 >= argc) UseMsg(); + pVNC->blackPixel = atoi(argv[i+1]); + return 2; + } + + if (strcmp (argv[i], "-whitepixel") == 0) { /* -whitepixel n */ + if (i + 1 >= argc) UseMsg(); + pVNC->whitePixel = atoi(argv[i+1]); + return 2; + } + + if (strcmp(argv[i], "-udpinputport") == 0) { /* -udpinputport port */ + if (i + 1 >= argc) UseMsg(); + pVNC->udpPort = atoi(argv[i+1]); + return 2; + } + + if (strcmp(argv[i], "-rfbport") == 0) { /* -rfbport port */ + if (i + 1 >= argc) UseMsg(); + pVNC->rfbPort = atoi(argv[i+1]); + return 2; + } + + if (strcmp(argv[i], "-rfbwait") == 0) { /* -rfbwait ms */ + if (i + 1 >= argc) UseMsg(); + rfbMaxClientWait = atoi(argv[i+1]); + return 2; + } + + if (strcmp(argv[i], "-nocursor") == 0) { + noCursor = TRUE; + return 1; + } + + if (strcmp(argv[i], "-rfbauth") == 0) { /* -rfbauth passwd-file */ + if (i + 1 >= argc) UseMsg(); + pVNC->rfbAuthPasswdFile = argv[i+1]; + return 2; + } + + if (strcmp(argv[i], "-loginauth") == 0) { + if (geteuid() == 0) { + /* Only when run as root! */ + pVNC->loginAuthEnabled = TRUE; + } + return 1; + } + + if (strcmp(argv[i], "-httpd") == 0) { + if (i + 1 >= argc) UseMsg(); + pVNC->httpDir = argv[i+1]; + return 2; + } + + if (strcmp(argv[i], "-httpport") == 0) { + if (i + 1 >= argc) UseMsg(); + pVNC->httpPort = atoi(argv[i+1]); + return 2; + } + + if (strcmp(argv[i], "-deferupdate") == 0) { /* -deferupdate ms */ + if (i + 1 >= argc) UseMsg(); + rfbDeferUpdateTime = atoi(argv[i+1]); + return 2; + } + + if (strcmp(argv[i], "-economictranslate") == 0) { + rfbEconomicTranslate = TRUE; + return 1; + } + + if (strcmp(argv[i], "-lazytight") == 0) { + rfbTightDisableGradient = TRUE; + return 1; + } + + if (strcmp(argv[i], "-desktop") == 0) { /* -desktop desktop-name */ + if (i + 1 >= argc) UseMsg(); + desktopName = argv[i+1]; + return 2; + } + +#if 0 /* not deemed useful on standalone server - leave for completeness */ + if (strcmp(argv[i], "-useraccept") == 0) { + pVNC->rfbUserAccept = TRUE; + return 1; + } +#endif + + if (strcmp(argv[i], "-alwaysshared") == 0) { + pVNC->rfbAlwaysShared = TRUE; + return 1; + } + + if (strcmp(argv[i], "-nevershared") == 0) { + pVNC->rfbNeverShared = TRUE; + return 1; + } + + if (strcmp(argv[i], "-dontdisconnect") == 0) { + pVNC->rfbDontDisconnect = TRUE; + return 1; + } + + /* Run server in view-only mode - Ehud Karni SW */ + if (strcmp(argv[i], "-viewonly") == 0) { + pVNC->rfbViewOnly = TRUE; + return 1; + } + + if (strcmp(argv[i], "-localhost") == 0) { + pVNC->interface.s_addr = htonl (INADDR_LOOPBACK); + return 1; + } + + if (strcmp(argv[i], "-interface") == 0) { /* -interface ipaddr */ + struct in_addr got; + unsigned long octet; + char *p, *end; + int q; + got.s_addr = 0; + if (i + 1 >= argc) { + UseMsg(); + return 2; + } + if (pVNC->interface.s_addr != htonl (INADDR_ANY)) { + /* Already set (-localhost?). */ + return 2; + } + p = argv[i + 1]; + for (q = 0; q < 4; q++) { + octet = strtoul (p, &end, 10); + if (p == end || octet > 255) { + UseMsg (); + return 2; + } + if ((q < 3 && *end != '.') || + (q == 3 && *end != '\0')) { + UseMsg (); + return 2; + } + got.s_addr = (got.s_addr << 8) | octet; + p = end + 1; + } + pVNC->interface.s_addr = htonl (got.s_addr); + return 2; + } + + if (strcmp(argv[i], "-inetd") == 0) { /* -inetd */ + int n; + for (n = 1; n < 100; n++) { + if (CheckDisplayNumber(n)) + break; + } + + if (n >= 100) + FatalError("-inetd: couldn't find free display number"); + + sprintf(inetdDisplayNumStr, "%d", n); + display = inetdDisplayNumStr; + + /* fds 0, 1 and 2 (stdin, out and err) are all the same socket to the + RFB client. OsInit() closes stdout and stdin, and we don't want + stderr to go to the RFB client, so make the client socket 3 and + close stderr. OsInit() will redirect stderr logging to an + appropriate log file or /dev/null if that doesn't work. */ + + dup2(0,3); + inetdSock = 3; + close(2); + + return 1; + } + + if (strcmp(argv[i], "-version") == 0) { + ErrorF("Xvnc version %s\n", XVNCRELEASE); + exit(0); + } + + if (inetdSock != -1 && argv[i][0] == ':') { + FatalError("can't specify both -inetd and :displaynumber"); + } + + return 0; +} + + +/* + * InitOutput is called every time the server resets. It should call + * AddScreen for each screen (FIXME - but we only ever have one), + * and in turn this will call rfbScreenInit. + */ + +/* Common pixmap formats */ + +static PixmapFormatRec formats[MAXFORMATS] = { + { 1, 1, BITMAP_SCANLINE_PAD }, + { 4, 8, BITMAP_SCANLINE_PAD }, + { 8, 8, BITMAP_SCANLINE_PAD }, + { 15, 16, BITMAP_SCANLINE_PAD }, + { 16, 16, BITMAP_SCANLINE_PAD }, + { 24, 32, BITMAP_SCANLINE_PAD }, +#ifdef RENDER + { 32, 32, BITMAP_SCANLINE_PAD }, +#endif +}; +#ifdef RENDER +static int numFormats = 7; +#else +static int numFormats = 6; +#endif + +void +InitOutput(screenInfo, argc, argv) + ScreenInfo *screenInfo; + int argc; + char **argv; +{ + int i; + initOutputCalled = TRUE; + + rfbLog("Xvnc version %s\n", XVNCRELEASE); + rfbLog("Copyright (C) 2001-2004 Alan Hourihane.\n"); + rfbLog("Copyright (C) 2000-2004 Constantin Kaplinsky\n"); + rfbLog("Copyright (C) 1999 AT&T Laboratories Cambridge\n"); + rfbLog("All Rights Reserved.\n"); + rfbLog("See http://www.tightvnc.com/ for information on TightVNC\n"); + rfbLog("See http://xf4vnc.sf.net for xf4vnc-specific information\n"); + rfbLog("Desktop name '%s' (%s:%s)\n",desktopName,rfbThisHost,display); + rfbLog("Protocol versions supported: %d.%d, %d.%d\n", + rfbProtocolMajorVersion, rfbProtocolMinorVersion, + rfbProtocolMajorVersion, rfbProtocolFallbackMinorVersion); + + VNC_LAST_CLIENT_ID = MakeAtom("VNC_LAST_CLIENT_ID", + strlen("VNC_LAST_CLIENT_ID"), TRUE); + VNC_CONNECT = MakeAtom("VNC_CONNECT", strlen("VNC_CONNECT"), TRUE); + + /* initialize pixmap formats */ + + screenInfo->imageByteOrder = IMAGE_BYTE_ORDER; + screenInfo->bitmapScanlineUnit = BITMAP_SCANLINE_UNIT; + screenInfo->bitmapScanlinePad = BITMAP_SCANLINE_PAD; + screenInfo->bitmapBitOrder = BITMAP_BIT_ORDER; + screenInfo->numPixmapFormats = numFormats; + for (i = 0; i < numFormats; i++) + screenInfo->formats[i] = formats[i]; + + /* initialize screen */ + + if (AddScreen(rfbScreenInit, argc, argv) == -1) { + FatalError("Couldn't add screen"); + } + +#ifdef CORBA + initialiseCORBA(argc, argv, desktopName); +#endif +} + +static void +rfbWakeupHandler ( + int i, + pointer blockData, + unsigned long err, + pointer pReadmask +){ + ScreenPtr pScreen = screenInfo.screens[i]; + VNCSCREENPTR(pScreen); + int e = (int)err; + + if (e < 0) + goto SKIPME; + + rfbRootPropertyChange(pScreen); + +#if XFREE86VNC + if (pScrn->vtSema) { + rfbCheckFds(pScreen); + httpCheckFds(pScreen); +#if 0 + rdpCheckFds(pScreen); +#endif +#ifdef CORBA + corbaCheckFds(); +#endif + } else { + rfbCheckFds(pScreen); +#if 0 + rdpCheckFds(pScreen); +#endif + } +#else + rfbCheckFds(pScreen); + httpCheckFds(pScreen); +#if 0 + rdpCheckFds(pScreen); +#endif +#ifdef CORBA + corbaCheckFds(); +#endif +#endif + +SKIPME: + + pScreen->WakeupHandler = pVNC->WakeupHandler; + (*pScreen->WakeupHandler) (i, blockData, err, pReadmask); + pScreen->WakeupHandler = rfbWakeupHandler; +} + +static Bool +rfbScreenInit(index, pScreen, argc, argv) + int index; + ScreenPtr pScreen; + int argc; + char ** argv; +{ + rfbScreenInfoPtr prfb = &rfbScreen; + int dpix = 75, dpiy = 75; + int ret; + unsigned char *pbits; + VisualPtr vis; +#ifdef RENDER + PictureScreenPtr ps; +#endif + + if (VNCGeneration != serverGeneration) { + VncExtensionInit(); + VNCGeneration = serverGeneration; + } + + if (monitorResolution != 0) { + dpix = monitorResolution; + dpiy = monitorResolution; + } + + prfb->rfbAuthTries = 0; + prfb->rfbAuthTooManyTries = FALSE; + prfb->rfbUserAccept = FALSE; + prfb->udpSockConnected = FALSE; + prfb->timer = NULL; + prfb->httpListenSock = -1; + prfb->httpSock = -1; + prfb->rfbListenSock = -1; + prfb->rdpListenSock = -1; + prfb->paddedWidthInBytes = PixmapBytePad(prfb->width, prfb->depth); + prfb->bitsPerPixel = rfbBitsPerPixel(prfb->depth); + pbits = rfbAllocateFramebufferMemory(prfb); + if (!pbits) return FALSE; + + miClearVisualTypes(); + + if (defaultColorVisualClass == -1) + defaultColorVisualClass = TrueColor; + + if (!miSetVisualTypes(prfb->depth, miGetDefaultVisualMask(prfb->depth), 8, + defaultColorVisualClass) ) + return FALSE; + + miSetPixmapDepths(); + + switch (prfb->bitsPerPixel) + { + case 8: + ret = fbScreenInit(pScreen, pbits, prfb->width, prfb->height, + dpix, dpiy, prfb->paddedWidthInBytes, 8); + break; + case 16: + ret = fbScreenInit(pScreen, pbits, prfb->width, prfb->height, + dpix, dpiy, prfb->paddedWidthInBytes / 2, 16); + if (prfb->depth == 15) { + blueBits = 5; greenBits = 5; redBits = 5; + } else { + blueBits = 5; greenBits = 6; redBits = 5; + } + break; + case 32: + ret = fbScreenInit(pScreen, pbits, prfb->width, prfb->height, + dpix, dpiy, prfb->paddedWidthInBytes / 4, 32); + blueBits = 8; greenBits = 8; redBits = 8; + break; + default: + return FALSE; + } + + if (!ret) return FALSE; + + miInitializeBackingStore(pScreen); + + if (prfb->bitsPerPixel > 8) { + if (strcasecmp(primaryOrder, "bgr") == 0) { + rfbLog("BGR format %d %d %d\n", blueBits, greenBits, redBits); + vis = pScreen->visuals + pScreen->numVisuals; + while (--vis >= pScreen->visuals) { + if ((vis->class | DynamicClass) == DirectColor) { + vis->offsetRed = 0; + vis->redMask = (1 << redBits) - 1; + vis->offsetGreen = redBits; + vis->greenMask = ((1 << greenBits) - 1) << vis->offsetGreen; + vis->offsetBlue = redBits + greenBits; + vis->blueMask = ((1 << blueBits) - 1) << vis->offsetBlue; + } + } + } else { + rfbLog("RGB format %d %d %d\n", blueBits, greenBits, redBits); + vis = pScreen->visuals + pScreen->numVisuals; + while (--vis >= pScreen->visuals) { + if ((vis->class | DynamicClass) == DirectColor) { + vis->offsetBlue = 0; + vis->blueMask = (1 << blueBits) - 1; + vis->offsetGreen = blueBits; + vis->greenMask = ((1 << greenBits) - 1) << vis->offsetGreen; + vis->offsetRed = blueBits + greenBits; + vis->redMask = ((1 << redBits) - 1) << vis->offsetRed; + } + } + } + } + + if (prfb->bitsPerPixel > 4) + fbPictureInit(pScreen, 0, 0); + + prfb->cursorIsDrawn = FALSE; + prfb->dontSendFramebufferUpdate = FALSE; + + prfb->CloseScreen = pScreen->CloseScreen; + prfb->WakeupHandler = pScreen->WakeupHandler; + prfb->CreateGC = pScreen->CreateGC; + prfb->PaintWindowBackground = pScreen->PaintWindowBackground; + prfb->PaintWindowBorder = pScreen->PaintWindowBorder; + prfb->CopyWindow = pScreen->CopyWindow; + prfb->ClearToBackground = pScreen->ClearToBackground; + prfb->RestoreAreas = pScreen->RestoreAreas; +#ifdef CHROMIUM + prfb->RealizeWindow = pScreen->RealizeWindow; + prfb->UnrealizeWindow = pScreen->UnrealizeWindow; + prfb->DestroyWindow = pScreen->DestroyWindow; + prfb->PositionWindow = pScreen->PositionWindow; + prfb->ResizeWindow = pScreen->ResizeWindow; + prfb->ClipNotify = pScreen->ClipNotify; +#endif +#ifdef RENDER + ps = GetPictureScreenIfSet(pScreen); + if (ps) + prfb->Composite = ps->Composite; +#endif + pScreen->CloseScreen = rfbCloseScreen; + pScreen->WakeupHandler = rfbWakeupHandler; + pScreen->CreateGC = rfbCreateGC; + pScreen->PaintWindowBackground = rfbPaintWindowBackground; + pScreen->PaintWindowBorder = rfbPaintWindowBorder; + pScreen->CopyWindow = rfbCopyWindow; + pScreen->ClearToBackground = rfbClearToBackground; + pScreen->RestoreAreas = rfbRestoreAreas; +#ifdef CHROMIUM + pScreen->RealizeWindow = rfbRealizeWindow; + pScreen->UnrealizeWindow = rfbUnrealizeWindow; + pScreen->DestroyWindow = rfbDestroyWindow; + pScreen->PositionWindow = rfbPositionWindow; + pScreen->ResizeWindow = rfbResizeWindow; + pScreen->ClipNotify = rfbClipNotify; +#endif +#ifdef RENDER + if (ps) + ps->Composite = rfbComposite; +#endif + + pScreen->InstallColormap = rfbInstallColormap; + pScreen->UninstallColormap = rfbUninstallColormap; + pScreen->ListInstalledColormaps = rfbListInstalledColormaps; + pScreen->StoreColors = rfbStoreColors; + + pScreen->SaveScreen = (SaveScreenProcPtr)rfbAlwaysTrue; + + rfbDCInitialize(pScreen, &rfbPointerCursorFuncs); + + if (noCursor) { + pScreen->DisplayCursor = (DisplayCursorProcPtr)rfbAlwaysTrue; + prfb->cursorIsDrawn = TRUE; + } + + pScreen->blackPixel = prfb->blackPixel; + pScreen->whitePixel = prfb->whitePixel; + + prfb->rfbServerFormat.bitsPerPixel = prfb->bitsPerPixel; + prfb->rfbServerFormat.depth = prfb->depth; + prfb->rfbServerFormat.bigEndian = !(*(char *)&rfbEndianTest); + + /* Find the root visual and set the server format */ + for (vis = pScreen->visuals; vis->vid != pScreen->rootVisual; vis++) + ; + prfb->rfbServerFormat.trueColour = (vis->class == TrueColor); + + if ( (vis->class == TrueColor) || (vis->class == DirectColor) ) { + prfb->rfbServerFormat.redMax = vis->redMask >> vis->offsetRed; + prfb->rfbServerFormat.greenMax = vis->greenMask >> vis->offsetGreen; + prfb->rfbServerFormat.blueMax = vis->blueMask >> vis->offsetBlue; + prfb->rfbServerFormat.redShift = vis->offsetRed; + prfb->rfbServerFormat.greenShift = vis->offsetGreen; + prfb->rfbServerFormat.blueShift = vis->offsetBlue; + } else { + prfb->rfbServerFormat.redMax + = prfb->rfbServerFormat.greenMax + = prfb->rfbServerFormat.blueMax = 0; + prfb->rfbServerFormat.redShift + = prfb->rfbServerFormat.greenShift + = prfb->rfbServerFormat.blueShift = 0; + } + + ret = fbCreateDefColormap(pScreen); + + rfbInitSockets(pScreen); +#if 0 + rdpInitSockets(pScreen); +#endif + if (inetdSock == -1) + httpInitSockets(pScreen); + + return ret; + +} /* end rfbScreenInit */ + + + +/* + * InitInput is also called every time the server resets. It is called after + * InitOutput so we can assume that rfbInitSockets has already been called. + */ +void +InitInput(argc, argv) + int argc; + char *argv[]; +{ + DeviceIntPtr p, k; + k = AddInputDevice(rfbKeybdProc, TRUE); + p = AddInputDevice(rfbMouseProc, TRUE); + RegisterKeyboardDevice(k); + RegisterPointerDevice(p); + + mieqInit(); + +#if 0 + mieqCheckForInput[0] = checkForInput[0]; + mieqCheckForInput[1] = checkForInput[1]; + SetInputCheck(&alwaysCheckForInput[0], &alwaysCheckForInput[1]); +#endif +} + + +static int +rfbKeybdProc(pDevice, onoff) + DeviceIntPtr pDevice; + int onoff; +{ + KeySymsRec keySyms; + CARD8 modMap[MAP_LENGTH]; + DevicePtr pDev = (DevicePtr)pDevice; + + switch (onoff) + { + case DEVICE_INIT: + vncSetKeyboardDevice(pDevice); + KbdDeviceInit(pDevice, &keySyms, modMap); + InitKeyboardDeviceStruct(pDev, &keySyms, modMap, + (BellProcPtr)rfbSendBell, + (KbdCtrlProcPtr)NoopDDA); + break; + case DEVICE_ON: + pDev->on = TRUE; + KbdDeviceOn(); + break; + case DEVICE_OFF: + pDev->on = FALSE; + KbdDeviceOff(); + break; + case DEVICE_CLOSE: + vncSetKeyboardDevice(NULL); + if (pDev->on) + KbdDeviceOff(); + break; + } + return Success; +} + +static int +rfbMouseProc(pDevice, onoff) + DeviceIntPtr pDevice; + int onoff; +{ + BYTE map[6]; + DevicePtr pDev = (DevicePtr)pDevice; + + switch (onoff) + { + case DEVICE_INIT: + PtrDeviceInit(); + map[1] = 1; + map[2] = 2; + map[3] = 3; + map[4] = 4; + map[5] = 5; + InitPointerDeviceStruct(pDev, map, 5, + GetMotionHistory, + PtrDeviceControl, + GetMaximumEventsNum(), 2 /* numAxes */); + vncSetPointerDevice(pDevice); + break; + + case DEVICE_ON: + pDev->on = TRUE; + PtrDeviceOn(pDevice); + break; + + case DEVICE_OFF: + pDev->on = FALSE; + PtrDeviceOff(); + break; + + case DEVICE_CLOSE: + vncSetPointerDevice(NULL); + if (pDev->on) + PtrDeviceOff(); + break; + } + return Success; +} + + +Bool +LegalModifier(unsigned int key, DeviceIntPtr pDev) +{ + return TRUE; +} + + +void +ProcessInputEvents(void) +{ +#if 0 + if (*mieqCheckForInput[0] != *mieqCheckForInput[1]) { +#endif + mieqProcessInputEvents(); +#if 0 + } +#endif +} + + +static Bool CheckDisplayNumber(int n) +{ + char fname[32]; + int sock; + struct sockaddr_in addr; + + sock = socket(AF_INET, SOCK_STREAM, 0); + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_ANY); + addr.sin_port = htons(6000+n); + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + close(sock); + return FALSE; + } + close(sock); + + sprintf(fname, "/tmp/.X%d-lock", n); + if (access(fname, F_OK) == 0) + return FALSE; + + sprintf(fname, "/tmp/.X11-unix/X%d", n); + if (access(fname, F_OK) == 0) + return FALSE; + + return TRUE; +} + +static Bool +rfbAlwaysTrue(void) +{ + return TRUE; +} + + +static unsigned char * +rfbAllocateFramebufferMemory(prfb) + rfbScreenInfoPtr prfb; +{ + if (prfb->pfbMemory) return prfb->pfbMemory; /* already done */ + + prfb->sizeInBytes = (prfb->paddedWidthInBytes * prfb->height); + + prfb->pfbMemory = (unsigned char *)Xalloc(prfb->sizeInBytes); + + return prfb->pfbMemory; +} + + +static Bool +rfbCursorOffScreen (ppScreen, x, y) + ScreenPtr *ppScreen; + int *x, *y; +{ + return FALSE; +} + +static void +rfbCrossScreen (pScreen, entering) + ScreenPtr pScreen; + Bool entering; +{ +} + +void +ddxGiveUp() +{ + Xfree(rfbScreen.pfbMemory); + if (initOutputCalled) { + char unixSocketName[32]; + sprintf(unixSocketName,"/tmp/.X11-unix/X%s",display); + unlink(unixSocketName); +#ifdef CORBA + shutdownCORBA(); +#endif + } +} + +void +AbortDDX() +{ + ddxGiveUp(); +} + +void +OsVendorInit() +{ +} + +void +OsVendorFatalError() +{ +} + +#ifdef DDXTIME /* from ServerOSDefines */ +CARD32 +GetTimeInMillis() +{ + struct timeval tp; + + X_GETTIMEOFDAY(&tp); + return(tp.tv_sec * 1000) + (tp.tv_usec / 1000); +} +#endif + +void +ddxUseMsg() +{ + ErrorF("-geometry WxH set framebuffer width & height\n"); + ErrorF("-depth D set framebuffer depth\n"); + ErrorF("-pixelformat format set pixel format (BGRnnn or RGBnnn)\n"); + ErrorF("-udpinputport port UDP port for keyboard/pointer data\n"); + ErrorF("-rfbport port TCP port for RFB protocol\n"); + ErrorF("-rfbwait time max time in ms to wait for RFB client\n"); + ErrorF("-nocursor don't put up a cursor\n"); + ErrorF("-rfbauth passwd-file use authentication on RFB protocol\n"); + ErrorF("-loginauth use login-style Unix authentication\n"); + ErrorF("-httpd dir serve files via HTTP from here\n"); + ErrorF("-httpport port port for HTTP\n"); + ErrorF("-deferupdate time time in ms to defer updates " + "(default 40)\n"); + ErrorF("-economictranslate less memory-hungry translation\n"); + ErrorF("-lazytight disable \"gradient\" filter in tight " + "encoding\n"); + ErrorF("-desktop name VNC desktop name (default x11)\n"); + ErrorF("-alwaysshared always treat new clients as shared\n"); + ErrorF("-nevershared never treat new clients as shared\n"); + ErrorF("-dontdisconnect don't disconnect existing clients when a " + "new non-shared\n" + " connection comes in (refuse new connection " + "instead)\n"); + ErrorF("-localhost only allow connections from localhost\n" + " to the vnc ports. Use -nolisten tcp to disable\n" + " remote X clients as well.\n"); + ErrorF("-viewonly let clients only view the desktop\n"); + ErrorF("-interface ipaddr only bind to specified interface " + "address\n"); + ErrorF("-inetd Xvnc is launched by inetd\n"); + exit(1); +} + + +void ddxInitGlobals(void) +{ + /* dummy function called by InitGlobals in os/utils.c */ +} + +int +NewInputDeviceRequest(InputOption *options, DeviceIntPtr *pdev) +{ + return BadValue; +} + +void +DeleteInputDeviceRequest(DeviceIntPtr dev) +{ +} + + +/* + * rfbLog prints a time-stamped message to the log file (stderr). + */ + +void rfbLog(char *format, ...) +{ + va_list args; + char buf[256]; + time_t clock; + + va_start(args, format); + + time(&clock); + strftime(buf, 255, "%d/%m/%Y %H:%M:%S ", localtime(&clock)); + fprintf(stderr, buf); + + vfprintf(stderr, format, args); + fflush(stderr); + + va_end(args); +} + +void rfbLogPerror(char *str) +{ + rfbLog(""); + perror(str); +} diff --git a/hw/vnc/kbdptr.c b/hw/vnc/kbdptr.c new file mode 100644 index 0000000..de31920 --- /dev/null +++ b/hw/vnc/kbdptr.c @@ -0,0 +1,458 @@ +/* + * kbdptr.c - deal with keyboard and pointer device over TCP & UDP. + * + * Modified for XFree86 4.x by Alan Hourihane + * + */ + +/* + * 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#include "rfb.h" +#include "X11/X.h" +#define NEED_EVENTS +#include "X11/Xproto.h" +#include "inputstr.h" +#define XK_CYRILLIC +#include +#include +#include "mi.h" +#include "mipointer.h" +#include "keyboard.h" + +#ifdef DMXVNC +#include "dmxinput.h" +#endif + +#define KEY_IS_PRESSED(keycode) \ + (kbdDevice->key->down[(keycode) >> 3] & (1 << ((keycode) & 7))) + +static void vncXConvertCase(KeySym sym, KeySym *lower, KeySym *upper); + +static DeviceIntPtr ptrDevice = NULL, kbdDevice = NULL; + + +void +vncSetKeyboardDevice(DeviceIntPtr kbd) +{ + if (kbdDevice && kbd) + return; /* set once */ + kbdDevice = kbd; +} + + +void +vncSetPointerDevice(DeviceIntPtr ptr) +{ + if (ptrDevice && ptr) + return; /* set once */ + ptrDevice = ptr; +} + + +#ifndef DMXVNC +static void +EnqueueMotion(DeviceIntPtr ptrDev, int x, int y) +{ + xEvent *events = (xEvent*) calloc(sizeof(xEvent), GetMaximumEventsNum()); + int detail = 0, valuators[2], nevents, i; + valuators[0] = x; + valuators[1] = y; + if (!ptrDev) { + ErrorF("VNC: In EnqueueMotion() ptrDev=NULL\n"); + return; + } + nevents = GetPointerEvents(events, ptrDev, MotionNotify, detail, + POINTER_ABSOLUTE, 0, 2, valuators); + for (i = 0; i < nevents; i++) + mieqEnqueue(ptrDev, events + i); + free(events); +} +#endif + + +static void +EnqueueButton(DeviceIntPtr ptrDev, int type, int detail) +{ + xEvent *events = (xEvent*) calloc(sizeof(xEvent), GetMaximumEventsNum()); + int nevents, i; + if (!ptrDev) { + ErrorF("VNC: In EnqueueButton() ptrDev=NULL\n"); + return; + } + nevents = GetPointerEvents(events, ptrDev, type, detail, + POINTER_ABSOLUTE, 0, 0, NULL/*valuators*/); + for (i = 0; i < nevents; i++) + mieqEnqueue(ptrDev, events + i); + free(events); +} + + +static void +EnqueueKey(DeviceIntPtr kbdDev, int type, int detail) +{ + xEvent *events = (xEvent*) calloc(sizeof(xEvent), GetMaximumEventsNum()); + int nevents, i; + if (!kbdDev) { + ErrorF("VNC: In EnqueueKey() kbdDev=NULL\n"); + return; + } + nevents = GetKeyboardEvents(events, kbdDev, type, detail); + for (i = 0; i < nevents; i++) + mieqEnqueue(kbdDev, events + i); + free(events); +} + + +/* + * Called when the rfbserver receives a rfbKeyEvent event from a client. + * Put an X keyboard event into the event queue. + */ +void +KbdAddEvent(Bool down, KeySym keySym, rfbClientPtr cl) +{ + const int type = down ? KeyPress : KeyRelease; + KeySymsPtr keySyms; + int i; + int keyCode = 0; + int freeIndex = -1; + Bool fakeShiftPress = FALSE; + Bool fakeShiftLRelease = FALSE; + Bool fakeShiftRRelease = FALSE; + Bool shiftMustBeReleased = FALSE; + Bool shiftMustBePressed = FALSE; + + if (!kbdDevice) + return; + + keySyms = &kbdDevice->key->curKeySyms; + +#ifdef CORBA + if (cl) { + CARD32 clientId = cl->sock; + ChangeWindowProperty(WindowTable[0], VNC_LAST_CLIENT_ID, XA_INTEGER, + 32, PropModeReplace, 1, (pointer)&clientId, TRUE); + } +#endif + + /* NOTE: This is where it gets hairy for XFree86 servers. + * I think the best way to deal with this is invent a new protocol + * to send over the wire the remote keyboard type and correctly + * handle that as XINPUT extension device (we're part of the way + * there for that. + * + * Alan. + */ +#if !XFREE86VNC + /* First check if it's one of our predefined keys. If so then we can make + some attempt at allowing an xmodmap inside a VNC desktop behave + something like you'd expect - e.g. if keys A & B are swapped over and + the VNC client sends an A, then map it to a B when generating the X + event. We don't attempt to do this for keycodes which we make up on the + fly because it's too hard... */ + + for (i = 0; i < N_PREDEFINED_KEYS * GLYPHS_PER_KEY; i++) { + if (keySym == map[i]) { + keyCode = MIN_KEY_CODE + i / GLYPHS_PER_KEY; + + if (map[(i/GLYPHS_PER_KEY) * GLYPHS_PER_KEY + 1] != NoSymbol) { + + /* this keycode has more than one symbol associated with it, + so shift state is important */ + + if ((i % GLYPHS_PER_KEY) == 0) + shiftMustBeReleased = TRUE; + else + shiftMustBePressed = TRUE; + } + break; + } + } +#endif + + if (!keyCode) { + + /* not one of our predefined keys - see if it's in the current keyboard + mapping (i.e. we've already allocated an extra keycode for it) */ + + if (keySyms->mapWidth < 2) { + ErrorF("KbdAddEvent: Sanity check failed - Keyboard mapping has " + "less than 2 keysyms per keycode (KeySym 0x%x)\n", (int)keySym); + return; + } + + for (i = 0; i < NO_OF_KEYS * keySyms->mapWidth; i++) { + if (keySym == keySyms->map[i]) { + keyCode = MIN_KEY_CODE + i / keySyms->mapWidth; + + if (keySyms->map[(i / keySyms->mapWidth) + * keySyms->mapWidth + 1] != NoSymbol) { + + /* this keycode has more than one symbol associated with + it, so shift state is important */ + + if ((i % keySyms->mapWidth) == 0) + shiftMustBeReleased = TRUE; + else + shiftMustBePressed = TRUE; + } + break; + } + if ((freeIndex == -1) && (keySyms->map[i] == NoSymbol) + && (i % keySyms->mapWidth) == 0) + { + freeIndex = i; + } + } + } + + if (!keyCode) { + KeySym lower, upper; + + /* we don't have an existing keycode - make one up on the fly and add + it to the keyboard mapping. Thanks to Vlad Harchev for pointing + out problems with non-ascii capitalisation. */ + + if (freeIndex == -1) { + ErrorF("KbdAddEvent: ignoring KeySym 0x%x - no free KeyCodes\n", + (int)keySym); + return; + } + + keyCode = MIN_KEY_CODE + freeIndex / keySyms->mapWidth; + + vncXConvertCase(keySym, &lower, &upper); + + if (lower == upper) { + keySyms->map[freeIndex] = keySym; + + } else { + keySyms->map[freeIndex] = lower; + keySyms->map[freeIndex+1] = upper; + + if (keySym == lower) + shiftMustBeReleased = TRUE; + else + shiftMustBePressed = TRUE; + } + + SendMappingNotify(MappingKeyboard, keyCode, 1, serverClient); + + ErrorF("KbdAddEvent: unknown KeySym 0x%x - allocating KeyCode %d\n", + (int)keySym, keyCode); + } + + if (down) { + if (shiftMustBePressed && !(kbdDevice->key->state & ShiftMask)) { + fakeShiftPress = TRUE; + EnqueueKey(kbdDevice, KeyPress, SHIFT_L_KEY_CODE); + } + if (shiftMustBeReleased && (kbdDevice->key->state & ShiftMask)) { + if (KEY_IS_PRESSED(SHIFT_L_KEY_CODE)) { + fakeShiftLRelease = TRUE; + EnqueueKey(kbdDevice, KeyRelease, SHIFT_L_KEY_CODE); + } + if (KEY_IS_PRESSED(SHIFT_R_KEY_CODE)) { + fakeShiftRRelease = TRUE; + EnqueueKey(kbdDevice, KeyRelease, SHIFT_R_KEY_CODE); + } + } + } + + EnqueueKey(kbdDevice, type, keyCode); + + if (fakeShiftPress) { + EnqueueKey(kbdDevice, KeyRelease, SHIFT_L_KEY_CODE); + } + if (fakeShiftLRelease) { + EnqueueKey(kbdDevice, KeyPress, SHIFT_L_KEY_CODE); + } + if (fakeShiftRRelease) { + EnqueueKey(kbdDevice, KeyPress, SHIFT_R_KEY_CODE); + } +} + + +/* + * Called when the rfbserver receives a rfbPointerEvent event from a client. + * Put an X mouse event into the event queue. + */ +void +PtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl) +{ + int i; + static int oldButtonMask = 0; + +#ifdef CORBA + if (cl) { + CARD32 clientId = cl->sock; + ChangeWindowProperty(WindowTable[0], VNC_LAST_CLIENT_ID, XA_INTEGER, + 32, PropModeReplace, 1, (pointer)&clientId, TRUE); + } +#endif + +#ifdef DMXVNC + dmxCoreMotion(&ptrDevice->public, x, y, 0, DMX_BLOCK); +#else + EnqueueMotion(ptrDevice, x, y ); +#endif + + for (i = 0; i < 5; i++) { + if ((buttonMask ^ oldButtonMask) & (1<key->down[i] != 0) { + for (j = 0; j < 8; j++) { + if (kbdDevice->key->down[i] & (1 << j)) { + int detail = (i << 3) | j; + EnqueueKey(kbdDevice, KeyRelease, detail); + } + } + } + } +} + + +/* copied from Xlib source */ + +static void vncXConvertCase(KeySym sym, KeySym *lower, KeySym *upper) +{ + *lower = sym; + *upper = sym; + switch(sym >> 8) { + case 0: /* Latin 1 */ + if ((sym >= XK_A) && (sym <= XK_Z)) + *lower += (XK_a - XK_A); + else if ((sym >= XK_a) && (sym <= XK_z)) + *upper -= (XK_a - XK_A); + else if ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis)) + *lower += (XK_agrave - XK_Agrave); + else if ((sym >= XK_agrave) && (sym <= XK_odiaeresis)) + *upper -= (XK_agrave - XK_Agrave); + else if ((sym >= XK_Ooblique) && (sym <= XK_Thorn)) + *lower += (XK_oslash - XK_Ooblique); + else if ((sym >= XK_oslash) && (sym <= XK_thorn)) + *upper -= (XK_oslash - XK_Ooblique); + break; + case 1: /* Latin 2 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym == XK_Aogonek) + *lower = XK_aogonek; + else if (sym >= XK_Lstroke && sym <= XK_Sacute) + *lower += (XK_lstroke - XK_Lstroke); + else if (sym >= XK_Scaron && sym <= XK_Zacute) + *lower += (XK_scaron - XK_Scaron); + else if (sym >= XK_Zcaron && sym <= XK_Zabovedot) + *lower += (XK_zcaron - XK_Zcaron); + else if (sym == XK_aogonek) + *upper = XK_Aogonek; + else if (sym >= XK_lstroke && sym <= XK_sacute) + *upper -= (XK_lstroke - XK_Lstroke); + else if (sym >= XK_scaron && sym <= XK_zacute) + *upper -= (XK_scaron - XK_Scaron); + else if (sym >= XK_zcaron && sym <= XK_zabovedot) + *upper -= (XK_zcaron - XK_Zcaron); + else if (sym >= XK_Racute && sym <= XK_Tcedilla) + *lower += (XK_racute - XK_Racute); + else if (sym >= XK_racute && sym <= XK_tcedilla) + *upper -= (XK_racute - XK_Racute); + break; + case 2: /* Latin 3 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XK_Hstroke && sym <= XK_Hcircumflex) + *lower += (XK_hstroke - XK_Hstroke); + else if (sym >= XK_Gbreve && sym <= XK_Jcircumflex) + *lower += (XK_gbreve - XK_Gbreve); + else if (sym >= XK_hstroke && sym <= XK_hcircumflex) + *upper -= (XK_hstroke - XK_Hstroke); + else if (sym >= XK_gbreve && sym <= XK_jcircumflex) + *upper -= (XK_gbreve - XK_Gbreve); + else if (sym >= XK_Cabovedot && sym <= XK_Scircumflex) + *lower += (XK_cabovedot - XK_Cabovedot); + else if (sym >= XK_cabovedot && sym <= XK_scircumflex) + *upper -= (XK_cabovedot - XK_Cabovedot); + break; + case 3: /* Latin 4 */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XK_Rcedilla && sym <= XK_Tslash) + *lower += (XK_rcedilla - XK_Rcedilla); + else if (sym >= XK_rcedilla && sym <= XK_tslash) + *upper -= (XK_rcedilla - XK_Rcedilla); + else if (sym == XK_ENG) + *lower = XK_eng; + else if (sym == XK_eng) + *upper = XK_ENG; + else if (sym >= XK_Amacron && sym <= XK_Umacron) + *lower += (XK_amacron - XK_Amacron); + else if (sym >= XK_amacron && sym <= XK_umacron) + *upper -= (XK_amacron - XK_Amacron); + break; + case 6: /* Cyrillic */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XK_Serbian_DJE && sym <= XK_Serbian_DZE) + *lower -= (XK_Serbian_DJE - XK_Serbian_dje); + else if (sym >= XK_Serbian_dje && sym <= XK_Serbian_dze) + *upper += (XK_Serbian_DJE - XK_Serbian_dje); + else if (sym >= XK_Cyrillic_YU && sym <= XK_Cyrillic_HARDSIGN) + *lower -= (XK_Cyrillic_YU - XK_Cyrillic_yu); + else if (sym >= XK_Cyrillic_yu && sym <= XK_Cyrillic_hardsign) + *upper += (XK_Cyrillic_YU - XK_Cyrillic_yu); + break; + case 7: /* Greek */ + /* Assume the KeySym is a legal value (ignore discontinuities) */ + if (sym >= XK_Greek_ALPHAaccent && sym <= XK_Greek_OMEGAaccent) + *lower += (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent); + else if (sym >= XK_Greek_alphaaccent && sym <= XK_Greek_omegaaccent && + sym != XK_Greek_iotaaccentdieresis && + sym != XK_Greek_upsilonaccentdieresis) + *upper -= (XK_Greek_alphaaccent - XK_Greek_ALPHAaccent); + else if (sym >= XK_Greek_ALPHA && sym <= XK_Greek_OMEGA) + *lower += (XK_Greek_alpha - XK_Greek_ALPHA); + else if (sym >= XK_Greek_alpha && sym <= XK_Greek_omega && + sym != XK_Greek_finalsmallsigma) + *upper -= (XK_Greek_alpha - XK_Greek_ALPHA); + break; + } +} diff --git a/hw/vnc/keyboard.h b/hw/vnc/keyboard.h new file mode 100644 index 0000000..ab5b1b3 --- /dev/null +++ b/hw/vnc/keyboard.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2002 Alan Hourihane. 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. + * + * Author: Alan Hourihane + */ + +#define MIN_KEY_CODE 8 +#define MAX_KEY_CODE 255 +#define NO_OF_KEYS (MAX_KEY_CODE - MIN_KEY_CODE + 1) +#define GLYPHS_PER_KEY 4 + +#define CONTROL_L_KEY_CODE (MIN_KEY_CODE + 29) +#define CONTROL_R_KEY_CODE (MIN_KEY_CODE + 101) +#define SHIFT_L_KEY_CODE (MIN_KEY_CODE + 42) +#define SHIFT_R_KEY_CODE (MIN_KEY_CODE + 54) +#define META_L_KEY_CODE (MIN_KEY_CODE + 107) +#define META_R_KEY_CODE (MIN_KEY_CODE + 108) +#define ALT_L_KEY_CODE (MIN_KEY_CODE + 56) +#define ALT_R_KEY_CODE (MIN_KEY_CODE + 105) + +static KeySym map[MAX_KEY_CODE * GLYPHS_PER_KEY] = { + /* 0x00 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol, + /* 0x01 */ XK_Escape, NoSymbol, NoSymbol, NoSymbol, + /* 0x02 */ XK_1, XK_exclam, NoSymbol, NoSymbol, + /* 0x03 */ XK_2, XK_at, NoSymbol, NoSymbol, + /* 0x04 */ XK_3, XK_numbersign, NoSymbol, NoSymbol, + /* 0x05 */ XK_4, XK_dollar, NoSymbol, NoSymbol, + /* 0x06 */ XK_5, XK_percent, NoSymbol, NoSymbol, + /* 0x07 */ XK_6, XK_asciicircum, NoSymbol, NoSymbol, + /* 0x08 */ XK_7, XK_ampersand, NoSymbol, NoSymbol, + /* 0x09 */ XK_8, XK_asterisk, NoSymbol, NoSymbol, + /* 0x0a */ XK_9, XK_parenleft, NoSymbol, NoSymbol, + /* 0x0b */ XK_0, XK_parenright, NoSymbol, NoSymbol, + /* 0x0c */ XK_minus, XK_underscore, NoSymbol, NoSymbol, + /* 0x0d */ XK_equal, XK_plus, NoSymbol, NoSymbol, + /* 0x0e */ XK_BackSpace, NoSymbol, NoSymbol, NoSymbol, + /* 0x0f */ XK_Tab, XK_ISO_Left_Tab,NoSymbol, NoSymbol, + /* 0x10 */ XK_q, XK_Q, NoSymbol, NoSymbol, + /* 0x11 */ XK_w, XK_W, NoSymbol, NoSymbol, + /* 0x12 */ XK_e, XK_E, NoSymbol, NoSymbol, + /* 0x13 */ XK_r, XK_R, NoSymbol, NoSymbol, + /* 0x14 */ XK_t, XK_T, NoSymbol, NoSymbol, + /* 0x15 */ XK_y, XK_Y, NoSymbol, NoSymbol, + /* 0x16 */ XK_u, XK_U, NoSymbol, NoSymbol, + /* 0x17 */ XK_i, XK_I, NoSymbol, NoSymbol, + /* 0x18 */ XK_o, XK_O, NoSymbol, NoSymbol, + /* 0x19 */ XK_p, XK_P, NoSymbol, NoSymbol, + /* 0x1a */ XK_bracketleft, XK_braceleft, NoSymbol, NoSymbol, + /* 0x1b */ XK_bracketright,XK_braceright, NoSymbol, NoSymbol, + /* 0x1c */ XK_Return, NoSymbol, NoSymbol, NoSymbol, + /* 0x1d */ XK_Control_L, NoSymbol, NoSymbol, NoSymbol, + /* 0x1e */ XK_a, XK_A, NoSymbol, NoSymbol, + /* 0x1f */ XK_s, XK_S, NoSymbol, NoSymbol, + /* 0x20 */ XK_d, XK_D, NoSymbol, NoSymbol, + /* 0x21 */ XK_f, XK_F, NoSymbol, NoSymbol, + /* 0x22 */ XK_g, XK_G, NoSymbol, NoSymbol, + /* 0x23 */ XK_h, XK_H, NoSymbol, NoSymbol, + /* 0x24 */ XK_j, XK_J, NoSymbol, NoSymbol, + /* 0x25 */ XK_k, XK_K, NoSymbol, NoSymbol, + /* 0x26 */ XK_l, XK_L, NoSymbol, NoSymbol, + /* 0x27 */ XK_semicolon, XK_colon, NoSymbol, NoSymbol, + /* 0x28 */ XK_quoteright, XK_quotedbl, NoSymbol, NoSymbol, + /* 0x29 */ XK_quoteleft, XK_asciitilde, NoSymbol, NoSymbol, + /* 0x2a */ XK_Shift_L, NoSymbol, NoSymbol, NoSymbol, + /* 0x2b */ XK_backslash, XK_bar, NoSymbol, NoSymbol, + /* 0x2c */ XK_z, XK_Z, NoSymbol, NoSymbol, + /* 0x2d */ XK_x, XK_X, NoSymbol, NoSymbol, + /* 0x2e */ XK_c, XK_C, NoSymbol, NoSymbol, + /* 0x2f */ XK_v, XK_V, NoSymbol, NoSymbol, + /* 0x30 */ XK_b, XK_B, NoSymbol, NoSymbol, + /* 0x31 */ XK_n, XK_N, NoSymbol, NoSymbol, + /* 0x32 */ XK_m, XK_M, NoSymbol, NoSymbol, + /* 0x33 */ XK_comma, XK_less, NoSymbol, NoSymbol, + /* 0x34 */ XK_period, XK_greater, NoSymbol, NoSymbol, + /* 0x35 */ XK_slash, XK_question, NoSymbol, NoSymbol, + /* 0x36 */ XK_Shift_R, NoSymbol, NoSymbol, NoSymbol, + /* 0x37 */ XK_KP_Multiply, NoSymbol, NoSymbol, NoSymbol, + /* 0x38 */ XK_Alt_L, XK_Meta_L, NoSymbol, NoSymbol, + /* 0x39 */ XK_space, NoSymbol, NoSymbol, NoSymbol, + /* 0x3a */ XK_Caps_Lock, NoSymbol, NoSymbol, NoSymbol, + /* 0x3b */ XK_F1, NoSymbol, NoSymbol, NoSymbol, + /* 0x3c */ XK_F2, NoSymbol, NoSymbol, NoSymbol, + /* 0x3d */ XK_F3, NoSymbol, NoSymbol, NoSymbol, + /* 0x3e */ XK_F4, NoSymbol, NoSymbol, NoSymbol, + /* 0x3f */ XK_F5, NoSymbol, NoSymbol, NoSymbol, + /* 0x40 */ XK_F6, NoSymbol, NoSymbol, NoSymbol, + /* 0x41 */ XK_F7, NoSymbol, NoSymbol, NoSymbol, + /* 0x42 */ XK_F8, NoSymbol, NoSymbol, NoSymbol, + /* 0x43 */ XK_F9, NoSymbol, NoSymbol, NoSymbol, + /* 0x44 */ XK_F10, NoSymbol, NoSymbol, NoSymbol, + /* 0x45 */ XK_Num_Lock, NoSymbol, NoSymbol, NoSymbol, + /* 0x46 */ XK_Scroll_Lock, NoSymbol, NoSymbol, NoSymbol, + /* 0x47 */ XK_KP_Home, XK_KP_7, NoSymbol, NoSymbol, + /* 0x48 */ XK_KP_Up, XK_KP_8, NoSymbol, NoSymbol, + /* 0x49 */ XK_KP_Prior, XK_KP_9, NoSymbol, NoSymbol, + /* 0x4a */ XK_KP_Subtract, NoSymbol, NoSymbol, NoSymbol, + /* 0x4b */ XK_KP_Left, XK_KP_4, NoSymbol, NoSymbol, + /* 0x4c */ XK_KP_Begin, XK_KP_5, NoSymbol, NoSymbol, + /* 0x4d */ XK_KP_Right, XK_KP_6, NoSymbol, NoSymbol, + /* 0x4e */ XK_KP_Add, NoSymbol, NoSymbol, NoSymbol, + /* 0x4f */ XK_KP_End, XK_KP_1, NoSymbol, NoSymbol, + /* 0x50 */ XK_KP_Down, XK_KP_2, NoSymbol, NoSymbol, + /* 0x51 */ XK_KP_Next, XK_KP_3, NoSymbol, NoSymbol, + /* 0x52 */ XK_KP_Insert, XK_KP_0, NoSymbol, NoSymbol, + /* 0x53 */ XK_KP_Delete, XK_KP_Decimal, NoSymbol, NoSymbol, + /* 0x54 */ XK_Sys_Req, NoSymbol, NoSymbol, NoSymbol, + /* 0x55 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol, + /* 0x56 */ XK_less, XK_greater, NoSymbol, NoSymbol, + /* 0x57 */ XK_F11, NoSymbol, NoSymbol, NoSymbol, + /* 0x58 */ XK_F12, NoSymbol, NoSymbol, NoSymbol, + /* 0x59 */ XK_Home, NoSymbol, NoSymbol, NoSymbol, + /* 0x5a */ XK_Up, NoSymbol, NoSymbol, NoSymbol, + /* 0x5b */ XK_Prior, NoSymbol, NoSymbol, NoSymbol, + /* 0x5c */ XK_Left, NoSymbol, NoSymbol, NoSymbol, + /* 0x5d */ XK_Begin, NoSymbol, NoSymbol, NoSymbol, + /* 0x5e */ XK_Right, NoSymbol, NoSymbol, NoSymbol, + /* 0x5f */ XK_End, NoSymbol, NoSymbol, NoSymbol, + /* 0x60 */ XK_Down, NoSymbol, NoSymbol, NoSymbol, + /* 0x61 */ XK_Next, NoSymbol, NoSymbol, NoSymbol, + /* 0x62 */ XK_Insert, NoSymbol, NoSymbol, NoSymbol, + /* 0x63 */ XK_Delete, NoSymbol, NoSymbol, NoSymbol, + /* 0x64 */ XK_KP_Enter, NoSymbol, NoSymbol, NoSymbol, + /* 0x65 */ XK_Control_R, NoSymbol, NoSymbol, NoSymbol, + /* 0x66 */ XK_Pause, NoSymbol, NoSymbol, NoSymbol, + /* 0x67 */ XK_Print, NoSymbol, NoSymbol, NoSymbol, + /* 0x68 */ XK_KP_Divide, NoSymbol, NoSymbol, NoSymbol, + /* 0x69 */ XK_Alt_R, XK_Meta_R, NoSymbol, NoSymbol, + /* 0x6a */ XK_Break, NoSymbol, NoSymbol, NoSymbol, + /* 0x6b */ XK_Meta_L, NoSymbol, NoSymbol, NoSymbol, + /* 0x6c */ XK_Meta_R, NoSymbol, NoSymbol, NoSymbol, + /* 0x6d */ XK_Menu, NoSymbol, NoSymbol, NoSymbol, + /* 0x6e */ XK_F13, NoSymbol, NoSymbol, NoSymbol, + /* 0x6f */ XK_F14, NoSymbol, NoSymbol, NoSymbol, + /* 0x70 */ XK_F15, NoSymbol, NoSymbol, NoSymbol, + /* 0x71 */ XK_F16, NoSymbol, NoSymbol, NoSymbol, + /* 0x72 */ XK_F17, NoSymbol, NoSymbol, NoSymbol, + /* 0x73 */ XK_backslash, XK_underscore, NoSymbol, NoSymbol, + /* 0x74 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol, + /* 0x75 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol, + /* 0x76 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol, + /* 0x77 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol, + /* 0x78 */ NoSymbol, NoSymbol, NoSymbol, NoSymbol, + /* 0x79 */ XK_Henkan, XK_Mode_switch, NoSymbol, NoSymbol, + /* 0x7a */ NoSymbol, NoSymbol, NoSymbol, NoSymbol, + /* 0x7b */ XK_Muhenkan, NoSymbol, NoSymbol, NoSymbol, + /* 0x7c */ NoSymbol, NoSymbol, NoSymbol, NoSymbol, + /* 0x7d */ XK_backslash, XK_bar, NoSymbol, NoSymbol, + /* 0x7e */ NoSymbol, NoSymbol, NoSymbol, NoSymbol, + /* 0x7f */ NoSymbol, NoSymbol, NoSymbol, NoSymbol, +}; + +#define N_PREDEFINED_KEYS (sizeof(map) / (sizeof(KeySym) * GLYPHS_PER_KEY)) diff --git a/hw/vnc/loginauth.c b/hw/vnc/loginauth.c new file mode 100644 index 0000000..7fd5304 --- /dev/null +++ b/hw/vnc/loginauth.c @@ -0,0 +1,143 @@ +/* + * loginauth.c - deal with login-style Unix authentication. + * + * This file implements the UnixLogin authentication protocol when setting up + * an RFB connection. + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * Copyright (C) 2003 Constantin 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#ifdef linux +#include "/usr/include/shadow.h" +#endif +#include +#include +#define _XOPEN_SOURCE +#include +#include +#include +#include "rfb.h" + +void rfbLoginAuthProcessClientMessage(rfbClientPtr cl) +{ + int n1 = 0, n2 = 0; + CARD32 loginLen, passwdLen, authResult; + char *loginBuf, *passwdBuf; + struct passwd *ps; + char *encPasswd1, *encPasswd2; + Bool ok; + + if ((n1 = ReadExact(cl->sock, (char *)&loginLen, + sizeof(loginLen))) <= 0 || + (n2 = ReadExact(cl->sock, (char *)&passwdLen, + sizeof(passwdLen))) <= 0) { + if (n1 != 0 || n2 != 0) + rfbLogPerror("rfbLoginAuthProcessClientMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + loginLen = Swap32IfLE(loginLen); + passwdLen = Swap32IfLE(passwdLen); + loginBuf = (char *)xalloc(loginLen + 1); + passwdBuf = (char *)xalloc(passwdLen + 1); + + n1 = n2 = 0; + if ((n1 = ReadExact(cl->sock, loginBuf, loginLen)) <= 0 || + (n2 = ReadExact(cl->sock, passwdBuf, passwdLen)) <= 0) { + if (n1 != 0 || n2 != 0) + rfbLogPerror("rfbLoginAuthProcessClientMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + loginBuf[loginLen] = '\0'; + passwdBuf[passwdLen] = '\0'; + + encPasswd1 = encPasswd2 = NULL; + + ps = getpwnam(loginBuf); + if (ps == NULL) { + rfbLog("rfbLoginAuthProcessClientMessage: " + "Cannot get password file entry for \"%s\"\n", loginBuf); + } else { + encPasswd1 = ps->pw_passwd; +#ifdef linux + if (strlen(ps->pw_passwd) == 1) { + struct spwd *sps; + + sps = getspnam(loginBuf); + if (sps == NULL) { + rfbLog("rfbLoginAuthProcessClientMessage:" + " getspnam() failed for user \"%s\"\n", loginBuf); + } else { + encPasswd1 = sps->sp_pwdp; + } + } +#endif + encPasswd2 = (char *)crypt(passwdBuf, encPasswd1); + memset(passwdBuf, 0, strlen(passwdBuf)); + } + + ok = FALSE; + if (encPasswd1 != NULL && encPasswd2 != NULL) { + if (strcmp(encPasswd1, encPasswd2) == 0) + ok = TRUE; + } + + if (!ok) { + rfbLog("rfbAuthProcessClientMessage: authentication failed from %s\n", + cl->host); + + if (rfbAuthConsiderBlocking(cl)) { + authResult = Swap32IfLE(rfbVncAuthTooMany); + } else { + authResult = Swap32IfLE(rfbVncAuthFailed); + } + + if (WriteExact(cl->sock, (char *)&authResult, + sizeof(authResult)) < 0) { + rfbLogPerror("rfbLoginAuthProcessClientMessage: write"); + } + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + rfbAuthUnblock(cl); + + cl->login = strdup(loginBuf); + rfbLog("Login-style authentication passed for user %s at %s\n", + cl->login, cl->host); + + authResult = Swap32IfLE(rfbVncAuthOK); + + if (WriteExact(cl->sock, (char *)&authResult, sizeof(authResult)) < 0) { + rfbLogPerror("rfbLoginAuthProcessClientMessage: write"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + cl->state = RFB_INITIALISATION; +} + diff --git a/hw/vnc/rdp.c b/hw/vnc/rdp.c new file mode 100644 index 0000000..867e527 --- /dev/null +++ b/hw/vnc/rdp.c @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2004 Alan Hourihane. 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#include +#include +#include +#include +#include +#include +#include "rfb.h" + +typedef struct rdpClientRec { + ScreenPtr pScreen; +} rdpClientRec, *rdpClientPtr; + +typedef struct rdpInRec { + char version; + char pad; + short length; + char hdrlen; + unsigned char pdu; +} rdpInRec, *rdpInPtr; + +static void +rdpCloseSock(ScreenPtr pScreen) +{ + VNCSCREENPTR(pScreen); + close(pVNC->rdpListenSock); + RemoveEnabledDevice(pVNC->rdpListenSock); + pVNC->rdpListenSock = -1; +} + +/* + * rdpNewClient is called when a new connection has been made by whatever + * means. + */ + +static rdpClientPtr +rdpNewClient(ScreenPtr pScreen, int sock) +{ + rdpInRec in; + rdpClientPtr cl; + BoxRec box; + struct sockaddr_in addr; + SOCKLEN_T addrlen = sizeof(struct sockaddr_in); + VNCSCREENPTR(pScreen); + int i; + + cl = (rdpClientPtr)xalloc(sizeof(rdpClientRec)); + + cl->pScreen = pScreen; + + in.version = 3; + in.pad = 0; + in.length = 0x600; /* big endian */ + in.hdrlen = 0x00; + in.pdu = 0xCC; + + if (WriteExact(sock, (char *)&in, sizeof(rdpInRec)) < 0) { + rfbLogPerror("rfbNewClient: write"); + rdpCloseSock(pScreen); + return NULL; + } + + return cl; +} +/* + * rdpCheckFds is called from ProcessInputEvents to check for input on the + * HTTP socket(s). If there is input to process, rdpProcessInput is called. + */ + +void +rdpCheckFds(ScreenPtr pScreen) +{ + VNCSCREENPTR(pScreen); + int nfds; + fd_set fds; + struct timeval tv; + struct sockaddr_in addr; + int sock; + const int one =1; + SOCKLEN_T addrlen = sizeof(addr); + + FD_ZERO(&fds); + FD_SET(pVNC->rdpListenSock, &fds); + tv.tv_sec = 0; + tv.tv_usec = 0; + nfds = select(pVNC->rdpListenSock + 1, &fds, NULL, NULL, &tv); + if (nfds == 0) { + return; + } + if (nfds < 0) { + if (errno != EINTR) + rfbLogPerror("httpCheckFds: select"); + return; + } + + if (pVNC->rdpListenSock != -1 && FD_ISSET(pVNC->rdpListenSock, &fds)) { + + if ((sock = accept(pVNC->rdpListenSock, + (struct sockaddr *)&addr, &addrlen)) < 0) { + rfbLogPerror("rdpCheckFds: accept"); + return; + } + + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { + rfbLogPerror("rdpCheckFds: fcntl"); + close(sock); + return; + } + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + rfbLogPerror("rdpCheckFds: setsockopt"); + close(sock); + return; + } + + rfbLog("\n"); + + rfbLog("Got RDP connection from client %s\n", inet_ntoa(addr.sin_addr)); + + AddEnabledDevice(sock); + + rdpNewClient(pScreen, sock); + } +} diff --git a/hw/vnc/rfb.h b/hw/vnc/rfb.h new file mode 100644 index 0000000..8a6d1ec --- /dev/null +++ b/hw/vnc/rfb.h @@ -0,0 +1,751 @@ +/* + * rfb.h - header file for RFB DDX implementation. + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * Copyright (C) 2000-2004 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 RFB_H_INCLUDED +#define RFB_H_INCLUDED + +#ifdef HAVE_DMX_CONFIG_H +#include "dmx-config.h" +#endif + +#ifdef DMXVNC +#include +#else +#include +#endif + +#include +#ifdef XFREE86VNC +#include "xf86.h" +#endif +#include "scrnintstr.h" +#include "colormapst.h" +#include "dixstruct.h" +#include "gcstruct.h" +#include "regionstr.h" +#include "windowstr.h" +#include "dixfontstr.h" +#include "mipointer.h" +#include +#include +#define _VNC_SERVER +#include +#include "picturestr.h" + +#if defined(sun) || defined(hpux) +#define SOCKLEN_T int +#else +#define SOCKLEN_T socklen_t +#endif + +/* It's a good idea to keep these values a bit greater than required. */ +#define MAX_ENCODINGS 10 +#define MAX_SECURITY_TYPES 4 +#define MAX_TUNNELING_CAPS 16 +#define MAX_AUTH_CAPS 16 + +/* + * HTTP_BUF_SIZE for http transfers + */ +#define HTTP_BUF_SIZE 32768 + +/* + * UPDATE_BUF_SIZE must be big enough to send at least one whole line of the + * framebuffer. So for a max screen width of say 2K with 32-bit pixels this + * means 8K minimum. + */ +#define UPDATE_BUF_SIZE 30000 + +extern DevPrivateKey VNCScreenKey; +extern DevPrivateKey rfbGCKey; + +#if XFREE86VNC || defined(DMXVNC) +#include "vncint.h" +#define VNCSCREENPTR(ptr) \ + vncScreenPtr pVNC = VNCPTR(ptr) +#else +#define VNCSCREENPTR(ptr) \ +/* Soon \ + rfbScreenInfoPtr pVNC = rfbScreen[ptr->myNum] \ +*/ \ + rfbScreenInfoPtr pVNC = &rfbScreen +#endif + +/* + * Per-screen (framebuffer) structure. There is only one of these, since we + * don't allow the X server to have multiple screens. + */ + +typedef struct +{ + int rfbPort; + int rdpPort; + int udpPort; + int rfbListenSock; + int rdpListenSock; + int udpSock; + int httpPort; + int httpListenSock; + int httpSock; + char * httpDir; + char buf[HTTP_BUF_SIZE]; + Bool udpSockConnected; + char * rfbAuthPasswdFile; + size_t buf_filled; + int maxFd; + fd_set allFds; + Bool noCursor; + Bool rfbAlwaysShared; + Bool rfbNeverShared; + Bool rfbDontDisconnect; + Bool rfbUserAccept; + Bool rfbViewOnly; + ColormapPtr savedColormap; + ColormapPtr rfbInstalledColormap; + rfbPixelFormat rfbServerFormat; + Bool rfbAuthTooManyTries; + int rfbAuthTries; + Bool loginAuthEnabled; + struct in_addr interface; + OsTimerPtr timer; + unsigned char updateBuf[UPDATE_BUF_SIZE]; + int ublen; + int width; + int paddedWidthInBytes; + int height; + int depth; + int bitsPerPixel; + int sizeInBytes; + unsigned char *pfbMemory; + Pixel blackPixel; + Pixel whitePixel; + + /* The following two members are used to minimise the amount of unnecessary + drawing caused by cursor movement. Whenever any drawing affects the + part of the screen where the cursor is, the cursor is removed first and + then the drawing is done (this is what the sprite routines test for). + Afterwards, however, we do not replace the cursor, even when the cursor + is logically being moved across the screen. We only draw the cursor + again just as we are about to send the client a framebuffer update. + + We need to be careful when removing and drawing the cursor because of + their relationship with the normal drawing routines. The drawing + routines can invoke the cursor routines, but also the cursor routines + themselves end up invoking drawing routines. + + Removing the cursor (rfbSpriteRemoveCursor) is eventually achieved by + doing a CopyArea from a pixmap to the screen, where the pixmap contains + the saved contents of the screen under the cursor. Before doing this, + however, we set cursorIsDrawn to FALSE. Then, when CopyArea is called, + it sees that cursorIsDrawn is FALSE and so doesn't feel the need to + (recursively!) remove the cursor before doing it. + + Putting up the cursor (rfbSpriteRestoreCursor) involves a call to + PushPixels. While this is happening, cursorIsDrawn must be FALSE so + that PushPixels doesn't think it has to remove the cursor first. + Obviously cursorIsDrawn is set to TRUE afterwards. + + Another problem we face is that drawing routines sometimes cause a + framebuffer update to be sent to the RFB client. When the RFB client is + already waiting for a framebuffer update and some drawing to the + framebuffer then happens, the drawing routine sees that the client is + ready, so it calls rfbSendFramebufferUpdate. If the cursor is not drawn + at this stage, it must be put up, and so rfbSpriteRestoreCursor is + called. However, if the original drawing routine was actually called + from within rfbSpriteRestoreCursor or rfbSpriteRemoveCursor we don't + want this to happen. So both the cursor routines set + dontSendFramebufferUpdate to TRUE, and all the drawing routines check + this before calling rfbSendFramebufferUpdate. */ + + Bool cursorIsDrawn; /* TRUE if the cursor is currently drawn */ + Bool dontSendFramebufferUpdate; /* TRUE while removing or drawing the + cursor */ + + /* wrapped screen functions */ + + CloseScreenProcPtr CloseScreen; + CreateGCProcPtr CreateGC; + PaintWindowBackgroundProcPtr PaintWindowBackground; + PaintWindowBorderProcPtr PaintWindowBorder; + CopyWindowProcPtr CopyWindow; + ClearToBackgroundProcPtr ClearToBackground; + RestoreAreasProcPtr RestoreAreas; + ScreenWakeupHandlerProcPtr WakeupHandler; +#ifdef CHROMIUM + RealizeWindowProcPtr RealizeWindow; + UnrealizeWindowProcPtr UnrealizeWindow; + DestroyWindowProcPtr DestroyWindow; + ResizeWindowProcPtr ResizeWindow; + PositionWindowProcPtr PositionWindow; + ClipNotifyProcPtr ClipNotify; +#endif +#ifdef RENDER + CompositeProcPtr Composite; +#endif + +} rfbScreenInfo, *rfbScreenInfoPtr; + + +/* + * rfbTranslateFnType is the type of translation functions. + */ + +struct rfbClientRec; +typedef void (*rfbTranslateFnType)(ScreenPtr pScreen, + char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + unsigned char *optr, + int bytesBetweenInputLines, + int width, int height, + int x, int y); + + +/* + * Per-client structure. + */ + +typedef struct rfbClientRec { + int sock; + char *host; + char *login; + + int protocol_minor_ver; /* RFB protocol minor version in use. */ + Bool protocol_tightvnc; /* TightVNC protocol extensions enabled */ + + /* Possible client states: */ + + enum { + RFB_PROTOCOL_VERSION, /* establishing protocol version */ + RFB_SECURITY_TYPE, /* negotiating security (RFB v.3.7) */ + RFB_TUNNELING_TYPE, /* establishing tunneling (RFB v.3.7t) */ + RFB_AUTH_TYPE, /* negotiating authentication (RFB v.3.7t) */ + RFB_AUTHENTICATION, /* authenticating (VNC authentication) */ + RFB_INITIALISATION, /* sending initialisation messages */ + RFB_NORMAL /* normal protocol messages */ + } state; + + Bool viewOnly; /* Do not accept input from this client. */ + + Bool reverseConnection; + + Bool readyForSetColourMapEntries; + + Bool useCopyRect; + int preferredEncoding; + int correMaxWidth, correMaxHeight; + + /* The list of security types sent to this client (protocol 3.7). + Note that the first entry is the number of list items following. */ + + CARD8 securityTypes[MAX_SECURITY_TYPES + 1]; + + /* Lists of capability codes sent to clients. We remember these + lists to restrict clients from choosing those tunneling and + authentication types that were not advertised. */ + + int nAuthCaps; + CARD32 authCaps[MAX_AUTH_CAPS]; + + /* This is not useful while we don't support tunneling: + int nTunnelingCaps; + CARD32 tunnelingCaps[MAX_TUNNELING_CAPS]; */ + + /* The following member is only used during VNC authentication */ + + CARD8 authChallenge[CHALLENGESIZE]; + + /* The following members represent the update needed to get the client's + framebuffer from its present state to the current state of our + framebuffer. + + If the client does not accept CopyRect encoding then the update is + simply represented as the region of the screen which has been modified + (modifiedRegion). + + If the client does accept CopyRect encoding, then the update consists of + two parts. First we have a single copy from one region of the screen to + another (the destination of the copy is copyRegion), and second we have + the region of the screen which has been modified in some other way + (modifiedRegion). + + Although the copy is of a single region, this region may have many + rectangles. When sending an update, the copyRegion is always sent + before the modifiedRegion. This is because the modifiedRegion may + overlap parts of the screen which are in the source of the copy. + + In fact during normal processing, the modifiedRegion may even overlap + the destination copyRegion. Just before an update is sent we remove + from the copyRegion anything in the modifiedRegion. */ + + RegionRec copyRegion; /* the destination region of the copy */ + int copyDX, copyDY; /* the translation by which the copy happens */ + + RegionRec modifiedRegion; /* the region of the screen modified in any + other way */ + + /* As part of the FramebufferUpdateRequest, a client can express interest + in a subrectangle of the whole framebuffer. This is stored in the + requestedRegion member. In the normal case this is the whole + framebuffer if the client is ready, empty if it's not. */ + + RegionRec requestedRegion; + + /* The following members represent the state of the "deferred update" timer + - when the framebuffer is modified and the client is ready, in most + cases it is more efficient to defer sending the update by a few + milliseconds so that several changes to the framebuffer can be combined + into a single update. */ + + Bool deferredUpdateScheduled; + OsTimerPtr deferredUpdateTimer; + + /* translateFn points to the translation function which is used to copy + and translate a rectangle from the framebuffer to an output buffer. */ + + rfbTranslateFnType translateFn; + + char *translateLookupTable; + + rfbPixelFormat format; + + /* statistics */ + + int rfbBytesSent[MAX_ENCODINGS]; + int rfbRectanglesSent[MAX_ENCODINGS]; + int rfbLastRectMarkersSent; + int rfbLastRectBytesSent; + int rfbCursorShapeBytesSent; + int rfbCursorShapeUpdatesSent; + int rfbCursorPosBytesSent; + int rfbCursorPosUpdatesSent; + int rfbFramebufferUpdateMessagesSent; + int rfbRawBytesEquivalent; + int rfbKeyEventsRcvd; + int rfbPointerEventsRcvd; + + /* zlib encoding -- necessary compression state info per client */ + + struct z_stream_s compStream; + Bool compStreamInited; + + CARD32 zlibCompressLevel; + + /* tight encoding -- preserve zlib streams' state for each client */ + + z_stream zsStruct[4]; + Bool zsActive[4]; + int zsLevel[4]; + int tightCompressLevel; + int tightQualityLevel; + + Bool enableLastRectEncoding; /* client supports LastRect encoding */ + Bool enableCursorShapeUpdates; /* client supports cursor shape updates */ + Bool enableCursorPosUpdates; /* client supports PointerPos updates */ +#ifdef CHROMIUM + Bool enableChromiumEncoding; /* client supports Chromium encoding */ +#endif + Bool useRichCursorEncoding; /* rfbEncodingRichCursor is preferred */ + Bool cursorWasChanged; /* cursor shape update should be sent */ + Bool cursorWasMoved; /* cursor position update should be sent */ + + int cursorX, cursorY; /* client's cursor position */ + + struct rfbClientRec *next; + + ScreenPtr pScreen; + int userAccepted; + +#ifdef CHROMIUM + unsigned int chromium_port; + unsigned int chromium_msport; +#endif +} rfbClientRec, *rfbClientPtr; + +#ifdef CHROMIUM +typedef struct CRWindowTable { + unsigned long CRwinId; + unsigned long XwinId; + BoxPtr clipRects; + int numRects; + struct CRWindowTable *next; +} CRWindowTable, *CRWindowTablePtr; + +extern struct CRWindowTable *windowTable; +#endif + +/* + * This macro is used to test whether there is a framebuffer update needing to + * be sent to the client. + */ + +#define FB_UPDATE_PENDING(cl) \ + ((!(cl)->enableCursorShapeUpdates && !pVNC->cursorIsDrawn) || \ + ((cl)->enableCursorShapeUpdates && (cl)->cursorWasChanged) || \ + ((cl)->enableCursorPosUpdates && (cl)->cursorWasMoved) || \ + REGION_NOTEMPTY(((cl)->pScreen),&(cl)->copyRegion) || \ + REGION_NOTEMPTY(((cl)->pScreen),&(cl)->modifiedRegion)) + +/* + * This macro creates an empty region (ie. a region with no areas) if it is + * given a rectangle with a width or height of zero. It appears that + * REGION_INTERSECT does not quite do the right thing with zero-width + * rectangles, but it should with completely empty regions. + */ + +#define SAFE_REGION_INIT(pscreen, preg, rect, size) \ +{ \ + if ( ( (rect) ) && \ + ( ( (rect)->x2 == (rect)->x1 ) || \ + ( (rect)->y2 == (rect)->y1 ) ) ) { \ + REGION_NULL( (pscreen), (preg) ); \ + } else { \ + REGION_INIT( (pscreen), (preg), (rect), (size) ); \ + } \ +} + +/* + * An rfbGCRec is where we store the pointers to the original GC funcs and ops + * which we wrap (NULL means not wrapped). + */ + +typedef struct { + GCFuncs *wrapFuncs; + GCOps *wrapOps; +} rfbGCRec, *rfbGCPtr; + + + +/* + * Macros for endian swapping. + */ + +#define Swap16(s) ((((s) & 0xff) << 8) | (((s) >> 8) & 0xff)) + +#define Swap32(l) (((l) >> 24) | \ + (((l) & 0x00ff0000) >> 8) | \ + (((l) & 0x0000ff00) << 8) | \ + ((l) << 24)) + +static const int rfbEndianTest = 1; + +#define Swap16IfLE(s) (*(const char *)&rfbEndianTest ? Swap16(s) : (s)) + +#define Swap32IfLE(l) (*(const char *)&rfbEndianTest ? Swap32(l) : (l)) + + +/* + * Macro to fill in an rfbCapabilityInfo structure (protocol 3.130). + * Normally, using macros is no good, but this macro saves us from + * writing constants twice -- it constructs signature names from codes. + * Note that "code_sym" argument should be a single symbol, not an expression. + */ + +#define SetCapInfo(cap_ptr, code_sym, vendor) \ +{ \ + rfbCapabilityInfo *pcap; \ + pcap = (cap_ptr); \ + pcap->code = Swap32IfLE(code_sym); \ + memcpy(pcap->vendorSignature, (vendor), \ + sz_rfbCapabilityInfoVendor); \ + memcpy(pcap->nameSignature, sig_##code_sym, \ + sz_rfbCapabilityInfoName); \ +} + + +/* init.c */ + +extern char *desktopName; +extern char rfbThisHost[]; +extern Atom VNC_LAST_CLIENT_ID; + +extern rfbScreenInfo rfbScreen; + +extern int inetdSock; + +extern int rfbBitsPerPixel(int depth); +extern void rfbLog(char *format, ...); +extern void rfbLogPerror(char *str); + + +/* sockets.c */ + +extern int rfbMaxClientWait; + +extern Bool rfbInitSockets(ScreenPtr pScreen); +extern void rfbDisconnectUDPSock(ScreenPtr pScreen); +extern void rfbCloseSock(ScreenPtr pScreen, int sock); +extern void rfbCheckFds(ScreenPtr pScreen); +extern void rfbWaitForClient(int sock); +extern int rfbConnect(ScreenPtr pScreen, char *host, int port); + +extern int ReadExact(int sock, char *buf, int len); +extern int WriteExact(int sock, char *buf, int len); +extern int ListenOnTCPPort(ScreenPtr pScreen, int port); +extern int ListenOnUDPPort(ScreenPtr pScreen, int port); +extern int ConnectToTcpAddr(char *host, int port); + + +/* cmap.c */ + + +extern int rfbListInstalledColormaps(ScreenPtr pScreen, Colormap *pmaps); +extern void rfbInstallColormap(ColormapPtr pmap); +extern void rfbUninstallColormap(ColormapPtr pmap); +extern void rfbStoreColors(ColormapPtr pmap, int ndef, xColorItem *pdefs); + + +/* draw.c */ + +extern int rfbDeferUpdateTime; + +extern void +rfbComposite( + CARD8 op, + PicturePtr pSrc, + PicturePtr pMask, + PicturePtr pDst, + INT16 xSrc, + INT16 ySrc, + INT16 xMask, + INT16 yMask, + INT16 xDst, + INT16 yDst, + CARD16 width, + CARD16 height +); + +extern void rfbGlyphs( + CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, + INT16 ySrc, + int nlistInit, + GlyphListPtr listInit, + GlyphPtr *glyphsInit +); +extern Bool rfbCloseScreen(int,ScreenPtr); +extern Bool rfbCreateGC(GCPtr); +extern void rfbPaintWindowBackground(WindowPtr, RegionPtr, int what); +extern void rfbPaintWindowBorder(WindowPtr, RegionPtr, int what); +extern void rfbCopyWindow(WindowPtr, DDXPointRec, RegionPtr); +#ifdef CHROMIUM +extern Bool rfbRealizeWindow(WindowPtr); +extern Bool rfbUnrealizeWindow(WindowPtr); +extern Bool rfbDestroyWindow(WindowPtr); +extern void rfbResizeWindow(WindowPtr, int x, int y, unsigned int w, unsigned int h, WindowPtr pSib); +extern Bool rfbPositionWindow(WindowPtr, int x, int y); +extern void rfbClipNotify(WindowPtr, int x, int y); +#endif +extern void rfbClearToBackground(WindowPtr, int x, int y, int w, + int h, Bool generateExposures); +extern RegionPtr rfbRestoreAreas(WindowPtr, RegionPtr); +extern void rfbScheduleUpdate(ScreenPtr pScreen); + +/* dispcur.c */ +extern Bool rfbDCInitialize(ScreenPtr pScreen, miPointerScreenFuncPtr screenFuncs); + + +/* cutpaste.c */ + +extern void rfbSetXCutText(char *str, int len); +extern void rfbGotXCutText(char *str, int len); + + +/* kbdptr.c */ + +extern Bool compatibleKbd; +extern unsigned char ptrAcceleration; + +extern void PtrDeviceInit(void); +extern void PtrDeviceOn(DeviceIntPtr pDev); +extern void PtrDeviceOff(void); +extern void PtrDeviceControl(DeviceIntPtr dev, PtrCtrl *ctrl); +extern void PtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl); + +extern void KbdDeviceInit(DeviceIntPtr pDevice, KeySymsPtr pKeySyms, CARD8 *pModMap); +extern void KbdDeviceOn(void); +extern void KbdDeviceOff(void); +extern void KbdAddEvent(Bool down, KeySym keySym, rfbClientPtr cl); +extern void KbdReleaseAllKeys(void); + +extern void vncSetKeyboardDevice(DeviceIntPtr kbd); +extern void vncSetPointerDevice(DeviceIntPtr ptr); + + +/* rfbserver.c */ + + +extern rfbClientPtr rfbClientHead; +extern rfbClientPtr pointerClient; + +extern void rfbNewClientConnection(ScreenPtr pScreen, int sock); +extern rfbClientPtr rfbReverseConnection(ScreenPtr pScreen, char *host, int port); +extern void rfbRootPropertyChange(ScreenPtr pScreen); +extern void rfbClientConnectionGone(int sock); +extern void rfbProcessClientMessage(ScreenPtr pScreen, int sock); +extern void rfbClientConnFailed(rfbClientPtr cl, char *reason); +extern void rfbNewUDPConnection(int sock); +extern void rfbProcessUDPInput(ScreenPtr pScreen, int sock); +extern Bool rfbSendFramebufferUpdate(ScreenPtr pScreen, rfbClientPtr cl); +extern Bool rfbSendRectEncodingRaw(rfbClientPtr cl, int x,int y,int w,int h); +extern Bool rfbSendUpdateBuf(rfbClientPtr cl); +extern Bool rfbSendSetColourMapEntries(rfbClientPtr cl, int firstColour, + int nColours); +extern void rfbSendBell(void); +extern void rfbSendServerCutText(char *str, int len); +extern void rfbUserAllow(int sock, int accept); +extern void rfbSetClip (WindowPtr pWin, BOOL enable); + +extern int GenerateVncConnectedEvent(int sock); +extern int GenerateVncDisconnectedEvent(int sock); + +#ifdef CHROMIUM +extern void rfbSendChromiumWindowShow(unsigned int winid, unsigned int show); +extern void rfbSendChromiumWindowDestroy(unsigned int winid); +extern void rfbSendChromiumMoveResizeWindow(unsigned int winid, int x, int y, unsigned int w, unsigned int h); +extern void rfbSendChromiumClipList(unsigned int winid, BoxPtr pClipRects, int numClipRects); +#endif + +/* translate.c */ + +extern Bool rfbEconomicTranslate; + +extern void rfbTranslateNone(ScreenPtr pScreen, char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + unsigned char *optr, + int bytesBetweenInputLines, + int width, int height, + int x, int y); +extern Bool rfbSetTranslateFunction(rfbClientPtr cl); +extern void rfbSetClientColourMaps(int firstColour, int nColours); +extern Bool rfbSetClientColourMap(rfbClientPtr cl, int firstColour, + int nColours); + + +/* httpd.c */ + +extern Bool httpInitSockets(ScreenPtr pScreen); +extern void httpCheckFds(ScreenPtr pScreen); + + +/* vncInit.c */ +extern void VNCInitForDMX(void); +extern void rfbWakeupHandlerDMX(void); + + +/* vncext.c */ + +#ifdef CHROMIUM +extern void rfbSendChromiumStart(unsigned int ipaddress, unsigned int crServerPort, unsigned int mothershipPort); +extern void rfbChromiumMonitorWindowID(unsigned int cr_windowid, unsigned long windowid); +int GenerateVncChromiumConnectedEvent(int sock); +#endif + + +/* auth.c */ + +extern void rfbAuthNewClient(rfbClientPtr cl); +extern void rfbProcessClientSecurityType(rfbClientPtr cl); +extern void rfbProcessClientTunnelingType(rfbClientPtr cl); +extern void rfbProcessClientAuthType(rfbClientPtr cl); +extern void rfbVncAuthProcessResponse(rfbClientPtr cl); + +/* Functions to prevent too many successive authentication failures */ +extern Bool rfbAuthConsiderBlocking(rfbClientPtr cl); +extern void rfbAuthUnblock(rfbClientPtr cl); +extern Bool rfbAuthIsBlocked(rfbClientPtr cl); + +/* loginauth.c */ + +extern void rfbLoginAuthProcessClientMessage(rfbClientPtr cl); + +/* rre.c */ + +extern Bool rfbSendRectEncodingRRE(rfbClientPtr cl, int x,int y,int w,int h); + + +/* corre.c */ + +extern Bool rfbSendRectEncodingCoRRE(rfbClientPtr cl, int x,int y,int w,int h); + + +/* hextile.c */ + +extern Bool rfbSendRectEncodingHextile(rfbClientPtr cl, int x, int y, int w, + int h); + + +/* zlib.c */ + +/* Minimum zlib rectangle size in bytes. Anything smaller will + * not compress well due to overhead. + */ +#define VNC_ENCODE_ZLIB_MIN_COMP_SIZE (17) + +/* Set maximum zlib rectangle size in pixels. Always allow at least + * two scan lines. + */ +#define ZLIB_MAX_RECT_SIZE (128*256) +#define ZLIB_MAX_SIZE(min) ((( min * 2 ) > ZLIB_MAX_RECT_SIZE ) ? \ + ( min * 2 ) : ZLIB_MAX_RECT_SIZE ) + +extern Bool rfbSendRectEncodingZlib(rfbClientPtr cl, int x, int y, int w, + int h); + + +/* tight.c */ + +#define TIGHT_DEFAULT_COMPRESSION 6 + +extern Bool rfbTightDisableGradient; + +extern int rfbNumCodedRectsTight(rfbClientPtr cl, int x,int y,int w,int h); +extern Bool rfbSendRectEncodingTight(rfbClientPtr cl, int x,int y,int w,int h); + + +/* cursor.c */ + +extern Bool rfbSendCursorShape(rfbClientPtr cl, ScreenPtr pScreen); +extern Bool rfbSendCursorPos(rfbClientPtr cl, ScreenPtr pScreen); + + +/* stats.c */ + +extern void rfbResetStats(rfbClientPtr cl); +extern void rfbPrintStats(rfbClientPtr cl); + + +/* dpms.c */ + +extern Bool DPMSSupported(void); +extern int DPSMGet(int *level); +extern void DPMSSet(int level); + + +#endif /* RFB_H_INCLUDED */ diff --git a/hw/vnc/rfbkeyb.c b/hw/vnc/rfbkeyb.c new file mode 100644 index 0000000..2405e66 --- /dev/null +++ b/hw/vnc/rfbkeyb.c @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2002 Alan Hourihane. 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. + * + * Author: Alan Hourihane + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#if XFREE86VNC +#ifndef XFree86LOADER +#include +#include +#endif + +#include +#include +#if !defined(DGUX) +#include +#endif +#include +#include /* Needed for InitValuator/Proximity stuff */ +#include + +#ifdef XFree86LOADER +#include +#endif +#else +#include +#include +#endif +#include "rfb.h" + + +extern void rfbSendBell(void); +extern DeviceIntPtr kbdDevice; + +static const char *DEFAULTS[] = { + NULL +}; + +#include +#include "keyboard.h" + +#ifdef XKB +#include +#include +#include + +#if XFREE86VNC + /* + * would like to use an XkbComponentNamesRec here but can't without + * pulling in a bunch of header files. :-( + */ +static char * xkbkeymap; +static char * xkbkeycodes; +static char * xkbtypes; +static char * xkbcompat; +static char * xkbsymbols; +static char * xkbgeometry; +static Bool xkbcomponents_specified; +static char * xkbrules; +static char * xkbmodel; +static char * xkblayout; +static char * xkbvariant; +static char * xkboptions; +#endif +#endif + +void +KbdDeviceInit(DeviceIntPtr pDevice, KeySymsPtr pKeySyms, CARD8 *pModMap) +{ + int i; + + for (i = 0; i < MAP_LENGTH; i++) + pModMap[i] = NoSymbol; + + pModMap[CONTROL_L_KEY_CODE] = ControlMask; + pModMap[CONTROL_R_KEY_CODE] = ControlMask; + pModMap[SHIFT_L_KEY_CODE] = ShiftMask; + pModMap[SHIFT_R_KEY_CODE] = ShiftMask; + pModMap[META_L_KEY_CODE] = Mod1Mask; + pModMap[META_R_KEY_CODE] = Mod1Mask; + pModMap[ALT_L_KEY_CODE] = Mod1Mask; + pModMap[ALT_R_KEY_CODE] = Mod1Mask; + + pKeySyms->minKeyCode = MIN_KEY_CODE; + pKeySyms->maxKeyCode = MAX_KEY_CODE; + pKeySyms->mapWidth = GLYPHS_PER_KEY; + + pKeySyms->map = (KeySym *)xalloc(sizeof(KeySym) + * MAP_LENGTH * GLYPHS_PER_KEY); + + if (!pKeySyms->map) { + ErrorF("xalloc failed\n"); + exit(1); + } + + for (i = 0; i < MAP_LENGTH * GLYPHS_PER_KEY; i++) + pKeySyms->map[i] = NoSymbol; + + for (i = 0; i < N_PREDEFINED_KEYS * GLYPHS_PER_KEY; i++) { + pKeySyms->map[i] = map[i]; + } +} + +void +KbdDeviceOn(void) +{ +} + + +void +KbdDeviceOff(void) +{ +} + +#if XFREE86VNC +static int +xf86rfbKeybControlProc(DeviceIntPtr device, int onoff) +{ + KeySymsRec keySyms; + CARD8 modMap[MAP_LENGTH]; + DevicePtr pDev = (DevicePtr)device; + + switch (onoff) + { + case DEVICE_INIT: + vncSetKeyboardDevice(device); + KbdDeviceInit(device, &keySyms, modMap); +#ifdef XKB + if (noXkbExtension) { +#endif + InitKeyboardDeviceStruct(pDev, &keySyms, modMap, + (BellProcPtr)rfbSendBell, + (KbdCtrlProcPtr)NoopDDA); +#ifdef XKB + } else { + XkbComponentNamesRec names; + if (xkbkeymap) { + names.keymap = xkbkeymap; + names.keycodes = NULL; + names.types = NULL; + names.compat = NULL; + names.symbols = NULL; + names.geometry = NULL; + } else { + names.keymap = NULL; + names.keycodes = xkbkeycodes; + names.types = xkbtypes; + names.compat = xkbcompat; + names.symbols = xkbsymbols; + names.geometry = xkbgeometry; + } + if ((xkbkeymap || xkbcomponents_specified) + && (xkbmodel == NULL || xkblayout == NULL)) { + xkbrules = NULL; + } +#if 0 + XkbSetRulesDflts(xkbrules, xkbmodel, + xkblayout, xkbvariant, + xkboptions); +#endif + XkbInitKeyboardDeviceStruct(device, + &names, + &keySyms, + modMap, + (BellProcPtr)rfbSendBell, + (KbdCtrlProcPtr)NoopDDA); + } +#endif + break; + case DEVICE_ON: + pDev->on = TRUE; + KbdDeviceOn(); + break; + case DEVICE_OFF: + pDev->on = FALSE; + KbdDeviceOff(); + break; + case DEVICE_CLOSE: + vncSetKeyboardDevice(NULL); + if (pDev->on) + KbdDeviceOff(); + break; + } + return Success; +} + +static void +xf86rfbKeybUninit(InputDriverPtr drv, + InputInfoPtr pInfo, + int flags) +{ + xf86rfbKeybControlProc(pInfo->dev, DEVICE_OFF); +} + +static InputInfoPtr +xf86rfbKeybInit(InputDriverPtr drv, + IDevPtr dev, + int flags) +{ + InputInfoPtr pInfo; + char *s; + Bool from; + + if (!(pInfo = xf86AllocateInput(drv, 0))) + return NULL; + + /* Initialise the InputInfoRec. */ + pInfo->name = dev->identifier; + pInfo->type_name = "rfbKeyb"; + pInfo->flags = XI86_KEYBOARD_CAPABLE; + pInfo->device_control = xf86rfbKeybControlProc; + pInfo->read_input = NULL; +#if 0 + pInfo->motion_history_proc = NULL; +#endif + pInfo->history_size = 0; + pInfo->control_proc = NULL; + pInfo->close_proc = NULL; + pInfo->switch_mode = NULL; + pInfo->conversion_proc = NULL; + pInfo->reverse_conversion_proc = NULL; + pInfo->fd = -1; + pInfo->dev = NULL; + pInfo->private_flags = 0; + pInfo->always_core_feedback = 0; + pInfo->conf_idev = dev; + + /* Collect the options, and process the common options. */ + xf86CollectInputOptions(pInfo, DEFAULTS, NULL); + xf86ProcessCommonOptions(pInfo, pInfo->options); + + /* Mark the device configured */ + pInfo->flags |= XI86_CONFIGURED; + +#ifdef XKB + from = X_DEFAULT; + if (noXkbExtension) + from = X_CMDLINE; + else if (xf86FindOption(dev->commonOptions, "XkbDisable")) { + noXkbExtension = + xf86SetBoolOption(dev->commonOptions, "XkbDisable", FALSE); + from = X_CONFIG; + } + if (noXkbExtension) + xf86Msg(from, "XKB: disabled\n"); + +#define NULL_IF_EMPTY(s) (s[0] ? s : (xfree(s), (char *)NULL)) + + if (!noXkbExtension) { + if ((s = xf86SetStrOption(dev->commonOptions, "XkbKeymap", NULL))) { + xkbkeymap = NULL_IF_EMPTY(s); + xf86Msg(X_CONFIG, "XKB: keymap: \"%s\" " + "(overrides other XKB settings)\n", xkbkeymap); + } else { + if ((s = xf86SetStrOption(dev->commonOptions, "XkbCompat", NULL))) { + xkbcompat = NULL_IF_EMPTY(s); + xkbcomponents_specified = TRUE; + xf86Msg(X_CONFIG, "XKB: compat: \"%s\"\n", s); + } + + if ((s = xf86SetStrOption(dev->commonOptions, "XkbTypes", NULL))) { + xkbtypes = NULL_IF_EMPTY(s); + xkbcomponents_specified = TRUE; + xf86Msg(X_CONFIG, "XKB: types: \"%s\"\n", s); + } + + if ((s = xf86SetStrOption(dev->commonOptions, "XkbKeycodes", NULL))) { + xkbkeycodes = NULL_IF_EMPTY(s); + xkbcomponents_specified = TRUE; + xf86Msg(X_CONFIG, "XKB: keycodes: \"%s\"\n", s); + } + + if ((s = xf86SetStrOption(dev->commonOptions, "XkbGeometry", NULL))) { + xkbgeometry = NULL_IF_EMPTY(s); + xkbcomponents_specified = TRUE; + xf86Msg(X_CONFIG, "XKB: geometry: \"%s\"\n", s); + } + + if ((s = xf86SetStrOption(dev->commonOptions, "XkbSymbols", NULL))) { + xkbsymbols = NULL_IF_EMPTY(s); + xkbcomponents_specified = TRUE; + xf86Msg(X_CONFIG, "XKB: symbols: \"%s\"\n", s); + } + + if ((s = xf86SetStrOption(dev->commonOptions, "XkbRules", NULL))) { + xkbrules = NULL_IF_EMPTY(s); + xkbcomponents_specified = TRUE; + xf86Msg(X_CONFIG, "XKB: rules: \"%s\"\n", s); + } + + if ((s = xf86SetStrOption(dev->commonOptions, "XkbModel", NULL))) { + xkbmodel = NULL_IF_EMPTY(s); + xkbcomponents_specified = TRUE; + xf86Msg(X_CONFIG, "XKB: model: \"%s\"\n", s); + } + + if ((s = xf86SetStrOption(dev->commonOptions, "XkbLayout", NULL))) { + xkblayout = NULL_IF_EMPTY(s); + xkbcomponents_specified = TRUE; + xf86Msg(X_CONFIG, "XKB: layout: \"%s\"\n", s); + } + + if ((s = xf86SetStrOption(dev->commonOptions, "XkbVariant", NULL))) { + xkbvariant = NULL_IF_EMPTY(s); + xkbcomponents_specified = TRUE; + xf86Msg(X_CONFIG, "XKB: variant: \"%s\"\n", s); + } + + if ((s = xf86SetStrOption(dev->commonOptions, "XkbOptions", NULL))) { + xkboptions = NULL_IF_EMPTY(s); + xkbcomponents_specified = TRUE; + xf86Msg(X_CONFIG, "XKB: options: \"%s\"\n", s); + } + } + } +#undef NULL_IF_EMPTY +#endif + + /* Return the configured device */ + return (pInfo); +} + +#ifdef XFree86LOADER +static +#endif +InputDriverRec RFBKEYB = { + 1, /* driver version */ + "rfbkeyb", /* driver name */ + NULL, /* identify */ + xf86rfbKeybInit, /* pre-init */ + xf86rfbKeybUninit, /* un-init */ + NULL, /* module */ + 0 /* ref count */ +}; + +/* + *************************************************************************** + * + * Dynamic loading functions + * + *************************************************************************** + */ +#ifdef XFree86LOADER +static void +xf86rfbKeybUnplug(pointer p) +{ +} + +static pointer +xf86rfbKeybPlug(pointer module, + pointer options, + int *errmaj, + int *errmin) +{ + xf86AddInputDriver(&RFBKEYB, module, 0); + + return module; +} + +void +vncInitKeyb(void) +{ + xf86AddInputDriver(&RFBKEYB, NULL, 0); +} + +static XF86ModuleVersionInfo xf86rfbKeybVersionRec = +{ + "rfbkeyb", + "xf4vnc Project, see http://xf4vnc.sf.net", + MODINFOSTRING1, + MODINFOSTRING2, + XF86_VERSION_CURRENT, + 1, 0, 0, + ABI_CLASS_XINPUT, + ABI_XINPUT_VERSION, + MOD_CLASS_XINPUT, + {0, 0, 0, 0} /* signature, to be patched into the file by */ + /* a tool */ +}; + +XF86ModuleData rfbkeybModuleData = {&xf86rfbKeybVersionRec, + xf86rfbKeybPlug, + xf86rfbKeybUnplug}; + +#endif /* XFree86LOADER */ +#endif diff --git a/hw/vnc/rfbmouse.c b/hw/vnc/rfbmouse.c new file mode 100644 index 0000000..40e6ec6 --- /dev/null +++ b/hw/vnc/rfbmouse.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2002 Alan Hourihane. 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. + * + * Author: Alan Hourihane + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#include +#if XFREE86VNC +#ifndef XFree86LOADER +#include +#include +#endif + +#include +#include +#if !defined(DGUX) +#include +#endif +#include +#include /* Needed for InitValuator/Proximity stuff */ +#include + +#ifdef XFree86LOADER +#include +#endif + +static const char *DEFAULTS[] = { + NULL +}; +#else +#include +#include +#endif +#include "rfb.h" + + +unsigned char ptrAcceleration = 50; + +void +PtrDeviceOn(DeviceIntPtr pDev) +{ +#if 0 + ptrAcceleration = (unsigned char)pDev->ptrfeed->ctrl.num; +#endif +} + +void +PtrDeviceInit(void) +{ +} + +void +PtrDeviceOff(void) +{ +} + + +void +PtrDeviceControl(DeviceIntPtr dev, PtrCtrl *ctrl) +{ +#if 0 + ptrAcceleration = (char)ctrl->num; + + if (udpSockConnected) { + if (write(udpSock, &ptrAcceleration, 1) <= 0) { + ErrorF("PtrDeviceControl: UDP input: write"); + rfbDisconnectUDPSock(); + } + } +#endif +} + +#if XFREE86VNC +static int +xf86rfbMouseControlProc(DeviceIntPtr device, int onoff) +{ + BYTE map[6]; + DevicePtr pDev = (DevicePtr)device; + void *func1; + int (*func2)(void); + + if (LoaderSymbol("GetMotionHistory")) + func1 = LoaderSymbol("GetMotionHistory"); + else + func1 = LoaderSymbol("miPointerGetMotionEvents"); + + if (LoaderSymbol("GetMotionHistorySize")) + func2 = LoaderSymbol("GetMotionHistorySize"); + else + func2 = LoaderSymbol("miPointerGetMotionBufferSize"); + + + switch (onoff) + { + case DEVICE_INIT: + vncSetPointerDevice(device); + PtrDeviceInit(); + map[1] = 1; + map[2] = 2; + map[3] = 3; + map[4] = 4; + map[5] = 5; + InitPointerDeviceStruct(pDev, map, 5, + func1, + PtrDeviceControl, + (*func2)(), 2); + break; + + case DEVICE_ON: + pDev->on = TRUE; + PtrDeviceOn(device); + break; + + case DEVICE_OFF: + pDev->on = FALSE; + PtrDeviceOff(); + break; + + case DEVICE_CLOSE: + vncSetPointerDevice(NULL); + if (pDev->on) + PtrDeviceOff(); + break; + } + return Success; +} + +static void +xf86rfbMouseUninit(InputDriverPtr drv, + InputInfoPtr pInfo, + int flags) +{ + xf86rfbMouseControlProc(pInfo->dev, DEVICE_OFF); +} + +static InputInfoPtr +xf86rfbMouseInit(InputDriverPtr drv, + IDevPtr dev, + int flags) +{ + InputInfoPtr pInfo; + + if (!(pInfo = xf86AllocateInput(drv, 0))) + return NULL; + + /* Initialise the InputInfoRec. */ + pInfo->name = dev->identifier; + pInfo->type_name = "rfbMouse"; + pInfo->flags = XI86_POINTER_CAPABLE | XI86_SEND_DRAG_EVENTS; + pInfo->device_control = xf86rfbMouseControlProc; + pInfo->read_input = NULL; +#if 0 + pInfo->motion_history_proc = xf86GetMotionEvents; +#endif + pInfo->history_size = 0; + pInfo->control_proc = NULL; + pInfo->close_proc = NULL; + pInfo->switch_mode = NULL; + pInfo->conversion_proc = NULL; + pInfo->reverse_conversion_proc = NULL; + pInfo->fd = -1; + pInfo->dev = NULL; + pInfo->private_flags = 0; + pInfo->always_core_feedback = 0; + pInfo->conf_idev = dev; + + /* Collect the options, and process the common options. */ + xf86CollectInputOptions(pInfo, DEFAULTS, NULL); + xf86ProcessCommonOptions(pInfo, pInfo->options); + + /* Mark the device configured */ + pInfo->flags |= XI86_CONFIGURED; + + /* Return the configured device */ + return (pInfo); +} + +#ifdef XFree86LOADER +static +#endif +InputDriverRec RFBMOUSE = { + 1, /* driver version */ + "rfbmouse", /* driver name */ + NULL, /* identify */ + xf86rfbMouseInit, /* pre-init */ + xf86rfbMouseUninit, /* un-init */ + NULL, /* module */ + 0 /* ref count */ +}; + +/* + *************************************************************************** + * + * Dynamic loading functions + * + *************************************************************************** + */ +#ifdef XFree86LOADER +static void +xf86rfbMouseUnplug(pointer p) +{ +} + +static pointer +xf86rfbMousePlug(pointer module, + pointer options, + int *errmaj, + int *errmin) +{ + xf86AddInputDriver(&RFBMOUSE, module, 0); + + return module; +} + +void +vncInitMouse(void) +{ + xf86AddInputDriver(&RFBMOUSE, NULL, 0); +} + +static XF86ModuleVersionInfo xf86rfbMouseVersionRec = +{ + "rfbmouse", + "xf4vnc Project, see http://xf4vnc.sf.net", + MODINFOSTRING1, + MODINFOSTRING2, + XF86_VERSION_CURRENT, + 1, 0, 0, + ABI_CLASS_XINPUT, + ABI_XINPUT_VERSION, + MOD_CLASS_XINPUT, + {0, 0, 0, 0} /* signature, to be patched into the file by */ + /* a tool */ +}; + +XF86ModuleData rfbmouseModuleData = {&xf86rfbMouseVersionRec, + xf86rfbMousePlug, + xf86rfbMouseUnplug}; + +#endif /* XFree86LOADER */ +#endif diff --git a/hw/vnc/rfbproto.h b/hw/vnc/rfbproto.h new file mode 100644 index 0000000..5ed86c5 --- /dev/null +++ b/hw/vnc/rfbproto.h @@ -0,0 +1,1362 @@ +/* + * Copyright (C) 2000-2004 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, versions 3.3, 3.7 and 3.7t + * (protocol 3.7t is effectively 3.7 with TightVNC extensions enabled) + * + * Uses types CARD for an n-bit unsigned integer, INT 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. + */ + + +/***************************************************************************** + * + * 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 _rfbRectangle { + CARD16 x; + CARD16 y; + CARD16 w; + CARD16 h; +} rfbRectangle; + +#define sz_rfbRectangle 8 + + +/*----------------------------------------------------------------------------- + * Structure used to specify pixel format. + */ + +typedef struct _rfbPixelFormat { + + 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 + + +/*----------------------------------------------------------------------------- + * Structure used to describe protocol options such as tunneling methods, + * authentication schemes and message types (protocol version 3.7t). + */ + +typedef struct _rfbCapabilityInfo { + + CARD32 code; /* numeric identifier */ + CARD8 vendorSignature[4]; /* vendor identification */ + CARD8 nameSignature[8]; /* abbreviated option name */ + +} rfbCapabilityInfo; + +#define sz_rfbCapabilityInfoVendor 4 +#define sz_rfbCapabilityInfoName 8 +#define sz_rfbCapabilityInfo 16 + +/* + * Vendors known by TightVNC: standard VNC/RealVNC, TridiaVNC, and TightVNC. + */ + +#define rfbStandardVendor "STDV" +#define rfbTridiaVncVendor "TRDV" +#define rfbTightVncVendor "TGHT" +#define rfbTungstenGraphicsVendor "TGIV" + + +/***************************************************************************** + * + * 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.7 + * this is "RFB 003.007\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 7 +#define rfbProtocolFallbackMinorVersion 3 + +typedef char rfbProtocolVersionMsg[13]; /* allow extra byte for null */ + +#define sz_rfbProtocolVersionMsg 12 + + +/* + * Negotiation of the security type (protocol version 3.7) + * + * Once the protocol version has been decided, the server either sends a list + * of supported security types, or informs the client about an error (when the + * number of security types is 0). Security type rfbSecTypeTight is used to + * enable TightVNC-specific protocol extensions. The value rfbSecTypeVncAuth + * stands for classic VNC authentication. + * + * The client selects a particular security type from the list provided by the + * server. + */ + +#define rfbSecTypeInvalid 0 +#define rfbSecTypeNone 1 +#define rfbSecTypeVncAuth 2 +#define rfbSecTypeTight 16 + + +/*----------------------------------------------------------------------------- + * Negotiation of Tunneling Capabilities (protocol version 3.7t) + * + * If the chosen security type is rfbSecTypeTight, the server sends a list of + * supported tunneling methods ("tunneling" refers to any additional layer of + * data transformation, such as encryption or external compression.) + * + * nTunnelTypes specifies the number of following rfbCapabilityInfo structures + * that list all supported tunneling methods in the order of preference. + * + * NOTE: If nTunnelTypes is 0, that tells the client that no tunneling can be + * used, and the client should not send a response requesting a tunneling + * method. + */ + +typedef struct _rfbTunnelingCapsMsg { + CARD32 nTunnelTypes; + /* followed by nTunnelTypes * rfbCapabilityInfo structures */ +} rfbTunnelingCapsMsg; + +#define sz_rfbTunnelingCapsMsg 4 + +/*----------------------------------------------------------------------------- + * Tunneling Method Request (protocol version 3.7t) + * + * If the list of tunneling capabilities sent by the server was not empty, the + * client should reply with a 32-bit code specifying a particular tunneling + * method. The following code should be used for no tunneling. + */ + +#define rfbNoTunneling 0 +#define sig_rfbNoTunneling "NOTUNNEL" + + +/*----------------------------------------------------------------------------- + * Negotiation of Authentication Capabilities (protocol version 3.7t) + * + * After setting up tunneling, the server sends a list of supported + * authentication schemes. + * + * nAuthTypes specifies the number of following rfbCapabilityInfo structures + * that list all supported authentication schemes in the order of preference. + * + * NOTE: If nAuthTypes is 0, that tells the client that no authentication is + * necessary, and the client should not send a response requesting an + * authentication scheme. + */ + +typedef struct _rfbAuthenticationCapsMsg { + CARD32 nAuthTypes; + /* followed by nAuthTypes * rfbCapabilityInfo structures */ +} rfbAuthenticationCapsMsg; + +#define sz_rfbAuthenticationCapsMsg 4 + +/*----------------------------------------------------------------------------- + * Authentication Scheme Request (protocol version 3.7t) + * + * If the list of authentication capabilities sent by the server was not empty, + * the client should reply with a 32-bit code specifying a particular + * authentication scheme. The following codes are supported. + */ + +#define rfbAuthNone 1 +#define rfbAuthVNC 2 +#define rfbAuthUnixLogin 129 +#define rfbAuthExternal 130 + +#define sig_rfbAuthNone "NOAUTH__" +#define sig_rfbAuthVNC "VNCAUTH_" +#define sig_rfbAuthUnixLogin "ULGNAUTH" +#define sig_rfbAuthExternal "XTRNAUTH" + + +/*----------------------------------------------------------------------------- + * Standard VNC Authentication (all protocol versions) + * + * Standard authentication result codes are defined below. + */ + +#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 _rfbClientInitMsg { + 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 _rfbServerInitMsg { + 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) + + +/*----------------------------------------------------------------------------- + * Server Interaction Capabilities Message (protocol version 3.7t) + * + * In the protocol version 3.7t, the server informs the client what message + * types it supports in addition to ones defined in the protocol version 3.7. + * Also, the server sends the list of all supported encodings (note that it's + * not necessary to advertise the "raw" encoding sinse it MUST be supported in + * RFB 3.x protocols). + * + * This data immediately follows the server initialisation message. + */ + +typedef struct _rfbInteractionCapsMsg { + CARD16 nServerMessageTypes; + CARD16 nClientMessageTypes; + CARD16 nEncodingTypes; + CARD16 pad; /* reserved, must be 0 */ + /* followed by nServerMessageTypes * rfbCapabilityInfo structures */ + /* followed by nClientMessageTypes * rfbCapabilityInfo structures */ +} rfbInteractionCapsMsg; + +#define sz_rfbInteractionCapsMsg 8 + + +/* + * 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 +/* Chromium extensions, use higher values */ +#define rfbChromiumStart 50 +#define rfbChromiumMoveResizeWindow 51 +#define rfbChromiumClipList 52 +#define rfbChromiumWindowShow 53 +#define rfbChromiumWindowDestroy 54 + +#define rfbFileListData 130 +#define rfbFileDownloadData 131 +#define rfbFileUploadCancel 132 +#define rfbFileDownloadFailed 133 + +/* signatures for non-standard messages */ +#define sig_rfbFileListData "FTS_LSDT" +#define sig_rfbFileDownloadData "FTS_DNDT" +#define sig_rfbFileUploadCancel "FTS_UPCN" +#define sig_rfbFileDownloadFailed "FTS_DNFL" + + +/* 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 +/* Chromium extensions, use higher values */ +#define rfbChromiumStop 50 +#define rfbChromiumExpose 51 + +#define rfbFileListRequest 130 +#define rfbFileDownloadRequest 131 +#define rfbFileUploadRequest 132 +#define rfbFileUploadData 133 +#define rfbFileDownloadCancel 134 +#define rfbFileUploadFailed 135 +#define rfbFileCreateDirRequest 136 + +/* signatures for non-standard messages */ +#define sig_rfbFileListRequest "FTC_LSRQ" +#define sig_rfbFileDownloadRequest "FTC_DNRQ" +#define sig_rfbFileUploadRequest "FTC_UPRQ" +#define sig_rfbFileUploadData "FTC_UPDT" +#define sig_rfbFileDownloadCancel "FTC_DNCN" +#define sig_rfbFileUploadFailed "FTC_UPFL" +#define sig_rfbFileCreateDirRequest "FTC_FCDR" + +/***************************************************************************** + * + * 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 + +/* signatures for basic encoding types */ +#define sig_rfbEncodingRaw "RAW_____" +#define sig_rfbEncodingCopyRect "COPYRECT" +#define sig_rfbEncodingRRE "RRE_____" +#define sig_rfbEncodingCoRRE "CORRE___" +#define sig_rfbEncodingHextile "HEXTILE_" +#define sig_rfbEncodingZlib "ZLIB____" +#define sig_rfbEncodingTight "TIGHT___" +#define sig_rfbEncodingZlibHex "ZLIBHEX_" +#define sig_rfbEncodingChromium "CHROMIUM" + +/* + * 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 -- not allocated yet. + */ + +#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 rfbEncodingPointerPos 0xFFFFFF18 + +#define rfbEncodingLastRect 0xFFFFFF20 +#define rfbEncodingNewFBSize 0xFFFFFF21 +#define rfbEncodingChromium 0xFFFFFF2F +#define rfbEncodingChromium2 0xFFFFFF30 + +#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 + +/* signatures for "fake" encoding types */ +#define sig_rfbEncodingCompressLevel0 "COMPRLVL" +#define sig_rfbEncodingXCursor "X11CURSR" +#define sig_rfbEncodingRichCursor "RCHCURSR" +#define sig_rfbEncodingPointerPos "POINTPOS" +#define sig_rfbEncodingLastRect "LASTRECT" +#define sig_rfbEncodingNewFBSize "NEWFBSIZ" +#define sig_rfbEncodingQualityLevel0 "JPEGQLVL" + + +/***************************************************************************** + * + * 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 _rfbFramebufferUpdateMsg { + 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 _rfbFramebufferUpdateRectHeader { + 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 _rfbCopyRect { + 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 [...] where each is + * []. + */ + +typedef struct _rfbRREHeader { + 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 + * [...] where each is + * []. This means that + * the whole rectangle must be at most 255x255 pixels. + */ + +typedef struct _rfbCoRRERectangle { + 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 compression Encoding. We have an rfbZlibHeader structure + * giving the number of bytes to follow. Finally the data follows in + * zlib compressed format. + */ + +typedef struct _rfbZlibHeader { + 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 + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * ZLIBHEX - zlib compressed Hextile Encoding. Essentially, this is the + * hextile encoding with zlib compression on the tiles that can not be + * efficiently encoded with one of the other hextile subencodings. The + * new zlib subencoding uses two bytes to specify the length of the + * compressed tile and then the compressed data follows. As with the + * raw sub-encoding, the zlib subencoding invalidates the other + * values, if they are also set. + */ + +#define rfbHextileZlibRaw (1 << 5) +#define rfbHextileZlibHex (1 << 6) + + +/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + * 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 _rfbXCursorColors { + 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). + */ + + +/*----------------------------------------------------------------------------- + * 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 _rfbSetColourMapEntriesMsg { + CARD8 type; /* always rfbSetColourMapEntries */ + CARD8 redIndex; /* used to be pad, but used for DirectColor */ + 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 _rfbBellMsg { + CARD8 type; /* always rfbBell */ +} rfbBellMsg; + +#define sz_rfbBellMsg 1 + + + +/*----------------------------------------------------------------------------- + * ServerCutText - the server has new text in its cut buffer. + */ + +typedef struct _rfbServerCutTextMsg { + CARD8 type; /* always rfbServerCutText */ + CARD8 pad1; + CARD16 pad2; + CARD32 length; + /* followed by char text[length] */ +} rfbServerCutTextMsg; + +#define sz_rfbServerCutTextMsg 8 + +/*----------------------------------------------------------------------------- + * ChromiumStart - a port number for the crserver + */ + +typedef struct { + CARD8 type; /* always rfbChromiumStart */ + CARD8 pad1; + CARD16 pad2; + CARD32 crServerPort; + CARD32 mothershipPort; +} rfbChromiumStartMsg; + +#define sz_rfbChromiumStartMsg 12 + + +/*----------------------------------------------------------------------------- + * ChromiumMoveResizeWindow - move a chromium window + */ + +typedef struct { + CARD8 type; /* always rfbChromiumMoveResizeWindow */ + CARD8 pad1; + CARD16 pad2; + CARD32 winid; + CARD32 x; + CARD32 y; + CARD32 w; + CARD32 h; +} rfbChromiumMoveResizeWindowMsg; + +#define sz_rfbChromiumMoveResizeWindowMsg 24 + +/*----------------------------------------------------------------------------- + * ChromiumClipList - send clip list + */ + +typedef struct { + CARD8 type; /* always rfbChromiumClipList */ + CARD8 pad1; + CARD16 pad2; + CARD32 winid; + CARD32 length; +} rfbChromiumClipListMsg; + +#define sz_rfbChromiumClipListMsg 12 + +/*----------------------------------------------------------------------------- + * ChromiumWindowShow - map or unmap a window + */ + +typedef struct { + CARD8 type; /* always rfbChromiumWindowShow */ + CARD8 pad1; + CARD16 pad2; + CARD32 winid; + CARD32 show; +} rfbChromiumWindowShowMsg; + +#define sz_rfbChromiumWindowShowMsg 12 + +/*----------------------------------------------------------------------------- + * ChromiumWindowDestroy - destroy a window + */ + +typedef struct { + CARD8 type; /* always rfbChromiumWindowDestroy */ + CARD8 pad1; + CARD16 pad2; + CARD32 winid; +} rfbChromiumWindowDestroyMsg; + +#define sz_rfbChromiumWindowDestroyMsg 8 + +/*----------------------------------------------------------------------------- + * FileListData + */ + +typedef struct _rfbFileListDataMsg { + CARD8 type; + CARD8 flags; + CARD16 numFiles; + CARD16 dataSize; + CARD16 compressedSize; + /* Followed by SizeData[numFiles] */ + /* Followed by Filenames[compressedSize] */ +} rfbFileListDataMsg; + +#define sz_rfbFileListDataMsg 8 + +/*----------------------------------------------------------------------------- + * FileDownloadData + */ + +typedef struct _rfbFileDownloadDataMsg { + CARD8 type; + CARD8 compressLevel; + CARD16 realSize; + CARD16 compressedSize; + /* Followed by File[copressedSize] */ +} rfbFileDownloadDataMsg; + +#define sz_rfbFileDownloadDataMsg 6 + + +/*----------------------------------------------------------------------------- + * FileUploadCancel + */ + +typedef struct _rfbFileUploadCancelMsg { + CARD8 type; + CARD8 unused; + CARD16 reasonLen; + /* Followed by reason[reasonLen] */ +} rfbFileUploadCancelMsg; + +#define sz_rfbFileUploadCancelMsg 4 + +/*----------------------------------------------------------------------------- + * FileDownloadFailed + */ + +typedef struct _rfbFileDownloadFailedMsg { + CARD8 type; + CARD8 unused; + CARD16 reasonLen; + /* Followed by reason[reasonLen] */ +} rfbFileDownloadFailedMsg; + +#define sz_rfbFileDownloadFailedMsg 4 + +/*----------------------------------------------------------------------------- + * Union of all server->client messages. + */ + +typedef union _rfbServerToClientMsg { + CARD8 type; + rfbFramebufferUpdateMsg fu; + rfbSetColourMapEntriesMsg scme; + rfbBellMsg b; + rfbServerCutTextMsg sct; + rfbFileListDataMsg fld; + rfbFileDownloadDataMsg fdd; + rfbFileUploadCancelMsg fuc; + rfbFileDownloadFailedMsg fdf; + rfbChromiumStartMsg scd; + rfbChromiumMoveResizeWindowMsg scm; + rfbChromiumClipListMsg sccl; + rfbChromiumWindowShowMsg scws; + rfbChromiumWindowDestroyMsg scwd; +} rfbServerToClientMsg; + + + +/***************************************************************************** + * + * Message definitions (client -> server) + * + *****************************************************************************/ + + +/*----------------------------------------------------------------------------- + * SetPixelFormat - tell the RFB server the format in which the client wants + * pixels sent. + */ + +typedef struct _rfbSetPixelFormatMsg { + 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 _rfbFixColourMapEntriesMsg { + 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 _rfbSetEncodingsMsg { + 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 _rfbFramebufferUpdateRequestMsg { + 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 _rfbKeyEventMsg { + 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 _rfbPointerEventMsg { + 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 rfbButton4Mask 8 +#define rfbButton5Mask 16 + +#define sz_rfbPointerEventMsg 6 + + + +/*----------------------------------------------------------------------------- + * ClientCutText - the client has new text in its cut buffer. + */ + +typedef struct _rfbClientCutTextMsg { + CARD8 type; /* always rfbClientCutText */ + CARD8 pad1; + CARD16 pad2; + CARD32 length; + /* followed by char text[length] */ +} rfbClientCutTextMsg; + +#define sz_rfbClientCutTextMsg 8 + +/*----------------------------------------------------------------------------- + * FileListRequest + */ + +typedef struct _rfbFileListRequestMsg { + CARD8 type; + CARD8 flags; + CARD16 dirNameSize; + /* Followed by char Dirname[dirNameSize] */ +} rfbFileListRequestMsg; + +#define sz_rfbFileListRequestMsg 4 + +/*----------------------------------------------------------------------------- + * FileDownloadRequest + */ + +typedef struct _rfbFileDownloadRequestMsg { + CARD8 type; + CARD8 compressedLevel; + CARD16 fNameSize; + CARD32 position; + /* Followed by char Filename[fNameSize] */ +} rfbFileDownloadRequestMsg; + +#define sz_rfbFileDownloadRequestMsg 8 + +/*----------------------------------------------------------------------------- + * FileUploadRequest + */ + +typedef struct _rfbFileUploadRequestMsg { + CARD8 type; + CARD8 compressedLevel; + CARD16 fNameSize; + CARD32 position; + /* Followed by char Filename[fNameSize] */ +} rfbFileUploadRequestMsg; + +#define sz_rfbFileUploadRequestMsg 8 + + +/*----------------------------------------------------------------------------- + * FileUploadData + */ + +typedef struct _rfbFileUploadDataMsg { + CARD8 type; + CARD8 compressedLevel; + CARD16 realSize; + CARD16 compressedSize; + /* Followed by File[compressedSize] */ +} rfbFileUploadDataMsg; + +#define sz_rfbFileUploadDataMsg 8 + +/*----------------------------------------------------------------------------- + * FileDownloadCancel + */ + +typedef struct _rfbFileDownloadCancelMsg { + CARD8 type; + CARD8 unused; + CARD16 reasonLen; + /* Followed by reason[reasonLen] */ +} rfbFileDownloadCancelMsg; + +#define sz_rfbFileDownloadCancelMsg 4 + +/*----------------------------------------------------------------------------- + * FileUploadFailed + */ + +typedef struct _rfbFileUploadFailedMsg { + CARD8 type; + CARD8 unused; + CARD16 reasonLen; + /* Followed by reason[reasonLen] */ +} rfbFileUploadFailedMsg; + +#define sz_rfbFileUploadFailedMsg 4 + +/*----------------------------------------------------------------------------- + * FileCreateDirRequest + */ + +typedef struct _rfbFileCreateDirRequestMsg { + CARD8 type; + CARD8 unused; + CARD16 dNameLen; + CARD32 dModTime; + /* Followed by dName[dNameLen] */ +} rfbFileCreateDirRequestMsg; + +#define sz_rfbFileCreateDirRequestMsg 8 + +/*----------------------------------------------------------------------------- + * ChromiumStop - the client has stopped the GL app. + */ + +typedef struct { + CARD8 type; /* always rfbChromiumStop */ + CARD8 pad1; + CARD16 pad2; + CARD32 port; +} rfbChromiumStopMsg; + +#define sz_rfbChromiumStopMsg 8 + +/*----------------------------------------------------------------------------- + * ChromiumExpose - redraw the window + */ + +typedef struct { + CARD8 type; /* always rfbChromiumExpose */ + CARD8 pad1; + CARD16 pad2; + CARD32 winid; +} rfbChromiumExposeMsg; + +#define sz_rfbChromiumExposeMsg 8 + +/*----------------------------------------------------------------------------- + * Union of all client->server messages. + */ + +typedef union _rfbClientToServerMsg { + CARD8 type; + rfbSetPixelFormatMsg spf; + rfbFixColourMapEntriesMsg fcme; + rfbSetEncodingsMsg se; + rfbFramebufferUpdateRequestMsg fur; + rfbKeyEventMsg ke; + rfbPointerEventMsg pe; + rfbClientCutTextMsg cct; + rfbFileListRequestMsg flr; + rfbFileDownloadRequestMsg fdr; + rfbFileUploadRequestMsg fupr; + rfbFileUploadDataMsg fud; + rfbFileDownloadCancelMsg fdc; + rfbFileUploadFailedMsg fuf; + rfbFileCreateDirRequestMsg fcdr; + rfbChromiumStopMsg csd; + rfbChromiumExposeMsg cse; +} rfbClientToServerMsg; diff --git a/hw/vnc/rfbserver.c b/hw/vnc/rfbserver.c new file mode 100644 index 0000000..b93087f --- /dev/null +++ b/hw/vnc/rfbserver.c @@ -0,0 +1,2308 @@ +/* + * rfbserver.c - deal with server-side of the RFB protocol. + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * Copyright (C) 2000-2004 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. + */ + +/* Use ``#define CORBA'' to enable CORBA control interface */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include "input.h" +#include "mipointer.h" +#if XFREE86VNC +#include +#endif +#ifdef CHROMIUM +#include "mivalidate.h" +#endif +#include "rfb.h" +#include "windowstr.h" +#include "sprite.h" +#include "propertyst.h" +#include +#include + +#ifdef CORBA +#include +#endif + +#ifdef CHROMIUM +struct CRWindowTable *windowTable = NULL; +#endif + +extern Atom VNC_CONNECT; + +rfbClientPtr rfbClientHead = NULL; /* list of all VNC clients/viewers */ +rfbClientPtr pointerClient = NULL; /* Mutex for pointer events */ + +static rfbClientPtr rfbNewClient(ScreenPtr pScreen, int sock); +static void rfbProcessClientProtocolVersion(rfbClientPtr cl); +static void rfbProcessClientInitMessage(rfbClientPtr cl); +static void rfbSendInteractionCaps(rfbClientPtr cl); +static void rfbProcessClientNormalMessage(rfbClientPtr cl); +static Bool rfbSendCopyRegion(rfbClientPtr cl, RegionPtr reg, int dx, int dy); +static Bool rfbSendLastRectMarker(rfbClientPtr cl); + +static char *text = NULL; + +void +rfbRootPropertyChange(ScreenPtr pScreen) +{ + PropertyPtr pProp; + WindowPtr pWin = WindowTable[pScreen->myNum]; + + pProp = wUserProps (pWin); + + while (pProp) { + if ((pProp->propertyName == XA_CUT_BUFFER0) && + (pProp->type == XA_STRING) && + (pProp->format == 8)) + { + /* Ensure we don't keep re-sending cut buffer */ + + if ( (text && pProp->data && strncmp(text, pProp->data, pProp->size)) || !text) + rfbGotXCutText(pProp->data, pProp->size); + + if (text) xfree(text); + text = xalloc(1 + pProp->size); + if (! text) return; + if (pProp->data) memcpy(text, pProp->data, pProp->size); + text[pProp->size] = '\0'; + + return; + } + if ((pProp->propertyName == VNC_CONNECT) && (pProp->type == XA_STRING) + && (pProp->format == 8) && (pProp->size > 0)) + { + int i; + rfbClientPtr cl; + int port = 5500; + char *host = (char *)Xalloc(pProp->size+1); + memcpy(host, pProp->data, pProp->size); + host[pProp->size] = 0; + for (i = 0; i < pProp->size; i++) { + if (host[i] == ':') { + port = atoi(&host[i+1]); + host[i] = 0; + } + } + + cl = rfbReverseConnection(pScreen, host, port); + +#ifdef CORBA + if (cl != NULL) + newConnection(cl, (KEYBOARD_DEVICE|POINTER_DEVICE), 1, 1, 1); +#endif + + ChangeWindowProperty(pWin, + pProp->propertyName, pProp->type, + pProp->format, PropModeReplace, + 0, NULL, + FALSE + ); + + free(host); + } + pProp = pProp->next; + } +} + +int +rfbBitsPerPixel(depth) + int depth; +{ + if (depth == 1) return 1; + else if (depth <= 8) return 8; + else if (depth <= 16) return 16; + else return 32; +} + +void +rfbUserAllow(int sock, int accept) +{ + rfbClientPtr cl; + + for (cl = rfbClientHead; cl; cl = cl->next) { + if (cl->sock == sock) { + cl->userAccepted = accept; + } + } +} + +/* + * rfbNewClientConnection is called from sockets.c when a new connection + * comes in. + */ + +void +rfbNewClientConnection(ScreenPtr pScreen, int sock) +{ + rfbClientPtr cl; + + cl = rfbNewClient(pScreen, sock); + + GenerateVncConnectedEvent(sock); + +#if XFREE86VNC + /* Someone is connected - disable VT switching */ + xf86EnableVTSwitch(FALSE); +#endif + +#ifdef CORBA + if (cl != NULL) + newConnection(cl, (KEYBOARD_DEVICE|POINTER_DEVICE), 1, 1, 1); +#endif +} + + +/* + * rfbReverseConnection is called by the CORBA stuff to make an outward + * connection to a "listening" RFB client. + */ + +rfbClientPtr +rfbReverseConnection(ScreenPtr pScreen, char *host, int port) +{ + int sock; + rfbClientPtr cl; + + if ((sock = rfbConnect(pScreen, host, port)) < 0) + return (rfbClientPtr)NULL; + + cl = rfbNewClient(pScreen, sock); + + if (cl) { + cl->reverseConnection = TRUE; + } + + return cl; +} + + +#ifdef CHROMIUM +/* + * rfbSetClip -- + * Generate expose event. + * This function is overkill and should be cleaned up, but it + * works for now. + */ + +void +rfbSetClip (WindowPtr pWin, BOOL enable) +{ + ScreenPtr pScreen = pWin->drawable.pScreen; + WindowPtr pChild; + Bool WasViewable = (Bool)(pWin->viewable); + Bool anyMarked = FALSE; + RegionPtr pOldClip = NULL, bsExposed; +#ifdef DO_SAVE_UNDERS + Bool dosave = FALSE; +#endif + WindowPtr pLayerWin; + BoxRec box; + + if (WasViewable) + { + for (pChild = pWin->firstChild; pChild; pChild = pChild->nextSib) + { + (void) (*pScreen->MarkOverlappedWindows)(pChild, + pChild, + &pLayerWin); + } + (*pScreen->MarkWindow) (pWin); + anyMarked = TRUE; + if (pWin->valdata) + { + if (HasBorder (pWin)) + { + RegionPtr borderVisible; + + borderVisible = REGION_CREATE(pScreen, NullBox, 1); + REGION_SUBTRACT(pScreen, borderVisible, + &pWin->borderClip, &pWin->winSize); + pWin->valdata->before.borderVisible = borderVisible; + } + pWin->valdata->before.resized = TRUE; + } + } + + /* + * Use REGION_BREAK to avoid optimizations in ValidateTree + * that assume the root borderClip can't change well, normally + * it doesn't...) + */ + if (enable) + { + box.x1 = 0; + box.y1 = 0; + box.x2 = pScreen->width; + box.y2 = pScreen->height; + REGION_INIT (pScreen, &pWin->winSize, &box, 1); + REGION_INIT (pScreen, &pWin->borderSize, &box, 1); + if (WasViewable) + REGION_RESET(pScreen, &pWin->borderClip, &box); + pWin->drawable.width = pScreen->width; + pWin->drawable.height = pScreen->height; + REGION_BREAK (pWin->drawable.pScreen, &pWin->clipList); + } + else + { + REGION_EMPTY(pScreen, &pWin->borderClip); + REGION_BREAK (pWin->drawable.pScreen, &pWin->clipList); + } + + ResizeChildrenWinSize (pWin, 0, 0, 0, 0); + + if (WasViewable) + { + if (pWin->backStorage) + { + pOldClip = REGION_CREATE(pScreen, NullBox, 1); + REGION_COPY(pScreen, pOldClip, &pWin->clipList); + } + + if (pWin->firstChild) + { + anyMarked |= (*pScreen->MarkOverlappedWindows)(pWin->firstChild, + pWin->firstChild, + (WindowPtr *)NULL); + } + else + { + (*pScreen->MarkWindow) (pWin); + anyMarked = TRUE; + } + +#ifdef DO_SAVE_UNDERS + if (DO_SAVE_UNDERS(pWin)) + { + dosave = (*pScreen->ChangeSaveUnder)(pLayerWin, pLayerWin); + } +#endif /* DO_SAVE_UNDERS */ + + if (anyMarked) + (*pScreen->ValidateTree)(pWin, NullWindow, VTOther); + } + + if (pWin->backStorage && + ((pWin->backingStore == Always) || WasViewable)) + { + if (!WasViewable) + pOldClip = &pWin->clipList; /* a convenient empty region */ + bsExposed = (*pScreen->TranslateBackingStore) + (pWin, 0, 0, pOldClip, + pWin->drawable.x, pWin->drawable.y); + if (WasViewable) + REGION_DESTROY(pScreen, pOldClip); + if (bsExposed) + { + RegionPtr valExposed = NullRegion; + + if (pWin->valdata) + valExposed = &pWin->valdata->after.exposed; + (*pScreen->WindowExposures) (pWin, valExposed, bsExposed); + if (valExposed) + REGION_EMPTY(pScreen, valExposed); + REGION_DESTROY(pScreen, bsExposed); + } + } + if (WasViewable) + { + if (anyMarked) + (*pScreen->HandleExposures)(pWin); +#ifdef DO_SAVE_UNDERS + if (dosave) + (*pScreen->PostChangeSaveUnder)(pLayerWin, pLayerWin); +#endif /* DO_SAVE_UNDERS */ + if (anyMarked && pScreen->PostValidateTree) + (*pScreen->PostValidateTree)(pWin, NullWindow, VTOther); + } + if (pWin->realized) + WindowsRestructured (); + FlushAllOutput (); +} +#endif /* CHROMIUM */ + +/* + * rfbNewClient is called when a new connection has been made by whatever + * means. + */ + +static rfbClientPtr +rfbNewClient(ScreenPtr pScreen, int sock) +{ + rfbProtocolVersionMsg pv; + rfbClientPtr cl; + BoxRec box; + struct sockaddr_in addr; + SOCKLEN_T addrlen = sizeof(struct sockaddr_in); + VNCSCREENPTR(pScreen); + int i; + + if (rfbClientHead == NULL) { + /* no other clients - make sure we don't think any keys are pressed */ + KbdReleaseAllKeys(); + } else { + rfbLog(" (other clients"); + for (cl = rfbClientHead; cl; cl = cl->next) { + rfbLog(" %s",cl->host); + } + rfbLog(")\n"); + } + + cl = (rfbClientPtr)xalloc(sizeof(rfbClientRec)); + +#ifdef CHROMIUM + cl->chromium_port = 0; /* no GL application on this port, yet */ +#endif + cl->userAccepted = 0; /* user hasn't even approached this yet .... */ + cl->sock = sock; + getpeername(sock, (struct sockaddr *)&addr, &addrlen); + cl->host = strdup(inet_ntoa(addr.sin_addr)); + cl->login = NULL; + + /* Dispatch client input to rfbProcessClientProtocolVersion(). */ + cl->state = RFB_PROTOCOL_VERSION; + + cl->viewOnly = FALSE; + cl->reverseConnection = FALSE; + cl->readyForSetColourMapEntries = FALSE; + cl->useCopyRect = FALSE; + cl->preferredEncoding = rfbEncodingRaw; + cl->correMaxWidth = 48; + cl->correMaxHeight = 48; + cl->pScreen = pScreen; + + REGION_NULL(pScreen,&cl->copyRegion); + cl->copyDX = 0; + cl->copyDY = 0; + + box.x1 = box.y1 = 0; + box.x2 = pVNC->width; + box.y2 = pVNC->height; + REGION_INIT(pScreen,&cl->modifiedRegion,&box,0); + + REGION_NULL(pScreen,&cl->requestedRegion); + + cl->deferredUpdateScheduled = FALSE; + cl->deferredUpdateTimer = NULL; + + cl->format = pVNC->rfbServerFormat; + cl->translateFn = rfbTranslateNone; + cl->translateLookupTable = NULL; + + cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION; + cl->tightQualityLevel = -1; + for (i = 0; i < 4; i++) + cl->zsActive[i] = FALSE; + + cl->enableCursorShapeUpdates = FALSE; + cl->enableCursorPosUpdates = FALSE; + cl->enableLastRectEncoding = FALSE; +#ifdef CHROMIUM + cl->enableChromiumEncoding = FALSE; +#endif + + cl->next = rfbClientHead; + rfbClientHead = cl; + + rfbResetStats(cl); + + cl->compStreamInited = FALSE; + cl->compStream.total_in = 0; + cl->compStream.total_out = 0; + cl->compStream.zalloc = Z_NULL; + cl->compStream.zfree = Z_NULL; + cl->compStream.opaque = Z_NULL; + + cl->zlibCompressLevel = 5; + + sprintf(pv,rfbProtocolVersionFormat,rfbProtocolMajorVersion, + rfbProtocolMinorVersion); + + if (WriteExact(sock, pv, sz_rfbProtocolVersionMsg) < 0) { + rfbLogPerror("rfbNewClient: write"); + rfbCloseSock(pScreen, sock); + return NULL; + } + + return cl; +} + + +/* + * rfbClientConnectionGone is called from sockets.c just after a connection + * has gone away. + */ + +void +rfbClientConnectionGone(sock) + int sock; +{ + rfbClientPtr cl, prev; + int i; +#if XFREE86VNC + int allowvt = TRUE; +#endif + + for (prev = NULL, cl = rfbClientHead; cl; prev = cl, cl = cl->next) { + if (sock == cl->sock) + break; + } + + if (!cl) { + rfbLog("rfbClientConnectionGone: unknown socket %d\n",sock); + return; + } + + rfbLog("rfbClientConnectionGone\n"); + + if (cl->login != NULL) { + rfbLog("VNC Client %s (%s) gone\n", cl->login, cl->host); + free(cl->login); + } else { + rfbLog("VNC Client %s gone\n", cl->host); + } + free(cl->host); + + /* Release the compression state structures if any. */ + if ( cl->compStreamInited == TRUE ) { + deflateEnd( &(cl->compStream) ); + } + + for (i = 0; i < 4; i++) { + if (cl->zsActive[i]) + deflateEnd(&cl->zsStruct[i]); + } + + if (pointerClient == cl) + pointerClient = NULL; + +#ifdef CORBA + destroyConnection(cl); +#endif + + if (prev) + prev->next = cl->next; + else + rfbClientHead = cl->next; + + REGION_UNINIT(cl->pScreen,&cl->copyRegion); + REGION_UNINIT(cl->pScreen,&cl->modifiedRegion); + TimerFree(cl->deferredUpdateTimer); + + rfbPrintStats(cl); + + if (cl->translateLookupTable) free(cl->translateLookupTable); + + xfree(cl); + + GenerateVncDisconnectedEvent(sock); + +#if XFREE86VNC + for (cl = rfbClientHead; cl; cl = cl->next) { + /* still someone connected */ + allowvt = FALSE; + } + + xf86EnableVTSwitch(allowvt); +#endif +} + + +/* + * rfbProcessClientMessage is called when there is data to read from a client. + */ + +void +rfbProcessClientMessage(ScreenPtr pScreen, int sock) +{ + rfbClientPtr cl; + + for (cl = rfbClientHead; cl; cl = cl->next) { + if (sock == cl->sock) + break; + } + + if (!cl) { + rfbLog("rfbProcessClientMessage: unknown socket %d\n",sock); + rfbCloseSock(pScreen, sock); + return; + } + +#ifdef CORBA + if (isClosePending(cl)) { + rfbLog("Closing connection to client %s\n", cl->host); + rfbCloseSock(pScreen, sock); + return; + } +#endif + + switch (cl->state) { + case RFB_PROTOCOL_VERSION: + rfbProcessClientProtocolVersion(cl); + break; + case RFB_SECURITY_TYPE: /* protocol 3.7 */ + rfbProcessClientSecurityType(cl); + break; + case RFB_TUNNELING_TYPE: /* protocol 3.7t */ + rfbProcessClientTunnelingType(cl); + break; + case RFB_AUTH_TYPE: /* protocol 3.7t */ + rfbProcessClientAuthType(cl); + break; + case RFB_AUTHENTICATION: + rfbVncAuthProcessResponse(cl); + break; + case RFB_INITIALISATION: + rfbProcessClientInitMessage(cl); + break; + default: + rfbProcessClientNormalMessage(cl); + } +} + + +/* + * rfbProcessClientProtocolVersion is called when the client sends its + * protocol version. + */ + +static void +rfbProcessClientProtocolVersion(cl) + rfbClientPtr cl; +{ + rfbProtocolVersionMsg pv; + int n, major, minor; + + if ((n = ReadExact(cl->sock, pv, sz_rfbProtocolVersionMsg)) <= 0) { + if (n == 0) + rfbLog("rfbProcessClientProtocolVersion: client gone\n"); + else + rfbLogPerror("rfbProcessClientProtocolVersion: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + pv[sz_rfbProtocolVersionMsg] = 0; + if (sscanf(pv,rfbProtocolVersionFormat,&major,&minor) != 2) { + rfbLog("rfbProcessClientProtocolVersion: not a valid RFB client\n"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + rfbLog("Using protocol version %d.%d\n", major, minor); + + if (major != rfbProtocolMajorVersion) { + rfbLog("RFB protocol version mismatch - server %d.%d, client %d.%d\n", + rfbProtocolMajorVersion,rfbProtocolMinorVersion,major,minor); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + /* Always use one of the two standard versions of the RFB protocol. */ + cl->protocol_minor_ver = minor; + if (minor > rfbProtocolMinorVersion) { + cl->protocol_minor_ver = rfbProtocolMinorVersion; + } else if (minor < rfbProtocolMinorVersion) { + cl->protocol_minor_ver = rfbProtocolFallbackMinorVersion; + } + if (minor != rfbProtocolMinorVersion && + minor != rfbProtocolFallbackMinorVersion) { + rfbLog("Non-standard protocol version %d.%d, using %d.%d instead\n", + major, minor, rfbProtocolMajorVersion, cl->protocol_minor_ver); + } + + /* TightVNC protocol extensions are not enabled yet. */ + cl->protocol_tightvnc = FALSE; + + rfbAuthNewClient(cl); +} + +/* + * rfbClientConnFailed is called when a client connection has failed + * before the authentication stage. + */ + +void +rfbClientConnFailed(cl, reason) + rfbClientPtr cl; + char *reason; +{ + int headerLen, reasonLen; + char buf[8]; + + headerLen = (cl->protocol_minor_ver >= 7) ? 1 : 4; + reasonLen = strlen(reason); + ((CARD32 *)buf)[0] = 0; + ((CARD32 *)buf)[1] = Swap32IfLE(reasonLen); + + if ( WriteExact(cl->sock, buf, headerLen) < 0 || + WriteExact(cl->sock, buf + 4, 4) < 0 || + WriteExact(cl->sock, reason, reasonLen) < 0 ) { + rfbLogPerror("rfbClientConnFailed: write"); + } + + rfbCloseSock(cl->pScreen, cl->sock); +} + + +/* + * rfbProcessClientInitMessage is called when the client sends its + * initialisation message. + */ + +static void +rfbProcessClientInitMessage(cl) + rfbClientPtr cl; +{ + VNCSCREENPTR(cl->pScreen); + rfbClientInitMsg ci; + char buf[256]; + rfbServerInitMsg *si = (rfbServerInitMsg *)buf; + struct passwd *user; + int len, n; + rfbClientPtr otherCl, nextCl; + + if ((n = ReadExact(cl->sock, (char *)&ci,sz_rfbClientInitMsg)) <= 0) { + if (n == 0) + rfbLog("rfbProcessClientInitMessage: client gone\n"); + else + rfbLogPerror("rfbProcessClientInitMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + si->framebufferWidth = Swap16IfLE(pVNC->width); + si->framebufferHeight = Swap16IfLE(pVNC->height); + si->format = pVNC->rfbServerFormat; + si->format.redMax = Swap16IfLE(si->format.redMax); + si->format.greenMax = Swap16IfLE(si->format.greenMax); + si->format.blueMax = Swap16IfLE(si->format.blueMax); + + user = getpwuid(getuid()); + + if (strlen(desktopName) > 128) /* sanity check on desktop name len */ + desktopName[128] = 0; + + if (user) { + sprintf(buf + sz_rfbServerInitMsg, "%s's %s desktop (%s:%s)", + user->pw_name, desktopName, rfbThisHost, display); + } else { + sprintf(buf + sz_rfbServerInitMsg, "%s desktop (%s:%s)", + desktopName, rfbThisHost, display); + } + len = strlen(buf + sz_rfbServerInitMsg); + si->nameLength = Swap32IfLE(len); + + if (WriteExact(cl->sock, buf, sz_rfbServerInitMsg + len) < 0) { + rfbLogPerror("rfbProcessClientInitMessage: write"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + if (cl->protocol_tightvnc) + rfbSendInteractionCaps(cl); /* protocol 3.7t */ + + /* Dispatch client input to rfbProcessClientNormalMessage(). */ + cl->state = RFB_NORMAL; + + if (!cl->reverseConnection && + (pVNC->rfbNeverShared || (!pVNC->rfbAlwaysShared && !ci.shared))) { + + if (pVNC->rfbDontDisconnect) { + for (otherCl = rfbClientHead; otherCl; otherCl = otherCl->next) { + if ((otherCl != cl) && (otherCl->state == RFB_NORMAL)) { + rfbLog("-dontdisconnect: Not shared & existing client\n"); + rfbLog(" refusing new client %s\n", cl->host); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + } + } else { + for (otherCl = rfbClientHead; otherCl; otherCl = nextCl) { + nextCl = otherCl->next; + if ((otherCl != cl) && (otherCl->state == RFB_NORMAL)) { + rfbLog("Not shared - closing connection to client %s\n", + otherCl->host); + rfbCloseSock(otherCl->pScreen, otherCl->sock); + } + } + } + } +} + + +/* + * rfbSendInteractionCaps is called after sending the server + * initialisation message, only if the protocol version is 3.130. + * In this function, we send the lists of supported protocol messages + * and encodings. + */ + +/* Update these constants on changing capability lists below! */ +#define N_SMSG_CAPS 0 +#define N_CMSG_CAPS 0 +#define N_ENC_CAPS 12 + +void +rfbSendInteractionCaps(cl) + rfbClientPtr cl; +{ + rfbInteractionCapsMsg intr_caps; + rfbCapabilityInfo enc_list[N_ENC_CAPS]; + int i; + + /* Fill in the header structure sent prior to capability lists. */ + intr_caps.nServerMessageTypes = Swap16IfLE(N_SMSG_CAPS); + intr_caps.nClientMessageTypes = Swap16IfLE(N_CMSG_CAPS); + intr_caps.nEncodingTypes = Swap16IfLE(N_ENC_CAPS); + intr_caps.pad = 0; + + /* Supported server->client message types. */ + /* For future file transfer support: + i = 0; + SetCapInfo(&smsg_list[i++], rfbFileListData, rfbTightVncVendor); + SetCapInfo(&smsg_list[i++], rfbFileDownloadData, rfbTightVncVendor); + SetCapInfo(&smsg_list[i++], rfbFileUploadCancel, rfbTightVncVendor); + SetCapInfo(&smsg_list[i++], rfbFileDownloadFailed, rfbTightVncVendor); + if (i != N_SMSG_CAPS) { + rfbLog("rfbSendInteractionCaps: assertion failed, i != N_SMSG_CAPS\n"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + */ + + /* Supported client->server message types. */ + /* For future file transfer support: + i = 0; + SetCapInfo(&cmsg_list[i++], rfbFileListRequest, rfbTightVncVendor); + SetCapInfo(&cmsg_list[i++], rfbFileDownloadRequest, rfbTightVncVendor); + SetCapInfo(&cmsg_list[i++], rfbFileUploadRequest, rfbTightVncVendor); + SetCapInfo(&cmsg_list[i++], rfbFileUploadData, rfbTightVncVendor); + SetCapInfo(&cmsg_list[i++], rfbFileDownloadCancel, rfbTightVncVendor); + SetCapInfo(&cmsg_list[i++], rfbFileUploadFailed, rfbTightVncVendor); + if (i != N_CMSG_CAPS) { + rfbLog("rfbSendInteractionCaps: assertion failed, i != N_CMSG_CAPS\n"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + */ + + /* Encoding types. */ + i = 0; + SetCapInfo(&enc_list[i++], rfbEncodingCopyRect, rfbStandardVendor); + SetCapInfo(&enc_list[i++], rfbEncodingRRE, rfbStandardVendor); + SetCapInfo(&enc_list[i++], rfbEncodingCoRRE, rfbStandardVendor); + SetCapInfo(&enc_list[i++], rfbEncodingHextile, rfbStandardVendor); + SetCapInfo(&enc_list[i++], rfbEncodingZlib, rfbTridiaVncVendor); + SetCapInfo(&enc_list[i++], rfbEncodingTight, rfbTightVncVendor); + SetCapInfo(&enc_list[i++], rfbEncodingCompressLevel0, rfbTightVncVendor); + SetCapInfo(&enc_list[i++], rfbEncodingQualityLevel0, rfbTightVncVendor); + SetCapInfo(&enc_list[i++], rfbEncodingXCursor, rfbTightVncVendor); + SetCapInfo(&enc_list[i++], rfbEncodingRichCursor, rfbTightVncVendor); + SetCapInfo(&enc_list[i++], rfbEncodingPointerPos, rfbTightVncVendor); + SetCapInfo(&enc_list[i++], rfbEncodingLastRect, rfbTightVncVendor); + if (i != N_ENC_CAPS) { + rfbLog("rfbSendInteractionCaps: assertion failed, i != N_ENC_CAPS\n"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + /* Send header and capability lists */ + if (WriteExact(cl->sock, (char *)&intr_caps, + sz_rfbInteractionCapsMsg) < 0 || + WriteExact(cl->sock, (char *)&enc_list[0], + sz_rfbCapabilityInfo * N_ENC_CAPS) < 0) { + rfbLogPerror("rfbSendInteractionCaps: write"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + /* Dispatch client input to rfbProcessClientNormalMessage(). */ + cl->state = RFB_NORMAL; +} + + +/* + * rfbProcessClientNormalMessage is called when the client has sent a normal + * protocol message. + */ + +static void +rfbProcessClientNormalMessage(cl) + rfbClientPtr cl; +{ + VNCSCREENPTR(cl->pScreen); + int n; + rfbClientToServerMsg msg; + char *str; + + if (pVNC->rfbUserAccept) { + /* + * We've asked for another level of user authentication + * If the user has not validated this connected, don't + * process it. + */ + /* + * NOTE: we do it here, so the vncviewer knows it's + * connected, but waiting for the first screen update + */ + if (cl->userAccepted == VNC_USER_UNDEFINED) { + usleep(10); + return; + } + if (cl->userAccepted == VNC_USER_DISCONNECT) { + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + } + + if ((n = ReadExact(cl->sock, (char *)&msg, 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + switch (msg.type) { + + case rfbSetPixelFormat: + + if ((n = ReadExact(cl->sock, ((char *)&msg) + 1, + sz_rfbSetPixelFormatMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + cl->format.bitsPerPixel = msg.spf.format.bitsPerPixel; + cl->format.depth = msg.spf.format.depth; + cl->format.bigEndian = (msg.spf.format.bigEndian ? 1 : 0); + cl->format.trueColour = (msg.spf.format.trueColour ? 1 : 0); + cl->format.redMax = Swap16IfLE(msg.spf.format.redMax); + cl->format.greenMax = Swap16IfLE(msg.spf.format.greenMax); + cl->format.blueMax = Swap16IfLE(msg.spf.format.blueMax); + cl->format.redShift = msg.spf.format.redShift; + cl->format.greenShift = msg.spf.format.greenShift; + cl->format.blueShift = msg.spf.format.blueShift; + + cl->readyForSetColourMapEntries = TRUE; + + rfbSetTranslateFunction(cl); + return; + + + case rfbFixColourMapEntries: + if ((n = ReadExact(cl->sock, ((char *)&msg) + 1, + sz_rfbFixColourMapEntriesMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + rfbLog("rfbProcessClientNormalMessage: %s", + "FixColourMapEntries unsupported\n"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + + + case rfbSetEncodings: + { + int i; + CARD32 enc; + + if ((n = ReadExact(cl->sock, ((char *)&msg) + 1, + sz_rfbSetEncodingsMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + msg.se.nEncodings = Swap16IfLE(msg.se.nEncodings); + + cl->preferredEncoding = -1; + cl->useCopyRect = FALSE; + cl->enableCursorShapeUpdates = FALSE; + cl->enableCursorPosUpdates = FALSE; + cl->enableLastRectEncoding = FALSE; +#ifdef CHROMIUM + cl->enableChromiumEncoding = FALSE; +#endif + cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION; + cl->tightQualityLevel = -1; + + for (i = 0; i < msg.se.nEncodings; i++) { + if ((n = ReadExact(cl->sock, (char *)&enc, 4)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + enc = Swap32IfLE(enc); + + switch (enc) { + + case rfbEncodingCopyRect: + cl->useCopyRect = TRUE; + rfbLog("Using copyrect encoding for client %s\n", + cl->host); + break; + case rfbEncodingRaw: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using raw encoding for client %s\n", + cl->host); + } + break; + case rfbEncodingRRE: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using rre encoding for client %s\n", + cl->host); + } + break; + case rfbEncodingCoRRE: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using CoRRE encoding for client %s\n", + cl->host); + } + break; + case rfbEncodingHextile: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using hextile encoding for client %s\n", + cl->host); + } + break; + case rfbEncodingZlib: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using zlib encoding for client %s\n", + cl->host); + } + break; + case rfbEncodingTight: + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = enc; + rfbLog("Using tight encoding for client %s\n", + cl->host); + } + break; + case rfbEncodingXCursor: + rfbLog("Enabling X-style cursor updates for client %s\n", + cl->host); + cl->enableCursorShapeUpdates = TRUE; + cl->useRichCursorEncoding = FALSE; + cl->cursorWasChanged = TRUE; + break; + case rfbEncodingRichCursor: + if (!cl->enableCursorShapeUpdates) { + rfbLog("Enabling full-color cursor updates for client " + "%s\n", cl->host); + cl->enableCursorShapeUpdates = TRUE; + cl->useRichCursorEncoding = TRUE; + cl->cursorWasChanged = TRUE; + } + break; + case rfbEncodingPointerPos: + if (!cl->enableCursorPosUpdates) { + rfbLog("Enabling cursor position updates for client %s\n", + cl->host); + cl->enableCursorPosUpdates = TRUE; + cl->cursorWasMoved = TRUE; + cl->cursorX = -1; + cl->cursorY = -1; + } + break; + case rfbEncodingLastRect: + if (!cl->enableLastRectEncoding) { + rfbLog("Enabling LastRect protocol extension for client " + "%s\n", cl->host); + cl->enableLastRectEncoding = TRUE; + } + break; +#ifdef CHROMIUM + case rfbEncodingChromium: + case rfbEncodingChromium2: + if (!cl->enableChromiumEncoding) { + cl->enableChromiumEncoding = TRUE; + /* This tells OpenGL apps/replicate SPUs that new viewer + * has attached. + */ + GenerateVncChromiumConnectedEvent(cl->sock); + if (enc == rfbEncodingChromium) { + /* Generate exposures for all windows */ + WindowPtr pWin = WindowTable[cl->pScreen->myNum]; + rfbSetClip(pWin, 1); + } + else { + /* don't generate exposures for Chromium2 because + * that confused DMX. + */ + } + } + break; +#endif + default: + if ( enc >= (CARD32)rfbEncodingCompressLevel0 && + enc <= (CARD32)rfbEncodingCompressLevel9 ) { + cl->zlibCompressLevel = enc & 0x0F; + cl->tightCompressLevel = enc & 0x0F; + rfbLog("Using compression level %d for client %s\n", + cl->tightCompressLevel, cl->host); + } else if ( enc >= (CARD32)rfbEncodingQualityLevel0 && + enc <= (CARD32)rfbEncodingQualityLevel9 ) { + cl->tightQualityLevel = enc & 0x0F; + rfbLog("Using image quality level %d for client %s\n", + cl->tightQualityLevel, cl->host); + } else { + rfbLog("rfbProcessClientNormalMessage: ignoring unknown " + "encoding %d\n", (int)enc); + } + } + } + + if (cl->preferredEncoding == -1) { + cl->preferredEncoding = rfbEncodingRaw; + rfbLog("No encoding specified - using raw encoding for client %s\n", + cl->host); + } + + if (cl->enableCursorPosUpdates && !cl->enableCursorShapeUpdates) { + rfbLog("Disabling cursor position updates for client %s\n", + cl->host); + cl->enableCursorPosUpdates = FALSE; + } + +#if XFREE86VNC + /* + * With XFree86 and the hardware cursor's we need to put up the + * cursor again, and if we've detected a cursor shapeless client + * we need to disable hardware cursors. + */ + if (!cl->enableCursorShapeUpdates) + pVNC->SWCursor = (Bool *)TRUE; + else + pVNC->SWCursor = (Bool *)FALSE; + + { + int x, y; + miPointerPosition(&x, &y); /*XXX deprecated*/ + (*pVNC->spriteFuncs->SetCursor)(cl->pScreen, pVNC->pCurs, x, y); + } +#endif + + return; + } + + + case rfbFramebufferUpdateRequest: + { + RegionRec tmpRegion; + BoxRec box; + +#ifdef CORBA + addCapability(cl, DISPLAY_DEVICE); +#endif + + if ((n = ReadExact(cl->sock, ((char *)&msg) + 1, + sz_rfbFramebufferUpdateRequestMsg-1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + box.x1 = Swap16IfLE(msg.fur.x); + box.y1 = Swap16IfLE(msg.fur.y); + box.x2 = box.x1 + Swap16IfLE(msg.fur.w); + box.y2 = box.y1 + Swap16IfLE(msg.fur.h); + SAFE_REGION_INIT(cl->pScreen,&tmpRegion,&box,0); + + REGION_UNION(cl->pScreen, &cl->requestedRegion, &cl->requestedRegion, + &tmpRegion); + + if (!cl->readyForSetColourMapEntries) { + /* client hasn't sent a SetPixelFormat so is using server's */ + cl->readyForSetColourMapEntries = TRUE; + if (!cl->format.trueColour) { + if (!rfbSetClientColourMap(cl, 0, 0)) { + REGION_UNINIT(cl->pScreen,&tmpRegion); + return; + } + } + } + + if (!msg.fur.incremental) { + REGION_UNION(cl->pScreen,&cl->modifiedRegion,&cl->modifiedRegion, + &tmpRegion); + REGION_SUBTRACT(cl->pScreen,&cl->copyRegion,&cl->copyRegion, + &tmpRegion); + } + +#ifndef DMXVNC + /* don't try to send any pixel data - we'll crash */ + if (FB_UPDATE_PENDING(cl)) { + rfbSendFramebufferUpdate(cl->pScreen, cl); + } +#endif + + REGION_UNINIT(cl->pScreen,&tmpRegion); + return; + } + + case rfbKeyEvent: + + cl->rfbKeyEventsRcvd++; + + if ((n = ReadExact(cl->sock, ((char *)&msg) + 1, + sz_rfbKeyEventMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + +#ifdef CORBA + addCapability(cl, KEYBOARD_DEVICE); + + if (!isKeyboardEnabled(cl)) + return; +#endif + /*ErrorF("Key event: %d\n", msg.ke.key);*/ + if (!pVNC->rfbViewOnly && !cl->viewOnly) { + KbdAddEvent(msg.ke.down, (KeySym)Swap32IfLE(msg.ke.key), cl); + } + return; + + + case rfbPointerEvent: + + cl->rfbPointerEventsRcvd++; + + if ((n = ReadExact(cl->sock, ((char *)&msg) + 1, + sz_rfbPointerEventMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + +#ifdef CORBA + addCapability(cl, POINTER_DEVICE); + + if (!isPointerEnabled(cl)) + return; +#endif + + if (pointerClient && (pointerClient != cl)) + return; + + if (msg.pe.buttonMask == 0) + pointerClient = NULL; + else + pointerClient = cl; + + if (!pVNC->rfbViewOnly && !cl->viewOnly) { + cl->cursorX = (int)Swap16IfLE(msg.pe.x); + cl->cursorY = (int)Swap16IfLE(msg.pe.y); + PtrAddEvent(msg.pe.buttonMask, cl->cursorX, cl->cursorY, cl); + } + return; + + + case rfbClientCutText: + + if ((n = ReadExact(cl->sock, ((char *)&msg) + 1, + sz_rfbClientCutTextMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + msg.cct.length = Swap32IfLE(msg.cct.length); + + str = (char *)xalloc(msg.cct.length); + + if ((n = ReadExact(cl->sock, str, msg.cct.length)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + xfree(str); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + /* NOTE: We do not accept cut text from a view-only client */ + if (!cl->viewOnly) + rfbSetXCutText(str, msg.cct.length); + + xfree(str); + return; + +#ifdef CHROMIUM + case rfbChromiumStop: + if ((n = ReadExact(cl->sock, ((char *)&msg) + 1, + sz_rfbChromiumStopMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + /* would we use msg.csd.port ??? */ + + cl->chromium_port = 0; + + /* tear down window information */ + { + CRWindowTable *wt, *nextWt = NULL; + + for (wt = windowTable; wt; wt = nextWt) { + nextWt = wt->next; + xfree(wt); + } + + windowTable = NULL; + } + + return; + + case rfbChromiumExpose: + if ((n = ReadExact(cl->sock, ((char *)&msg) + 1, + sz_rfbChromiumExposeMsg - 1)) <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: read"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } + + /* find the window and re-expose it */ + { + CRWindowTable *wt, *nextWt = NULL; + + for (wt = windowTable; wt; wt = nextWt) { + nextWt = wt->next; + if (wt->CRwinId == msg.cse.winid) { + WindowPtr pWin; + pWin = LookupIDByType(wt->XwinId, RT_WINDOW); + if (pWin) { + miSendExposures(pWin, &pWin->clipList, + pWin->drawable.x, + pWin->drawable.y); + FlushAllOutput(); + } + } + } + } + + return; +#endif + + default: + + rfbLog("rfbProcessClientNormalMessage: unknown message type %d\n", + msg.type); + rfbLog(" ... closing connection\n"); + rfbCloseSock(cl->pScreen, cl->sock); + return; + } +} + + + +/* + * rfbSendFramebufferUpdate - send the currently pending framebuffer update to + * the RFB client. + */ + +Bool +rfbSendFramebufferUpdate(pScreen, cl) + ScreenPtr pScreen; + rfbClientPtr cl; +{ + VNCSCREENPTR(pScreen); + int i; + int nUpdateRegionRects; + rfbFramebufferUpdateMsg *fu = (rfbFramebufferUpdateMsg *)pVNC->updateBuf; + RegionRec updateRegion, updateCopyRegion; + int dx, dy; + Bool sendCursorShape = FALSE; + Bool sendCursorPos = FALSE; + + /* + * If this client understands cursor shape updates, cursor should be + * removed from the framebuffer. Otherwise, make sure it's put up. + */ + +#if !XFREE86VNC + if (cl->enableCursorShapeUpdates) { + if (pVNC->cursorIsDrawn) + rfbSpriteRemoveCursor(pScreen); + if (!pVNC->cursorIsDrawn && cl->cursorWasChanged) + sendCursorShape = TRUE; + } else { + if (!pVNC->cursorIsDrawn) + rfbSpriteRestoreCursor(pScreen); + } +#else + if (cl->enableCursorShapeUpdates) + if (cl->cursorWasChanged) + sendCursorShape = TRUE; +#endif + + /* + * Do we plan to send cursor position update? + */ + + if (cl->enableCursorPosUpdates && cl->cursorWasMoved) + sendCursorPos = TRUE; + + /* + * The modifiedRegion may overlap the destination copyRegion. We remove + * any overlapping bits from the copyRegion (since they'd only be + * overwritten anyway). + */ + + REGION_SUBTRACT(pScreen, &cl->copyRegion, &cl->copyRegion, + &cl->modifiedRegion); + + /* + * The client is interested in the region requestedRegion. The region + * which should be updated now is the intersection of requestedRegion + * and the union of modifiedRegion and copyRegion. If it's empty then + * no update is needed. + */ + + REGION_NULL(pScreen,&updateRegion); + REGION_UNION(pScreen, &updateRegion, &cl->copyRegion, + &cl->modifiedRegion); + REGION_INTERSECT(pScreen, &updateRegion, &cl->requestedRegion, + &updateRegion); + + if ( !REGION_NOTEMPTY(pScreen,&updateRegion) && + !sendCursorShape && !sendCursorPos ) { + REGION_UNINIT(pScreen,&updateRegion); + return TRUE; + } + + /* + * We assume that the client doesn't have any pixel data outside the + * requestedRegion. In other words, both the source and destination of a + * copy must lie within requestedRegion. So the region we can send as a + * copy is the intersection of the copyRegion with both the requestedRegion + * and the requestedRegion translated by the amount of the copy. We set + * updateCopyRegion to this. + */ + + REGION_NULL(pScreen,&updateCopyRegion); + REGION_INTERSECT(pScreen, &updateCopyRegion, &cl->copyRegion, + &cl->requestedRegion); + REGION_TRANSLATE(pScreen, &cl->requestedRegion, cl->copyDX, cl->copyDY); + REGION_INTERSECT(pScreen, &updateCopyRegion, &updateCopyRegion, + &cl->requestedRegion); + dx = cl->copyDX; + dy = cl->copyDY; + + /* + * Next we remove updateCopyRegion from updateRegion so that updateRegion + * is the part of this update which is sent as ordinary pixel data (i.e not + * a copy). + */ + + REGION_SUBTRACT(pScreen, &updateRegion, &updateRegion, &updateCopyRegion); + + /* + * Finally we leave modifiedRegion to be the remainder (if any) of parts of + * the screen which are modified but outside the requestedRegion. We also + * empty both the requestedRegion and the copyRegion - note that we never + * carry over a copyRegion for a future update. + */ + + REGION_UNION(pScreen, &cl->modifiedRegion, &cl->modifiedRegion, + &cl->copyRegion); + REGION_SUBTRACT(pScreen, &cl->modifiedRegion, &cl->modifiedRegion, + &updateRegion); + REGION_SUBTRACT(pScreen, &cl->modifiedRegion, &cl->modifiedRegion, + &updateCopyRegion); + + REGION_EMPTY(pScreen, &cl->requestedRegion); + REGION_EMPTY(pScreen, &cl->copyRegion); + cl->copyDX = 0; + cl->copyDY = 0; + + /* + * Now send the update. + */ + + cl->rfbFramebufferUpdateMessagesSent++; + + if (cl->preferredEncoding == rfbEncodingCoRRE) { + nUpdateRegionRects = 0; + + for (i = 0; i < REGION_NUM_RECTS(&updateRegion); i++) { + int x = REGION_RECTS(&updateRegion)[i].x1; + int y = REGION_RECTS(&updateRegion)[i].y1; + int w = REGION_RECTS(&updateRegion)[i].x2 - x; + int h = REGION_RECTS(&updateRegion)[i].y2 - y; + nUpdateRegionRects += (((w-1) / cl->correMaxWidth + 1) + * ((h-1) / cl->correMaxHeight + 1)); + } + } else if (cl->preferredEncoding == rfbEncodingZlib) { + nUpdateRegionRects = 0; + + for (i = 0; i < REGION_NUM_RECTS(&updateRegion); i++) { + int x = REGION_RECTS(&updateRegion)[i].x1; + int y = REGION_RECTS(&updateRegion)[i].y1; + int w = REGION_RECTS(&updateRegion)[i].x2 - x; + int h = REGION_RECTS(&updateRegion)[i].y2 - y; + nUpdateRegionRects += (((h-1) / (ZLIB_MAX_SIZE( w ) / w)) + 1); + } + } else if (cl->preferredEncoding == rfbEncodingTight) { + nUpdateRegionRects = 0; + + for (i = 0; i < REGION_NUM_RECTS(&updateRegion); i++) { + int x = REGION_RECTS(&updateRegion)[i].x1; + int y = REGION_RECTS(&updateRegion)[i].y1; + int w = REGION_RECTS(&updateRegion)[i].x2 - x; + int h = REGION_RECTS(&updateRegion)[i].y2 - y; + int n = rfbNumCodedRectsTight(cl, x, y, w, h); + if (n == 0) { + nUpdateRegionRects = 0xFFFF; + break; + } + nUpdateRegionRects += n; + } + } else { + nUpdateRegionRects = REGION_NUM_RECTS(&updateRegion); + } + + fu->type = rfbFramebufferUpdate; + if (nUpdateRegionRects != 0xFFFF) { + fu->nRects = Swap16IfLE(REGION_NUM_RECTS(&updateCopyRegion) + + nUpdateRegionRects + + !!sendCursorShape + !!sendCursorPos); + } else { + fu->nRects = 0xFFFF; + } + pVNC->ublen = sz_rfbFramebufferUpdateMsg; + + if (sendCursorShape) { + cl->cursorWasChanged = FALSE; + if (!rfbSendCursorShape(cl, pScreen)) + return FALSE; + } + + if (sendCursorPos) { + cl->cursorWasMoved = FALSE; + if (!rfbSendCursorPos(cl, pScreen)) + return FALSE; + } + + if (REGION_NOTEMPTY(pScreen,&updateCopyRegion)) { + if (!rfbSendCopyRegion(cl,&updateCopyRegion,dx,dy)) { + REGION_UNINIT(pScreen,&updateRegion); + REGION_UNINIT(pScreen,&updateCopyRegion); + return FALSE; + } + } + + REGION_UNINIT(pScreen,&updateCopyRegion); + + for (i = 0; i < REGION_NUM_RECTS(&updateRegion); i++) { + int x = REGION_RECTS(&updateRegion)[i].x1; + int y = REGION_RECTS(&updateRegion)[i].y1; + int w = REGION_RECTS(&updateRegion)[i].x2 - x; + int h = REGION_RECTS(&updateRegion)[i].y2 - y; + + cl->rfbRawBytesEquivalent += (sz_rfbFramebufferUpdateRectHeader + + w * (cl->format.bitsPerPixel / 8) * h); + + switch (cl->preferredEncoding) { + case rfbEncodingRaw: + if (!rfbSendRectEncodingRaw(cl, x, y, w, h)) { + REGION_UNINIT(pScreen,&updateRegion); + return FALSE; + } + break; + case rfbEncodingRRE: + if (!rfbSendRectEncodingRRE(cl, x, y, w, h)) { + REGION_UNINIT(pScreen,&updateRegion); + return FALSE; + } + break; + case rfbEncodingCoRRE: + if (!rfbSendRectEncodingCoRRE(cl, x, y, w, h)) { + REGION_UNINIT(pScreen,&updateRegion); + return FALSE; + } + break; + case rfbEncodingHextile: + if (!rfbSendRectEncodingHextile(cl, x, y, w, h)) { + REGION_UNINIT(pScreen,&updateRegion); + return FALSE; + } + break; + case rfbEncodingZlib: + if (!rfbSendRectEncodingZlib(cl, x, y, w, h)) { + REGION_UNINIT(pScreen,&updateRegion); + return FALSE; + } + break; + case rfbEncodingTight: + if (!rfbSendRectEncodingTight(cl, x, y, w, h)) { + REGION_UNINIT(pScreen,&updateRegion); + return FALSE; + } + break; + } + } + + REGION_UNINIT(pScreen,&updateRegion); + + if (nUpdateRegionRects == 0xFFFF && !rfbSendLastRectMarker(cl)) + return FALSE; + + if (!rfbSendUpdateBuf(cl)) + return FALSE; + + return TRUE; +} + + + +/* + * Send the copy region as a string of CopyRect encoded rectangles. + * The only slightly tricky thing is that we should send the messages in + * the correct order so that an earlier CopyRect will not corrupt the source + * of a later one. + */ + +static Bool +rfbSendCopyRegion(cl, reg, dx, dy) + rfbClientPtr cl; + RegionPtr reg; + int dx, dy; +{ + VNCSCREENPTR(cl->pScreen); + int nrects, nrectsInBand, x_inc, y_inc, thisRect, firstInNextBand; + int x, y, w, h; + rfbFramebufferUpdateRectHeader rect; + rfbCopyRect cr; + + nrects = REGION_NUM_RECTS(reg); + + if (dx <= 0) { + x_inc = 1; + } else { + x_inc = -1; + } + + if (dy <= 0) { + thisRect = 0; + y_inc = 1; + } else { + thisRect = nrects - 1; + y_inc = -1; + } + + while (nrects > 0) { + + firstInNextBand = thisRect; + nrectsInBand = 0; + + while ((nrects > 0) && + (REGION_RECTS(reg)[firstInNextBand].y1 + == REGION_RECTS(reg)[thisRect].y1)) + { + firstInNextBand += y_inc; + nrects--; + nrectsInBand++; + } + + if (x_inc != y_inc) { + thisRect = firstInNextBand - y_inc; + } + + while (nrectsInBand > 0) { + if ((pVNC->ublen + sz_rfbFramebufferUpdateRectHeader + + sz_rfbCopyRect) > UPDATE_BUF_SIZE) + { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + x = REGION_RECTS(reg)[thisRect].x1; + y = REGION_RECTS(reg)[thisRect].y1; + w = REGION_RECTS(reg)[thisRect].x2 - x; + h = REGION_RECTS(reg)[thisRect].y2 - y; + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingCopyRect); + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + pVNC->ublen += sz_rfbFramebufferUpdateRectHeader; + + cr.srcX = Swap16IfLE(x - dx); + cr.srcY = Swap16IfLE(y - dy); + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&cr, sz_rfbCopyRect); + pVNC->ublen += sz_rfbCopyRect; + + cl->rfbRectanglesSent[rfbEncodingCopyRect]++; + cl->rfbBytesSent[rfbEncodingCopyRect] + += sz_rfbFramebufferUpdateRectHeader + sz_rfbCopyRect; + + thisRect += x_inc; + nrectsInBand--; + } + + thisRect = firstInNextBand; + } + + return TRUE; +} + + +/* + * Send a given rectangle in raw encoding (rfbEncodingRaw). + */ + +Bool +rfbSendRectEncodingRaw(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + VNCSCREENPTR(cl->pScreen); + rfbFramebufferUpdateRectHeader rect; + int nlines; + int bytesPerLine = w * (cl->format.bitsPerPixel / 8); + int newy = y; + + /* Flush the buffer to guarantee correct alignment for translateFn(). */ + if (pVNC->ublen > 0) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingRaw); + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); + pVNC->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbRectanglesSent[rfbEncodingRaw]++; + cl->rfbBytesSent[rfbEncodingRaw] + += sz_rfbFramebufferUpdateRectHeader + bytesPerLine * h; + + nlines = (UPDATE_BUF_SIZE - pVNC->ublen) / bytesPerLine; + + while (TRUE) { + if (nlines > h) + nlines = h; + + (*cl->translateFn)(cl->pScreen, cl->translateLookupTable, &pVNC->rfbServerFormat, + &cl->format, &pVNC->updateBuf[pVNC->ublen], + pVNC->paddedWidthInBytes, w, nlines, x, newy); + + pVNC->ublen += nlines * bytesPerLine; + h -= nlines; + newy += nlines; + + if (h == 0) /* rect fitted in buffer, do next one */ + return TRUE; + + /* buffer full - flush partial rect and do another nlines */ + + if (!rfbSendUpdateBuf(cl)) + return FALSE; + + nlines = (UPDATE_BUF_SIZE - pVNC->ublen) / bytesPerLine; + if (nlines == 0) { + rfbLog("rfbSendRectEncodingRaw: send buffer too small for %d " + "bytes per line\n", bytesPerLine); + rfbCloseSock(cl->pScreen, cl->sock); + return FALSE; + } + } +} + + +/* + * Send an empty rectangle with encoding field set to value of + * rfbEncodingLastRect to notify client that this is the last + * rectangle in framebuffer update ("LastRect" extension of RFB + * protocol). + */ + +static Bool +rfbSendLastRectMarker(cl) + rfbClientPtr cl; +{ + VNCSCREENPTR(cl->pScreen); + rfbFramebufferUpdateRectHeader rect; + + if (pVNC->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.encoding = Swap32IfLE(rfbEncodingLastRect); + rect.r.x = 0; + rect.r.y = 0; + rect.r.w = 0; + rect.r.h = 0; + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&rect,sz_rfbFramebufferUpdateRectHeader); + pVNC->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbLastRectMarkersSent++; + cl->rfbLastRectBytesSent += sz_rfbFramebufferUpdateRectHeader; + + return TRUE; +} + + +/* + * Send the contents of pVNC->updateBuf. Returns 1 if successful, -1 if + * not (errno should be set). + */ + +Bool +rfbSendUpdateBuf(cl) + rfbClientPtr cl; +{ + VNCSCREENPTR(cl->pScreen); + + /* + int i; + for (i = 0; i < pVNC->ublen; i++) { + rfbLog("%02x ",((unsigned char *)pVNC->updateBuf)[i]); + } + rfbLog("\n"); + */ + + if (pVNC->ublen > 0 && WriteExact(cl->sock, (char*)pVNC->updateBuf, pVNC->ublen) < 0) { + rfbLogPerror("rfbSendUpdateBuf: write"); + rfbCloseSock(cl->pScreen, cl->sock); + return FALSE; + } + + pVNC->ublen = 0; + return TRUE; +} + + + +/* + * rfbSendSetColourMapEntries sends a SetColourMapEntries message to the + * client, using values from the currently installed colormap. + */ + +Bool +rfbSendSetColourMapEntries(cl, firstColour, nColours) + rfbClientPtr cl; + int firstColour; + int nColours; +{ +#if !XFREE86VNC + VNCSCREENPTR(cl->pScreen); +#endif + char buf[sz_rfbSetColourMapEntriesMsg + 256 * 3 * 2]; + rfbSetColourMapEntriesMsg *scme = (rfbSetColourMapEntriesMsg *)buf; + CARD16 *rgb = (CARD16 *)(&buf[sz_rfbSetColourMapEntriesMsg]); + EntryPtr pent; + EntryPtr redEntry, greenEntry, blueEntry; + unsigned short redPart, greenPart, bluePart; + int i, len; + + scme->type = rfbSetColourMapEntries; + scme->nColours = Swap16IfLE(nColours); + + len = sz_rfbSetColourMapEntriesMsg; + + /* PseudoColor */ +#if XFREE86VNC + if (miInstalledMaps[cl->pScreen->myNum]->class == PseudoColor) { +#else + if (pVNC->rfbInstalledColormap->class == PseudoColor) { +#endif + scme->firstColour = Swap16IfLE(firstColour); +#if XFREE86VNC + pent = (EntryPtr)&miInstalledMaps[cl->pScreen->myNum]->red[firstColour]; +#else + pent = (EntryPtr)&pVNC->rfbInstalledColormap->red[firstColour]; +#endif + for (i = 0; i < nColours; i++) { + if (pent->fShared) { + rgb[i*3] = Swap16IfLE(pent->co.shco.red->color); + rgb[i*3+1] = Swap16IfLE(pent->co.shco.green->color); + rgb[i*3+2] = Swap16IfLE(pent->co.shco.blue->color); + } else { + rgb[i*3] = Swap16IfLE(pent->co.local.red); + rgb[i*3+1] = Swap16IfLE(pent->co.local.green); + rgb[i*3+2] = Swap16IfLE(pent->co.local.blue); + } + pent++; + } + } + + else { + + /* Break the DirectColor pixel into its r/g/b components */ +#if XFREE86VNC + redPart = (firstColour & miInstalledMaps[cl->pScreen->myNum]->pVisual->redMask) + >> miInstalledMaps[cl->pScreen->myNum]->pVisual->offsetRed; + greenPart = (firstColour & miInstalledMaps[cl->pScreen->myNum]->pVisual->greenMask) + >> miInstalledMaps[cl->pScreen->myNum]->pVisual->offsetGreen; + bluePart = (firstColour & miInstalledMaps[cl->pScreen->myNum]->pVisual->blueMask) + >> miInstalledMaps[cl->pScreen->myNum]->pVisual->offsetBlue; +#else + redPart = (firstColour & pVNC->rfbInstalledColormap->pVisual->redMask) + >> pVNC->rfbInstalledColormap->pVisual->offsetRed; + greenPart = (firstColour & pVNC->rfbInstalledColormap->pVisual->greenMask) + >> pVNC->rfbInstalledColormap->pVisual->offsetGreen; + bluePart = (firstColour & pVNC->rfbInstalledColormap->pVisual->blueMask) + >> pVNC->rfbInstalledColormap->pVisual->offsetBlue; +#endif + + /* + * The firstColour field is only 16 bits. To support 24-bit pixels we + * sneak the red component in the 8-bit padding field which we renamed + * to redIndex. Green and blue are in firstColour (MSB, LSB respectively). + */ + scme->redIndex = Swap16IfLE(redPart); + scme->firstColour = Swap16IfLE((greenPart << 8) | bluePart); + +#if XFREE86VNC + redEntry = (EntryPtr)&miInstalledMaps[cl->pScreen->myNum]->red[redPart]; + greenEntry = (EntryPtr)&miInstalledMaps[cl->pScreen->myNum]->green[greenPart]; + blueEntry = (EntryPtr)&miInstalledMaps[cl->pScreen->myNum]->blue[bluePart]; +#else + redEntry = (EntryPtr)&pVNC->rfbInstalledColormap->red[redPart]; + greenEntry = (EntryPtr)&pVNC->rfbInstalledColormap->green[greenPart]; + blueEntry = (EntryPtr)&pVNC->rfbInstalledColormap->blue[bluePart]; +#endif + for (i = 0; i < nColours; i++) { + if (redEntry->fShared) + rgb[i*3] = Swap16IfLE(redEntry->co.shco.red->color); + else + rgb[i*3] = Swap16IfLE(redEntry->co.local.red); + + if (greenEntry->fShared) + rgb[i*3+1] = Swap16IfLE(greenEntry->co.shco.green->color); + else + rgb[i*3+1] = Swap16IfLE(greenEntry->co.local.green); + + if (blueEntry->fShared) + rgb[i*3+2] = Swap16IfLE(blueEntry->co.shco.blue->color); + else + rgb[i*3+2] = Swap16IfLE(blueEntry->co.local.blue); + + redEntry++; + greenEntry++; + blueEntry++; + } + } + + len += nColours * 3 * 2; + + if (WriteExact(cl->sock, buf, len) < 0) { + rfbLogPerror("rfbSendSetColourMapEntries: write"); + rfbCloseSock(cl->pScreen, cl->sock); + return FALSE; + } + return TRUE; +} + + +/* + * rfbSendBell sends a Bell message to all the clients. + */ + +void +rfbSendBell(void) +{ + rfbClientPtr cl, nextCl; + rfbBellMsg b; + + for (cl = rfbClientHead; cl; cl = nextCl) { + nextCl = cl->next; + b.type = rfbBell; + if (WriteExact(cl->sock, (char *)&b, sz_rfbBellMsg) < 0) { + rfbLogPerror("rfbSendBell: write"); + rfbCloseSock(cl->pScreen, cl->sock); + } + } +} + +#ifdef CHROMIUM +#ifdef sun +extern int inet_aton(const char *cp, struct in_addr *inp); +#endif + + +/** + * This sends a ChromiumStart message to all VNC viewers running on the + * host named by ipaddress. + * This is done in response to an OpenGL/Chromium app calling + * XVncChromiumStart(). + */ +void +rfbSendChromiumStart(unsigned int ipaddress, unsigned int crServerPort, + unsigned int mothershipPort) +{ + rfbClientPtr cl, nextCl; + rfbChromiumStartMsg scd; + struct in_addr ip; + unsigned int vncipaddress; + + /*rfbLog("Enter %s\n", __func__);*/ + /* loop over vnc viewers/clients */ + for (cl = rfbClientHead; cl; cl = nextCl) { + nextCl = cl->next; + /*rfbLog("%s: cl=%p enableCr=%d\n", __func__, + (void*) cl, cl->enableChromiumEncoding); + */ + if (!cl->enableChromiumEncoding) { + /* viewer is not chromium-enhanced */ + continue; + } + inet_aton(cl->host, &ip); + memcpy(&vncipaddress, &ip, sizeof(unsigned int)); + rfbLog("%s: ipaddr=0x%x vncipaddr=0x%x cl->port=%d\n", + __func__, ipaddress, vncipaddress, cl->chromium_port); + if (ipaddress == vncipaddress /**&& !cl->chromium_port**/) { + cl->chromium_port = crServerPort; + cl->chromium_msport = mothershipPort; + scd.type = rfbChromiumStart; + scd.crServerPort = crServerPort; + scd.mothershipPort = mothershipPort; + if (WriteExact(cl->sock, (char *)&scd, + sz_rfbChromiumStartMsg) < 0) { + rfbLogPerror("rfbSendChromiumStart: write"); + rfbCloseSock(cl->pScreen, cl->sock); + } + /* We only start one client at a time, so break now! */ + break; + } + } + /*rfbLog("Leave %s\n", __func__);*/ +} + + +/** + * Begin monitoring the given X windowid. + * When we detect size/position/visibility changes we'll send a + * rfbChromiumMoveResizeWindow, rfbChromiumClipList, or rfbChromiumWindowShow + * message to the VNC viewer, passing the corresponding Chromium window id. + */ +void +rfbChromiumMonitorWindowID(unsigned int cr_windowid, unsigned long xwindowid) +{ + CRWindowTable *newRec, *wt, *nextWt = NULL; + + if (xwindowid && !cr_windowid) { + /* stop monitoring the X window, remove from list */ + CRWindowTable *prev = NULL; + for (wt = windowTable; wt; wt = nextWt) { + nextWt = wt->next; + if (wt->XwinId == xwindowid) { + /* remove */ + if (prev) + prev->next = wt->next; + else + windowTable = wt->next; + xfree(wt); + } + else { + prev = wt; + } + } + return; + } + + /* See if we're already monitoring this window */ + for (wt = windowTable; wt; wt = nextWt) { + nextWt = wt->next; + /* and if so, update it's window ID */ + if (wt->CRwinId == cr_windowid) { + wt->XwinId = xwindowid; + return; + } + } + + /* o.k, new window so create new slot information */ + newRec = (CRWindowTable *)xalloc(sizeof(CRWindowTable)); + if (!newRec) { + rfbLog("Out of memory allocating CRWindowTable.\n"); + return; + } + + newRec->next = NULL; + newRec->CRwinId = cr_windowid; + newRec->XwinId = xwindowid; + newRec->clipRects = NULL; + newRec->numRects = 0; + + if (!windowTable) { + windowTable = newRec; + } + else { + for (wt = windowTable; wt; wt = nextWt) { + nextWt = wt->next; + if (!wt->next) /* found the next slot */ + wt->next = newRec; + } + } +} + + +void +rfbSendChromiumMoveResizeWindow(unsigned int winid, int x, int y, unsigned int w, unsigned int h) +{ + rfbClientPtr cl, nextCl; + rfbChromiumMoveResizeWindowMsg scm; + + for (cl = rfbClientHead; cl; cl = nextCl) { + nextCl = cl->next; + if (!cl->enableChromiumEncoding) + continue; + if (cl->chromium_port) { + scm.type = rfbChromiumMoveResizeWindow; + scm.winid = winid; + scm.x = x; + scm.y = y; + scm.w = w; + scm.h = h; + if (WriteExact(cl->sock, (char *)&scm, + sz_rfbChromiumMoveResizeWindowMsg) < 0) { + rfbLogPerror("rfbSendChromiumMoveResizeWindow: write\n"); + rfbCloseSock(cl->pScreen, cl->sock); + continue; + } + } + } +} + +void +rfbSendChromiumClipList(unsigned int winid, BoxPtr pClipRects, int numClipRects) +{ + rfbClientPtr cl, nextCl; + rfbChromiumClipListMsg sccl; + int len = sizeof(BoxRec) * numClipRects; + + for (cl = rfbClientHead; cl; cl = nextCl) { + nextCl = cl->next; + if (!cl->enableChromiumEncoding) + continue; + if (cl->chromium_port) { + sccl.type = rfbChromiumClipList; + sccl.winid = winid; + sccl.length = Swap32IfLE(len); + if (WriteExact(cl->sock, (char *)&sccl, + sz_rfbChromiumClipListMsg) < 0) { + rfbLogPerror("rfbSendChromiumClipList: write\n"); + rfbCloseSock(cl->pScreen, cl->sock); + continue; + } + if (WriteExact(cl->sock, (char *)pClipRects, len) < 0) { + rfbLogPerror("rfbSendChromiumClipList: write\n"); + rfbCloseSock(cl->pScreen, cl->sock); + continue; + } + } + } +} + +void +rfbSendChromiumWindowShow(unsigned int winid, unsigned int show) +{ + rfbClientPtr cl, nextCl; + rfbChromiumWindowShowMsg scws; + + for (cl = rfbClientHead; cl; cl = nextCl) { + nextCl = cl->next; + if (!cl->enableChromiumEncoding) + continue; + if (cl->chromium_port) { + scws.type = rfbChromiumWindowShow; + scws.winid = winid; + scws.show = show; + if (WriteExact(cl->sock, (char *)&scws, + sz_rfbChromiumWindowShowMsg) < 0) { + rfbLogPerror("rfbSendChromiumWindowShow: write\n"); + rfbCloseSock(cl->pScreen, cl->sock); + continue; + } + } + } +} + +void +rfbSendChromiumWindowDestroy(unsigned int winid) +{ + rfbClientPtr cl, nextCl; + rfbChromiumWindowDestroyMsg scwd; + + for (cl = rfbClientHead; cl; cl = nextCl) { + nextCl = cl->next; + if (!cl->enableChromiumEncoding) + continue; + if (cl->chromium_port) { + scwd.type = rfbChromiumWindowDestroy; + scwd.winid = winid; + if (WriteExact(cl->sock, (char *)&scwd, + sz_rfbChromiumWindowDestroyMsg) < 0) { + rfbLogPerror("rfbSendChromiumWindowDestroy: write\n"); + rfbCloseSock(cl->pScreen, cl->sock); + continue; + } + } + } +} +#endif /* CHROMIUM */ + +/* + * rfbSendServerCutText sends a ServerCutText message to all the clients. + */ + +void +rfbSendServerCutText(char *str, int len) +{ + rfbClientPtr cl, nextCl = NULL; + rfbServerCutTextMsg sct; + + for (cl = rfbClientHead; cl; cl = nextCl) { + if (cl->state != RFB_NORMAL) continue; + nextCl = cl->next; + sct.type = rfbServerCutText; + sct.length = Swap32IfLE(len); + if (WriteExact(cl->sock, (char *)&sct, + sz_rfbServerCutTextMsg) < 0) { + rfbLogPerror("rfbSendServerCutText: write\n"); + rfbCloseSock(cl->pScreen, cl->sock); + continue; + } + if (WriteExact(cl->sock, str, len) < 0) { + rfbLogPerror("rfbSendServerCutText: write\n"); + rfbCloseSock(cl->pScreen, cl->sock); + } + } +} + + + + +/***************************************************************************** + * + * UDP can be used for keyboard and pointer events when the underlying + * network is highly reliable. This is really here to support ORL's + * videotile, whose TCP implementation doesn't like sending lots of small + * packets (such as 100s of pen readings per second!). + */ + +void +rfbNewUDPConnection(sock) + int sock; +{ + if (write(sock, &ptrAcceleration, 1) < 0) { + rfbLogPerror("rfbNewUDPConnection: write"); + } +} + +/* + * Because UDP is a message based service, we can't read the first byte and + * then the rest of the packet separately like we do with TCP. We will always + * get a whole packet delivered in one go, so we ask read() for the maximum + * number of bytes we can possibly get. + */ + +void +rfbProcessUDPInput(ScreenPtr pScreen, int sock) +{ + VNCSCREENPTR(pScreen); + int n; + rfbClientToServerMsg msg; + + if ((n = read(sock, (char *)&msg, sizeof(msg))) <= 0) { + if (n < 0) { + rfbLogPerror("rfbProcessUDPInput: read"); + } + rfbDisconnectUDPSock(pScreen); + return; + } + + switch (msg.type) { + + case rfbKeyEvent: + if (n != sz_rfbKeyEventMsg) { + rfbLog("rfbProcessUDPInput: key event incorrect length\n"); + rfbDisconnectUDPSock(pScreen); + return; + } + if (!pVNC->rfbViewOnly) { + KbdAddEvent(msg.ke.down, (KeySym)Swap32IfLE(msg.ke.key), 0); + } + break; + + case rfbPointerEvent: + if (n != sz_rfbPointerEventMsg) { + rfbLog("rfbProcessUDPInput: ptr event incorrect length\n"); + rfbDisconnectUDPSock(pScreen); + return; + } + if (!pVNC->rfbViewOnly) { + PtrAddEvent(msg.pe.buttonMask, + Swap16IfLE(msg.pe.x), Swap16IfLE(msg.pe.y), 0); + } + break; + + default: + rfbLog("rfbProcessUDPInput: unknown message type %d\n", + msg.type); + rfbDisconnectUDPSock(pScreen); + } +} diff --git a/hw/vnc/rre.c b/hw/vnc/rre.c new file mode 100644 index 0000000..09043c8 --- /dev/null +++ b/hw/vnc/rre.c @@ -0,0 +1,324 @@ +/* + * rre.c + * + * Routines to implement Rise-and-Run-length Encoding (RRE). This + * code is based on krw's original javatel rfbserver. + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#include +#include "rfb.h" + +/* + * rreBeforeBuf contains pixel data in the client's format. + * rreAfterBuf contains the RRE encoded version. If the RRE encoded version is + * larger than the raw data or if it exceeds rreAfterBufSize then + * raw encoding is used instead. + */ + +static int rreBeforeBufSize = 0; +static unsigned char *rreBeforeBuf = NULL; + +static int rreAfterBufSize = 0; +static unsigned char *rreAfterBuf = NULL; +static int rreAfterBufLen; + +static int subrectEncode8(CARD8 *data, int w, int h); +static int subrectEncode16(CARD16 *data, int w, int h); +static int subrectEncode32(CARD32 *data, int w, int h); +static CARD32 getBgColour(char *data, int size, int bpp); + + +/* + * rfbSendRectEncodingRRE - send a given rectangle using RRE encoding. + */ + +Bool +rfbSendRectEncodingRRE(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + VNCSCREENPTR(cl->pScreen); + rfbFramebufferUpdateRectHeader rect; + rfbRREHeader hdr; + int nSubrects; + int i; + int maxRawSize = (pVNC->width * pVNC->height + * (cl->format.bitsPerPixel / 8)); + + if (rreBeforeBufSize < maxRawSize) { + rreBeforeBufSize = maxRawSize; + if (rreBeforeBuf == NULL) + rreBeforeBuf = (unsigned char *)xalloc(rreBeforeBufSize); + else + rreBeforeBuf = (unsigned char *)xrealloc(rreBeforeBuf, rreBeforeBufSize); + } + + if (rreAfterBufSize < maxRawSize) { + rreAfterBufSize = maxRawSize; + if (rreAfterBuf == NULL) + rreAfterBuf = (unsigned char *)xalloc(rreAfterBufSize); + else + rreAfterBuf = (unsigned char *)xrealloc(rreAfterBuf, rreAfterBufSize); + } + + (*cl->translateFn)(cl->pScreen, cl->translateLookupTable, + &pVNC->rfbServerFormat, + &cl->format, rreBeforeBuf, + pVNC->paddedWidthInBytes, w, h, x, y); + + switch (cl->format.bitsPerPixel) { + case 8: + nSubrects = subrectEncode8((CARD8 *)rreBeforeBuf, w, h); + break; + case 16: + nSubrects = subrectEncode16((CARD16 *)rreBeforeBuf, w, h); + break; + case 32: + nSubrects = subrectEncode32((CARD32 *)rreBeforeBuf, w, h); + break; + default: + rfbLog("getBgColour: bpp %d?\n",cl->format.bitsPerPixel); + exit(1); + } + + if (nSubrects < 0) { + + /* RRE encoding was too large, use raw */ + + return rfbSendRectEncodingRaw(cl, x, y, w, h); + } + + cl->rfbRectanglesSent[rfbEncodingRRE]++; + cl->rfbBytesSent[rfbEncodingRRE] += (sz_rfbFramebufferUpdateRectHeader + + sz_rfbRREHeader + rreAfterBufLen); + + if (pVNC->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbRREHeader + > UPDATE_BUF_SIZE) + { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingRRE); + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + pVNC->ublen += sz_rfbFramebufferUpdateRectHeader; + + hdr.nSubrects = Swap32IfLE(nSubrects); + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&hdr, sz_rfbRREHeader); + pVNC->ublen += sz_rfbRREHeader; + + for (i = 0; i < rreAfterBufLen;) { + + int bytesToCopy = UPDATE_BUF_SIZE - pVNC->ublen; + + if (i + bytesToCopy > rreAfterBufLen) { + bytesToCopy = rreAfterBufLen - i; + } + + memcpy(&pVNC->updateBuf[pVNC->ublen], &rreAfterBuf[i], bytesToCopy); + + pVNC->ublen += bytesToCopy; + i += bytesToCopy; + + if (pVNC->ublen == UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + } + + return TRUE; +} + + + +/* + * subrectEncode() encodes the given multicoloured rectangle as a background + * colour overwritten by single-coloured rectangles. It returns the number + * of subrectangles in the encoded buffer, or -1 if subrect encoding won't + * fit in the buffer. It puts the encoded rectangles in rreAfterBuf. The + * single-colour rectangle partition is not optimal, but does find the biggest + * horizontal or vertical rectangle top-left anchored to each consecutive + * coordinate position. + * + * The coding scheme is simply [...] where each + * is []. + */ + +#define DEFINE_SUBRECT_ENCODE(bpp) \ +static int \ +subrectEncode##bpp(data,w,h) \ + CARD##bpp *data; \ + int w; \ + int h; \ +{ \ + CARD##bpp cl; \ + rfbRectangle subrect; \ + int x,y; \ + int i,j; \ + int hx=0,hy,vx=0,vy; \ + int hyflag; \ + CARD##bpp *seg; \ + CARD##bpp *line; \ + int hw,hh,vw,vh; \ + int thex,they,thew,theh; \ + int numsubs = 0; \ + int newLen; \ + CARD##bpp bg = (CARD##bpp)getBgColour((char*)data,w*h,bpp); \ + \ + *((CARD##bpp*)rreAfterBuf) = bg; \ + \ + rreAfterBufLen = (bpp/8); \ + \ + for (y=0; y 0) && (i >= hx)) {hy += 1;} else {hyflag = 0;} \ + } \ + vy = j-1; \ + \ + /* We now have two possible subrects: (x,y,hx,hy) and (x,y,vx,vy) \ + * We'll choose the bigger of the two. \ + */ \ + hw = hx-x+1; \ + hh = hy-y+1; \ + vw = vx-x+1; \ + vh = vy-y+1; \ + \ + thex = x; \ + they = y; \ + \ + if ((hw*hh) > (vw*vh)) { \ + thew = hw; \ + theh = hh; \ + } else { \ + thew = vw; \ + theh = vh; \ + } \ + \ + subrect.x = Swap16IfLE(thex); \ + subrect.y = Swap16IfLE(they); \ + subrect.w = Swap16IfLE(thew); \ + subrect.h = Swap16IfLE(theh); \ + \ + newLen = rreAfterBufLen + (bpp/8) + sz_rfbRectangle; \ + if ((newLen > (w * h * (bpp/8))) || (newLen > rreAfterBufSize)) \ + return -1; \ + \ + numsubs += 1; \ + *((CARD##bpp*)(rreAfterBuf + rreAfterBufLen)) = cl; \ + rreAfterBufLen += (bpp/8); \ + memcpy(&rreAfterBuf[rreAfterBufLen],&subrect,sz_rfbRectangle); \ + rreAfterBufLen += sz_rfbRectangle; \ + \ + /* \ + * Now mark the subrect as done. \ + */ \ + for (j=they; j < (they+theh); j++) { \ + for (i=thex; i < (thex+thew); i++) { \ + data[j*w+i] = bg; \ + } \ + } \ + } \ + } \ + } \ + \ + return numsubs; \ +} + +DEFINE_SUBRECT_ENCODE(8) +DEFINE_SUBRECT_ENCODE(16) +DEFINE_SUBRECT_ENCODE(32) + + +/* + * getBgColour() gets the most prevalent colour in a byte array. + */ +static CARD32 +getBgColour(data,size,bpp) + char *data; + int size; + int bpp; +{ + +#define NUMCLRS 256 + + static int counts[NUMCLRS]; + int i,j,k; + + int maxcount = 0; + CARD8 maxclr = 0; + + if (bpp != 8) { + if (bpp == 16) { + return ((CARD16 *)data)[0]; + } else if (bpp == 32) { + return ((CARD32 *)data)[0]; + } else { + rfbLog("getBgColour: bpp %d?\n",bpp); + exit(1); + } + } + + for (i=0; i= NUMCLRS) { + rfbLog("getBgColour: unusual colour = %d\n", k); + exit(1); + } + counts[k] += 1; + if (counts[k] > maxcount) { + maxcount = counts[k]; + maxclr = ((CARD8 *)data)[j]; + } + } + + return maxclr; +} diff --git a/hw/vnc/sockets.c b/hw/vnc/sockets.c new file mode 100644 index 0000000..7eff68c --- /dev/null +++ b/hw/vnc/sockets.c @@ -0,0 +1,656 @@ +/* + * sockets.c - deal with TCP & UDP sockets. + * + * This code should be independent of any changes in the RFB protocol. It just + * deals with the X server scheduling stuff, calling rfbNewClientConnection and + * rfbProcessClientMessage to actually deal with the protocol. If a socket + * needs to be closed for any reason then rfbCloseSock should be called, and + * this in turn will call rfbClientConnectionGone. To make an active + * connection out, call rfbConnect - note that this does _not_ call + * rfbNewClientConnection. + * + * This file is divided into two types of function. Those beginning with + * "rfb" are specific to sockets using the RFB protocol. Those without the + * "rfb" prefix are more general socket routines (which are used by the http + * code). + * + * Thanks to Karl Hakimian for pointing out that some platforms return EAGAIN + * not EWOULDBLOCK. + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "windowstr.h" + +#ifndef USE_LIBWRAP +#define USE_LIBWRAP 0 +#endif +#if USE_LIBWRAP +#include +#include +int allow_severity = LOG_INFO; +int deny_severity = LOG_WARNING; +#endif + +#include "rfb.h" + +int rfbMaxClientWait = 20000; /* time (ms) after which we decide client has + gone away - needed to stop us hanging */ + +static struct sockaddr_in udpRemoteAddr; + +/* + * rfbInitSockets sets up the TCP and UDP sockets to listen for RFB + * connections. It does nothing if called again. + */ + +Bool +rfbInitSockets(ScreenPtr pScreen) +{ + VNCSCREENPTR(pScreen); + + if (inetdSock != -1) { + const int one = 1; + + if (fcntl(inetdSock, F_SETFL, O_NONBLOCK) < 0) { + rfbLogPerror("fcntl"); + return FALSE; + } + + if (setsockopt(inetdSock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + rfbLogPerror("setsockopt"); + return FALSE; + } + + AddEnabledDevice(inetdSock); + FD_ZERO(&pVNC->allFds); + FD_SET(inetdSock, &pVNC->allFds); + pVNC->maxFd = inetdSock; + return TRUE; + } + + if (pVNC->rfbPort == 0) { + pVNC->rfbPort = 5900 + atoi(display) + pScreen->myNum; + } + + if ((pVNC->rfbListenSock = ListenOnTCPPort(pScreen, pVNC->rfbPort)) < 0) { + rfbLogPerror("ListenOnTCPPort"); + pVNC->rfbPort = 0; + return FALSE; + } + + rfbLog("Listening for VNC connections on TCP port %d\n", pVNC->rfbPort); + + AddEnabledDevice(pVNC->rfbListenSock); + + FD_ZERO(&pVNC->allFds); + FD_SET(pVNC->rfbListenSock, &pVNC->allFds); + pVNC->maxFd = pVNC->rfbListenSock; + + if (pVNC->udpPort != 0) { + rfbLog("rfbInitSockets: listening for input on UDP port %d\n",pVNC->udpPort); + + if ((pVNC->udpSock = ListenOnUDPPort(pScreen, pVNC->udpPort)) < 0) { + rfbLogPerror("ListenOnUDPPort"); + return FALSE; + } + AddEnabledDevice(pVNC->udpSock); + FD_SET(pVNC->udpSock, &pVNC->allFds); + pVNC->maxFd = max(pVNC->udpSock,pVNC->maxFd); + } + + return TRUE; +} + + +/* + * rfbCheckFds is called from ProcessInputEvents to check for input on the RFB + * socket(s). If there is input to process, the appropriate function in the + * RFB server code will be called (rfbNewClientConnection, + * rfbProcessClientMessage, etc). + */ + +void +rfbCheckFds(ScreenPtr pScreen) +{ + VNCSCREENPTR(pScreen); +#if XFREE86VNC + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; +#endif + int nfds; + fd_set fds; + struct timeval tv; + struct sockaddr_in addr; + SOCKLEN_T addrlen = sizeof(addr); + char buf[6]; + const int one = 1; + int sock; + static Bool inetdInitDone = FALSE; + + if (!inetdInitDone && inetdSock != -1) { + rfbNewClientConnection(pScreen, inetdSock); + inetdInitDone = TRUE; + } + + memcpy((char *)&fds, (char *)&pVNC->allFds, sizeof(fd_set)); + tv.tv_sec = 0; + tv.tv_usec = 0; + nfds = select(pVNC->maxFd + 1, &fds, NULL, NULL, &tv); + if (nfds == 0) { + return; + } + if (nfds < 0) { + if (errno != EINTR) + rfbLogPerror("rfbCheckFds: select"); + return; + } + + if (pVNC->rfbListenSock != -1 && FD_ISSET(pVNC->rfbListenSock, &fds)) { + + if ((sock = accept(pVNC->rfbListenSock, + (struct sockaddr *)&addr, &addrlen)) < 0) { + rfbLogPerror("rfbCheckFds: accept"); + return; + } + + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { + rfbLogPerror("rfbCheckFds: fcntl"); + close(sock); + return; + } + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + rfbLogPerror("rfbCheckFds: setsockopt"); + close(sock); + return; + } + + rfbLog("\n"); + +#if USE_LIBWRAP + if (!hosts_ctl("Xvnc", STRING_UNKNOWN, inet_ntoa(addr.sin_addr), + STRING_UNKNOWN)) { + rfbLog("Rejected connection from client %s\n", + inet_ntoa(addr.sin_addr)); + close(sock); + return; + } +#endif + + rfbLog("Got VNC connection from client %s\n", inet_ntoa(addr.sin_addr)); + + AddEnabledDevice(sock); + FD_SET(sock, &pVNC->allFds); + pVNC->maxFd = max(sock,pVNC->maxFd); + + rfbNewClientConnection(pScreen, sock); + + FD_CLR(pVNC->rfbListenSock, &fds); + if (--nfds == 0) + return; + } + + if ((pVNC->udpSock != -1) && FD_ISSET(pVNC->udpSock, &fds)) { + + if (recvfrom(pVNC->udpSock, buf, 1, MSG_PEEK, + (struct sockaddr *)&addr, &addrlen) < 0) { + + rfbLogPerror("rfbCheckFds: UDP: recvfrom"); + rfbDisconnectUDPSock(pScreen); + + } else { + + if (!pVNC->udpSockConnected || + (memcmp(&addr, &udpRemoteAddr, addrlen) != 0)) + { + /* new remote end */ + rfbLog("rfbCheckFds: UDP: got connection\n"); + + memcpy(&udpRemoteAddr, &addr, addrlen); + pVNC->udpSockConnected = TRUE; + + if (connect(pVNC->udpSock, + (struct sockaddr *)&addr, addrlen) < 0) { + rfbLogPerror("rfbCheckFds: UDP: connect"); + rfbDisconnectUDPSock(pScreen); + return; + } + + rfbNewUDPConnection(pVNC->udpSock); + } + + rfbProcessUDPInput(pScreen, pVNC->udpSock); + } + + FD_CLR(pVNC->udpSock, &fds); + if (--nfds == 0) + return; + } + + for (sock = 0; sock <= pVNC->maxFd; sock++) { + if (FD_ISSET(sock, &fds) && FD_ISSET(sock, &pVNC->allFds)) { +#if XFREE86VNC + if (!pScrn->vtSema) + rfbCloseSock(pScreen, sock); + else +#endif + rfbProcessClientMessage(pScreen, sock); + } + } +} + + +void +rfbDisconnectUDPSock(ScreenPtr pScreen) +{ + VNCSCREENPTR(pScreen); + pVNC->udpSockConnected = FALSE; +} + + +void +rfbCloseSock(ScreenPtr pScreen, int sock) +{ + VNCSCREENPTR(pScreen); + close(sock); + RemoveEnabledDevice(sock); + FD_CLR(sock, &pVNC->allFds); + rfbClientConnectionGone(sock); + if (sock == inetdSock) + GiveUp(0); +} + +#if 0 +/* + * rfbWaitForClient can be called to wait for the RFB client to send us a + * message. When one is received it is processed by calling + * rfbProcessClientMessage(). + */ + +void +rfbWaitForClient(sock) + int sock; +{ + int n; + fd_set fds; + struct timeval tv; + + FD_ZERO(&fds); + FD_SET(sock, &fds); + tv.tv_sec = rfbMaxClientWait / 1000; + tv.tv_usec = (rfbMaxClientWait % 1000) * 1000; + n = select(sock+1, &fds, NULL, NULL, &tv); + if (n < 0) { + rfbLogPerror("rfbWaitForClient: select"); + exit(1); + } + if (n == 0) { + rfbCloseSock(sock); + return; + } + + rfbProcessClientMessage(sock); +} +#endif + +/* + * rfbConnect is called to make a connection out to a given TCP address. + */ + +int +rfbConnect(ScreenPtr pScreen, char *host, int port) +{ + VNCSCREENPTR(pScreen); + int sock; + int one = 1; + + rfbLog("\n"); + rfbLog("Making connection to client on host %s port %d\n", + host,port); + + if ((sock = ConnectToTcpAddr(host, port)) < 0) { + rfbLogPerror("connection failed"); + return -1; + } + + if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) { + rfbLogPerror("fcntl failed"); + close(sock); + return -1; + } + + if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + rfbLogPerror("setsockopt failed"); + close(sock); + return -1; + } + + AddEnabledDevice(sock); + FD_SET(sock, &pVNC->allFds); + pVNC->maxFd = max(sock,pVNC->maxFd); + + return sock; +} + + + + +/* + * ReadExact reads an exact number of bytes on a TCP socket. Returns 1 if + * those bytes have been read, 0 if the other end has closed, or -1 if an error + * occurred (errno is set to ETIMEDOUT if it timed out). + */ + +int +ReadExact(sock, buf, len) + int sock; + char *buf; + int len; +{ + int n; + fd_set fds; + int tries = 5; + struct timeval tv; + + while (len > 0) { + n = read(sock, buf, len); + + if (n > 0) { + + buf += n; + len -= n; + + } else if (n == 0) { + + return 0; + + } else { + if (errno != EWOULDBLOCK && errno != EAGAIN) { + return n; + } + + do { + FD_ZERO(&fds); + FD_SET(sock, &fds); + tv.tv_sec = rfbMaxClientWait / 1000; + tv.tv_usec = (rfbMaxClientWait % 1000) * 1000; + n = select(sock+1, &fds, NULL, NULL, &tv); + tries--; + + /* We really need to retry if we get EINTR, so spin */ + /* If after 5 attempts we're still broke, abort.... */ + + } while ((n < 0 && errno == EINTR) && tries > 0); + + if (n < 0) { + rfbLogPerror("ReadExact: select"); + return n; + } + if (n == 0) { + errno = ETIMEDOUT; + return -1; + } + } + } + return 1; +} + + + +/* + * WriteExact writes an exact number of bytes on a TCP socket. Returns 1 if + * those bytes have been written, or -1 if an error occurred (errno is set to + * ETIMEDOUT if it timed out). + */ + +int +WriteExact(sock, buf, len) + int sock; + char *buf; + int len; +{ + int n; + fd_set fds; + struct timeval tv; +#if 0 + int totalTimeWaited = 0; +#endif + + while (len > 0) { + n = write(sock, buf, len); + + if (n > 0) { + + buf += n; + len -= n; + + } else if (n == 0) { + + rfbLog("WriteExact: write returned 0?\n"); + return -1; + + } else { + if (errno != EWOULDBLOCK && errno != EAGAIN) { + return n; + } + +#if 0 + /* Retry every 5 seconds until we exceed rfbMaxClientWait. We + need to do this because select doesn't necessarily return + immediately when the other end has gone away */ + + FD_ZERO(&fds); + FD_SET(sock, &fds); + tv.tv_sec = 5; + tv.tv_usec = 0; +#else + /* We're in the WakeupHandler now, so don't wait */ + + FD_ZERO(&fds); + FD_SET(sock, &fds); + tv.tv_sec = 0; + tv.tv_usec = 0; +#endif + n = select(sock+1, NULL, &fds, NULL, &tv); +#if 0 + if (n < 0) { + rfbLogPerror("WriteExact: select"); + return n; + } + if (n == 0) { + totalTimeWaited += 5000; + if (totalTimeWaited >= rfbMaxClientWait) { + errno = ETIMEDOUT; + return -1; + } + } else { + totalTimeWaited = 0; + } +#endif + } + } + return 1; +} + + +int +ListenOnTCPPort(ScreenPtr pScreen, int port) +{ + VNCSCREENPTR(pScreen); + struct sockaddr_in addr; + int sock; + int one = 1; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = pVNC->interface.s_addr; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + return -1; + } + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&one, sizeof(one)) < 0) { + close(sock); + return -1; + } + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + close(sock); + return -1; + } + if (listen(sock, 5) < 0) { + close(sock); + return -1; + } + + return sock; +} + + +int +ConnectToTcpAddr(host, port) + char *host; + int port; +{ + struct hostent *hp; + int sock, n; + struct sockaddr_in addr; + int tries = 5; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + + if ((addr.sin_addr.s_addr = inet_addr(host)) == -1) + { + if (!(hp = gethostbyname(host))) { + errno = EINVAL; + return -1; + } + addr.sin_addr.s_addr = *(unsigned long *)hp->h_addr; + } + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + return -1; + } + + do { + sock = socket(AF_INET, SOCK_STREAM, 0); + tries--; + + /* We really need to retry if we get EINTR, so spin */ + /* If after 5 attempts we're still broke, abort.... */ + + } while ((sock < 0 && errno == EINTR) && tries > 0); + + if (sock < 0) { + return -1; + } + + tries = 5; + + do { + n = connect(sock, (struct sockaddr *)&addr, (sizeof(addr))); + tries--; + + /* We really need to retry if we get EINTR, so spin */ + /* If after 5 attempts we're still broke, abort.... */ + + } while ((n < 0 && errno == EINTR) && tries > 0); + + if (n < 0) { + close(sock); + return -1; + } + + return sock; +} + + +int +ListenOnUDPPort(ScreenPtr pScreen, int port) +{ + VNCSCREENPTR(pScreen); + struct sockaddr_in addr; + int sock; + int one = 1; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = pVNC->interface.s_addr; + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + return -1; + } + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&one, sizeof(one)) < 0) { + return -1; + } + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + return -1; + } + + return sock; +} + +#if 0 +/* + * rdpInitSockets sets up the TCP for RDP + * connections. It does nothing if called again. + */ + +Bool +rdpInitSockets(ScreenPtr pScreen) +{ + VNCSCREENPTR(pScreen); + + if ((pVNC->rdpListenSock = ListenOnTCPPort(pScreen, pVNC->rdpPort)) < 0) { + rfbLogPerror("ListenOnTCPPort"); + pVNC->rdpPort = 0; + return FALSE; + } + + rfbLog("Listening for RDP connections on TCP port %d\n", pVNC->rdpPort); + + AddEnabledDevice(pVNC->rdpListenSock); + + return TRUE; +} +#endif diff --git a/hw/vnc/sprite.c b/hw/vnc/sprite.c new file mode 100644 index 0000000..c1e8685 --- /dev/null +++ b/hw/vnc/sprite.c @@ -0,0 +1,2279 @@ +/* + * sprite.c + * + * software sprite routines - based on misprite + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ + +/* $XConsortium: misprite.c,v 5.47 94/04/17 20:27:53 dpw Exp $ */ + +/* + +Copyright (c) 1989 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. +*/ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#include "rfb.h" +# include +# include +# include +# include +# include "misc.h" +# include "pixmapstr.h" +# include "input.h" +# include "mi.h" +# include "cursorstr.h" +# include "scrnintstr.h" +# include "colormapst.h" +# include "windowstr.h" +# include "gcstruct.h" +# include "mipointer.h" +# include "spritest.h" +# include "dixfontstr.h" + +/* + * screen wrappers + */ + +static Bool rfbSpriteCloseScreen(int i, ScreenPtr pScreen); +static void rfbSpriteGetImage(DrawablePtr pDrawable, int sx, int sy, + int w, int h, unsigned int format, + unsigned long planemask, char *pdstLine); +static void rfbSpriteGetSpans(DrawablePtr pDrawable, int wMax, + DDXPointPtr ppt, int *pwidth, int nspans, + char *pdstStart); +static void rfbSpriteSourceValidate(DrawablePtr pDrawable, int x, int y, + int width, int height); +static Bool rfbSpriteCreateGC(GCPtr pGC); +static void rfbSpriteBlockHandler(int i, pointer blockData, + pointer pTimeout, + pointer pReadMask); +static void rfbSpriteInstallColormap(ColormapPtr pMap); +static void rfbSpriteStoreColors(ColormapPtr pMap, int ndef, + xColorItem *pdef); + +static void rfbSpritePaintWindowBackground(WindowPtr pWin, + RegionPtr pRegion, int what); +static void rfbSpritePaintWindowBorder(WindowPtr pWin, + RegionPtr pRegion, int what); +static void rfbSpriteCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, + RegionPtr pRegion); +static void rfbSpriteClearToBackground(WindowPtr pWin, int x, int y, + int w, int h, + Bool generateExposures); + +static void rfbSpriteSaveDoomedAreas(WindowPtr pWin, + RegionPtr pObscured, int dx, + int dy); +static RegionPtr rfbSpriteRestoreAreas(WindowPtr pWin, RegionPtr pRgnExposed); +static void rfbSpriteComputeSaved(ScreenPtr pScreen); + +static DevPrivateKey rfbSpriteScreenKey = &rfbSpriteScreenKey; + +#define SCREEN_PROLOGUE(pScreen, field) ((pScreen)->field = \ + ((rfbSpriteScreenPtr)dixLookupPrivate(&(pScreen)->devPrivates, \ + rfbSpriteScreenKey))->field) + +#define SCREEN_EPILOGUE(pScreen, field, wrapper)\ + ((pScreen)->field = wrapper) + +/* + * GC func wrappers + */ + +static void rfbSpriteValidateGC(GCPtr pGC, unsigned long stateChanges, + DrawablePtr pDrawable); +static void rfbSpriteCopyGC(GCPtr pGCSrc, unsigned long mask, GCPtr pGCDst); +static void rfbSpriteDestroyGC(GCPtr pGC); +static void rfbSpriteChangeGC(GCPtr pGC, unsigned long mask); +static void rfbSpriteChangeClip(GCPtr pGC, int type, pointer pvalue, int nrects); +static void rfbSpriteDestroyClip(GCPtr pGC); +static void rfbSpriteCopyClip(GCPtr pgcDst, GCPtr pgcSrc); + +static GCFuncs rfbSpriteGCFuncs = { + rfbSpriteValidateGC, + rfbSpriteChangeGC, + rfbSpriteCopyGC, + rfbSpriteDestroyGC, + rfbSpriteChangeClip, + rfbSpriteDestroyClip, + rfbSpriteCopyClip, +}; + +static DevPrivateKey rfbSpriteGCKey = &rfbSpriteGCKey; + +#define GC_FUNC_PROLOGUE(pGC) \ + rfbSpriteGCPtr pGCPriv = \ + (rfbSpriteGCPtr)dixLookupPrivate(&(pGC)->devPrivates, rfbSpriteGCKey); \ + (pGC)->funcs = pGCPriv->wrapFuncs; \ + if (pGCPriv->wrapOps) \ + (pGC)->ops = pGCPriv->wrapOps; + +#define GC_FUNC_EPILOGUE(pGC) \ + pGCPriv->wrapFuncs = (pGC)->funcs; \ + (pGC)->funcs = &rfbSpriteGCFuncs; \ + if (pGCPriv->wrapOps) \ + { \ + pGCPriv->wrapOps = (pGC)->ops; \ + (pGC)->ops = &rfbSpriteGCOps; \ + } + +/* + * GC op wrappers + */ + +static void rfbSpriteFillSpans(DrawablePtr pDrawable, GCPtr pGC, + int nInit, DDXPointPtr pptInit, + int *pwidthInit, int fSorted); +static void rfbSpriteSetSpans(DrawablePtr pDrawable, GCPtr pGC, + char *psrc, DDXPointPtr ppt, int *pwidth, + int nspans, int fSorted); +static void rfbSpritePutImage(DrawablePtr pDrawable, GCPtr pGC, + int depth, int x, int y, int w, int h, + int leftPad, int format, char *pBits); +static RegionPtr rfbSpriteCopyArea(DrawablePtr pSrc, DrawablePtr pDst, + GCPtr pGC, int srcx, int srcy, int w, + int h, int dstx, int dsty); +static RegionPtr rfbSpriteCopyPlane(DrawablePtr pSrc, DrawablePtr pDst, + GCPtr pGC, int srcx, int srcy, int w, + int h, int dstx, int dsty, + unsigned long plane); +static void rfbSpritePolyPoint(DrawablePtr pDrawable, GCPtr pGC, + int mode, int npt, xPoint *pptInit); +static void rfbSpritePolylines(DrawablePtr pDrawable, GCPtr pGC, + int mode, int npt, DDXPointPtr pptInit); +static void rfbSpritePolySegment(DrawablePtr pDrawable, GCPtr pGC, + int nseg, xSegment *pSegs); +static void rfbSpritePolyRectangle(DrawablePtr pDrawable, GCPtr pGC, + int nrects, xRectangle *pRects); +static void rfbSpritePolyArc(DrawablePtr pDrawable, GCPtr pGC, + int narcs, xArc *parcs); +static void rfbSpriteFillPolygon(DrawablePtr pDrawable, GCPtr pGC, + int shape, int mode, int count, + DDXPointPtr pPts); +static void rfbSpritePolyFillRect(DrawablePtr pDrawable, GCPtr pGC, + int nrectFill, xRectangle *prectInit); +static void rfbSpritePolyFillArc(DrawablePtr pDrawable, GCPtr pGC, + int narcs, xArc *parcs); +static int rfbSpritePolyText8(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, int count, char *chars); +static int rfbSpritePolyText16(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, int count, + unsigned short *chars); +static void rfbSpriteImageText8(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, int count, char *chars); +static void rfbSpriteImageText16(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, int count, + unsigned short *chars); +static void rfbSpriteImageGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr *ppci, + pointer pglyphBase); +static void rfbSpritePolyGlyphBlt(DrawablePtr pDrawable, GCPtr pGC, + int x, int y, unsigned int nglyph, + CharInfoPtr *ppci, + pointer pglyphBase); +static void rfbSpritePushPixels(GCPtr pGC, PixmapPtr pBitMap, + DrawablePtr pDst, int w, int h, + int x, int y); +#ifdef NEED_LINEHELPER +static void rfbSpriteLineHelper(); +#endif + +static GCOps rfbSpriteGCOps = { + rfbSpriteFillSpans, rfbSpriteSetSpans, rfbSpritePutImage, + rfbSpriteCopyArea, rfbSpriteCopyPlane, rfbSpritePolyPoint, + rfbSpritePolylines, rfbSpritePolySegment, rfbSpritePolyRectangle, + rfbSpritePolyArc, rfbSpriteFillPolygon, rfbSpritePolyFillRect, + rfbSpritePolyFillArc, rfbSpritePolyText8, rfbSpritePolyText16, + rfbSpriteImageText8, rfbSpriteImageText16, rfbSpriteImageGlyphBlt, + rfbSpritePolyGlyphBlt, rfbSpritePushPixels +#ifdef NEED_LINEHELPER + , rfbSpriteLineHelper +#endif +}; + +/* + * testing only -- remove cursor for every draw. Eventually, + * each draw operation will perform a bounding box check against + * the saved cursor area + */ + +#define GC_SETUP_CHEAP(pDrawable) \ + rfbSpriteScreenPtr pScreenPriv = (rfbSpriteScreenPtr) \ + dixLookupPrivate(&pDrawable->pScreen->devPrivates, \ + rfbSpriteScreenKey); + +#define GC_SETUP(pDrawable, pGC) \ + GC_SETUP_CHEAP(pDrawable) \ + rfbSpriteGCPtr pGCPrivate = \ + (rfbSpriteGCPtr)dixLookupPrivate(&(pGC)->devPrivates, rfbSpriteGCKey); \ + GCFuncs *oldFuncs = pGC->funcs; + +#define GC_SETUP_AND_CHECK(pDrawable, pGC) \ + GC_SETUP(pDrawable, pGC); \ + if (GC_CHECK((WindowPtr)pDrawable)) \ + rfbSpriteRemoveCursor (pDrawable->pScreen); + +#define GC_CHECK(pWin) \ + (pVNC->cursorIsDrawn && \ + (pScreenPriv->pCacheWin == pWin ? \ + pScreenPriv->isInCacheWin : ( \ + (pScreenPriv->pCacheWin = (pWin)) , \ + (pScreenPriv->isInCacheWin = \ + (pWin)->drawable.x < pScreenPriv->saved.x2 && \ + pScreenPriv->saved.x1 < (pWin)->drawable.x + \ + (int) (pWin)->drawable.width && \ + (pWin)->drawable.y < pScreenPriv->saved.y2 && \ + pScreenPriv->saved.y1 < (pWin)->drawable.y + \ + (int) (pWin)->drawable.height &&\ + RECT_IN_REGION((pWin)->drawable.pScreen, &(pWin)->borderClip, \ + &pScreenPriv->saved) != rgnOUT)))) + +#define GC_OP_PROLOGUE(pGC) { \ + (pGC)->funcs = pGCPrivate->wrapFuncs; \ + (pGC)->ops = pGCPrivate->wrapOps; \ + } + +#define GC_OP_EPILOGUE(pGC) { \ + pGCPrivate->wrapOps = (pGC)->ops; \ + (pGC)->funcs = oldFuncs; \ + (pGC)->ops = &rfbSpriteGCOps; \ + } + +/* + * pointer-sprite method table + */ + +static Bool rfbSpriteRealizeCursor (ScreenPtr pScreen, CursorPtr pCursor); +static Bool rfbSpriteUnrealizeCursor (ScreenPtr pScreen, CursorPtr pCursor); +static void rfbSpriteSetCursor (ScreenPtr pScreen, CursorPtr pCursor, int x, int y); +static void rfbSpriteMoveCursor (ScreenPtr pScreen, int x, int y); + +miPointerSpriteFuncRec rfbSpritePointerFuncs = { + rfbSpriteRealizeCursor, + rfbSpriteUnrealizeCursor, + rfbSpriteSetCursor, + rfbSpriteMoveCursor, +}; + +/* + * other misc functions + */ + +static Bool rfbDisplayCursor (ScreenPtr pScreen, CursorPtr pCursor); + + +/* + * rfbSpriteInitialize -- called from device-dependent screen + * initialization proc after all of the function pointers have + * been stored in the screen structure. + */ + +Bool +rfbSpriteInitialize (pScreen, cursorFuncs, screenFuncs) + ScreenPtr pScreen; + rfbSpriteCursorFuncPtr cursorFuncs; + miPointerScreenFuncPtr screenFuncs; +{ + rfbSpriteScreenPtr pPriv; + VisualPtr pVisual; + + if (!dixRequestPrivate(rfbSpriteGCKey, sizeof(rfbSpriteGCRec))) + return FALSE; + + pPriv = (rfbSpriteScreenPtr) xalloc (sizeof (rfbSpriteScreenRec)); + if (!pPriv) + return FALSE; + if (!miPointerInitialize (pScreen, &rfbSpritePointerFuncs, screenFuncs,TRUE)) + { + xfree ((pointer) pPriv); + return FALSE; + } + for (pVisual = pScreen->visuals; + pVisual->vid != pScreen->rootVisual; + pVisual++) + ; + pPriv->pVisual = pVisual; + pPriv->CloseScreen = pScreen->CloseScreen; + pPriv->GetImage = pScreen->GetImage; + pPriv->GetSpans = pScreen->GetSpans; + pPriv->SourceValidate = pScreen->SourceValidate; + pPriv->CreateGC = pScreen->CreateGC; +#if 0 + pPriv->BlockHandler = pScreen->BlockHandler; +#endif + pPriv->InstallColormap = pScreen->InstallColormap; + pPriv->StoreColors = pScreen->StoreColors; + pPriv->DisplayCursor = pScreen->DisplayCursor; + + pPriv->PaintWindowBackground = pScreen->PaintWindowBackground; + pPriv->PaintWindowBorder = pScreen->PaintWindowBorder; + pPriv->CopyWindow = pScreen->CopyWindow; + pPriv->ClearToBackground = pScreen->ClearToBackground; + + pPriv->SaveDoomedAreas = pScreen->SaveDoomedAreas; + pPriv->RestoreAreas = pScreen->RestoreAreas; + + pPriv->pCursor = NULL; + pPriv->x = 0; + pPriv->y = 0; + pPriv->shouldBeUp = FALSE; + pPriv->pCacheWin = NullWindow; + pPriv->isInCacheWin = FALSE; + pPriv->checkPixels = TRUE; + pPriv->pInstalledMap = NULL; + pPriv->pColormap = NULL; + pPriv->funcs = cursorFuncs; + pPriv->colors[SOURCE_COLOR].red = 0; + pPriv->colors[SOURCE_COLOR].green = 0; + pPriv->colors[SOURCE_COLOR].blue = 0; + pPriv->colors[MASK_COLOR].red = 0; + pPriv->colors[MASK_COLOR].green = 0; + pPriv->colors[MASK_COLOR].blue = 0; + dixSetPrivate(&pScreen->devPrivates, rfbSpriteScreenKey, pPriv); + pScreen->CloseScreen = rfbSpriteCloseScreen; + pScreen->GetImage = rfbSpriteGetImage; + pScreen->GetSpans = rfbSpriteGetSpans; + pScreen->SourceValidate = rfbSpriteSourceValidate; + pScreen->CreateGC = rfbSpriteCreateGC; +#if 0 + pScreen->BlockHandler = rfbSpriteBlockHandler; +#endif + pScreen->InstallColormap = rfbSpriteInstallColormap; + pScreen->StoreColors = rfbSpriteStoreColors; + + pScreen->PaintWindowBackground = rfbSpritePaintWindowBackground; + pScreen->PaintWindowBorder = rfbSpritePaintWindowBorder; + pScreen->CopyWindow = rfbSpriteCopyWindow; + pScreen->ClearToBackground = rfbSpriteClearToBackground; + + pScreen->SaveDoomedAreas = rfbSpriteSaveDoomedAreas; + pScreen->RestoreAreas = rfbSpriteRestoreAreas; + + pScreen->DisplayCursor = rfbDisplayCursor; + + return TRUE; +} + +/* + * Screen wrappers + */ + +/* + * CloseScreen wrapper -- unwrap everything, free the private data + * and call the wrapped function + */ + +static Bool +rfbSpriteCloseScreen (i, pScreen) + ScreenPtr pScreen; +{ + rfbSpriteScreenPtr pScreenPriv; + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + + pScreen->CloseScreen = pScreenPriv->CloseScreen; + pScreen->GetImage = pScreenPriv->GetImage; + pScreen->GetSpans = pScreenPriv->GetSpans; + pScreen->SourceValidate = pScreenPriv->SourceValidate; + pScreen->CreateGC = pScreenPriv->CreateGC; +#if 0 + pScreen->BlockHandler = pScreenPriv->BlockHandler; +#endif + pScreen->InstallColormap = pScreenPriv->InstallColormap; + pScreen->StoreColors = pScreenPriv->StoreColors; + + pScreen->PaintWindowBackground = pScreenPriv->PaintWindowBackground; + pScreen->PaintWindowBorder = pScreenPriv->PaintWindowBorder; + pScreen->CopyWindow = pScreenPriv->CopyWindow; + pScreen->ClearToBackground = pScreenPriv->ClearToBackground; + + pScreen->SaveDoomedAreas = pScreenPriv->SaveDoomedAreas; + pScreen->RestoreAreas = pScreenPriv->RestoreAreas; + + xfree ((pointer) pScreenPriv); + + return (*pScreen->CloseScreen) (i, pScreen); +} + +static void +rfbSpriteGetImage (pDrawable, sx, sy, w, h, format, planemask, pdstLine) + DrawablePtr pDrawable; + int sx, sy, w, h; + unsigned int format; + unsigned long planemask; + char *pdstLine; +{ + ScreenPtr pScreen = pDrawable->pScreen; + rfbSpriteScreenPtr pScreenPriv; + VNCSCREENPTR(pScreen); + + SCREEN_PROLOGUE (pScreen, GetImage); + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + + if (pDrawable->type == DRAWABLE_WINDOW && + pVNC->cursorIsDrawn && + ORG_OVERLAP(&pScreenPriv->saved,pDrawable->x,pDrawable->y, sx, sy, w, h)) + { + rfbSpriteRemoveCursor (pScreen); + } + + (*pScreen->GetImage) (pDrawable, sx, sy, w, h, + format, planemask, pdstLine); + + SCREEN_EPILOGUE (pScreen, GetImage, rfbSpriteGetImage); +} + +static void +rfbSpriteGetSpans (pDrawable, wMax, ppt, pwidth, nspans, pdstStart) + DrawablePtr pDrawable; + int wMax; + DDXPointPtr ppt; + int *pwidth; + int nspans; + char *pdstStart; +{ + ScreenPtr pScreen = pDrawable->pScreen; + rfbSpriteScreenPtr pScreenPriv; + VNCSCREENPTR(pScreen); + + SCREEN_PROLOGUE (pScreen, GetSpans); + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + + if (pDrawable->type == DRAWABLE_WINDOW && pVNC->cursorIsDrawn) + { + register DDXPointPtr pts; + register int *widths; + register int nPts; + register int xorg, + yorg; + + xorg = pDrawable->x; + yorg = pDrawable->y; + + for (pts = ppt, widths = pwidth, nPts = nspans; + nPts--; + pts++, widths++) + { + if (SPN_OVERLAP(&pScreenPriv->saved,pts->y+yorg, + pts->x+xorg,*widths)) + { + rfbSpriteRemoveCursor (pScreen); + break; + } + } + } + + (*pScreen->GetSpans) (pDrawable, wMax, ppt, pwidth, nspans, pdstStart); + + SCREEN_EPILOGUE (pScreen, GetSpans, rfbSpriteGetSpans); +} + +static void +rfbSpriteSourceValidate (pDrawable, x, y, width, height) + DrawablePtr pDrawable; + int x, y, width, height; +{ + ScreenPtr pScreen = pDrawable->pScreen; + rfbSpriteScreenPtr pScreenPriv; + VNCSCREENPTR(pScreen); + + SCREEN_PROLOGUE (pScreen, SourceValidate); + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + + if (pDrawable->type == DRAWABLE_WINDOW && pVNC->cursorIsDrawn && + ORG_OVERLAP(&pScreenPriv->saved, pDrawable->x, pDrawable->y, + x, y, width, height)) + { + rfbSpriteRemoveCursor (pScreen); + } + + if (pScreen->SourceValidate) + (*pScreen->SourceValidate) (pDrawable, x, y, width, height); + + SCREEN_EPILOGUE (pScreen, SourceValidate, rfbSpriteSourceValidate); +} + +static Bool +rfbSpriteCreateGC (pGC) + GCPtr pGC; +{ + ScreenPtr pScreen = pGC->pScreen; + Bool ret; + rfbSpriteGCPtr pPriv; + + SCREEN_PROLOGUE (pScreen, CreateGC); + + pPriv = (rfbSpriteGCPtr)dixLookupPrivate(&(pGC)->devPrivates, rfbSpriteGCKey); + + ret = (*pScreen->CreateGC) (pGC); + + pPriv->wrapOps = NULL; + pPriv->wrapFuncs = pGC->funcs; + pGC->funcs = &rfbSpriteGCFuncs; + + SCREEN_EPILOGUE (pScreen, CreateGC, rfbSpriteCreateGC); + + return ret; +} + +static void +rfbSpriteBlockHandler (i, blockData, pTimeout, pReadmask) + int i; + pointer blockData; + pointer pTimeout; + pointer pReadmask; +{ + ScreenPtr pScreen = screenInfo.screens[i]; + rfbSpriteScreenPtr pPriv; + VNCSCREENPTR(pScreen); + + pPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + + SCREEN_PROLOGUE(pScreen, BlockHandler); + + (*pScreen->BlockHandler) (i, blockData, pTimeout, pReadmask); + + SCREEN_EPILOGUE(pScreen, BlockHandler, rfbSpriteBlockHandler); + + if (!pVNC->cursorIsDrawn && pPriv->shouldBeUp) + rfbSpriteRestoreCursor (pScreen); +} + +static void +rfbSpriteInstallColormap (pMap) + ColormapPtr pMap; +{ + ScreenPtr pScreen = pMap->pScreen; + rfbSpriteScreenPtr pPriv; + VNCSCREENPTR(pScreen); + + pPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + + SCREEN_PROLOGUE(pScreen, InstallColormap); + + (*pScreen->InstallColormap) (pMap); + + SCREEN_EPILOGUE(pScreen, InstallColormap, rfbSpriteInstallColormap); + + pPriv->pInstalledMap = pMap; + if (pPriv->pColormap != pMap) + { + pPriv->checkPixels = TRUE; + if (pVNC->cursorIsDrawn) + rfbSpriteRemoveCursor (pScreen); + } +} + +static void +rfbSpriteStoreColors (pMap, ndef, pdef) + ColormapPtr pMap; + int ndef; + xColorItem *pdef; +{ + ScreenPtr pScreen = pMap->pScreen; + rfbSpriteScreenPtr pPriv; + int i; + int updated; + VisualPtr pVisual; + VNCSCREENPTR(pScreen); + + pPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + + SCREEN_PROLOGUE(pScreen, StoreColors); + + (*pScreen->StoreColors) (pMap, ndef, pdef); + + SCREEN_EPILOGUE(pScreen, StoreColors, rfbSpriteStoreColors); + + if (pPriv->pColormap == pMap) + { + updated = 0; + pVisual = pMap->pVisual; + if (pVisual->class == DirectColor) + { + /* Direct color - match on any of the subfields */ + +#define MaskMatch(a,b,mask) ((a) & ((pVisual->mask) == (b)) & (pVisual->mask)) + +#define UpdateDAC(plane,dac,mask) {\ + if (MaskMatch (pPriv->colors[plane].pixel,pdef[i].pixel,mask)) {\ + pPriv->colors[plane].dac = pdef[i].dac; \ + updated = 1; \ + } \ +} + +#define CheckDirect(plane) \ + UpdateDAC(plane,red,redMask) \ + UpdateDAC(plane,green,greenMask) \ + UpdateDAC(plane,blue,blueMask) + + for (i = 0; i < ndef; i++) + { + CheckDirect (SOURCE_COLOR) + CheckDirect (MASK_COLOR) + } + } + else + { + /* PseudoColor/GrayScale - match on exact pixel */ + for (i = 0; i < ndef; i++) + { + if (pdef[i].pixel == pPriv->colors[SOURCE_COLOR].pixel) + { + pPriv->colors[SOURCE_COLOR] = pdef[i]; + if (++updated == 2) + break; + } + if (pdef[i].pixel == pPriv->colors[MASK_COLOR].pixel) + { + pPriv->colors[MASK_COLOR] = pdef[i]; + if (++updated == 2) + break; + } + } + } + if (updated) + { + pPriv->checkPixels = TRUE; + if (pVNC->cursorIsDrawn) + rfbSpriteRemoveCursor (pScreen); + } + } +} + +static void +rfbSpriteFindColors (ScreenPtr pScreen) +{ + rfbSpriteScreenPtr pScreenPriv = (rfbSpriteScreenPtr) + dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + CursorPtr pCursor; + xColorItem *sourceColor, *maskColor; + + pCursor = pScreenPriv->pCursor; + sourceColor = &pScreenPriv->colors[SOURCE_COLOR]; + maskColor = &pScreenPriv->colors[MASK_COLOR]; + if (pScreenPriv->pColormap != pScreenPriv->pInstalledMap || + !(pCursor->foreRed == sourceColor->red && + pCursor->foreGreen == sourceColor->green && + pCursor->foreBlue == sourceColor->blue && + pCursor->backRed == maskColor->red && + pCursor->backGreen == maskColor->green && + pCursor->backBlue == maskColor->blue)) + { + pScreenPriv->pColormap = pScreenPriv->pInstalledMap; + sourceColor->red = pCursor->foreRed; + sourceColor->green = pCursor->foreGreen; + sourceColor->blue = pCursor->foreBlue; + FakeAllocColor (pScreenPriv->pColormap, sourceColor); + maskColor->red = pCursor->backRed; + maskColor->green = pCursor->backGreen; + maskColor->blue = pCursor->backBlue; + FakeAllocColor (pScreenPriv->pColormap, maskColor); + /* "free" the pixels right away, don't let this confuse you */ + FakeFreeColor(pScreenPriv->pColormap, sourceColor->pixel); + FakeFreeColor(pScreenPriv->pColormap, maskColor->pixel); + } + pScreenPriv->checkPixels = FALSE; +} + +/* + * BackingStore wrappers + */ + +static void +rfbSpriteSaveDoomedAreas (pWin, pObscured, dx, dy) + WindowPtr pWin; + RegionPtr pObscured; + int dx, dy; +{ + ScreenPtr pScreen = pWin->drawable.pScreen; + rfbSpriteScreenPtr pScreenPriv; + BoxRec cursorBox; + VNCSCREENPTR(pScreen); + + SCREEN_PROLOGUE (pScreen, SaveDoomedAreas); + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + if (pVNC->cursorIsDrawn) + { + cursorBox = pScreenPriv->saved; + + if (dx || dy) + { + cursorBox.x1 += dx; + cursorBox.y1 += dy; + cursorBox.x2 += dx; + cursorBox.y2 += dy; + } + if (RECT_IN_REGION( pScreen, pObscured, &cursorBox) != rgnOUT) + rfbSpriteRemoveCursor (pScreen); + } + + (*pScreen->SaveDoomedAreas) (pWin, pObscured, dx, dy); + + SCREEN_EPILOGUE (pScreen, SaveDoomedAreas, rfbSpriteSaveDoomedAreas); +} + +static RegionPtr +rfbSpriteRestoreAreas (pWin, prgnExposed) + WindowPtr pWin; + RegionPtr prgnExposed; +{ + ScreenPtr pScreen = pWin->drawable.pScreen; + rfbSpriteScreenPtr pScreenPriv; + RegionPtr result; + VNCSCREENPTR(pScreen); + + SCREEN_PROLOGUE (pScreen, RestoreAreas); + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + if (pVNC->cursorIsDrawn) + { + if (RECT_IN_REGION( pScreen, prgnExposed, &pScreenPriv->saved) != rgnOUT) + rfbSpriteRemoveCursor (pScreen); + } + + result = (*pScreen->RestoreAreas) (pWin, prgnExposed); + + SCREEN_EPILOGUE (pScreen, RestoreAreas, rfbSpriteRestoreAreas); + + return result; +} + +/* + * Window wrappers + */ + +static void +rfbSpritePaintWindowBackground (pWin, pRegion, what) + WindowPtr pWin; + RegionPtr pRegion; + int what; +{ + ScreenPtr pScreen = pWin->drawable.pScreen; + rfbSpriteScreenPtr pScreenPriv; + VNCSCREENPTR(pScreen); + + SCREEN_PROLOGUE (pScreen, PaintWindowBackground); + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + if (pVNC->cursorIsDrawn) + { + /* + * If the cursor is on the same screen as the window, check the + * region to paint for the cursor and remove it as necessary + */ + if (RECT_IN_REGION( pScreen, pRegion, &pScreenPriv->saved) != rgnOUT) + rfbSpriteRemoveCursor (pScreen); + } + + (*pScreen->PaintWindowBackground) (pWin, pRegion, what); + + SCREEN_EPILOGUE (pScreen, PaintWindowBackground, rfbSpritePaintWindowBackground); +} + +static void +rfbSpritePaintWindowBorder (pWin, pRegion, what) + WindowPtr pWin; + RegionPtr pRegion; + int what; +{ + ScreenPtr pScreen = pWin->drawable.pScreen; + rfbSpriteScreenPtr pScreenPriv; + VNCSCREENPTR(pScreen); + + SCREEN_PROLOGUE (pScreen, PaintWindowBorder); + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + if (pVNC->cursorIsDrawn) + { + /* + * If the cursor is on the same screen as the window, check the + * region to paint for the cursor and remove it as necessary + */ + if (RECT_IN_REGION( pScreen, pRegion, &pScreenPriv->saved) != rgnOUT) + rfbSpriteRemoveCursor (pScreen); + } + + (*pScreen->PaintWindowBorder) (pWin, pRegion, what); + + SCREEN_EPILOGUE (pScreen, PaintWindowBorder, rfbSpritePaintWindowBorder); +} + +static void +rfbSpriteCopyWindow (pWin, ptOldOrg, pRegion) + WindowPtr pWin; + DDXPointRec ptOldOrg; + RegionPtr pRegion; +{ + ScreenPtr pScreen = pWin->drawable.pScreen; + rfbSpriteScreenPtr pScreenPriv; + BoxRec cursorBox; + int dx, dy; + VNCSCREENPTR(pScreen); + + SCREEN_PROLOGUE (pScreen, CopyWindow); + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + if (pVNC->cursorIsDrawn) + { + /* + * check both the source and the destination areas. The given + * region is source relative, so offset the cursor box by + * the delta position + */ + cursorBox = pScreenPriv->saved; + dx = pWin->drawable.x - ptOldOrg.x; + dy = pWin->drawable.y - ptOldOrg.y; + cursorBox.x1 -= dx; + cursorBox.x2 -= dx; + cursorBox.y1 -= dy; + cursorBox.y2 -= dy; + if (RECT_IN_REGION( pScreen, pRegion, &pScreenPriv->saved) != rgnOUT || + RECT_IN_REGION( pScreen, pRegion, &cursorBox) != rgnOUT) + rfbSpriteRemoveCursor (pScreen); + } + + (*pScreen->CopyWindow) (pWin, ptOldOrg, pRegion); + + SCREEN_EPILOGUE (pScreen, CopyWindow, rfbSpriteCopyWindow); +} + +static void +rfbSpriteClearToBackground (pWin, x, y, w, h, generateExposures) + WindowPtr pWin; + short x,y; + unsigned short w,h; + Bool generateExposures; +{ + ScreenPtr pScreen = pWin->drawable.pScreen; + rfbSpriteScreenPtr pScreenPriv; + int realw, realh; + VNCSCREENPTR(pScreen); + + SCREEN_PROLOGUE (pScreen, ClearToBackground); + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + if (GC_CHECK(pWin)) + { + if (!(realw = w)) + realw = (int) pWin->drawable.width - x; + if (!(realh = h)) + realh = (int) pWin->drawable.height - y; + if (ORG_OVERLAP(&pScreenPriv->saved, pWin->drawable.x, pWin->drawable.y, + x, y, realw, realh)) + { + rfbSpriteRemoveCursor (pScreen); + } + } + + (*pScreen->ClearToBackground) (pWin, x, y, w, h, generateExposures); + + SCREEN_EPILOGUE (pScreen, ClearToBackground, rfbSpriteClearToBackground); +} + +/* + * GC Func wrappers + */ + +static void +rfbSpriteValidateGC (pGC, changes, pDrawable) + GCPtr pGC; + unsigned long changes; + DrawablePtr pDrawable; +{ + GC_FUNC_PROLOGUE (pGC); + + (*pGC->funcs->ValidateGC) (pGC, changes, pDrawable); + + pGCPriv->wrapOps = NULL; + if (pDrawable->type == DRAWABLE_WINDOW && ((WindowPtr) pDrawable)->viewable) + { + WindowPtr pWin; + RegionPtr pRegion; + + pWin = (WindowPtr) pDrawable; + pRegion = &pWin->clipList; + if (pGC->subWindowMode == IncludeInferiors) + pRegion = &pWin->borderClip; + if (REGION_NOTEMPTY(pDrawable->pScreen, pRegion)) + pGCPriv->wrapOps = pGC->ops; + } + + GC_FUNC_EPILOGUE (pGC); +} + +static void +rfbSpriteChangeGC (pGC, mask) + GCPtr pGC; + unsigned long mask; +{ + GC_FUNC_PROLOGUE (pGC); + + (*pGC->funcs->ChangeGC) (pGC, mask); + + GC_FUNC_EPILOGUE (pGC); +} + +static void +rfbSpriteCopyGC (pGCSrc, mask, pGCDst) + GCPtr pGCSrc, pGCDst; + unsigned long mask; +{ + GC_FUNC_PROLOGUE (pGCDst); + + (*pGCDst->funcs->CopyGC) (pGCSrc, mask, pGCDst); + + GC_FUNC_EPILOGUE (pGCDst); +} + +static void +rfbSpriteDestroyGC (pGC) + GCPtr pGC; +{ + GC_FUNC_PROLOGUE (pGC); + + (*pGC->funcs->DestroyGC) (pGC); + + GC_FUNC_EPILOGUE (pGC); +} + +static void +rfbSpriteChangeClip (pGC, type, pvalue, nrects) + GCPtr pGC; + int type; + pointer pvalue; + int nrects; +{ + GC_FUNC_PROLOGUE (pGC); + + (*pGC->funcs->ChangeClip) (pGC, type, pvalue, nrects); + + GC_FUNC_EPILOGUE (pGC); +} + +static void +rfbSpriteCopyClip(pgcDst, pgcSrc) + GCPtr pgcDst, pgcSrc; +{ + GC_FUNC_PROLOGUE (pgcDst); + + (* pgcDst->funcs->CopyClip)(pgcDst, pgcSrc); + + GC_FUNC_EPILOGUE (pgcDst); +} + +static void +rfbSpriteDestroyClip(pGC) + GCPtr pGC; +{ + GC_FUNC_PROLOGUE (pGC); + + (* pGC->funcs->DestroyClip)(pGC); + + GC_FUNC_EPILOGUE (pGC); +} + +/* + * GC Op wrappers + */ + +static void +rfbSpriteFillSpans(pDrawable, pGC, nInit, pptInit, pwidthInit, fSorted) + DrawablePtr pDrawable; + GCPtr pGC; + int nInit; /* number of spans to fill */ + DDXPointPtr pptInit; /* pointer to list of start points */ + int *pwidthInit; /* pointer to list of n widths */ + int fSorted; +{ + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP(pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + register DDXPointPtr pts; + register int *widths; + register int nPts; + + for (pts = pptInit, widths = pwidthInit, nPts = nInit; + nPts--; + pts++, widths++) + { + if (SPN_OVERLAP(&pScreenPriv->saved,pts->y,pts->x,*widths)) + { + rfbSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->FillSpans) (pDrawable, pGC, nInit, pptInit, pwidthInit, fSorted); + + GC_OP_EPILOGUE (pGC); +} + +static void +rfbSpriteSetSpans(pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted) + DrawablePtr pDrawable; + GCPtr pGC; + char *psrc; + register DDXPointPtr ppt; + int *pwidth; + int nspans; + int fSorted; +{ + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP(pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + register DDXPointPtr pts; + register int *widths; + register int nPts; + + for (pts = ppt, widths = pwidth, nPts = nspans; + nPts--; + pts++, widths++) + { + if (SPN_OVERLAP(&pScreenPriv->saved,pts->y,pts->x,*widths)) + { + rfbSpriteRemoveCursor(pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->SetSpans) (pDrawable, pGC, psrc, ppt, pwidth, nspans, fSorted); + + GC_OP_EPILOGUE (pGC); +} + +static void +rfbSpritePutImage(pDrawable, pGC, depth, x, y, w, h, leftPad, format, pBits) + DrawablePtr pDrawable; + GCPtr pGC; + int depth; + int x; + int y; + int w; + int h; + int format; + char *pBits; +{ + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP(pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + if (ORG_OVERLAP(&pScreenPriv->saved,pDrawable->x,pDrawable->y, + x,y,w,h)) + { + rfbSpriteRemoveCursor (pDrawable->pScreen); + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PutImage) (pDrawable, pGC, depth, x, y, w, h, leftPad, format, pBits); + + GC_OP_EPILOGUE (pGC); +} + +static RegionPtr +rfbSpriteCopyArea (pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty) + DrawablePtr pSrc; + DrawablePtr pDst; + GCPtr pGC; + int srcx; + int srcy; + int w; + int h; + int dstx; + int dsty; +{ + RegionPtr rgn; + VNCSCREENPTR(pGC->pScreen); + + GC_SETUP(pDst, pGC); + + /* check destination/source overlap. */ + if (GC_CHECK((WindowPtr) pDst) && + (ORG_OVERLAP(&pScreenPriv->saved,pDst->x,pDst->y,dstx,dsty,w,h) || + ((pDst == pSrc) && + ORG_OVERLAP(&pScreenPriv->saved,pSrc->x,pSrc->y,srcx,srcy,w,h)))) + { + rfbSpriteRemoveCursor (pDst->pScreen); + } + + GC_OP_PROLOGUE (pGC); + + rgn = (*pGC->ops->CopyArea) (pSrc, pDst, pGC, srcx, srcy, w, h, + dstx, dsty); + + GC_OP_EPILOGUE (pGC); + + return rgn; +} + +static RegionPtr +rfbSpriteCopyPlane (pSrc, pDst, pGC, srcx, srcy, w, h, dstx, dsty, plane) + DrawablePtr pSrc; + DrawablePtr pDst; + register GCPtr pGC; + int srcx, + srcy; + int w, + h; + int dstx, + dsty; + unsigned long plane; +{ + RegionPtr rgn; + VNCSCREENPTR(pGC->pScreen); + + GC_SETUP(pDst, pGC); + + /* + * check destination/source for overlap. + */ + if (GC_CHECK((WindowPtr) pDst) && + (ORG_OVERLAP(&pScreenPriv->saved,pDst->x,pDst->y,dstx,dsty,w,h) || + ((pDst == pSrc) && + ORG_OVERLAP(&pScreenPriv->saved,pSrc->x,pSrc->y,srcx,srcy,w,h)))) + { + rfbSpriteRemoveCursor (pDst->pScreen); + } + + GC_OP_PROLOGUE (pGC); + + rgn = (*pGC->ops->CopyPlane) (pSrc, pDst, pGC, srcx, srcy, w, h, + dstx, dsty, plane); + + GC_OP_EPILOGUE (pGC); + + return rgn; +} + +static void +rfbSpritePolyPoint (pDrawable, pGC, mode, npt, pptInit) + DrawablePtr pDrawable; + GCPtr pGC; + int mode; /* Origin or Previous */ + int npt; + xPoint *pptInit; +{ + xPoint t; + int n; + BoxRec cursor; + register xPoint *pts; + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP (pDrawable, pGC); + + if (npt && GC_CHECK((WindowPtr) pDrawable)) + { + cursor.x1 = pScreenPriv->saved.x1 - pDrawable->x; + cursor.y1 = pScreenPriv->saved.y1 - pDrawable->y; + cursor.x2 = pScreenPriv->saved.x2 - pDrawable->x; + cursor.y2 = pScreenPriv->saved.y2 - pDrawable->y; + + if (mode == CoordModePrevious) + { + t.x = 0; + t.y = 0; + for (pts = pptInit, n = npt; n--; pts++) + { + t.x += pts->x; + t.y += pts->y; + if (cursor.x1 <= t.x && t.x <= cursor.x2 && + cursor.y1 <= t.y && t.y <= cursor.y2) + { + rfbSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + else + { + for (pts = pptInit, n = npt; n--; pts++) + { + if (cursor.x1 <= pts->x && pts->x <= cursor.x2 && + cursor.y1 <= pts->y && pts->y <= cursor.y2) + { + rfbSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PolyPoint) (pDrawable, pGC, mode, npt, pptInit); + + GC_OP_EPILOGUE (pGC); +} + +static void +rfbSpritePolylines (pDrawable, pGC, mode, npt, pptInit) + DrawablePtr pDrawable; + GCPtr pGC; + int mode; + int npt; + DDXPointPtr pptInit; +{ + BoxPtr cursor; + register DDXPointPtr pts; + int n; + int x, y, x1, y1, x2, y2; + int lw; + int extra; + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP (pDrawable, pGC); + + if (npt && GC_CHECK((WindowPtr) pDrawable)) + { + cursor = &pScreenPriv->saved; + lw = pGC->lineWidth; + x = pptInit->x + pDrawable->x; + y = pptInit->y + pDrawable->y; + + if (npt == 1) + { + extra = lw >> 1; + if (LINE_OVERLAP(cursor, x, y, x, y, extra)) + rfbSpriteRemoveCursor (pDrawable->pScreen); + } + else + { + extra = lw >> 1; + /* + * mitered joins can project quite a way from + * the line end; the 11 degree miter limit limits + * this extension to 10.43 * lw / 2, rounded up + * and converted to int yields 6 * lw + */ + if (pGC->joinStyle == JoinMiter) + extra = 6 * lw; + else if (pGC->capStyle == CapProjecting) + extra = lw; + for (pts = pptInit + 1, n = npt - 1; n--; pts++) + { + x1 = x; + y1 = y; + if (mode == CoordModeOrigin) + { + x2 = pDrawable->x + pts->x; + y2 = pDrawable->y + pts->y; + } + else + { + x2 = x + pts->x; + y2 = y + pts->y; + } + x = x2; + y = y2; + LINE_SORT(x1, y1, x2, y2); + if (LINE_OVERLAP(cursor, x1, y1, x2, y2, extra)) + { + rfbSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + } + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->Polylines) (pDrawable, pGC, mode, npt, pptInit); + + GC_OP_EPILOGUE (pGC); +} + +static void +rfbSpritePolySegment(pDrawable, pGC, nseg, pSegs) + DrawablePtr pDrawable; + GCPtr pGC; + int nseg; + xSegment *pSegs; +{ + int n; + register xSegment *segs; + BoxPtr cursor; + int x1, y1, x2, y2; + int extra; + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP(pDrawable, pGC); + + if (nseg && GC_CHECK((WindowPtr) pDrawable)) + { + cursor = &pScreenPriv->saved; + extra = pGC->lineWidth >> 1; + if (pGC->capStyle == CapProjecting) + extra = pGC->lineWidth; + for (segs = pSegs, n = nseg; n--; segs++) + { + x1 = segs->x1 + pDrawable->x; + y1 = segs->y1 + pDrawable->y; + x2 = segs->x2 + pDrawable->x; + y2 = segs->y2 + pDrawable->y; + LINE_SORT(x1, y1, x2, y2); + if (LINE_OVERLAP(cursor, x1, y1, x2, y2, extra)) + { + rfbSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PolySegment) (pDrawable, pGC, nseg, pSegs); + + GC_OP_EPILOGUE (pGC); +} + +static void +rfbSpritePolyRectangle(pDrawable, pGC, nrects, pRects) + DrawablePtr pDrawable; + GCPtr pGC; + int nrects; + xRectangle *pRects; +{ + register xRectangle *rects; + BoxPtr cursor; + int lw; + int n; + int x1, y1, x2, y2; + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP (pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + lw = pGC->lineWidth >> 1; + cursor = &pScreenPriv->saved; + for (rects = pRects, n = nrects; n--; rects++) + { + x1 = rects->x + pDrawable->x; + y1 = rects->y + pDrawable->y; + x2 = x1 + (int)rects->width; + y2 = y1 + (int)rects->height; + if (LINE_OVERLAP(cursor, x1, y1, x2, y1, lw) || + LINE_OVERLAP(cursor, x2, y1, x2, y2, lw) || + LINE_OVERLAP(cursor, x1, y2, x2, y2, lw) || + LINE_OVERLAP(cursor, x1, y1, x1, y2, lw)) + { + rfbSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PolyRectangle) (pDrawable, pGC, nrects, pRects); + + GC_OP_EPILOGUE (pGC); +} + +static void +rfbSpritePolyArc(pDrawable, pGC, narcs, parcs) + DrawablePtr pDrawable; + register GCPtr pGC; + int narcs; + xArc *parcs; +{ + BoxPtr cursor; + int lw; + int n; + register xArc *arcs; + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP (pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + lw = pGC->lineWidth >> 1; + cursor = &pScreenPriv->saved; + for (arcs = parcs, n = narcs; n--; arcs++) + { + if (ORG_OVERLAP (cursor, pDrawable->x, pDrawable->y, + arcs->x - lw, arcs->y - lw, + (int) arcs->width + pGC->lineWidth, + (int) arcs->height + pGC->lineWidth)) + { + rfbSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PolyArc) (pDrawable, pGC, narcs, parcs); + + GC_OP_EPILOGUE (pGC); +} + +static void +rfbSpriteFillPolygon(pDrawable, pGC, shape, mode, count, pPts) + register DrawablePtr pDrawable; + register GCPtr pGC; + int shape, mode; + int count; + DDXPointPtr pPts; +{ + int x, y, minx, miny, maxx, maxy; + register DDXPointPtr pts; + int n; + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP (pDrawable, pGC); + + if (count && GC_CHECK((WindowPtr) pDrawable)) + { + x = pDrawable->x; + y = pDrawable->y; + pts = pPts; + minx = maxx = pts->x; + miny = maxy = pts->y; + pts++; + n = count - 1; + + if (mode == CoordModeOrigin) + { + for (; n--; pts++) + { + if (pts->x < minx) + minx = pts->x; + else if (pts->x > maxx) + maxx = pts->x; + if (pts->y < miny) + miny = pts->y; + else if (pts->y > maxy) + maxy = pts->y; + } + minx += x; + miny += y; + maxx += x; + maxy += y; + } + else + { + x += minx; + y += miny; + minx = maxx = x; + miny = maxy = y; + for (; n--; pts++) + { + x += pts->x; + y += pts->y; + if (x < minx) + minx = x; + else if (x > maxx) + maxx = x; + if (y < miny) + miny = y; + else if (y > maxy) + maxy = y; + } + } + if (BOX_OVERLAP(&pScreenPriv->saved,minx,miny,maxx,maxy)) + rfbSpriteRemoveCursor (pDrawable->pScreen); + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->FillPolygon) (pDrawable, pGC, shape, mode, count, pPts); + + GC_OP_EPILOGUE (pGC); +} + +static void +rfbSpritePolyFillRect(pDrawable, pGC, nrectFill, prectInit) + DrawablePtr pDrawable; + GCPtr pGC; + int nrectFill; /* number of rectangles to fill */ + xRectangle *prectInit; /* Pointer to first rectangle to fill */ +{ + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP(pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + register int nRect; + register xRectangle *pRect; + register int xorg, yorg; + + xorg = pDrawable->x; + yorg = pDrawable->y; + + for (nRect = nrectFill, pRect = prectInit; nRect--; pRect++) { + if (ORGRECT_OVERLAP(&pScreenPriv->saved,xorg,yorg,pRect)){ + rfbSpriteRemoveCursor(pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PolyFillRect) (pDrawable, pGC, nrectFill, prectInit); + + GC_OP_EPILOGUE (pGC); +} + +static void +rfbSpritePolyFillArc(pDrawable, pGC, narcs, parcs) + DrawablePtr pDrawable; + GCPtr pGC; + int narcs; + xArc *parcs; +{ + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP(pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + { + register int n; + BoxPtr cursor; + register xArc *arcs; + + cursor = &pScreenPriv->saved; + + for (arcs = parcs, n = narcs; n--; arcs++) + { + if (ORG_OVERLAP(cursor, pDrawable->x, pDrawable->y, + arcs->x, arcs->y, + (int) arcs->width, (int) arcs->height)) + { + rfbSpriteRemoveCursor (pDrawable->pScreen); + break; + } + } + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PolyFillArc) (pDrawable, pGC, narcs, parcs); + + GC_OP_EPILOGUE (pGC); +} + +/* + * general Poly/Image text function. Extract glyph information, + * compute bounding box and remove cursor if it is overlapped. + */ + +static Bool +rfbSpriteTextOverlap (DrawablePtr pDraw, FontPtr font, int x, int y, unsigned int n, CharInfoPtr *charinfo, Bool imageblt, unsigned int w, BoxPtr cursorBox) +{ + ExtentInfoRec extents; + + x += pDraw->x; + y += pDraw->y; + + if (FONTMINBOUNDS(font,characterWidth) >= 0) + { + /* compute an approximate (but covering) bounding box */ + if (!imageblt || (charinfo[0]->metrics.leftSideBearing < 0)) + extents.overallLeft = charinfo[0]->metrics.leftSideBearing; + else + extents.overallLeft = 0; + if (w) + extents.overallRight = w - charinfo[n-1]->metrics.characterWidth; + else + extents.overallRight = FONTMAXBOUNDS(font,characterWidth) + * (n - 1); + if (imageblt && (charinfo[n-1]->metrics.characterWidth > + charinfo[n-1]->metrics.rightSideBearing)) + extents.overallRight += charinfo[n-1]->metrics.characterWidth; + else + extents.overallRight += charinfo[n-1]->metrics.rightSideBearing; + if (imageblt && FONTASCENT(font) > FONTMAXBOUNDS(font,ascent)) + extents.overallAscent = FONTASCENT(font); + else + extents.overallAscent = FONTMAXBOUNDS(font, ascent); + if (imageblt && FONTDESCENT(font) > FONTMAXBOUNDS(font,descent)) + extents.overallDescent = FONTDESCENT(font); + else + extents.overallDescent = FONTMAXBOUNDS(font,descent); + if (!BOX_OVERLAP(cursorBox, + x + extents.overallLeft, + y - extents.overallAscent, + x + extents.overallRight, + y + extents.overallDescent)) + return FALSE; + else if (imageblt && w) + return TRUE; + /* if it does overlap, fall through and compute exactly, because + * taking down the cursor is expensive enough to make this worth it + */ + } + QueryGlyphExtents(font, charinfo, n, &extents); + if (imageblt) + { + if (extents.overallWidth > extents.overallRight) + extents.overallRight = extents.overallWidth; + if (extents.overallWidth < extents.overallLeft) + extents.overallLeft = extents.overallWidth; + if (extents.overallLeft > 0) + extents.overallLeft = 0; + if (extents.fontAscent > extents.overallAscent) + extents.overallAscent = extents.fontAscent; + if (extents.fontDescent > extents.overallDescent) + extents.overallDescent = extents.fontDescent; + } + return (BOX_OVERLAP(cursorBox, + x + extents.overallLeft, + y - extents.overallAscent, + x + extents.overallRight, + y + extents.overallDescent)); +} + +/* + * values for textType: + */ +#define TT_POLY8 0 +#define TT_IMAGE8 1 +#define TT_POLY16 2 +#define TT_IMAGE16 3 + +static int +rfbSpriteText (DrawablePtr pDraw, GCPtr pGC, int x, int y, unsigned long count, char *chars, FontEncoding fontEncoding, Bool textType, BoxPtr cursorBox) +{ + CharInfoPtr *charinfo; + register CharInfoPtr *info; + unsigned long i; + unsigned int n; + int w; + void (*drawFunc)() = NULL; + + Bool imageblt; + + imageblt = (textType == TT_IMAGE8) || (textType == TT_IMAGE16); + + charinfo = (CharInfoPtr *) xalloc(count * sizeof(CharInfoPtr)); + if (!charinfo) + return x; + + GetGlyphs(pGC->font, count, (unsigned char *)chars, + fontEncoding, &i, charinfo); + n = (unsigned int)i; + w = 0; + if (!imageblt) + for (info = charinfo; i--; info++) + w += (*info)->metrics.characterWidth; + + if (n != 0) { + if (rfbSpriteTextOverlap(pDraw, pGC->font, x, y, n, charinfo, imageblt, w, cursorBox)) + rfbSpriteRemoveCursor(pDraw->pScreen); + +#ifdef AVOID_GLYPHBLT + /* + * On displays like Apollos, which do not optimize the GlyphBlt functions because they + * convert fonts to their internal form in RealizeFont and optimize text directly, we + * want to invoke the text functions here, not the GlyphBlt functions. + */ + switch (textType) + { + case TT_POLY8: + drawFunc = (void (*)())pGC->ops->PolyText8; + break; + case TT_IMAGE8: + drawFunc = pGC->ops->ImageText8; + break; + case TT_POLY16: + drawFunc = (void (*)())pGC->ops->PolyText16; + break; + case TT_IMAGE16: + drawFunc = pGC->ops->ImageText16; + break; + } + (*drawFunc) (pDraw, pGC, x, y, (int) count, chars); +#else /* don't AVOID_GLYPHBLT */ + /* + * On the other hand, if the device does use GlyphBlt ultimately to do text, we + * don't want to slow it down by invoking the text functions and having them call + * GetGlyphs all over again, so we go directly to the GlyphBlt functions here. + */ + drawFunc = imageblt ? pGC->ops->ImageGlyphBlt : pGC->ops->PolyGlyphBlt; + (*drawFunc) (pDraw, pGC, x, y, n, charinfo, FONTGLYPHS(pGC->font)); +#endif /* AVOID_GLYPHBLT */ + } + xfree(charinfo); + return x + w; +} + +static int +rfbSpritePolyText8(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, char *chars) +{ + int ret; + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP (pDrawable, pGC); + + GC_OP_PROLOGUE (pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + ret = rfbSpriteText (pDrawable, pGC, x, y, (unsigned long)count, chars, + Linear8Bit, TT_POLY8, &pScreenPriv->saved); + else + ret = (*pGC->ops->PolyText8) (pDrawable, pGC, x, y, count, chars); + + GC_OP_EPILOGUE (pGC); + return ret; +} + +static int +rfbSpritePolyText16(DrawablePtr pDrawable, GCPtr pGC, int x, int y, int count, unsigned short *chars) +{ + int ret; + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP(pDrawable, pGC); + + GC_OP_PROLOGUE (pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + ret = rfbSpriteText (pDrawable, pGC, x, y, (unsigned long)count, + (char *)chars, + FONTLASTROW(pGC->font) == 0 ? + Linear16Bit : TwoD16Bit, TT_POLY16, &pScreenPriv->saved); + else + ret = (*pGC->ops->PolyText16) (pDrawable, pGC, x, y, count, chars); + + GC_OP_EPILOGUE (pGC); + return ret; +} + +static void +rfbSpriteImageText8(pDrawable, pGC, x, y, count, chars) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + int count; + char *chars; +{ + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP(pDrawable, pGC); + + GC_OP_PROLOGUE (pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + (void) rfbSpriteText (pDrawable, pGC, x, y, (unsigned long)count, + chars, Linear8Bit, TT_IMAGE8, &pScreenPriv->saved); + else + (*pGC->ops->ImageText8) (pDrawable, pGC, x, y, count, chars); + + GC_OP_EPILOGUE (pGC); +} + +static void +rfbSpriteImageText16(pDrawable, pGC, x, y, count, chars) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + int count; + unsigned short *chars; +{ + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP(pDrawable, pGC); + + GC_OP_PROLOGUE (pGC); + + if (GC_CHECK((WindowPtr) pDrawable)) + (void) rfbSpriteText (pDrawable, pGC, x, y, (unsigned long)count, + (char *)chars, + FONTLASTROW(pGC->font) == 0 ? + Linear16Bit : TwoD16Bit, TT_IMAGE16, &pScreenPriv->saved); + else + (*pGC->ops->ImageText16) (pDrawable, pGC, x, y, count, chars); + + GC_OP_EPILOGUE (pGC); +} + +static void +rfbSpriteImageGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + unsigned int nglyph; + CharInfoPtr *ppci; /* array of character info */ + pointer pglyphBase; /* start of array of glyphs */ +{ + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP(pDrawable, pGC); + + GC_OP_PROLOGUE (pGC); + + if (GC_CHECK((WindowPtr) pDrawable) && + rfbSpriteTextOverlap (pDrawable, pGC->font, x, y, nglyph, ppci, TRUE, 0, &pScreenPriv->saved)) + { + rfbSpriteRemoveCursor(pDrawable->pScreen); + } + (*pGC->ops->ImageGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); + + GC_OP_EPILOGUE (pGC); +} + +static void +rfbSpritePolyGlyphBlt(pDrawable, pGC, x, y, nglyph, ppci, pglyphBase) + DrawablePtr pDrawable; + GCPtr pGC; + int x, y; + unsigned int nglyph; + CharInfoPtr *ppci; /* array of character info */ + pointer pglyphBase; /* start of array of glyphs */ +{ + VNCSCREENPTR(pDrawable->pScreen); + + GC_SETUP (pDrawable, pGC); + + GC_OP_PROLOGUE (pGC); + + if (GC_CHECK((WindowPtr) pDrawable) && + rfbSpriteTextOverlap (pDrawable, pGC->font, x, y, nglyph, ppci, FALSE, 0, &pScreenPriv->saved)) + { + rfbSpriteRemoveCursor(pDrawable->pScreen); + } + (*pGC->ops->PolyGlyphBlt) (pDrawable, pGC, x, y, nglyph, ppci, pglyphBase); + + GC_OP_EPILOGUE (pGC); +} + +static void +rfbSpritePushPixels(pGC, pBitMap, pDrawable, w, h, x, y) + GCPtr pGC; + PixmapPtr pBitMap; + DrawablePtr pDrawable; + int w, h, x, y; +{ + VNCSCREENPTR(pDrawable->pScreen); + GC_SETUP(pDrawable, pGC); + + if (GC_CHECK((WindowPtr) pDrawable) && + ORG_OVERLAP(&pScreenPriv->saved,pDrawable->x,pDrawable->y,x,y,w,h)) + { + rfbSpriteRemoveCursor (pDrawable->pScreen); + } + + GC_OP_PROLOGUE (pGC); + + (*pGC->ops->PushPixels) (pGC, pBitMap, pDrawable, w, h, x, y); + + GC_OP_EPILOGUE (pGC); +} + +#ifdef NEED_LINEHELPER +/* + * I don't expect this routine will ever be called, as the GC + * will have been unwrapped for the line drawing + */ + +static void +rfbSpriteLineHelper() +{ + FatalError("rfbSpriteLineHelper called\n"); +} +#endif + +/* + * miPointer interface routines + */ + +#define SPRITE_PAD 8 + +static Bool +rfbSpriteRealizeCursor (ScreenPtr pScreen, CursorPtr pCursor) +{ + rfbSpriteScreenPtr pScreenPriv; + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + if (pCursor == pScreenPriv->pCursor) + pScreenPriv->checkPixels = TRUE; + return (*pScreenPriv->funcs->RealizeCursor) (pScreen, pCursor); +} + +static Bool +rfbSpriteUnrealizeCursor (ScreenPtr pScreen, CursorPtr pCursor) +{ + rfbSpriteScreenPtr pScreenPriv; + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + return (*pScreenPriv->funcs->UnrealizeCursor) (pScreen, pCursor); +} + +static void +rfbSpriteSetCursor (ScreenPtr pScreen, CursorPtr pCursor, int x, int y) +{ + rfbSpriteScreenPtr pScreenPriv; + rfbClientPtr cl, nextCl; + VNCSCREENPTR(pScreen); + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + if (!pCursor) + { + pScreenPriv->shouldBeUp = FALSE; + if (pVNC->cursorIsDrawn) + rfbSpriteRemoveCursor (pScreen); + pScreenPriv->pCursor = 0; + return; + } + pScreenPriv->shouldBeUp = TRUE; + if (pScreenPriv->x == x && + pScreenPriv->y == y && + pScreenPriv->pCursor == pCursor && + !pScreenPriv->checkPixels) + { + return; + } + pScreenPriv->x = x; + pScreenPriv->y = y; + pScreenPriv->pCacheWin = NullWindow; + if (pScreenPriv->checkPixels || pScreenPriv->pCursor != pCursor) + { + pScreenPriv->pCursor = pCursor; + rfbSpriteFindColors (pScreen); + } + if (pVNC->cursorIsDrawn) { + int sx, sy; + /* + * check to see if the old saved region + * encloses the new sprite, in which case we use + * the flicker-free MoveCursor primitive. + */ + sx = pScreenPriv->x - (int)pCursor->bits->xhot; + sy = pScreenPriv->y - (int)pCursor->bits->yhot; + if (sx + (int) pCursor->bits->width >= pScreenPriv->saved.x1 && + sx < pScreenPriv->saved.x2 && + sy + (int) pCursor->bits->height >= pScreenPriv->saved.y1 && + sy < pScreenPriv->saved.y2 && + (int) pCursor->bits->width + (2 * SPRITE_PAD) == + pScreenPriv->saved.x2 - pScreenPriv->saved.x1 && + (int) pCursor->bits->height + (2 * SPRITE_PAD) == + pScreenPriv->saved.y2 - pScreenPriv->saved.y1 + ) + { + pVNC->cursorIsDrawn = FALSE; + if (!(sx >= pScreenPriv->saved.x1 && + sx + (int)pCursor->bits->width < pScreenPriv->saved.x2 && + sy >= pScreenPriv->saved.y1 && + sy + (int)pCursor->bits->height < pScreenPriv->saved.y2)) + { + int oldx1, oldy1, dx, dy; + + oldx1 = pScreenPriv->saved.x1; + oldy1 = pScreenPriv->saved.y1; + dx = oldx1 - (sx - SPRITE_PAD); + dy = oldy1 - (sy - SPRITE_PAD); + pScreenPriv->saved.x1 -= dx; + pScreenPriv->saved.y1 -= dy; + pScreenPriv->saved.x2 -= dx; + pScreenPriv->saved.y2 -= dy; + (void) (*pScreenPriv->funcs->ChangeSave) (pScreen, + pScreenPriv->saved.x1, + pScreenPriv->saved.y1, + pScreenPriv->saved.x2 - pScreenPriv->saved.x1, + pScreenPriv->saved.y2 - pScreenPriv->saved.y1, + dx, dy); + } + (void) (*pScreenPriv->funcs->MoveCursor) (pScreen, pCursor, + pScreenPriv->saved.x1, + pScreenPriv->saved.y1, + pScreenPriv->saved.x2 - pScreenPriv->saved.x1, + pScreenPriv->saved.y2 - pScreenPriv->saved.y1, + sx - pScreenPriv->saved.x1, + sy - pScreenPriv->saved.y1, + pScreenPriv->colors[SOURCE_COLOR].pixel, + pScreenPriv->colors[MASK_COLOR].pixel); + pVNC->cursorIsDrawn = TRUE; + } + else + { + rfbSpriteRemoveCursor (pScreen); + } + } +#if 0 + if (!pVNC->cursorIsDrawn && pScreenPriv->pCursor) + rfbSpriteRestoreCursor (pScreen); +#endif + if (pVNC->cursorIsDrawn) + rfbSpriteRemoveCursor (pScreen); + + for (cl = rfbClientHead; cl; cl = nextCl) { + nextCl = cl->next; + if (cl->enableCursorPosUpdates) { + if (x == cl->cursorX && y == cl->cursorY) { + cl->cursorWasMoved = FALSE; + continue; + } + cl->cursorWasMoved = TRUE; + } + if (REGION_NOTEMPTY(pScreen,&cl->requestedRegion)) { + /* cursorIsDrawn is guaranteed to be FALSE here, so we definitely + want to send a screen update to the client, even if that's only + putting up the cursor */ + rfbSendFramebufferUpdate(pScreen, cl); + } + } +} + +static void +rfbSpriteMoveCursor (ScreenPtr pScreen, int x, int y) +{ + rfbSpriteScreenPtr pScreenPriv; + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + rfbSpriteSetCursor (pScreen, pScreenPriv->pCursor, x, y); +} + +/* + * undraw/draw cursor + */ + +void +rfbSpriteRemoveCursor (pScreen) + ScreenPtr pScreen; +{ + rfbSpriteScreenPtr pScreenPriv; + VNCSCREENPTR(pScreen); + + if (!pVNC->cursorIsDrawn) + return; + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + + pVNC->dontSendFramebufferUpdate = TRUE; + pVNC->cursorIsDrawn = FALSE; + pScreenPriv->pCacheWin = NullWindow; + if (!(*pScreenPriv->funcs->RestoreUnderCursor) (pScreen, + pScreenPriv->saved.x1, + pScreenPriv->saved.y1, + pScreenPriv->saved.x2 - pScreenPriv->saved.x1, + pScreenPriv->saved.y2 - pScreenPriv->saved.y1)) + { + pVNC->cursorIsDrawn = TRUE; + } + pVNC->dontSendFramebufferUpdate = FALSE; +} + + +void +rfbSpriteRestoreCursor (pScreen) + ScreenPtr pScreen; +{ + rfbSpriteScreenPtr pScreenPriv; + int x, y; + CursorPtr pCursor; + VNCSCREENPTR(pScreen); + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + pCursor = pScreenPriv->pCursor; + + if (pVNC->cursorIsDrawn || !pCursor) + return; + + pVNC->dontSendFramebufferUpdate = TRUE; + + rfbSpriteComputeSaved (pScreen); + + x = pScreenPriv->x - (int)pCursor->bits->xhot; + y = pScreenPriv->y - (int)pCursor->bits->yhot; + if ((*pScreenPriv->funcs->SaveUnderCursor) (pScreen, + pScreenPriv->saved.x1, + pScreenPriv->saved.y1, + pScreenPriv->saved.x2 - pScreenPriv->saved.x1, + pScreenPriv->saved.y2 - pScreenPriv->saved.y1)) + { + if (pScreenPriv->checkPixels) + rfbSpriteFindColors (pScreen); + if ((*pScreenPriv->funcs->PutUpCursor) (pScreen, pCursor, x, y, + pScreenPriv->colors[SOURCE_COLOR].pixel, + pScreenPriv->colors[MASK_COLOR].pixel)) + pVNC->cursorIsDrawn = TRUE; + } + + pVNC->dontSendFramebufferUpdate = FALSE; +} + +/* + * compute the desired area of the screen to save + */ + +static void +rfbSpriteComputeSaved (pScreen) + ScreenPtr pScreen; +{ + rfbSpriteScreenPtr pScreenPriv; + int x, y, w, h; + int wpad, hpad; + CursorPtr pCursor; + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + pCursor = pScreenPriv->pCursor; + x = pScreenPriv->x - (int)pCursor->bits->xhot; + y = pScreenPriv->y - (int)pCursor->bits->yhot; + w = pCursor->bits->width; + h = pCursor->bits->height; + wpad = SPRITE_PAD; + hpad = SPRITE_PAD; + pScreenPriv->saved.x1 = x - wpad; + pScreenPriv->saved.y1 = y - hpad; + pScreenPriv->saved.x2 = pScreenPriv->saved.x1 + w + wpad * 2; + pScreenPriv->saved.y2 = pScreenPriv->saved.y1 + h + hpad * 2; +} + + +/* + * this function is called when the cursor shape is being changed + */ + +static Bool +rfbDisplayCursor(pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + rfbClientPtr cl; + rfbSpriteScreenPtr pPriv; + Bool status; + + for (cl = rfbClientHead; cl ; cl = cl->next) { + if (cl->enableCursorShapeUpdates) + cl->cursorWasChanged = TRUE; + } + + pPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + status = (*pPriv->DisplayCursor)(pScreen, pCursor); + + /* send new cursor shape to interested viewers */ + for (cl = rfbClientHead; cl ; cl = cl->next) { + if (cl->cursorWasChanged) { + rfbSendFramebufferUpdate(pScreen, cl); + } + } + + return status; +} + + +/* + * obtain current cursor pointer + */ + +CursorPtr +rfbSpriteGetCursorPtr (pScreen) + ScreenPtr pScreen; +{ + rfbSpriteScreenPtr pScreenPriv; + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + + return pScreenPriv->pCursor; +} + +/* + * obtain current cursor position + */ + +void +rfbSpriteGetCursorPos (pScreen, px, py) + ScreenPtr pScreen; + int *px, *py; +{ + rfbSpriteScreenPtr pScreenPriv; + + pScreenPriv = (rfbSpriteScreenPtr)dixLookupPrivate(&pScreen->devPrivates, + rfbSpriteScreenKey); + + *px = pScreenPriv->x; + *py = pScreenPriv->y; +} + diff --git a/hw/vnc/sprite.h b/hw/vnc/sprite.h new file mode 100644 index 0000000..ed34726 --- /dev/null +++ b/hw/vnc/sprite.h @@ -0,0 +1,141 @@ +/* + * sprite.h + * + * software-sprite/sprite drawing - based on misprite + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ + +/* + +Copyright (c) 1989 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. +*/ + +typedef struct { + Bool (*RealizeCursor)( + ScreenPtr /*pScreen*/, + CursorPtr /*pCursor*/ +); + Bool (*UnrealizeCursor)( + ScreenPtr /*pScreen*/, + CursorPtr /*pCursor*/ +); + Bool (*PutUpCursor)( + ScreenPtr /*pScreen*/, + CursorPtr /*pCursor*/, + int /*x*/, + int /*y*/, + unsigned long /*source*/, + unsigned long /*mask*/ +); + Bool (*SaveUnderCursor)( + ScreenPtr /*pScreen*/, + int /*x*/, + int /*y*/, + int /*w*/, + int /*h*/ +); + Bool (*RestoreUnderCursor)( + ScreenPtr /*pScreen*/, + int /*x*/, + int /*y*/, + int /*w*/, + int /*h*/ +); + Bool (*MoveCursor)( + ScreenPtr /*pScreen*/, + CursorPtr /*pCursor*/, + int /*x*/, + int /*y*/, + int /*w*/, + int /*h*/, + int /*dx*/, + int /*dy*/, + unsigned long /*source*/, + unsigned long /*mask*/ +); + Bool (*ChangeSave)( + ScreenPtr /*pScreen*/, + int /*x*/, + int /*y*/, + int /*w*/, + int /*h*/, + int /*dx*/, + int /*dy*/ +); + +} rfbSpriteCursorFuncRec, *rfbSpriteCursorFuncPtr; + +extern Bool rfbSpriteInitialize( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/, + rfbSpriteCursorFuncPtr /*cursorFuncs*/, + miPointerScreenFuncPtr /*screenFuncs*/ +#endif +); + +extern void rfbSpriteRestoreCursor( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/ +#endif +); + +extern void rfbSpriteRemoveCursor( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/ +#endif +); + +extern CursorPtr rfbSpriteGetCursorPtr( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/ +#endif +); + +extern void rfbSpriteGetCursorPos( +#if NeedFunctionPrototypes + ScreenPtr /*pScreen*/, + int * /*px*/, + int * /*py*/ +#endif +); diff --git a/hw/vnc/spritest.h b/hw/vnc/spritest.h new file mode 100644 index 0000000..8593ee0 --- /dev/null +++ b/hw/vnc/spritest.h @@ -0,0 +1,138 @@ +/* + * spritest.h + * + * sprite structures - based on misprite + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ + +/* + +Copyright (c) 1989 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. +*/ + +# include "sprite.h" + +#ifdef RENDER +# include "picturestr.h" +#endif + +/* + * per screen information + */ + +typedef struct { + CloseScreenProcPtr CloseScreen; + GetImageProcPtr GetImage; + GetSpansProcPtr GetSpans; + SourceValidateProcPtr SourceValidate; + CreateGCProcPtr CreateGC; + ScreenBlockHandlerProcPtr BlockHandler; + InstallColormapProcPtr InstallColormap; + StoreColorsProcPtr StoreColors; + PaintWindowBackgroundProcPtr PaintWindowBackground; + PaintWindowBorderProcPtr PaintWindowBorder; + CopyWindowProcPtr CopyWindow; + ClearToBackgroundProcPtr ClearToBackground; + SaveDoomedAreasProcPtr SaveDoomedAreas; + RestoreAreasProcPtr RestoreAreas; + DisplayCursorProcPtr DisplayCursor; +#ifdef RENDER + CompositeProcPtr Composite; + GlyphsProcPtr Glyphs; +#endif + + CursorPtr pCursor; + int x; + int y; + Bool shouldBeUp; + BoxRec saved; + WindowPtr pCacheWin; + Bool isInCacheWin; + Bool checkPixels; + xColorItem colors[2]; + ColormapPtr pInstalledMap; + ColormapPtr pColormap; + VisualPtr pVisual; + rfbSpriteCursorFuncPtr funcs; +} rfbSpriteScreenRec, *rfbSpriteScreenPtr; + +#define SOURCE_COLOR 0 +#define MASK_COLOR 1 + +typedef struct { + GCFuncs *wrapFuncs; + GCOps *wrapOps; +} rfbSpriteGCRec, *rfbSpriteGCPtr; + +/* + * Overlap BoxPtr and Box elements + */ +#define BOX_OVERLAP(pCbox,X1,Y1,X2,Y2) \ + (((pCbox)->x1 <= (X2)) && ((X1) <= (pCbox)->x2) && \ + ((pCbox)->y1 <= (Y2)) && ((Y1) <= (pCbox)->y2)) + +/* + * Overlap BoxPtr, origins, and rectangle + */ +#define ORG_OVERLAP(pCbox,xorg,yorg,x,y,w,h) \ + BOX_OVERLAP((pCbox),(x)+(xorg),(y)+(yorg),(x)+(xorg)+(w),(y)+(yorg)+(h)) + +/* + * Overlap BoxPtr, origins and RectPtr + */ +#define ORGRECT_OVERLAP(pCbox,xorg,yorg,pRect) \ + ORG_OVERLAP((pCbox),(xorg),(yorg),(pRect)->x,(pRect)->y, \ + (int)((pRect)->width), (int)((pRect)->height)) +/* + * Overlap BoxPtr and horizontal span + */ +#define SPN_OVERLAP(pCbox,y,x,w) BOX_OVERLAP((pCbox),(x),(y),(x)+(w),(y)) + +#define LINE_SORT(x1,y1,x2,y2) \ +{ int _t; \ + if (x1 > x2) { _t = x1; x1 = x2; x2 = _t; } \ + if (y1 > y2) { _t = y1; y1 = y2; y2 = _t; } } + +#define LINE_OVERLAP(pCbox,x1,y1,x2,y2,lw2) \ + BOX_OVERLAP((pCbox), (x1)-(lw2), (y1)-(lw2), (x2)+(lw2), (y2)+(lw2)) diff --git a/hw/vnc/stats.c b/hw/vnc/stats.c new file mode 100644 index 0000000..83366ec --- /dev/null +++ b/hw/vnc/stats.c @@ -0,0 +1,117 @@ +/* + * stats.c + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * Copyright (C) 2002 Constantin Kaplinsky. 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#include +#include +#include "rfb.h" + +static char* encNames[] = { + "raw", "copyRect", "RRE", "[encoding 3]", "CoRRE", "hextile", + "zlib", "tight", "[encoding 8]", "[encoding 9]" +}; + + +void +rfbResetStats(rfbClientPtr cl) +{ + int i; + for (i = 0; i < MAX_ENCODINGS; i++) { + cl->rfbBytesSent[i] = 0; + cl->rfbRectanglesSent[i] = 0; + } + cl->rfbLastRectMarkersSent = 0; + cl->rfbLastRectBytesSent = 0; + cl->rfbCursorShapeBytesSent = 0; + cl->rfbCursorShapeUpdatesSent = 0; + cl->rfbCursorPosBytesSent = 0; + cl->rfbCursorPosUpdatesSent = 0; + cl->rfbFramebufferUpdateMessagesSent = 0; + cl->rfbRawBytesEquivalent = 0; + cl->rfbKeyEventsRcvd = 0; + cl->rfbPointerEventsRcvd = 0; +} + +void +rfbPrintStats(rfbClientPtr cl) +{ + int i; + int totalRectanglesSent = 0; + int totalBytesSent = 0; + + rfbLog("Statistics:\n"); + + if ((cl->rfbKeyEventsRcvd != 0) || (cl->rfbPointerEventsRcvd != 0)) + rfbLog(" key events received %d, pointer events %d\n", + cl->rfbKeyEventsRcvd, cl->rfbPointerEventsRcvd); + + for (i = 0; i < MAX_ENCODINGS; i++) { + totalRectanglesSent += cl->rfbRectanglesSent[i]; + totalBytesSent += cl->rfbBytesSent[i]; + } + totalRectanglesSent += (cl->rfbCursorShapeUpdatesSent + + cl->rfbCursorPosUpdatesSent + + cl->rfbLastRectMarkersSent); + totalBytesSent += (cl->rfbCursorShapeBytesSent + + cl->rfbCursorPosBytesSent + + cl->rfbLastRectBytesSent); + + rfbLog(" framebuffer updates %d, rectangles %d, bytes %d\n", + cl->rfbFramebufferUpdateMessagesSent, totalRectanglesSent, + totalBytesSent); + + if (cl->rfbLastRectMarkersSent != 0) + rfbLog(" LastRect markers %d, bytes %d\n", + cl->rfbLastRectMarkersSent, cl->rfbLastRectBytesSent); + + if (cl->rfbCursorShapeUpdatesSent != 0) + rfbLog(" cursor shape updates %d, bytes %d\n", + cl->rfbCursorShapeUpdatesSent, cl->rfbCursorShapeBytesSent); + + if (cl->rfbCursorPosUpdatesSent != 0) + rfbLog(" cursor position updates %d, bytes %d\n", + cl->rfbCursorPosUpdatesSent, cl->rfbCursorPosBytesSent); + + for (i = 0; i < MAX_ENCODINGS; i++) { + if (cl->rfbRectanglesSent[i] != 0) + rfbLog(" %s rectangles %d, bytes %d\n", + encNames[i], cl->rfbRectanglesSent[i], cl->rfbBytesSent[i]); + } + + if ((totalBytesSent - cl->rfbBytesSent[rfbEncodingCopyRect]) != 0) { + rfbLog(" raw bytes equivalent %d, compression ratio %f\n", + cl->rfbRawBytesEquivalent, + (double)cl->rfbRawBytesEquivalent + / (double)(totalBytesSent - + cl->rfbBytesSent[rfbEncodingCopyRect] - + cl->rfbCursorShapeBytesSent - + cl->rfbCursorPosBytesSent - + cl->rfbLastRectBytesSent)); + } +} diff --git a/hw/vnc/symlink-vnc.sh b/hw/vnc/symlink-vnc.sh new file mode 100755 index 0000000..b4347a1 --- /dev/null +++ b/hw/vnc/symlink-vnc.sh @@ -0,0 +1,198 @@ +#!/bin/sh + +# +# A script that symlinks source files from vnc to modular +# +# Author: Soren Sandmann (sandmann@redhat.com) (original) +# adapted for vnc by Alan Hourihane + +# +# Things we would like to do +# +# - Check that all the relevant files exist +# - AUTHORS, autogen.sh, configure.ac, ... +# - Check that we have actually linked everything +# - if a file doesn't need to be linked, then it needs +# to be listed as "not-linked" +# - Compute diffs between all the files (shouldn't be necessary) +# - possibly check that files are listet in Makefile.am's +# - Clean target directory of irrelevant files +# + +check_destinations () { + # don't do anything - we are relying on the side + # effect of dst_dir + true +} + +check_exist() { + # Check whether $1 exists + + if [ ! -e $1 ] ; then + error "$1 not found" + fi +} + +delete_existing() { + # Delete $2 + + rm -f $2 +} + +link_files() { + # Link $1 to $2 + + if [ ! -e $2 ] ; then + ln -s $1 $2 + fi +} + +main() { + check_args $1 $2 + + run check_destinations "Creating destination directories" + run check_exist "Checking that the source files exist" + run delete_existing "Deleting existing files" + run link_files "Linking files" +} + +## actual symlinking + +symlink_vnc() { + src_dir . + dst_dir . + action d3des.c + action d3des.h + action auth.c + action cmap.c + action corre.c + action cursor.c + action cutpaste.c + action dispcur.c + action draw.c + action hextile.c + action httpd.c + action kbdptr.c + action keyboard.h + action loginauth.c + action rdp.c + action rfb.h + action rfbkeyb.c + action rfbmouse.c + action rfbproto.h + action rfbserver.c + action rre.c + action sockets.c + action sprite.c + action sprite.h + action spritest.h + action stats.c + action tableinitcmtemplate.c + action tableinittctemplate.c + action tabletranstemplate.c + action tight.c + action translate.c + action vncauth.c + action vncauth.h + action vncext.c + action xistubs.c + action zlib.c +} + +######### +# +# Helper functions +# +######### + +error() { + echo + echo \ \ \ error:\ \ \ $1 + exit 1 +} + +# printing out what's going on +run_module() { + # $1 module + # $2 explanation + echo -n $EXPLANATION for $1 module ...\ + symlink_$1 + echo DONE +} + +run() { + # $1 what to do + # $2 explanation + + ACTION=$1 EXPLANATION=$2 run_module vnc +} + +src_dir() { + REAL_SRC_DIR=$SRC_DIR/$1 + if [ ! -d $REAL_SRC_DIR ] ; then + error "Source directory $REAL_SRC_DIR does not exist" + fi +} + +dst_dir() { + REAL_DST_DIR=$DST_DIR/$1 + if [ ! -d $REAL_DST_DIR ] ; then + mkdir -p $REAL_DST_DIR + fi +} + +action() { + if [ -z $2 ] ; then + $ACTION $REAL_SRC_DIR/$1 $REAL_DST_DIR/$1 + else + $ACTION $REAL_SRC_DIR/$1 $REAL_DST_DIR/$2 + fi +} + +usage() { + echo symlink.sh src-dir dst-dir + echo src-dir: the xc directory of the monolithic source tree + echo dst-dir: the modular source tree containing proto, app, lib, ... +} + +# Check commandline args +check_args() { + if [ -z $1 ] ; then + echo Missing source dir + usage + exit 1 + fi + + if [ -z $2 ] ; then + echo Missing destination dir + usage + exit 1 + fi + + if [ ! -d $1 ] ; then + echo $1 is not a dir + usage + exit 1 + fi + + if [ ! -d $2 ] ; then + echo $2 is not a dir + usage + exit 1 + fi + + if [ $1 = $2 ] ; then + echo source and destination can\'t be the same + usage + exit 1 + fi + + D=`dirname "$relpath"` + B=`basename "$relpath"` + abspath="`cd \"$D\" 2>/dev/null && pwd || echo \"$D\"`/$B" + + SRC_DIR=`( cd $1 ; pwd )` + DST_DIR=`(cd $2 ; pwd )` +} + +main $1 $2 diff --git a/hw/vnc/tableinitcmtemplate.c b/hw/vnc/tableinitcmtemplate.c new file mode 100644 index 0000000..c65c3d2 --- /dev/null +++ b/hw/vnc/tableinitcmtemplate.c @@ -0,0 +1,93 @@ +/* + * tableinitcmtemplate.c - template for initialising lookup tables for + * translation from a colour map to true colour. + * + * This file shouldn't be compiled. It is included multiple times by + * translate.c, each time with a different definition of the macro OUT. + * For each value of OUT, this file defines a function which allocates an + * appropriately sized lookup table and initialises it. + * + * I know this code isn't nice to read because of all the macros, but + * efficiency is important here. + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#if !defined(OUT) +#error "This file shouldn't be compiled." +#error "It is included as part of translate.c" +#endif + +#define OUT_T CONCAT2E(CARD,OUT) +#define SwapOUT(x) CONCAT2E(Swap,OUT(x)) +#define rfbInitColourMapSingleTableOUT \ + CONCAT2E(rfbInitColourMapSingleTable,OUT) + +static void +rfbInitColourMapSingleTableOUT (ScreenPtr pScreen, char **table, rfbPixelFormat *in, + rfbPixelFormat *out) +{ + VNCSCREENPTR(pScreen); + int i, r, g, b; + OUT_T *t; + EntryPtr pent; + int nEntries = 1 << in->bitsPerPixel; + + if (*table) free(*table); + *table = (char *)malloc(nEntries * sizeof(OUT_T)); + t = (OUT_T *)*table; + +#if XFREE86VNC + pent = (EntryPtr)&miInstalledMaps[pScreen->myNum]->red[0]; +#else + pent = (EntryPtr)&pVNC->rfbInstalledColormap->red[0]; +#endif + + for (i = 0; i < nEntries; i++) { + if (pent->fShared) { + r = pent->co.shco.red->color; + g = pent->co.shco.green->color; + b = pent->co.shco.blue->color; + } else { + r = pent->co.local.red; + g = pent->co.local.green; + b = pent->co.local.blue; + } + t[i] = ((((r * out->redMax + 32767) / 65535) << out->redShift) | + (((g * out->greenMax + 32767) / 65535) << out->greenShift) | + (((b * out->blueMax + 32767) / 65535) << out->blueShift)); +#if (OUT != 8) + if (out->bigEndian != in->bigEndian) { + t[i] = SwapOUT(t[i]); + } +#endif + pent++; + } +} + +#undef OUT_T +#undef SwapOUT +#undef rfbInitColourMapSingleTableOUT diff --git a/hw/vnc/tableinittctemplate.c b/hw/vnc/tableinittctemplate.c new file mode 100644 index 0000000..1af8a06 --- /dev/null +++ b/hw/vnc/tableinittctemplate.c @@ -0,0 +1,146 @@ +/* + * tableinittctemplate.c - template for initialising lookup tables for + * truecolour to truecolour translation. + * + * This file shouldn't be compiled. It is included multiple times by + * translate.c, each time with a different definition of the macro OUT. + * For each value of OUT, this file defines two functions for initialising + * lookup tables. One is for truecolour translation using a single lookup + * table, the other is for truecolour translation using three separate + * lookup tables for the red, green and blue values. + * + * I know this code isn't nice to read because of all the macros, but + * efficiency is important here. + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#if !defined(OUT) +#error "This file shouldn't be compiled." +#error "It is included as part of translate.c" +#endif + +#define OUT_T CONCAT2E(CARD,OUT) +#define SwapOUT(x) CONCAT2E(Swap,OUT(x)) +#define rfbInitTrueColourSingleTableOUT \ + CONCAT2E(rfbInitTrueColourSingleTable,OUT) +#define rfbInitTrueColourRGBTablesOUT CONCAT2E(rfbInitTrueColourRGBTables,OUT) +#define rfbInitOneRGBTableOUT CONCAT2E(rfbInitOneRGBTable,OUT) + +static void +rfbInitOneRGBTableOUT (OUT_T *table, int inMax, int outMax, int outShift, + int swap); + + +/* + * rfbInitTrueColourSingleTable sets up a single lookup table for truecolour + * translation. + */ + +static void +rfbInitTrueColourSingleTableOUT (ScreenPtr pScreen, char **table, rfbPixelFormat *in, + rfbPixelFormat *out) +{ + int i; + int inRed, inGreen, inBlue, outRed, outGreen, outBlue; + OUT_T *t; + int nEntries = 1 << in->bitsPerPixel; + + if (*table) free(*table); + *table = (char *)malloc(nEntries * sizeof(OUT_T)); + t = (OUT_T *)*table; + + for (i = 0; i < nEntries; i++) { + inRed = (i >> in->redShift) & in->redMax; + inGreen = (i >> in->greenShift) & in->greenMax; + inBlue = (i >> in->blueShift) & in->blueMax; + + outRed = (inRed * out->redMax + in->redMax / 2) / in->redMax; + outGreen = (inGreen * out->greenMax + in->greenMax / 2) / in->greenMax; + outBlue = (inBlue * out->blueMax + in->blueMax / 2) / in->blueMax; + + t[i] = ((outRed << out->redShift) | + (outGreen << out->greenShift) | + (outBlue << out->blueShift)); +#if (OUT != 8) + if (out->bigEndian != in->bigEndian) { + t[i] = SwapOUT(t[i]); + } +#endif + } +} + + +/* + * rfbInitTrueColourRGBTables sets up three separate lookup tables for the + * red, green and blue values. + */ + +static void +rfbInitTrueColourRGBTablesOUT (ScreenPtr pScreen, char **table, rfbPixelFormat *in, + rfbPixelFormat *out) +{ + OUT_T *redTable; + OUT_T *greenTable; + OUT_T *blueTable; + + if (*table) free(*table); + *table = (char *)malloc((in->redMax + in->greenMax + in->blueMax + 3) + * sizeof(OUT_T)); + redTable = (OUT_T *)*table; + greenTable = redTable + in->redMax + 1; + blueTable = greenTable + in->greenMax + 1; + + rfbInitOneRGBTableOUT (redTable, in->redMax, out->redMax, + out->redShift, (out->bigEndian != in->bigEndian)); + rfbInitOneRGBTableOUT (greenTable, in->greenMax, out->greenMax, + out->greenShift, (out->bigEndian != in->bigEndian)); + rfbInitOneRGBTableOUT (blueTable, in->blueMax, out->blueMax, + out->blueShift, (out->bigEndian != in->bigEndian)); +} + +static void +rfbInitOneRGBTableOUT (OUT_T *table, int inMax, int outMax, int outShift, + int swap) +{ + int i; + int nEntries = inMax + 1; + + for (i = 0; i < nEntries; i++) { + table[i] = ((i * outMax + inMax / 2) / inMax) << outShift; +#if (OUT != 8) + if (swap) { + table[i] = SwapOUT(table[i]); + } +#endif + } +} + +#undef OUT_T +#undef SwapOUT +#undef rfbInitTrueColourSingleTableOUT +#undef rfbInitTrueColourRGBTablesOUT +#undef rfbInitOneRGBTableOUT diff --git a/hw/vnc/tabletranstemplate.c b/hw/vnc/tabletranstemplate.c new file mode 100644 index 0000000..d8baa44 --- /dev/null +++ b/hw/vnc/tabletranstemplate.c @@ -0,0 +1,135 @@ +/* + * tabletranstemplate.c - template for translation using lookup tables. + * + * This file shouldn't be compiled. It is included multiple times by + * translate.c, each time with different definitions of the macros IN and OUT. + * + * For each pair of values IN and OUT, this file defines two functions for + * translating a given rectangle of pixel data. One uses a single lookup + * table, and the other uses three separate lookup tables for the red, green + * and blue values. + * + * I know this code isn't nice to read because of all the macros, but + * efficiency is important here. + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#if !defined(IN) || !defined(OUT) +#error "This file shouldn't be compiled." +#error "It is included as part of translate.c" +#endif + +#define IN_T CONCAT2E(CARD,IN) +#define OUT_T CONCAT2E(CARD,OUT) +#define rfbTranslateWithSingleTableINtoOUT \ + CONCAT4E(rfbTranslateWithSingleTable,IN,to,OUT) +#define rfbTranslateWithRGBTablesINtoOUT \ + CONCAT4E(rfbTranslateWithRGBTables,IN,to,OUT) + +/* + * rfbTranslateWithSingleTableINtoOUT translates a rectangle of pixel data + * using a single lookup table. + */ + +static void +rfbTranslateWithSingleTableINtoOUT (ScreenPtr pScreen, char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + unsigned char *optr, + int bytesBetweenInputLines, + int width, int height, + int x, int y) +{ + IN_T *ip; + OUT_T *op = (OUT_T *)optr; + OUT_T *opLineEnd; + OUT_T *t = (OUT_T *)table; + DrawablePtr pDraw = (DrawablePtr)WindowTable[pScreen->myNum]; + int truewidth = PixmapBytePad(width, in->bitsPerPixel); + unsigned char *iptr = malloc(truewidth * height * in->bitsPerPixel / 8); + int ipextra = truewidth - width; + (*pScreen->GetImage)(pDraw, x, y, truewidth, height, ZPixmap, ~0, (char*)iptr); + ip = (IN_T *)iptr; + + while (height > 0) { + opLineEnd = op + width; + + while (op < opLineEnd) { + *(op++) = t[*(ip++)]; + } + + ip += ipextra; + height--; + } + free(iptr); +} + + +/* + * rfbTranslateWithRGBTablesINtoOUT translates a rectangle of pixel data + * using three separate lookup tables for the red, green and blue values. + */ + +static void +rfbTranslateWithRGBTablesINtoOUT (ScreenPtr pScreen, char *table, rfbPixelFormat *in, + rfbPixelFormat *out, + unsigned char *optr, + int bytesBetweenInputLines, + int width, int height, + int x, int y) +{ + IN_T *ip; + OUT_T *op = (OUT_T *)optr; + OUT_T *opLineEnd; + OUT_T *redTable = (OUT_T *)table; + OUT_T *greenTable = redTable + in->redMax + 1; + OUT_T *blueTable = greenTable + in->greenMax + 1; + DrawablePtr pDraw = (DrawablePtr)WindowTable[pScreen->myNum]; + int truewidth = PixmapBytePad(width, in->bitsPerPixel); + unsigned char *iptr = malloc(truewidth * height * in->bitsPerPixel / 8); + int ipextra = truewidth - width; + (*pScreen->GetImage)(pDraw, x, y, truewidth, height, ZPixmap, ~0, (char*)iptr); + ip = (IN_T *)iptr; + + while (height > 0) { + opLineEnd = op + width; + + while (op < opLineEnd) { + *(op++) = (redTable[(*ip >> in->redShift) & in->redMax] | + greenTable[(*ip >> in->greenShift) & in->greenMax] | + blueTable[(*ip >> in->blueShift) & in->blueMax]); + ip++; + } + ip += ipextra; + height--; + } + free(iptr); +} + +#undef IN_T +#undef OUT_T +#undef rfbTranslateWithSingleTableINtoOUT +#undef rfbTranslateWithRGBTablesINtoOUT diff --git a/hw/vnc/tight.c b/hw/vnc/tight.c new file mode 100644 index 0000000..bbe1744 --- /dev/null +++ b/hw/vnc/tight.c @@ -0,0 +1,1827 @@ +/* + * tight.c + * + * Routines to implement Tight Encoding + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * Copyright (C) 2000, 2001 Const Kaplinsky. 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#include +#include "rfb.h" +#include + + +/* Note: The following constant should not be changed. */ +#define TIGHT_MIN_TO_COMPRESS 12 + +/* The parameters below may be adjusted. */ +#define MIN_SPLIT_RECT_SIZE 4096 +#define MIN_SOLID_SUBRECT_SIZE 2048 +#define MAX_SPLIT_TILE_SIZE 16 + +/* May be set to TRUE with "-lazytight" Xvnc option. */ +Bool rfbTightDisableGradient = FALSE; + +/* This variable is set on every rfbSendRectEncodingTight() call. */ +static Bool usePixelFormat24; + + +/* Compression level stuff. The following array contains various + encoder parameters for each of 10 compression levels (0..9). + Last three parameters correspond to JPEG quality levels (0..9). */ + +typedef struct TIGHT_CONF_s { + int maxRectSize, maxRectWidth; + int monoMinRectSize, gradientMinRectSize; + int idxZlibLevel, monoZlibLevel, rawZlibLevel, gradientZlibLevel; + int gradientThreshold, gradientThreshold24; + int idxMaxColorsDivisor; + int jpegQuality, jpegThreshold, jpegThreshold24; +} TIGHT_CONF; + +static TIGHT_CONF tightConf[10] = { + { 512, 32, 6, 65536, 0, 0, 0, 0, 0, 0, 4, 5, 10000, 23000 }, + { 2048, 128, 6, 65536, 1, 1, 1, 0, 0, 0, 8, 10, 8000, 18000 }, + { 6144, 256, 8, 65536, 3, 3, 2, 0, 0, 0, 24, 15, 6500, 15000 }, + { 10240, 1024, 12, 65536, 5, 5, 3, 0, 0, 0, 32, 25, 5000, 12000 }, + { 16384, 2048, 12, 65536, 6, 6, 4, 0, 0, 0, 32, 37, 4000, 10000 }, + { 32768, 2048, 12, 4096, 7, 7, 5, 4, 150, 380, 32, 50, 3000, 8000 }, + { 65536, 2048, 16, 4096, 7, 7, 6, 4, 170, 420, 48, 60, 2000, 5000 }, + { 65536, 2048, 16, 4096, 8, 8, 7, 5, 180, 450, 64, 70, 1000, 2500 }, + { 65536, 2048, 32, 8192, 9, 9, 8, 6, 190, 475, 64, 75, 500, 1200 }, + { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 } +}; + +/* Stuff dealing with palettes. */ + +typedef struct COLOR_LIST_s { + struct COLOR_LIST_s *next; + int idx; + CARD32 rgb; +} COLOR_LIST; + +typedef struct PALETTE_ENTRY_s { + COLOR_LIST *listNode; + int numPixels; +} PALETTE_ENTRY; + +typedef struct PALETTE_s { + PALETTE_ENTRY entry[256]; + COLOR_LIST *hash[256]; + COLOR_LIST list[256]; +} PALETTE; + +static int paletteNumColors, paletteMaxColors; +static CARD32 monoBackground, monoForeground; +static PALETTE palette; + +/* Pointers to dynamically-allocated buffers. */ + +static int tightBeforeBufSize = 0; +static unsigned char *tightBeforeBuf = NULL; + +static int tightAfterBufSize = 0; +static unsigned char *tightAfterBuf = NULL; + +static int *prevRowBuf = NULL; + + +/* Prototypes for static functions. */ + +static void FindBestSolidArea (ScreenPtr pScreen, int x, int y, int w, int h, + CARD32 colorValue, int *w_ptr, int *h_ptr); +static void ExtendSolidArea (ScreenPtr pScreen, int x, int y, int w, int h, + CARD32 colorValue, + int *x_ptr, int *y_ptr, int *w_ptr, int *h_ptr); +static Bool CheckSolidTile (ScreenPtr pScreen, int x, int y, int w, int h, + CARD32 *colorPtr, Bool needSameColor); +static Bool CheckSolidTile8 (ScreenPtr pScreen, int x, int y, int w, int h, + CARD32 *colorPtr, Bool needSameColor); +static Bool CheckSolidTile16 (ScreenPtr pScreen, int x, int y, int w, int h, + CARD32 *colorPtr, Bool needSameColor); +static Bool CheckSolidTile32 (ScreenPtr pScreen, int x, int y, int w, int h, + CARD32 *colorPtr, Bool needSameColor); + +static Bool SendRectSimple (rfbClientPtr cl, int x, int y, int w, int h); +static Bool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h); +static Bool SendTightHeader (rfbClientPtr cl, int x, int y, int w, int h); + +static Bool SendSolidRect (rfbClientPtr cl); +static Bool SendMonoRect (rfbClientPtr cl, int w, int h); +static Bool SendIndexedRect (rfbClientPtr cl, int w, int h); +static Bool SendFullColorRect (rfbClientPtr cl, int w, int h); +static Bool SendGradientRect (rfbClientPtr cl, int w, int h); + +static Bool CompressData(rfbClientPtr cl, int streamId, int dataLen, + int zlibLevel, int zlibStrategy); +static Bool SendCompressedData(rfbClientPtr cl, int compressedLen); + +static void FillPalette8(int count); +static void FillPalette16(int count); +static void FillPalette32(int count); + +static void PaletteReset(void); +static int PaletteInsert(CARD32 rgb, int numPixels, int bpp); + +static void Pack24(ScreenPtr pScreen, unsigned char *buf, rfbPixelFormat *fmt, int count); + +static void EncodeIndexedRect16(CARD8 *buf, int count); +static void EncodeIndexedRect32(CARD8 *buf, int count); + +static void EncodeMonoRect8(CARD8 *buf, int w, int h); +static void EncodeMonoRect16(CARD8 *buf, int w, int h); +static void EncodeMonoRect32(CARD8 *buf, int w, int h); + +static void FilterGradient24(ScreenPtr pScreen, unsigned char *buf, rfbPixelFormat *fmt, int w, int h); +static void FilterGradient16(ScreenPtr pScreen, CARD16 *buf, rfbPixelFormat *fmt, int w, int h); +static void FilterGradient32(ScreenPtr pScreen, CARD32 *buf, rfbPixelFormat *fmt, int w, int h); + +static int DetectSmoothImage(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); +static unsigned long DetectSmoothImage24(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); +static unsigned long DetectSmoothImage16(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); +static unsigned long DetectSmoothImage32(rfbClientPtr cl, rfbPixelFormat *fmt, int w, int h); + +static Bool SendJpegRect(rfbClientPtr cl, const CARD8 *buffer, int w, int h, int quality); +static void PrepareRowForJpeg(ScreenPtr pScreen, CARD8 *dst, const CARD8 *buffer, int row, int width); +static void PrepareRowForJpeg24(ScreenPtr pScreen, CARD8 *dst, const CARD8 *buffer, int row, int width); +static void PrepareRowForJpeg16(ScreenPtr pScreen, CARD8 *dst, const CARD8 *buffer, int row, int width); +static void PrepareRowForJpeg32(ScreenPtr pScreen, CARD8 *dst, const CARD8 *buffer, int row, int width); + +static void JpegInitDestination(j_compress_ptr cinfo); +static boolean JpegEmptyOutputBuffer(j_compress_ptr cinfo); +static void JpegTermDestination(j_compress_ptr cinfo); +static void JpegSetDstManager(j_compress_ptr cinfo); + + +/* + * Tight encoding implementation. + */ + +int +rfbNumCodedRectsTight(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + int maxRectSize, maxRectWidth; + int subrectMaxWidth, subrectMaxHeight; + + /* No matter how many rectangles we will send if LastRect markers + are used to terminate rectangle stream. */ + if (cl->enableLastRectEncoding && w * h >= MIN_SPLIT_RECT_SIZE) + return 0; + + maxRectSize = tightConf[cl->tightCompressLevel].maxRectSize; + maxRectWidth = tightConf[cl->tightCompressLevel].maxRectWidth; + + if (w > maxRectWidth || w * h > maxRectSize) { + subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; + subrectMaxHeight = maxRectSize / subrectMaxWidth; + return (((w - 1) / maxRectWidth + 1) * + ((h - 1) / subrectMaxHeight + 1)); + } else { + return 1; + } +} + +Bool +rfbSendRectEncodingTight(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + VNCSCREENPTR(cl->pScreen); + int nMaxRows; + CARD32 colorValue; + int dx, dy, dw, dh; + int x_best, y_best, w_best, h_best; + + if ( cl->format.depth == 24 && cl->format.redMax == 0xFF && + cl->format.greenMax == 0xFF && cl->format.blueMax == 0xFF ) { + usePixelFormat24 = TRUE; + } else { + usePixelFormat24 = FALSE; + } + + if (!cl->enableLastRectEncoding || w * h < MIN_SPLIT_RECT_SIZE) + return SendRectSimple(cl, x, y, w, h); + + /* Make sure we can write at least one pixel into tightBeforeBuf. */ + + if (tightBeforeBufSize < 4) { + tightBeforeBufSize = 4; + if (tightBeforeBuf == NULL) + tightBeforeBuf = (unsigned char *)xalloc(tightBeforeBufSize); + else + tightBeforeBuf = (unsigned char *)xrealloc(tightBeforeBuf, + tightBeforeBufSize); + } + + /* Calculate maximum number of rows in one non-solid rectangle. */ + + { + int maxRectSize, maxRectWidth, nMaxWidth; + + maxRectSize = tightConf[cl->tightCompressLevel].maxRectSize; + maxRectWidth = tightConf[cl->tightCompressLevel].maxRectWidth; + nMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; + nMaxRows = maxRectSize / nMaxWidth; + } + + /* Try to find large solid-color areas and send them separately. */ + + for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) { + + /* If a rectangle becomes too large, send its upper part now. */ + + if (dy - y >= nMaxRows) { + if (!SendRectSimple(cl, x, y, w, nMaxRows)) + return 0; + y += nMaxRows; + h -= nMaxRows; + } + + dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? + MAX_SPLIT_TILE_SIZE : (y + h - dy); + + for (dx = x; dx < x + w; dx += MAX_SPLIT_TILE_SIZE) { + + dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w) ? + MAX_SPLIT_TILE_SIZE : (x + w - dx); + + if (CheckSolidTile(cl->pScreen, dx, dy, dw, dh, &colorValue, FALSE)) { + + /* Get dimensions of solid-color area. */ + + FindBestSolidArea(cl->pScreen, dx, dy, w - (dx - x), h - (dy - y), + colorValue, &w_best, &h_best); + + /* Make sure a solid rectangle is large enough + (or the whole rectangle is of the same color). */ + + if ( w_best * h_best != w * h && + w_best * h_best < MIN_SOLID_SUBRECT_SIZE ) + continue; + + /* Try to extend solid rectangle to maximum size. */ + + x_best = dx; y_best = dy; + ExtendSolidArea(cl->pScreen, x, y, w, h, colorValue, + &x_best, &y_best, &w_best, &h_best); + + /* Send rectangles at top and left to solid-color area. */ + + if ( y_best != y && + !SendRectSimple(cl, x, y, w, y_best-y) ) + return FALSE; + if ( x_best != x && + !rfbSendRectEncodingTight(cl, x, y_best, + x_best-x, h_best) ) + return FALSE; + + /* Send solid-color rectangle. */ + + if (!SendTightHeader(cl, x_best, y_best, w_best, h_best)) + return FALSE; + + (*cl->translateFn)(cl->pScreen, cl->translateLookupTable, + &pVNC->rfbServerFormat, + &cl->format, tightBeforeBuf, + pVNC->paddedWidthInBytes, 1, 1, + x_best, y_best); + + if (!SendSolidRect(cl)) + return FALSE; + + /* Send remaining rectangles (at right and bottom). */ + + if ( x_best + w_best != x + w && + !rfbSendRectEncodingTight(cl, x_best+w_best, y_best, + w-(x_best-x)-w_best, h_best) ) + return FALSE; + if ( y_best + h_best != y + h && + !rfbSendRectEncodingTight(cl, x, y_best+h_best, + w, h-(y_best-y)-h_best) ) + return FALSE; + + /* Return after all recursive calls are done. */ + + return TRUE; + } + + } + + } + + /* No suitable solid-color rectangles found. */ + + return SendRectSimple(cl, x, y, w, h); +} + +static void +FindBestSolidArea(pScreen, x, y, w, h, colorValue, w_ptr, h_ptr) + ScreenPtr pScreen; + int x, y, w, h; + CARD32 colorValue; + int *w_ptr, *h_ptr; +{ + int dx, dy, dw, dh; + int w_prev; + int w_best = 0, h_best = 0; + + w_prev = w; + + for (dy = y; dy < y + h; dy += MAX_SPLIT_TILE_SIZE) { + + dh = (dy + MAX_SPLIT_TILE_SIZE <= y + h) ? + MAX_SPLIT_TILE_SIZE : (y + h - dy); + dw = (w_prev > MAX_SPLIT_TILE_SIZE) ? + MAX_SPLIT_TILE_SIZE : w_prev; + + if (!CheckSolidTile(pScreen, x, dy, dw, dh, &colorValue, TRUE)) + break; + + for (dx = x + dw; dx < x + w_prev;) { + dw = (dx + MAX_SPLIT_TILE_SIZE <= x + w_prev) ? + MAX_SPLIT_TILE_SIZE : (x + w_prev - dx); + if (!CheckSolidTile(pScreen, dx, dy, dw, dh, &colorValue, TRUE)) + break; + dx += dw; + } + + w_prev = dx - x; + if (w_prev * (dy + dh - y) > w_best * h_best) { + w_best = w_prev; + h_best = dy + dh - y; + } + } + + *w_ptr = w_best; + *h_ptr = h_best; +} + +static void +ExtendSolidArea(pScreen, x, y, w, h, colorValue, x_ptr, y_ptr, w_ptr, h_ptr) + ScreenPtr pScreen; + int x, y, w, h; + CARD32 colorValue; + int *x_ptr, *y_ptr, *w_ptr, *h_ptr; +{ + int cx, cy; + + /* Try to extend the area upwards. */ + for ( cy = *y_ptr - 1; + cy >= y && CheckSolidTile(pScreen, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE); + cy-- ); + *h_ptr += *y_ptr - (cy + 1); + *y_ptr = cy + 1; + + /* ... downwards. */ + for ( cy = *y_ptr + *h_ptr; + cy < y + h && + CheckSolidTile(pScreen, *x_ptr, cy, *w_ptr, 1, &colorValue, TRUE); + cy++ ); + *h_ptr += cy - (*y_ptr + *h_ptr); + + /* ... to the left. */ + for ( cx = *x_ptr - 1; + cx >= x && CheckSolidTile(pScreen, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE); + cx-- ); + *w_ptr += *x_ptr - (cx + 1); + *x_ptr = cx + 1; + + /* ... to the right. */ + for ( cx = *x_ptr + *w_ptr; + cx < x + w && + CheckSolidTile(pScreen, cx, *y_ptr, 1, *h_ptr, &colorValue, TRUE); + cx++ ); + *w_ptr += cx - (*x_ptr + *w_ptr); +} + +/* + * Check if a rectangle is all of the same color. If needSameColor is + * set to non-zero, then also check that its color equals to the + * *colorPtr value. The result is 1 if the test is successfull, and in + * that case new color will be stored in *colorPtr. + */ + +static Bool +CheckSolidTile(pScreen, x, y, w, h, colorPtr, needSameColor) + ScreenPtr pScreen; + int x, y, w, h; + CARD32 *colorPtr; + Bool needSameColor; +{ + VNCSCREENPTR(pScreen); + switch(pVNC->rfbServerFormat.bitsPerPixel) { + case 32: + return CheckSolidTile32(pScreen, x, y, w, h, colorPtr, needSameColor); + case 16: + return CheckSolidTile16(pScreen, x, y, w, h, colorPtr, needSameColor); + default: + return CheckSolidTile8(pScreen, x, y, w, h, colorPtr, needSameColor); + } +} + +#define DEFINE_CHECK_SOLID_FUNCTION(bpp) \ + \ +static Bool \ +CheckSolidTile##bpp(pScreen, x, y, w, h, colorPtr, needSameColor) \ + ScreenPtr pScreen; \ + int x, y; \ + CARD32 *colorPtr; \ + Bool needSameColor; \ +{ \ + VNCSCREENPTR(pScreen); \ + CARD##bpp *fbptr; \ + CARD##bpp colorValue; \ + int dx, dy; \ + \ + fbptr = (CARD##bpp *) \ + &pVNC->pfbMemory[y * pVNC->paddedWidthInBytes + x * (bpp/8)]; \ + \ + colorValue = *fbptr; \ + if (needSameColor && (CARD32)colorValue != *colorPtr) \ + return FALSE; \ + \ + for (dy = 0; dy < h; dy++) { \ + for (dx = 0; dx < w; dx++) { \ + if (colorValue != fbptr[dx]) \ + return FALSE; \ + } \ + fbptr = (CARD##bpp *)((CARD8 *)fbptr + pVNC->paddedWidthInBytes); \ + } \ + \ + *colorPtr = (CARD32)colorValue; \ + return TRUE; \ +} + +DEFINE_CHECK_SOLID_FUNCTION(8) +DEFINE_CHECK_SOLID_FUNCTION(16) +DEFINE_CHECK_SOLID_FUNCTION(32) + +static Bool +SendRectSimple(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + int maxBeforeSize, maxAfterSize; + int maxRectSize, maxRectWidth; + int subrectMaxWidth, subrectMaxHeight; + int dx, dy; + int rw, rh; + + maxRectSize = tightConf[cl->tightCompressLevel].maxRectSize; + maxRectWidth = tightConf[cl->tightCompressLevel].maxRectWidth; + + maxBeforeSize = maxRectSize * (cl->format.bitsPerPixel / 8); + maxAfterSize = maxBeforeSize + (maxBeforeSize + 99) / 100 + 12; + + if (tightBeforeBufSize < maxBeforeSize) { + tightBeforeBufSize = maxBeforeSize; + if (tightBeforeBuf == NULL) + tightBeforeBuf = (unsigned char *)xalloc(tightBeforeBufSize); + else + tightBeforeBuf = (unsigned char *)xrealloc(tightBeforeBuf, + tightBeforeBufSize); + } + + if (tightAfterBufSize < maxAfterSize) { + tightAfterBufSize = maxAfterSize; + if (tightAfterBuf == NULL) + tightAfterBuf = (unsigned char *)xalloc(tightAfterBufSize); + else + tightAfterBuf = (unsigned char *)xrealloc(tightAfterBuf, + tightAfterBufSize); + } + + if (w > maxRectWidth || w * h > maxRectSize) { + subrectMaxWidth = (w > maxRectWidth) ? maxRectWidth : w; + subrectMaxHeight = maxRectSize / subrectMaxWidth; + + for (dy = 0; dy < h; dy += subrectMaxHeight) { + for (dx = 0; dx < w; dx += maxRectWidth) { + rw = (dx + maxRectWidth < w) ? maxRectWidth : w - dx; + rh = (dy + subrectMaxHeight < h) ? subrectMaxHeight : h - dy; + if (!SendSubrect(cl, x+dx, y+dy, rw, rh)) + return FALSE; + } + } + } else { + if (!SendSubrect(cl, x, y, w, h)) + return FALSE; + } + + return TRUE; +} + +static Bool +SendSubrect(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + VNCSCREENPTR(cl->pScreen); + Bool success = FALSE; + + /* Send pending data if there is more than 128 bytes. */ + if (pVNC->ublen > 128) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + if (!SendTightHeader(cl, x, y, w, h)) + return FALSE; + + (*cl->translateFn)(cl->pScreen, cl->translateLookupTable, &pVNC->rfbServerFormat, + &cl->format, tightBeforeBuf, + pVNC->paddedWidthInBytes, w, h, x, y); + + paletteMaxColors = w * h / tightConf[cl->tightCompressLevel].idxMaxColorsDivisor; + if ( paletteMaxColors < 2 && + w * h >= tightConf[cl->tightCompressLevel].monoMinRectSize ) { + paletteMaxColors = 2; + } + switch (cl->format.bitsPerPixel) { + case 8: + FillPalette8(w * h); + break; + case 16: + FillPalette16(w * h); + break; + default: + FillPalette32(w * h); + } + + switch (paletteNumColors) { + case 0: + /* Truecolor image */ + if (DetectSmoothImage(cl, &cl->format, w, h)) { + if (cl->tightQualityLevel != -1) { + success = SendJpegRect(cl, tightBeforeBuf, w, h, + tightConf[cl->tightQualityLevel].jpegQuality); + } else { + success = SendGradientRect(cl, w, h); + } + } else { + success = SendFullColorRect(cl, w, h); + } + break; + case 1: + /* Solid rectangle */ + success = SendSolidRect(cl); + break; + case 2: + /* Two-color rectangle */ + success = SendMonoRect(cl, w, h); + break; + default: + /* Up to 256 different colors */ + if ( paletteNumColors > 96 && + cl->tightQualityLevel != -1 && cl->tightQualityLevel <= 3 && + DetectSmoothImage(cl, &cl->format, w, h) ) { + success = SendJpegRect(cl, tightBeforeBuf, w, h, + tightConf[cl->tightQualityLevel].jpegQuality); + } else { + success = SendIndexedRect(cl, w, h); + } + } + return success; +} + +static Bool +SendTightHeader(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + VNCSCREENPTR(cl->pScreen); + rfbFramebufferUpdateRectHeader rect; + + if (pVNC->ublen + sz_rfbFramebufferUpdateRectHeader > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingTight); + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + pVNC->ublen += sz_rfbFramebufferUpdateRectHeader; + + cl->rfbRectanglesSent[rfbEncodingTight]++; + cl->rfbBytesSent[rfbEncodingTight] += sz_rfbFramebufferUpdateRectHeader; + + return TRUE; +} + +/* + * Subencoding implementations. + */ + +static Bool +SendSolidRect(cl) + rfbClientPtr cl; +{ + VNCSCREENPTR(cl->pScreen); + int len; + + if (usePixelFormat24) { + Pack24(cl->pScreen, tightBeforeBuf, &cl->format, 1); + len = 3; + } else + len = cl->format.bitsPerPixel / 8; + + if (pVNC->ublen + 1 + len > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + pVNC->updateBuf[pVNC->ublen++] = (char)(rfbTightFill << 4); + memcpy (&pVNC->updateBuf[pVNC->ublen], tightBeforeBuf, len); + pVNC->ublen += len; + + cl->rfbBytesSent[rfbEncodingTight] += len + 1; + + return TRUE; +} + +static Bool +SendMonoRect(cl, w, h) + rfbClientPtr cl; + int w, h; +{ + VNCSCREENPTR(cl->pScreen); + int streamId = 1; + int paletteLen, dataLen; + + if ( (pVNC->ublen + TIGHT_MIN_TO_COMPRESS + 6 + + 2 * cl->format.bitsPerPixel / 8) > UPDATE_BUF_SIZE ) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + /* Prepare tight encoding header. */ + dataLen = (w + 7) / 8; + dataLen *= h; + + pVNC->updateBuf[pVNC->ublen++] = (streamId | rfbTightExplicitFilter) << 4; + pVNC->updateBuf[pVNC->ublen++] = rfbTightFilterPalette; + pVNC->updateBuf[pVNC->ublen++] = 1; + + /* Prepare palette, convert image. */ + switch (cl->format.bitsPerPixel) { + + case 32: + EncodeMonoRect32((CARD8 *)tightBeforeBuf, w, h); + + ((CARD32 *)tightAfterBuf)[0] = monoBackground; + ((CARD32 *)tightAfterBuf)[1] = monoForeground; + if (usePixelFormat24) { + Pack24(cl->pScreen, tightAfterBuf, &cl->format, 2); + paletteLen = 6; + } else + paletteLen = 8; + + memcpy(&pVNC->updateBuf[pVNC->ublen], tightAfterBuf, paletteLen); + pVNC->ublen += paletteLen; + cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteLen; + break; + + case 16: + EncodeMonoRect16((CARD8 *)tightBeforeBuf, w, h); + + ((CARD16 *)tightAfterBuf)[0] = (CARD16)monoBackground; + ((CARD16 *)tightAfterBuf)[1] = (CARD16)monoForeground; + + memcpy(&pVNC->updateBuf[pVNC->ublen], tightAfterBuf, 4); + pVNC->ublen += 4; + cl->rfbBytesSent[rfbEncodingTight] += 7; + break; + + default: + EncodeMonoRect8((CARD8 *)tightBeforeBuf, w, h); + + pVNC->updateBuf[pVNC->ublen++] = (char)monoBackground; + pVNC->updateBuf[pVNC->ublen++] = (char)monoForeground; + cl->rfbBytesSent[rfbEncodingTight] += 5; + } + + return CompressData(cl, streamId, dataLen, + tightConf[cl->tightCompressLevel].monoZlibLevel, + Z_DEFAULT_STRATEGY); +} + +static Bool +SendIndexedRect(cl, w, h) + rfbClientPtr cl; + int w, h; +{ + VNCSCREENPTR(cl->pScreen); + int streamId = 2; + int i, entryLen; + + if ( (pVNC->ublen + TIGHT_MIN_TO_COMPRESS + 6 + + paletteNumColors * cl->format.bitsPerPixel / 8) > UPDATE_BUF_SIZE ) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + /* Prepare tight encoding header. */ + pVNC->updateBuf[pVNC->ublen++] = (streamId | rfbTightExplicitFilter) << 4; + pVNC->updateBuf[pVNC->ublen++] = rfbTightFilterPalette; + pVNC->updateBuf[pVNC->ublen++] = (char)(paletteNumColors - 1); + + /* Prepare palette, convert image. */ + switch (cl->format.bitsPerPixel) { + + case 32: + EncodeIndexedRect32((CARD8 *)tightBeforeBuf, w * h); + + for (i = 0; i < paletteNumColors; i++) { + ((CARD32 *)tightAfterBuf)[i] = + palette.entry[i].listNode->rgb; + } + if (usePixelFormat24) { + Pack24(cl->pScreen, tightAfterBuf, &cl->format, paletteNumColors); + entryLen = 3; + } else + entryLen = 4; + + memcpy(&pVNC->updateBuf[pVNC->ublen], tightAfterBuf, paletteNumColors * entryLen); + pVNC->ublen += paletteNumColors * entryLen; + cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * entryLen; + break; + + case 16: + EncodeIndexedRect16((CARD8 *)tightBeforeBuf, w * h); + + for (i = 0; i < paletteNumColors; i++) { + ((CARD16 *)tightAfterBuf)[i] = + (CARD16)palette.entry[i].listNode->rgb; + } + + memcpy(&pVNC->updateBuf[pVNC->ublen], tightAfterBuf, paletteNumColors * 2); + pVNC->ublen += paletteNumColors * 2; + cl->rfbBytesSent[rfbEncodingTight] += 3 + paletteNumColors * 2; + break; + + default: + return FALSE; /* Should never happen. */ + } + + return CompressData(cl, streamId, w * h, + tightConf[cl->tightCompressLevel].idxZlibLevel, + Z_DEFAULT_STRATEGY); +} + +static Bool +SendFullColorRect(cl, w, h) + rfbClientPtr cl; + int w, h; +{ + VNCSCREENPTR(cl->pScreen); + int streamId = 0; + int len; + + if (pVNC->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + pVNC->updateBuf[pVNC->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */ + cl->rfbBytesSent[rfbEncodingTight]++; + + if (usePixelFormat24) { + Pack24(cl->pScreen, tightBeforeBuf, &cl->format, w * h); + len = 3; + } else + len = cl->format.bitsPerPixel / 8; + + return CompressData(cl, streamId, w * h * len, + tightConf[cl->tightCompressLevel].rawZlibLevel, + Z_DEFAULT_STRATEGY); +} + +static Bool +SendGradientRect(cl, w, h) + rfbClientPtr cl; + int w, h; +{ + VNCSCREENPTR(cl->pScreen); + int streamId = 3; + int len; + + if (cl->format.bitsPerPixel == 8) + return SendFullColorRect(cl, w, h); + + if (pVNC->ublen + TIGHT_MIN_TO_COMPRESS + 2 > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + if (prevRowBuf == NULL) + prevRowBuf = (int *)xalloc(2048 * 3 * sizeof(int)); + + pVNC->updateBuf[pVNC->ublen++] = (streamId | rfbTightExplicitFilter) << 4; + pVNC->updateBuf[pVNC->ublen++] = rfbTightFilterGradient; + cl->rfbBytesSent[rfbEncodingTight] += 2; + + if (usePixelFormat24) { + FilterGradient24(cl->pScreen, tightBeforeBuf, &cl->format, w, h); + len = 3; + } else if (cl->format.bitsPerPixel == 32) { + FilterGradient32(cl->pScreen, (CARD32 *)tightBeforeBuf, &cl->format, w, h); + len = 4; + } else { + FilterGradient16(cl->pScreen, (CARD16 *)tightBeforeBuf, &cl->format, w, h); + len = 2; + } + + return CompressData(cl, streamId, w * h * len, + tightConf[cl->tightCompressLevel].gradientZlibLevel, + Z_FILTERED); +} + +/** + * Compress the data in the tightBeforeBuf buffer. + * \param dataLen - amount of data in tightBeforeBuf to compress, in bytes + */ +static Bool +CompressData(cl, streamId, dataLen, zlibLevel, zlibStrategy) + rfbClientPtr cl; + int streamId, dataLen, zlibLevel, zlibStrategy; +{ + VNCSCREENPTR(cl->pScreen); + z_streamp pz; + int err; + + if (dataLen < TIGHT_MIN_TO_COMPRESS) { + memcpy(&pVNC->updateBuf[pVNC->ublen], tightBeforeBuf, dataLen); + pVNC->ublen += dataLen; + cl->rfbBytesSent[rfbEncodingTight] += dataLen; + return TRUE; + } + + pz = &cl->zsStruct[streamId]; + + /* Initialize compression stream if needed. */ + if (!cl->zsActive[streamId]) { + pz->zalloc = Z_NULL; + pz->zfree = Z_NULL; + pz->opaque = Z_NULL; + + err = deflateInit2 (pz, zlibLevel, Z_DEFLATED, MAX_WBITS, + MAX_MEM_LEVEL, zlibStrategy); + if (err != Z_OK) + return FALSE; + + cl->zsActive[streamId] = TRUE; + cl->zsLevel[streamId] = zlibLevel; + } + + /* Prepare buffer pointers. */ + pz->next_in = (Bytef *)tightBeforeBuf; + pz->avail_in = dataLen; + pz->next_out = (Bytef *)tightAfterBuf; + pz->avail_out = tightAfterBufSize; + + /* Change compression parameters if needed. */ + if (zlibLevel != cl->zsLevel[streamId]) { + if (deflateParams (pz, zlibLevel, zlibStrategy) != Z_OK) { + return FALSE; + } + cl->zsLevel[streamId] = zlibLevel; + } + + /* Actual compression. */ + if ( deflate (pz, Z_SYNC_FLUSH) != Z_OK || + pz->avail_in != 0 || pz->avail_out == 0 ) { + return FALSE; + } + + return SendCompressedData(cl, tightAfterBufSize - pz->avail_out); +} + +static Bool SendCompressedData(cl, compressedLen) + rfbClientPtr cl; + int compressedLen; +{ + VNCSCREENPTR(cl->pScreen); + int i, portionLen; + + pVNC->updateBuf[pVNC->ublen++] = compressedLen & 0x7F; + cl->rfbBytesSent[rfbEncodingTight]++; + if (compressedLen > 0x7F) { + pVNC->updateBuf[pVNC->ublen-1] |= 0x80; + pVNC->updateBuf[pVNC->ublen++] = compressedLen >> 7 & 0x7F; + cl->rfbBytesSent[rfbEncodingTight]++; + if (compressedLen > 0x3FFF) { + pVNC->updateBuf[pVNC->ublen-1] |= 0x80; + pVNC->updateBuf[pVNC->ublen++] = compressedLen >> 14 & 0xFF; + cl->rfbBytesSent[rfbEncodingTight]++; + } + } + + portionLen = UPDATE_BUF_SIZE; + for (i = 0; i < compressedLen; i += portionLen) { + if (i + portionLen > compressedLen) { + portionLen = compressedLen - i; + } + if (pVNC->ublen + portionLen > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + memcpy(&pVNC->updateBuf[pVNC->ublen], &tightAfterBuf[i], portionLen); + pVNC->ublen += portionLen; + } + cl->rfbBytesSent[rfbEncodingTight] += compressedLen; + return TRUE; +} + +/* + * Code to determine how many different colors used in rectangle. + */ + +static void +FillPalette8(count) + int count; +{ + CARD8 *data = (CARD8 *)tightBeforeBuf; + CARD8 c0, c1; + int i, n0, n1; + + paletteNumColors = 0; + + c0 = data[0]; + for (i = 1; i < count && data[i] == c0; i++); + if (i == count) { + paletteNumColors = 1; + return; /* Solid rectangle */ + } + + if (paletteMaxColors < 2) + return; + + n0 = i; + c1 = data[i]; + n1 = 0; + for (i++; i < count; i++) { + if (data[i] == c0) { + n0++; + } else if (data[i] == c1) { + n1++; + } else + break; + } + if (i == count) { + if (n0 > n1) { + monoBackground = (CARD32)c0; + monoForeground = (CARD32)c1; + } else { + monoBackground = (CARD32)c1; + monoForeground = (CARD32)c0; + } + paletteNumColors = 2; /* Two colors */ + } +} + +#define DEFINE_FILL_PALETTE_FUNCTION(bpp) \ + \ +static void \ +FillPalette##bpp(count) \ + int count; \ +{ \ + CARD##bpp *data = (CARD##bpp *)tightBeforeBuf; \ + CARD##bpp c0, c1, ci = 0; \ + int i, n0, n1, ni; \ + \ + c0 = data[0]; \ + for (i = 1; i < count && data[i] == c0; i++); \ + if (i >= count) { \ + paletteNumColors = 1; /* Solid rectangle */ \ + return; \ + } \ + \ + if (paletteMaxColors < 2) { \ + paletteNumColors = 0; /* Full-color encoding preferred */ \ + return; \ + } \ + \ + n0 = i; \ + c1 = data[i]; \ + n1 = 0; \ + for (i++; i < count; i++) { \ + ci = data[i]; \ + if (ci == c0) { \ + n0++; \ + } else if (ci == c1) { \ + n1++; \ + } else \ + break; \ + } \ + if (i >= count) { \ + if (n0 > n1) { \ + monoBackground = (CARD32)c0; \ + monoForeground = (CARD32)c1; \ + } else { \ + monoBackground = (CARD32)c1; \ + monoForeground = (CARD32)c0; \ + } \ + paletteNumColors = 2; /* Two colors */ \ + return; \ + } \ + \ + PaletteReset(); \ + PaletteInsert (c0, (CARD32)n0, bpp); \ + PaletteInsert (c1, (CARD32)n1, bpp); \ + \ + ni = 1; \ + for (i++; i < count; i++) { \ + if (data[i] == ci) { \ + ni++; \ + } else { \ + if (!PaletteInsert (ci, (CARD32)ni, bpp)) \ + return; \ + ci = data[i]; \ + ni = 1; \ + } \ + } \ + PaletteInsert (ci, (CARD32)ni, bpp); \ +} + +DEFINE_FILL_PALETTE_FUNCTION(16) +DEFINE_FILL_PALETTE_FUNCTION(32) + + +/* + * Functions to operate with palette structures. + */ + +#define HASH_FUNC16(rgb) ((int)((((rgb) >> 8) + (rgb)) & 0xFF)) +#define HASH_FUNC32(rgb) ((int)((((rgb) >> 16) + ((rgb) >> 8)) & 0xFF)) + +static void +PaletteReset(void) +{ + paletteNumColors = 0; + memset(palette.hash, 0, 256 * sizeof(COLOR_LIST *)); +} + +static int +PaletteInsert(CARD32 rgb, int numPixels, int bpp) +{ + COLOR_LIST *pnode; + COLOR_LIST *prev_pnode = NULL; + int hash_key, idx, new_idx, count; + + hash_key = (bpp == 16) ? HASH_FUNC16(rgb) : HASH_FUNC32(rgb); + + pnode = palette.hash[hash_key]; + + while (pnode != NULL) { + if (pnode->rgb == rgb) { + /* Such palette entry already exists. */ + new_idx = idx = pnode->idx; + count = palette.entry[idx].numPixels + numPixels; + if (new_idx && palette.entry[new_idx-1].numPixels < count) { + do { + palette.entry[new_idx] = palette.entry[new_idx-1]; + palette.entry[new_idx].listNode->idx = new_idx; + new_idx--; + } + while (new_idx && palette.entry[new_idx-1].numPixels < count); + palette.entry[new_idx].listNode = pnode; + pnode->idx = new_idx; + } + palette.entry[new_idx].numPixels = count; + return paletteNumColors; + } + prev_pnode = pnode; + pnode = pnode->next; + } + + /* Check if palette is full. */ + if (paletteNumColors == 256 || paletteNumColors == paletteMaxColors) { + paletteNumColors = 0; + return 0; + } + + /* Move palette entries with lesser pixel counts. */ + for ( idx = paletteNumColors; + idx > 0 && palette.entry[idx-1].numPixels < numPixels; + idx-- ) { + palette.entry[idx] = palette.entry[idx-1]; + palette.entry[idx].listNode->idx = idx; + } + + /* Add new palette entry into the freed slot. */ + pnode = &palette.list[paletteNumColors]; + if (prev_pnode != NULL) { + prev_pnode->next = pnode; + } else { + palette.hash[hash_key] = pnode; + } + pnode->next = NULL; + pnode->idx = idx; + pnode->rgb = rgb; + palette.entry[idx].listNode = pnode; + palette.entry[idx].numPixels = numPixels; + + return (++paletteNumColors); +} + + +/* + * Converting 32-bit color samples into 24-bit colors. + * Should be called only when redMax, greenMax and blueMax are 255. + * Color components assumed to be byte-aligned. + */ + +static void Pack24(pScreen, buf, fmt, count) + ScreenPtr pScreen; + unsigned char *buf; + rfbPixelFormat *fmt; + int count; +{ + VNCSCREENPTR(pScreen); + CARD32 *buf32; + CARD32 pix; + int r_shift, g_shift, b_shift; + + buf32 = (CARD32 *)buf; + + if (!pVNC->rfbServerFormat.bigEndian == !fmt->bigEndian) { + r_shift = fmt->redShift; + g_shift = fmt->greenShift; + b_shift = fmt->blueShift; + } else { + r_shift = 24 - fmt->redShift; + g_shift = 24 - fmt->greenShift; + b_shift = 24 - fmt->blueShift; + } + + while (count--) { + pix = *buf32++; + *buf++ = (char)(pix >> r_shift); + *buf++ = (char)(pix >> g_shift); + *buf++ = (char)(pix >> b_shift); + } +} + + +/* + * Converting truecolor samples into palette indices. + */ + +#define DEFINE_IDX_ENCODE_FUNCTION(bpp) \ + \ +static void \ +EncodeIndexedRect##bpp(buf, count) \ + CARD8 *buf; \ + int count; \ +{ \ + COLOR_LIST *pnode; \ + CARD##bpp *src; \ + CARD##bpp rgb; \ + int rep = 0; \ + \ + src = (CARD##bpp *) buf; \ + \ + while (count--) { \ + rgb = *src++; \ + while (count && *src == rgb) { \ + rep++, src++, count--; \ + } \ + pnode = palette.hash[HASH_FUNC##bpp(rgb)]; \ + while (pnode != NULL) { \ + if ((CARD##bpp)pnode->rgb == rgb) { \ + *buf++ = (CARD8)pnode->idx; \ + while (rep) { \ + *buf++ = (CARD8)pnode->idx; \ + rep--; \ + } \ + break; \ + } \ + pnode = pnode->next; \ + } \ + } \ +} + +DEFINE_IDX_ENCODE_FUNCTION(16) +DEFINE_IDX_ENCODE_FUNCTION(32) + +#define DEFINE_MONO_ENCODE_FUNCTION(bpp) \ + \ +static void \ +EncodeMonoRect##bpp(buf, w, h) \ + CARD8 *buf; \ + int w, h; \ +{ \ + CARD##bpp *ptr; \ + CARD##bpp bg; \ + unsigned int value, mask; \ + int aligned_width; \ + int x, y, bg_bits; \ + \ + ptr = (CARD##bpp *) buf; \ + bg = (CARD##bpp) monoBackground; \ + aligned_width = w - w % 8; \ + \ + for (y = 0; y < h; y++) { \ + for (x = 0; x < aligned_width; x += 8) { \ + for (bg_bits = 0; bg_bits < 8; bg_bits++) { \ + if (*ptr++ != bg) \ + break; \ + } \ + if (bg_bits == 8) { \ + *buf++ = 0; \ + continue; \ + } \ + mask = 0x80 >> bg_bits; \ + value = mask; \ + for (bg_bits++; bg_bits < 8; bg_bits++) { \ + mask >>= 1; \ + if (*ptr++ != bg) { \ + value |= mask; \ + } \ + } \ + *buf++ = (CARD8)value; \ + } \ + \ + mask = 0x80; \ + value = 0; \ + if (x >= w) \ + continue; \ + \ + for (; x < w; x++) { \ + if (*ptr++ != bg) { \ + value |= mask; \ + } \ + mask >>= 1; \ + } \ + *buf++ = (CARD8)value; \ + } \ +} + +DEFINE_MONO_ENCODE_FUNCTION(8) +DEFINE_MONO_ENCODE_FUNCTION(16) +DEFINE_MONO_ENCODE_FUNCTION(32) + + +/* + * ``Gradient'' filter for 24-bit color samples. + * Should be called only when redMax, greenMax and blueMax are 255. + * Color components assumed to be byte-aligned. + */ + +static void +FilterGradient24(pScreen, buf, fmt, w, h) + ScreenPtr pScreen; + unsigned char *buf; + rfbPixelFormat *fmt; + int w, h; +{ + VNCSCREENPTR(pScreen); + CARD32 *buf32; + CARD32 pix32; + int *prevRowPtr; + int shiftBits[3]; + int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3]; + int prediction; + int x, y, c; + + buf32 = (CARD32 *)buf; + memset (prevRowBuf, 0, w * 3 * sizeof(int)); + + if (!pVNC->rfbServerFormat.bigEndian == !fmt->bigEndian) { + shiftBits[0] = fmt->redShift; + shiftBits[1] = fmt->greenShift; + shiftBits[2] = fmt->blueShift; + } else { + shiftBits[0] = 24 - fmt->redShift; + shiftBits[1] = 24 - fmt->greenShift; + shiftBits[2] = 24 - fmt->blueShift; + } + + for (y = 0; y < h; y++) { + for (c = 0; c < 3; c++) { + pixUpper[c] = 0; + pixHere[c] = 0; + } + prevRowPtr = prevRowBuf; + for (x = 0; x < w; x++) { + pix32 = *buf32++; + for (c = 0; c < 3; c++) { + pixUpperLeft[c] = pixUpper[c]; + pixLeft[c] = pixHere[c]; + pixUpper[c] = *prevRowPtr; + pixHere[c] = (int)(pix32 >> shiftBits[c] & 0xFF); + *prevRowPtr++ = pixHere[c]; + + prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c]; + if (prediction < 0) { + prediction = 0; + } else if (prediction > 0xFF) { + prediction = 0xFF; + } + *buf++ = (char)(pixHere[c] - prediction); + } + } + } +} + + +/* + * ``Gradient'' filter for other color depths. + */ + +#define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \ + \ +static void \ +FilterGradient##bpp(pScreen, buf, fmt, w, h) \ + ScreenPtr pScreen; \ + CARD##bpp *buf; \ + rfbPixelFormat *fmt; \ + int w, h; \ +{ \ + VNCSCREENPTR(pScreen); \ + CARD##bpp pix, diff; \ + Bool endianMismatch; \ + int *prevRowPtr; \ + int maxColor[3], shiftBits[3]; \ + int pixHere[3], pixUpper[3], pixLeft[3], pixUpperLeft[3]; \ + int prediction; \ + int x, y, c; \ + \ + memset (prevRowBuf, 0, w * 3 * sizeof(int)); \ + \ + endianMismatch = (!pVNC->rfbServerFormat.bigEndian != !fmt->bigEndian); \ + \ + maxColor[0] = fmt->redMax; \ + maxColor[1] = fmt->greenMax; \ + maxColor[2] = fmt->blueMax; \ + shiftBits[0] = fmt->redShift; \ + shiftBits[1] = fmt->greenShift; \ + shiftBits[2] = fmt->blueShift; \ + \ + for (y = 0; y < h; y++) { \ + for (c = 0; c < 3; c++) { \ + pixUpper[c] = 0; \ + pixHere[c] = 0; \ + } \ + prevRowPtr = prevRowBuf; \ + for (x = 0; x < w; x++) { \ + pix = *buf; \ + if (endianMismatch) { \ + pix = Swap##bpp(pix); \ + } \ + diff = 0; \ + for (c = 0; c < 3; c++) { \ + pixUpperLeft[c] = pixUpper[c]; \ + pixLeft[c] = pixHere[c]; \ + pixUpper[c] = *prevRowPtr; \ + pixHere[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \ + *prevRowPtr++ = pixHere[c]; \ + \ + prediction = pixLeft[c] + pixUpper[c] - pixUpperLeft[c]; \ + if (prediction < 0) { \ + prediction = 0; \ + } else if (prediction > maxColor[c]) { \ + prediction = maxColor[c]; \ + } \ + diff |= ((pixHere[c] - prediction) & maxColor[c]) \ + << shiftBits[c]; \ + } \ + if (endianMismatch) { \ + diff = Swap##bpp(diff); \ + } \ + *buf++ = diff; \ + } \ + } \ +} + +DEFINE_GRADIENT_FILTER_FUNCTION(16) +DEFINE_GRADIENT_FILTER_FUNCTION(32) + + +/* + * Code to guess if given rectangle is suitable for smooth image + * compression (by applying "gradient" filter or JPEG coder). + */ + +#define JPEG_MIN_RECT_SIZE 4096 + +#define DETECT_SUBROW_WIDTH 7 +#define DETECT_MIN_WIDTH 8 +#define DETECT_MIN_HEIGHT 8 + +static int +DetectSmoothImage (cl, fmt, w, h) + rfbClientPtr cl; + rfbPixelFormat *fmt; + int w, h; +{ + VNCSCREENPTR(cl->pScreen); + unsigned long avgError; + + if ( pVNC->rfbServerFormat.bitsPerPixel == 8 || fmt->bitsPerPixel == 8 || + w < DETECT_MIN_WIDTH || h < DETECT_MIN_HEIGHT ) { + return 0; + } + + if (cl->tightQualityLevel != -1) { + if (w * h < JPEG_MIN_RECT_SIZE) { + return 0; + } + } else { + if ( rfbTightDisableGradient || + w * h < tightConf[cl->tightCompressLevel].gradientMinRectSize ) { + return 0; + } + } + + if (fmt->bitsPerPixel == 32) { + if (usePixelFormat24) { + avgError = DetectSmoothImage24(cl, fmt, w, h); + if (cl->tightQualityLevel != -1) { + return (avgError < tightConf[cl->tightQualityLevel].jpegThreshold24); + } + return (avgError < tightConf[cl->tightCompressLevel].gradientThreshold24); + } else { + avgError = DetectSmoothImage32(cl, fmt, w, h); + } + } else { + avgError = DetectSmoothImage16(cl, fmt, w, h); + } + if (cl->tightQualityLevel != -1) { + return (avgError < tightConf[cl->tightQualityLevel].jpegThreshold); + } + return (avgError < tightConf[cl->tightCompressLevel].gradientThreshold); +} + +static unsigned long +DetectSmoothImage24 (cl, fmt, w, h) + rfbClientPtr cl; + rfbPixelFormat *fmt; + int w, h; +{ + int off; + int x, y, d, dx, c; + int diffStat[256]; + int pixelCount = 0; + int pix, left[3]; + unsigned long avgError; + + /* If client is big-endian, color samples begin from the second + byte (offset 1) of a 32-bit pixel value. */ + off = (fmt->bigEndian != 0); + + memset(diffStat, 0, 256*sizeof(int)); + + y = 0, x = 0; + while (y < h && x < w) { + for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) { + for (c = 0; c < 3; c++) { + left[c] = (int)tightBeforeBuf[((y+d)*w+x+d)*4+off+c] & 0xFF; + } + for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) { + for (c = 0; c < 3; c++) { + pix = (int)tightBeforeBuf[((y+d)*w+x+d+dx)*4+off+c] & 0xFF; + diffStat[abs(pix - left[c])]++; + left[c] = pix; + } + pixelCount++; + } + } + if (w > h) { + x += h; + y = 0; + } else { + x = 0; + y += w; + } + } + + if (diffStat[0] * 33 / pixelCount >= 95) + return 0; + + avgError = 0; + for (c = 1; c < 8; c++) { + avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); + if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2) + return 0; + } + for (; c < 256; c++) { + avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); + } + avgError /= (pixelCount * 3 - diffStat[0]); + + return avgError; +} + +#define DEFINE_DETECT_FUNCTION(bpp) \ + \ +static unsigned long \ +DetectSmoothImage##bpp (cl, fmt, w, h) \ + rfbClientPtr cl; \ + rfbPixelFormat *fmt; \ + int w, h; \ +{ \ + VNCSCREENPTR(cl->pScreen); \ + Bool endianMismatch; \ + CARD##bpp pix; \ + int maxColor[3], shiftBits[3]; \ + int x, y, d, dx, c; \ + int diffStat[256]; \ + int pixelCount = 0; \ + int sample, sum, left[3]; \ + unsigned long avgError; \ + \ + endianMismatch = (!pVNC->rfbServerFormat.bigEndian != !fmt->bigEndian); \ + \ + maxColor[0] = fmt->redMax; \ + maxColor[1] = fmt->greenMax; \ + maxColor[2] = fmt->blueMax; \ + shiftBits[0] = fmt->redShift; \ + shiftBits[1] = fmt->greenShift; \ + shiftBits[2] = fmt->blueShift; \ + \ + memset(diffStat, 0, 256*sizeof(int)); \ + \ + y = 0, x = 0; \ + while (y < h && x < w) { \ + for (d = 0; d < h - y && d < w - x - DETECT_SUBROW_WIDTH; d++) { \ + pix = ((CARD##bpp *)tightBeforeBuf)[(y+d)*w+x+d]; \ + if (endianMismatch) { \ + pix = Swap##bpp(pix); \ + } \ + for (c = 0; c < 3; c++) { \ + left[c] = (int)(pix >> shiftBits[c] & maxColor[c]); \ + } \ + for (dx = 1; dx <= DETECT_SUBROW_WIDTH; dx++) { \ + pix = ((CARD##bpp *)tightBeforeBuf)[(y+d)*w+x+d+dx]; \ + if (endianMismatch) { \ + pix = Swap##bpp(pix); \ + } \ + sum = 0; \ + for (c = 0; c < 3; c++) { \ + sample = (int)(pix >> shiftBits[c] & maxColor[c]); \ + sum += abs(sample - left[c]); \ + left[c] = sample; \ + } \ + if (sum > 255) \ + sum = 255; \ + diffStat[sum]++; \ + pixelCount++; \ + } \ + } \ + if (w > h) { \ + x += h; \ + y = 0; \ + } else { \ + x = 0; \ + y += w; \ + } \ + } \ + \ + if ((diffStat[0] + diffStat[1]) * 100 / pixelCount >= 90) \ + return 0; \ + \ + avgError = 0; \ + for (c = 1; c < 8; c++) { \ + avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \ + if (diffStat[c] == 0 || diffStat[c] > diffStat[c-1] * 2) \ + return 0; \ + } \ + for (; c < 256; c++) { \ + avgError += (unsigned long)diffStat[c] * (unsigned long)(c * c); \ + } \ + avgError /= (pixelCount - diffStat[0]); \ + \ + return avgError; \ +} + +DEFINE_DETECT_FUNCTION(16) +DEFINE_DETECT_FUNCTION(32) + + +/* + * JPEG compression stuff. + */ + +static struct jpeg_destination_mgr jpegDstManager; +static Bool jpegError; +static int jpegDstDataLen; + +/** + * Send the given image rect with jpeg compression. + * \param buffer the source image buffer + * \param w width of source image in pixels + * \param h height of source image in pixels + * \param quality jpeg enoding quality + */ +static Bool +SendJpegRect(rfbClientPtr cl, const CARD8 *buffer, int w, int h, + int quality) +{ + VNCSCREENPTR(cl->pScreen); + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + CARD8 *srcBuf; + JSAMPROW rowPointer[1]; + int i; + + if (pVNC->rfbServerFormat.bitsPerPixel == 8) + return SendFullColorRect(cl, w, h); + + srcBuf = (CARD8 *)xalloc(w * 3); + if (srcBuf == NULL) { + return SendFullColorRect(cl, w, h); + } + rowPointer[0] = srcBuf; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + cinfo.image_width = w; + cinfo.image_height = h; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE); + + JpegSetDstManager (&cinfo); + + jpeg_start_compress(&cinfo, TRUE); + + for (i = 0; i < h; i++) { + PrepareRowForJpeg(cl->pScreen, srcBuf, buffer, i, w); + jpeg_write_scanlines(&cinfo, rowPointer, 1); + if (jpegError) + break; + } + + if (!jpegError) + jpeg_finish_compress(&cinfo); + + jpeg_destroy_compress(&cinfo); + xfree((char *)srcBuf); + + if (jpegError) + return SendFullColorRect(cl, w, h); + + if (pVNC->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + pVNC->updateBuf[pVNC->ublen++] = (char)(rfbTightJpeg << 4); + cl->rfbBytesSent[rfbEncodingTight]++; + + return SendCompressedData(cl, jpegDstDataLen); +} + +/** + * Convert pixel data to format needed for jpeg library. + * \dst destination buffer (24bpp) + * \buffer source buffer (16 or 32bpp) + * \row which image row to prepare/convert + * \width width of source buffer, in pixels + */ +static void +PrepareRowForJpeg(ScreenPtr pScreen, CARD8 *dst, const CARD8 *buffer, + int row, int width) +{ + VNCSCREENPTR(pScreen); + if (pVNC->rfbServerFormat.bitsPerPixel == 32) { + if ( pVNC->rfbServerFormat.redMax == 0xFF && + pVNC->rfbServerFormat.greenMax == 0xFF && + pVNC->rfbServerFormat.blueMax == 0xFF ) { + PrepareRowForJpeg24(pScreen, dst, buffer, row, width); + } else { + PrepareRowForJpeg32(pScreen, dst, buffer, row, width); + } + } else { + /* 16 bpp assumed. */ + PrepareRowForJpeg16(pScreen, dst, buffer, row, width); + } +} + +/** + * Src buffer is 32bpp, dst buffer is 24bpp (optimized case). + */ +static void +PrepareRowForJpeg24(ScreenPtr pScreen, CARD8 *dst, const CARD8 *buffer, + int row, int width) +{ + VNCSCREENPTR(pScreen); + const CARD32 *src = ((const CARD32 *) buffer) + row * width; + while (width--) { + CARD32 pix = *src++; + *dst++ = (CARD8)(pix >> pVNC->rfbServerFormat.redShift); + *dst++ = (CARD8)(pix >> pVNC->rfbServerFormat.greenShift); + *dst++ = (CARD8)(pix >> pVNC->rfbServerFormat.blueShift); + } +} + +/** + * PrepareRowForJpeg16/32: convert 16bpp or 32bpp pixels to 24bpp + */ +#define DEFINE_JPEG_GET_ROW_FUNCTION(bpp) \ + \ +static void \ +PrepareRowForJpeg##bpp(ScreenPtr pScreen, CARD8 *dst, const CARD8 *buffer, \ + int row, int width) \ +{ \ + VNCSCREENPTR(pScreen); \ + CARD##bpp *fbptr; \ + CARD##bpp pix; \ + int inRed, inGreen, inBlue; \ + \ + fbptr = ((CARD##bpp *) buffer) + row * width; \ + \ + while (width--) { \ + pix = *fbptr++; \ + \ + inRed = (int) \ + (pix >> pVNC->rfbServerFormat.redShift & pVNC->rfbServerFormat.redMax); \ + inGreen = (int) \ + (pix >> pVNC->rfbServerFormat.greenShift & pVNC->rfbServerFormat.greenMax); \ + inBlue = (int) \ + (pix >> pVNC->rfbServerFormat.blueShift & pVNC->rfbServerFormat.blueMax); \ + \ + *dst++ = (CARD8)((inRed * 255 + pVNC->rfbServerFormat.redMax / 2) / \ + pVNC->rfbServerFormat.redMax); \ + *dst++ = (CARD8)((inGreen * 255 + pVNC->rfbServerFormat.greenMax / 2) / \ + pVNC->rfbServerFormat.greenMax); \ + *dst++ = (CARD8)((inBlue * 255 + pVNC->rfbServerFormat.blueMax / 2) / \ + pVNC->rfbServerFormat.blueMax); \ + } \ +} + +DEFINE_JPEG_GET_ROW_FUNCTION(16) +DEFINE_JPEG_GET_ROW_FUNCTION(32) + +/* + * Destination manager implementation for JPEG library. + */ + +static void +JpegInitDestination(j_compress_ptr cinfo) +{ + jpegError = FALSE; + jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf; + jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize; +} + +static boolean +JpegEmptyOutputBuffer(j_compress_ptr cinfo) +{ + jpegError = TRUE; + jpegDstManager.next_output_byte = (JOCTET *)tightAfterBuf; + jpegDstManager.free_in_buffer = (size_t)tightAfterBufSize; + + return TRUE; +} + +static void +JpegTermDestination(j_compress_ptr cinfo) +{ + jpegDstDataLen = tightAfterBufSize - jpegDstManager.free_in_buffer; +} + +static void +JpegSetDstManager(j_compress_ptr cinfo) +{ + jpegDstManager.init_destination = JpegInitDestination; + jpegDstManager.empty_output_buffer = JpegEmptyOutputBuffer; + jpegDstManager.term_destination = JpegTermDestination; + cinfo->dest = &jpegDstManager; +} + diff --git a/hw/vnc/translate.c b/hw/vnc/translate.c new file mode 100644 index 0000000..b9ab53e --- /dev/null +++ b/hw/vnc/translate.c @@ -0,0 +1,502 @@ +/* + * translate.c - translate between different pixel formats + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#include +#include "rfb.h" +#if XFREE86VNC +#include +#endif + +static void PrintPixelFormat(rfbPixelFormat *pf); +static Bool rfbSetClientColourMapBGR233(rfbClientPtr cl); + +Bool rfbEconomicTranslate = FALSE; + +/* + * Some standard pixel formats. + */ + +static const rfbPixelFormat BGR233Format = { + 8, 8, 0, 1, 7, 7, 3, 0, 3, 6 +}; + + +/* + * Macro to compare pixel formats. + */ + +#define PF_EQ(x,y) \ + ((x.bitsPerPixel == y.bitsPerPixel) && \ + (x.depth == y.depth) && \ + ((x.bigEndian == y.bigEndian) || (x.bitsPerPixel == 8)) && \ + (x.trueColour == y.trueColour) && \ + (!x.trueColour || ((x.redMax == y.redMax) && \ + (x.greenMax == y.greenMax) && \ + (x.blueMax == y.blueMax) && \ + (x.redShift == y.redShift) && \ + (x.greenShift == y.greenShift) && \ + (x.blueShift == y.blueShift)))) + +#define CONCAT2(a,b) a##b +#define CONCAT2E(a,b) CONCAT2(a,b) +#define CONCAT4(a,b,c,d) a##b##c##d +#define CONCAT4E(a,b,c,d) CONCAT4(a,b,c,d) + +#define OUT 8 +#include "tableinittctemplate.c" +#include "tableinitcmtemplate.c" +#define IN 8 +#include "tabletranstemplate.c" +#undef IN +#define IN 16 +#include "tabletranstemplate.c" +#undef IN +#define IN 32 +#include "tabletranstemplate.c" +#undef IN +#undef OUT + +#define OUT 16 +#include "tableinittctemplate.c" +#include "tableinitcmtemplate.c" +#define IN 8 +#include "tabletranstemplate.c" +#undef IN +#define IN 16 +#include "tabletranstemplate.c" +#undef IN +#define IN 32 +#include "tabletranstemplate.c" +#undef IN +#undef OUT + +#define OUT 32 +#include "tableinittctemplate.c" +#include "tableinitcmtemplate.c" +#define IN 8 +#include "tabletranstemplate.c" +#undef IN +#define IN 16 +#include "tabletranstemplate.c" +#undef IN +#define IN 32 +#include "tabletranstemplate.c" +#undef IN +#undef OUT + +typedef void (*rfbInitTableFnType)(ScreenPtr pScreen, char **table, rfbPixelFormat *in, + rfbPixelFormat *out); + +rfbInitTableFnType rfbInitTrueColourSingleTableFns[3] = { + rfbInitTrueColourSingleTable8, + rfbInitTrueColourSingleTable16, + rfbInitTrueColourSingleTable32 +}; + +rfbInitTableFnType rfbInitColourMapSingleTableFns[3] = { + rfbInitColourMapSingleTable8, + rfbInitColourMapSingleTable16, + rfbInitColourMapSingleTable32 +}; + +rfbInitTableFnType rfbInitTrueColourRGBTablesFns[3] = { + rfbInitTrueColourRGBTables8, + rfbInitTrueColourRGBTables16, + rfbInitTrueColourRGBTables32 +}; + +rfbTranslateFnType rfbTranslateWithSingleTableFns[3][3] = { + { rfbTranslateWithSingleTable8to8, + rfbTranslateWithSingleTable8to16, + rfbTranslateWithSingleTable8to32 }, + { rfbTranslateWithSingleTable16to8, + rfbTranslateWithSingleTable16to16, + rfbTranslateWithSingleTable16to32 }, + { rfbTranslateWithSingleTable32to8, + rfbTranslateWithSingleTable32to16, + rfbTranslateWithSingleTable32to32 } +}; + +rfbTranslateFnType rfbTranslateWithRGBTablesFns[3][3] = { + { rfbTranslateWithRGBTables8to8, + rfbTranslateWithRGBTables8to16, + rfbTranslateWithRGBTables8to32 }, + { rfbTranslateWithRGBTables16to8, + rfbTranslateWithRGBTables16to16, + rfbTranslateWithRGBTables16to32 }, + { rfbTranslateWithRGBTables32to8, + rfbTranslateWithRGBTables32to16, + rfbTranslateWithRGBTables32to32 } +}; + + + +/* + * rfbTranslateNone is used when no translation is required. + */ + +void +rfbTranslateNone(ScreenPtr pScreen, char *table, rfbPixelFormat *in, rfbPixelFormat *out, + unsigned char *optr, int bytesBetweenInputLines, + int width, int height, int x, int y) +{ + VNCSCREENPTR(pScreen); + DrawablePtr pDraw = (DrawablePtr)WindowTable[pScreen->myNum]; + int truewidth = PixmapBytePad(width, in->bitsPerPixel) / 4; + + if ((x + truewidth > pVNC->width) || truewidth != width) { + unsigned char *buffer = malloc(truewidth * height * in->bitsPerPixel / 8); + unsigned char *buf = buffer; + + (*pScreen->GetImage)(pDraw, x, y, truewidth, height, ZPixmap, ~0, (char*)buf); + while (height--) { + memcpy(optr, buf, width * in->bitsPerPixel / 8); + optr += width * in->bitsPerPixel / 8; + buf += truewidth * in->bitsPerPixel / 8; + } + free(buffer); + return; + } + + (*pScreen->GetImage)(pDraw, x, y, width, height, ZPixmap, ~0, (char*)optr); +} + + +/* + * rfbSetTranslateFunction sets the translation function. + */ + +Bool +rfbSetTranslateFunction(cl) + rfbClientPtr cl; +{ + VNCSCREENPTR(cl->pScreen); + rfbLog("Pixel format for client %s:\n",cl->host); + PrintPixelFormat(&cl->format); + + /* + * Check that bits per pixel values are valid + */ + + if ((pVNC->rfbServerFormat.bitsPerPixel != 8) && + (pVNC->rfbServerFormat.bitsPerPixel != 16) && + (pVNC->rfbServerFormat.bitsPerPixel != 32)) + { + rfbLog("%s: server bits per pixel not 8, 16 or 32\n", + "rfbSetTranslateFunction"); + rfbCloseSock(cl->pScreen, cl->sock); + return FALSE; + } + + if ((cl->format.bitsPerPixel != 8) && + (cl->format.bitsPerPixel != 16) && + (cl->format.bitsPerPixel != 32)) + { + rfbLog("%s: client bits per pixel not 8, 16 or 32\n", + "rfbSetTranslateFunction"); + rfbCloseSock(cl->pScreen, cl->sock); + return FALSE; + } + + if (!pVNC->rfbServerFormat.trueColour && + (pVNC->rfbServerFormat.bitsPerPixel != 8) && +#if XFREE86VNC + (miInstalledMaps[cl->pScreen->myNum]->class == PseudoColor)) { +#else + (pVNC->rfbInstalledColormap->class == PseudoColor)) { +#endif + rfbLog("rfbSetTranslateFunction: server has colour map " + "but %d-bit - can only cope with 8-bit colour maps\n", + pVNC->rfbServerFormat.bitsPerPixel); + rfbCloseSock(cl->pScreen, cl->sock); + return FALSE; + } + + if (!cl->format.trueColour && + (cl->format.bitsPerPixel != 8) && +#if XFREE86VNC + (miInstalledMaps[cl->pScreen->myNum]->class == PseudoColor)) { +#else + (pVNC->rfbInstalledColormap->class == PseudoColor) ) { +#endif + rfbLog("rfbSetTranslateFunction: client has colour map " + "but %d-bit - can only cope with 8-bit colour maps\n", + cl->format.bitsPerPixel); + rfbCloseSock(cl->pScreen, cl->sock); + return FALSE; + } + + /* + * bpp is valid, now work out how to translate + */ + + if (!cl->format.trueColour) { + + /* ? -> colour map */ + + if (!pVNC->rfbServerFormat.trueColour) { + + /* colour map -> colour map */ + +#if XFREE86VNC + if (miInstalledMaps[cl->pScreen->myNum]->class == DirectColor) { +#else + if (pVNC->rfbInstalledColormap->class == DirectColor) { +#endif + rfbLog("rfbSetTranslateFunction: client is %d-bit DirectColor," + " client has colour map\n",cl->format.bitsPerPixel); + + cl->translateFn = rfbTranslateWithRGBTablesFns + [pVNC->rfbServerFormat.bitsPerPixel / 16] + [cl->format.bitsPerPixel / 16]; + + (*rfbInitTrueColourRGBTablesFns [cl->format.bitsPerPixel / 16]) + (cl->pScreen, &cl->translateLookupTable, + &pVNC->rfbServerFormat, &cl->format); + + return rfbSetClientColourMap(cl, 0, 0); + + /* PseudoColor colormap */ + + } else { + rfbLog("rfbSetTranslateFunction: both 8-bit colour map: " + "no translation needed\n"); + cl->translateFn = rfbTranslateNone; + return rfbSetClientColourMap(cl, 0, 0); + } + } + + /* + * truecolour -> colour map + * + * Set client's colour map to BGR233, then effectively it's + * truecolour as well + */ + + if (!rfbSetClientColourMapBGR233(cl)) + return FALSE; + + cl->format = BGR233Format; + } + + /* ? -> truecolour */ + + if (!pVNC->rfbServerFormat.trueColour) { + + /* colour map -> truecolour */ + + rfbLog("rfbSetTranslateFunction: client is %d-bit trueColour," + " server has colour map\n",cl->format.bitsPerPixel); + + cl->translateFn = rfbTranslateWithSingleTableFns + [pVNC->rfbServerFormat.bitsPerPixel / 16] + [cl->format.bitsPerPixel / 16]; + + return rfbSetClientColourMap(cl, 0, 0); + } + + /* truecolour -> truecolour */ + + if (PF_EQ(cl->format,pVNC->rfbServerFormat)) { + + /* client & server the same */ + + rfbLog(" no translation needed\n"); + cl->translateFn = rfbTranslateNone; + return TRUE; + } + + if ((pVNC->rfbServerFormat.bitsPerPixel < 16) || + (!rfbEconomicTranslate && (pVNC->rfbServerFormat.bitsPerPixel == 16))) { + + /* we can use a single lookup table for <= 16 bpp */ + + cl->translateFn = rfbTranslateWithSingleTableFns + [pVNC->rfbServerFormat.bitsPerPixel / 16] + [cl->format.bitsPerPixel / 16]; + + (*rfbInitTrueColourSingleTableFns + [cl->format.bitsPerPixel / 16]) (cl->pScreen, + &cl->translateLookupTable, + &pVNC->rfbServerFormat, &cl->format); + + } else { + + /* otherwise we use three separate tables for red, green and blue */ + + cl->translateFn = rfbTranslateWithRGBTablesFns + [pVNC->rfbServerFormat.bitsPerPixel / 16] + [cl->format.bitsPerPixel / 16]; + + (*rfbInitTrueColourRGBTablesFns + [cl->format.bitsPerPixel / 16]) (cl->pScreen, + &cl->translateLookupTable, + &pVNC->rfbServerFormat, &cl->format); + } + + return TRUE; +} + + + +/* + * rfbSetClientColourMapBGR233 sets the client's colour map so that it's + * just like an 8-bit BGR233 true colour client. + */ + +static Bool +rfbSetClientColourMapBGR233(rfbClientPtr cl) +{ + char buf[sz_rfbSetColourMapEntriesMsg + 256 * 3 * 2]; + rfbSetColourMapEntriesMsg *scme = (rfbSetColourMapEntriesMsg *)buf; + CARD16 *rgb = (CARD16 *)(&buf[sz_rfbSetColourMapEntriesMsg]); + int i, len; + int r, g, b; + + if (cl->format.bitsPerPixel != 8) { + rfbLog("%s: client not 8 bits per pixel\n", + "rfbSetClientColourMapBGR233"); + rfbCloseSock(cl->pScreen, cl->sock); + return FALSE; + } + + scme->type = rfbSetColourMapEntries; + + scme->firstColour = Swap16IfLE(0); + scme->nColours = Swap16IfLE(256); + + len = sz_rfbSetColourMapEntriesMsg; + + i = 0; + + for (b = 0; b < 4; b++) { + for (g = 0; g < 8; g++) { + for (r = 0; r < 8; r++) { + rgb[i++] = Swap16IfLE(r * 65535 / 7); + rgb[i++] = Swap16IfLE(g * 65535 / 7); + rgb[i++] = Swap16IfLE(b * 65535 / 3); + } + } + } + + len += 256 * 3 * 2; + + if (WriteExact(cl->sock, buf, len) < 0) { + rfbLogPerror("rfbSetClientColourMapBGR233: write"); + rfbCloseSock(cl->pScreen, cl->sock); + return FALSE; + } + return TRUE; +} + + +/* + * rfbSetClientColourMap is called to set the client's colour map. If the + * client is a true colour client, we simply update our own translation table + * and mark the whole screen as having been modified. + */ + +Bool +rfbSetClientColourMap(cl, firstColour, nColours) + rfbClientPtr cl; + int firstColour; + int nColours; +{ + VNCSCREENPTR(cl->pScreen); + BoxRec box; + + if (nColours == 0) { +#if XFREE86VNC + nColours = miInstalledMaps[cl->pScreen->myNum]->pVisual->ColormapEntries; +#else + nColours = pVNC->rfbInstalledColormap->pVisual->ColormapEntries; +#endif + } + + if (pVNC->rfbServerFormat.trueColour || !cl->readyForSetColourMapEntries) { + return TRUE; + } + + if (cl->format.trueColour) { + (*rfbInitColourMapSingleTableFns + [cl->format.bitsPerPixel / 16]) (cl->pScreen, + &cl->translateLookupTable, + &pVNC->rfbServerFormat, &cl->format); + + REGION_UNINIT(cl->pScreen,&cl->modifiedRegion); + box.x1 = box.y1 = 0; + box.x2 = pVNC->width; + box.y2 = pVNC->height; + REGION_INIT(cl->pScreen,&cl->modifiedRegion,&box,0); + + return TRUE; + } + + return rfbSendSetColourMapEntries(cl, firstColour, nColours); +} + + +/* + * rfbSetClientColourMaps sets the colour map for each RFB client. + */ + +void +rfbSetClientColourMaps(firstColour, nColours) + int firstColour; + int nColours; +{ + rfbClientPtr cl, nextCl; + + for (cl = rfbClientHead; cl; cl = nextCl) { + nextCl = cl->next; + rfbSetClientColourMap(cl, firstColour, nColours); + } +} + + +static void +PrintPixelFormat(pf) + rfbPixelFormat *pf; +{ + if (pf->bitsPerPixel == 1) { + rfbLog(" 1 bpp, %s sig bit in each byte is leftmost on the screen.\n", + (pf->bigEndian ? "most" : "least")); + } else { + rfbLog(" %d bpp, depth %d%s\n",pf->bitsPerPixel,pf->depth, + ((pf->bitsPerPixel == 8) ? "" + : (pf->bigEndian ? ", big endian" : ", little endian"))); + if (pf->trueColour) { + rfbLog(" true colour: max r %d g %d b %d, shift r %d g %d b %d\n", + pf->redMax, pf->greenMax, pf->blueMax, + pf->redShift, pf->greenShift, pf->blueShift); + } else { + rfbLog(" uses a colour map (not true colour).\n"); + } + } +} diff --git a/hw/vnc/vncauth.c b/hw/vnc/vncauth.c new file mode 100644 index 0000000..a70cfa5 --- /dev/null +++ b/hw/vnc/vncauth.c @@ -0,0 +1,252 @@ +/* + * 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. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * Make sure we call srandom() only once. + */ + +static int s_srandom_called = 0; + +/* + * 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. + */ + +static unsigned char s_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. + * + * NOTE: This function is preserved only for compatibility with the original + * AT&T VNC software. Use vncEncryptAndStorePasswd2() instead. + */ + +int +vncEncryptAndStorePasswd(char *passwd, char *fname) +{ + return (vncEncryptAndStorePasswd2(passwd, NULL, fname) == 0); +} + +/* + * Encrypt one or two passwords and store them in a file. Returns 1 if + * successful, 0 if the file could not be written (note that the original + * vncEncryptAndStorePasswd() function returns inverse values). The + * passwdViewOnly pointer may be NULL. + * + * NOTE: The file name of "-" denotes stdout. + */ + +int +vncEncryptAndStorePasswd2(char *passwd, char *passwdViewOnly, char *fname) +{ + FILE *fp; + int bytesToWrite, bytesWrote; + unsigned char encryptedPasswd[16] = { + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0 + }; + + if (strcmp(fname, "-") != 0) { + fp = fopen(fname, "w"); + if (fp == NULL) { + return 0; + } + chmod(fname, S_IRUSR|S_IWUSR); + } else { + fp = stdout; + } + + strncpy((char*)encryptedPasswd, passwd, 8); + if (passwdViewOnly != NULL) + strncpy((char*)encryptedPasswd + 8, passwdViewOnly, 8); + + /* Do encryption in-place - this way we overwrite our copies of + plaintext passwords. */ + + deskey(s_fixedkey, EN0); + des(encryptedPasswd, encryptedPasswd); + if (passwdViewOnly != NULL) + des(encryptedPasswd + 8, encryptedPasswd + 8); + + bytesToWrite = (passwdViewOnly == NULL) ? 8 : 16; + bytesWrote = fwrite(encryptedPasswd, 1, bytesToWrite, fp); + + if (fp != stdout) { + fclose(fp); + } + return (bytesWrote == bytesToWrite); +} + + +/* + * 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. + * + * NOTE: This function is preserved only for compatibility with the original + * AT&T VNC software. Use vncDecryptPasswdFromFile2() instead. + */ + +char * +vncDecryptPasswdFromFile(char *fname) +{ + char *passwd; + + passwd = malloc(9); + + if (passwd != NULL) { + if (vncDecryptPasswdFromFile2(fname, passwd, NULL) == 0) { + free(passwd); + passwd = NULL; + } + } + + return passwd; +} + +/* + * Decrypt one or two passwords from a file. Returns the number of + * passwords read (1, 2, or 0 on error). On success, the passwords are + * written into buffers passwdFullControl[] and passwdViewOnly[] if + * they are not NULL. If the pointers to buffers are not NULL, then + * the buffers should be at least of 9 bytes length. + */ + +int +vncDecryptPasswdFromFile2(char *fname, + char *passwdFullControl, char *passwdViewOnly) +{ + FILE *fp; + int i, ch; + unsigned char passwd[16]; + + if (strcmp(fname, "-") != 0) { + if ((fp = fopen(fname,"r")) == NULL) + return 0; /* Could not open the file */ + } else { + fp = stdin; + } + + for (i = 0; i < 16; i++) { + ch = getc(fp); + if (ch == EOF) + break; + passwd[i] = ch; + } + + if (fp != stdin) + fclose(fp); + + if (i < 8) + return 0; /* Could not read eight bytes */ + + deskey(s_fixedkey, DE1); + + /* Decoding first (full-control) password */ + if (passwdFullControl != NULL) { + des(passwd, passwd); + memcpy(passwdFullControl, passwd, 8); + passwdFullControl[8] = '\0'; + } + + /* Decoding second (view-only) password if available */ + if (i == 16 && passwdViewOnly != NULL) { + des(&passwd[8], &passwd[8]); + memcpy(passwdViewOnly, &passwd[8], 8); + passwdViewOnly[8] = '\0'; + } + + /* Destroying our copy of clear-text passwords */ + memset(passwd, 0, 16); + + return (i < 16) ? 1 : 2; +} + + +/* + * Generate CHALLENGESIZE random bytes for use in challenge-response + * authentication. + */ + +void +vncRandomBytes(unsigned char *bytes) +{ + int i; + unsigned int seed; + + if (!s_srandom_called) { + seed = (unsigned int)time(0) ^ (unsigned int)getpid(); + srandom(seed); + s_srandom_called = 1; + } + + 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/hw/vnc/vncauth.h b/hw/vnc/vncauth.h new file mode 100644 index 0000000..adc280b --- /dev/null +++ b/hw/vnc/vncauth.h @@ -0,0 +1,33 @@ +/* + * 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); +extern int vncEncryptAndStorePasswd2(char *passwd, char *passwdViewOnly, char *fname); +extern int vncDecryptPasswdFromFile2(char *fname, char *passwdFullControl, char *passwdViewOnly); + diff --git a/hw/vnc/vncext.c b/hw/vnc/vncext.c new file mode 100644 index 0000000..80a3433 --- /dev/null +++ b/hw/vnc/vncext.c @@ -0,0 +1,800 @@ +/* + * Copyright (C) 2002 Alan Hourihane. 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. + * + * Author: Alan Hourihane + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#include "rfb.h" +#include "extnsionst.h" +#define _VNC_SERVER +#include +#ifdef XFree86LOADER +#include "xf86Module.h" +#endif + +#include +#include +#include +#include + +DevPrivateKey vncCreateScreenResourcesKey = &vncCreateScreenResourcesKey; +DevPrivateKey rfbGCKey = &rfbGCKey; + +int VncSelectNotify(ClientPtr client, BOOL onoff); +void VncExtensionInit(void); + +static int VncErrorBase; /* first vnc error number */ +static int VncEventBase; /* first vnc event number */ + +#define USE_ORIG_RES_CODE 0 + +#if USE_ORIG_RES_CODE +unsigned long VncResourceGeneration = 0; + +static RESTYPE VncNotifyList; + +static XID faked; + +typedef struct _VncNotifyListRec { + struct _VncNotifyListRec *next; + ClientPtr client; +} VncNotifyListRec, *VncNotifyListPtr; +#endif + + +/** + * Each X client that uses libVnc to talk to this extension will + * get recorded by one of these records. + */ +typedef struct _VncClientRec { + ClientPtr client; + struct _VncClientRec *next; + XID fakeID; + RESTYPE res; +} VncClientRec, *VncClientRecPtr; + +static VncClientRecPtr ClientsList = NULL; + + +/** + * Remove client record from list + */ +static void +VncRemoveClientRecord(ClientPtr client) +{ + /* XXX need 'deleteresource' flag? */ + VncClientRecPtr p = ClientsList, prev = NULL; + rfbLog("%s client %p\n", __func__, (void *) client); + while (p) { + if (p->client == client) { + /* remove this one */ + if (prev) + prev->next = p->next; + else + ClientsList = p->next; + xfree(p); + return; + } + prev = p; + p = p->next; + } +} + + +/** + * This callback will be called by X's resource manager to delete the + * given resource. We create one resource for each client. When X + * deletes the resource, we know the client is going away. + */ +static int +VncDestroyClientResourceCallback(pointer pn, XID id) +{ + VncClientRecPtr rec = (VncClientRecPtr) pn; + VncRemoveClientRecord(rec->client); + return Success; +} + + +/** + * Add a client record to the list of clients (unless already in list). + * We'll create a new, unique resource for this client. This is used + * to detect when an X client goes away. + */ +static void +VncSaveClientRecord(ClientPtr client) +{ + VncClientRecPtr rec = ClientsList; + + rfbLog("%s saving client %p\n", __func__, (void *) client); + + /* look if already in list */ + while (rec) { + if (rec->client == client) { + return; /* already in list */ + } + rec = rec->next; + } + + /* allocate new client record */ + rec = (VncClientRecPtr) xalloc(sizeof(VncClientRec)); + if (rec) { + rec->client = client; + rec->fakeID = FakeClientID(client->index); + rec->res = CreateNewResourceType(VncDestroyClientResourceCallback); + if (!AddResource(rec->fakeID, rec->res, rec)) { + xfree(rec); + } + + /* insert at head of list */ + rec->next = ClientsList; + ClientsList = rec; + } +} + + + +static int +ProcVncQueryVersion(ClientPtr client) +{ + xVncQueryVersionReply rep; + + REQUEST_SIZE_MATCH(xVncQueryVersionReq); + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + rep.majorVersion = VNC_MAJOR_VERSION; + rep.minorVersion = VNC_MINOR_VERSION; + if(client->swapped) + { + register char n; + swaps(&rep.sequenceNumber, n); + swaps(&rep.majorVersion, n); + swaps(&rep.minorVersion, n); + } + (void)WriteToClient(client, SIZEOF(xVncQueryVersionReply), + (char *)&rep); + return (client->noClientException); +} /* ProcVncQueryVersion */ + +static int +ProcVncConnection(ClientPtr client) +{ + REQUEST(xVncConnectionReq); + xVncConnectionReply rep; + + rfbUserAllow( stuff->sock, stuff->accept ); + + REQUEST_SIZE_MATCH(xVncConnectionReq); + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + rep.sock = 0; + rep.accept = 0; + if(client->swapped) + { + register char n; + swaps(&rep.sequenceNumber, n); + swaps(&rep.sock, n); + swaps(&rep.accept, n); + } + (void)WriteToClient(client, SIZEOF(xVncConnectionReply), + (char *)&rep); + return (client->noClientException); +} /* ProcVncConnection */ + +static int +ProcVncSelectNotify(ClientPtr client) +{ + REQUEST(xVncSelectNotifyReq); + xVncSelectNotifyReply rep; + + VncSelectNotify(client, stuff->onoff); + + REQUEST_SIZE_MATCH(xVncSelectNotifyReq); + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + if(client->swapped) + { + register char n; + swaps(&rep.sequenceNumber, n); + } + (void)WriteToClient(client, SIZEOF(xVncSelectNotifyReply), + (char *)&rep); + return (client->noClientException); + +} + +static int +ProcVncListConnections(ClientPtr client) +{ + xVncListConnectionsReply rep; + rfbClientPtr cl; + int count = 0; + struct in_addr ipaddress; + xVncConnectionListInfo Info; + + /* count connections */ + for (cl = rfbClientHead; cl; cl = cl->next) +#ifdef CHROMIUM + /* + * Fix this, as it should be generic, but we're only using it + * for Chromium at the moment, so we only list the connections + * that have a valid chromium encoding. We should be able to list + * any type requested. FIXME! XXXX + * See furthur down this function too!!! + */ + if (cl->enableChromiumEncoding) +#endif + count++; + + REQUEST_SIZE_MATCH(xVncListConnectionsReq); + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.count = count; + rep.length = count * sizeof(xVncConnectionListInfo) >> 2; + if(client->swapped) + { + register char n; + swaps(&rep.sequenceNumber, n); + swaps(&rep.count, n); + } + (void)WriteToClient(client, SIZEOF(xVncListConnectionsReply), + (char *)&rep); + + for (cl = rfbClientHead; cl; cl = cl->next) { +#ifdef CHROMIUM + /* + * Fix this, as it should be generic, but we're only using it + * for Chromium at the moment, so we only list the connections + * that have a valid chromium encoding. We should be able to list + * any type requested. FIXME! XXXX + */ + if (!cl->enableChromiumEncoding) + continue; +#endif + inet_aton(cl->host, &ipaddress); + memcpy(&Info, &ipaddress, sizeof(struct in_addr)); + WriteToClient(client, SIZEOF(xVncConnectionListInfo), (char*)&Info); + } + + return (client->noClientException); +} /* ProcVncListConnections */ + +#ifdef CHROMIUM +static int +ProcVncChromiumStart(ClientPtr client) +{ + REQUEST(xVncChromiumStartReq); + xVncChromiumStartReply rep; + + rfbSendChromiumStart(stuff->ipaddress, stuff->crServerPort, stuff->mothershipPort); + + REQUEST_SIZE_MATCH(xVncChromiumStartReq); + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + if(client->swapped) + { + register char n; + swaps(&rep.sequenceNumber, n); + } + (void)WriteToClient(client, SIZEOF(xVncChromiumStartReply), + (char *)&rep); + return (client->noClientException); +} /* ProcVncChromiumStart */ + +static int +ProcVncChromiumMonitor(ClientPtr client) +{ + REQUEST(xVncChromiumMonitorReq); + xVncChromiumMonitorReply rep; + + rfbChromiumMonitorWindowID(stuff->cr_windowid, stuff->windowid); + + REQUEST_SIZE_MATCH(xVncChromiumMonitorReq); + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + if(client->swapped) + { + register char n; + swaps(&rep.sequenceNumber, n); + } + (void)WriteToClient(client, SIZEOF(xVncChromiumMonitorReply), + (char *)&rep); + return (client->noClientException); +} /* ProcVncChromiumMonitor */ +#endif /* CHROMIUM */ + +static int +ProcVncDispatch(ClientPtr client) +{ + REQUEST(xReq); + + switch (stuff->data) + { + case X_VncQueryVersion: + return ProcVncQueryVersion(client); + case X_VncSelectNotify: + return ProcVncSelectNotify(client); + case X_VncConnection: + return ProcVncConnection(client); + case X_VncListConnections: + return ProcVncListConnections(client); +#ifdef CHROMIUM + case X_VncChromiumStart: + return ProcVncChromiumStart(client); + case X_VncChromiumMonitor: + return ProcVncChromiumMonitor(client); +#endif + default: + return BadRequest; + } +} /* ProcVncDispatch */ + +static int +SProcVncQueryVersion(ClientPtr client) +{ + REQUEST(xVncQueryVersionReq); + register char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH(xVncQueryVersionReq); + swaps(&stuff->majorVersion, n); + swaps(&stuff->minorVersion,n); + return ProcVncQueryVersion(client); +} /* SProcVncQueryVersion */ + +static int +SProcVncSelectNotify(ClientPtr client) +{ + REQUEST(xVncSelectNotifyReq); + register char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH(xVncSelectNotifyReq); + return ProcVncSelectNotify(client); +} /* SProcVncSelectNotify */ + +static int +SProcVncListConnections(ClientPtr client) +{ + REQUEST(xVncListConnectionsReq); + register char n; + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH(xVncListConnectionsReq); + return ProcVncListConnections(client); +} /* SProcVncListConnections */ + +#ifdef CHROMIUM +static int +SProcVncChromiumStart(ClientPtr client) +{ + REQUEST(xVncChromiumStartReq); + register char n; + + swaps(&stuff->ipaddress, n); + swaps(&stuff->crServerPort, n); + swaps(&stuff->mothershipPort, n); + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH(xVncChromiumStartReq); + return ProcVncChromiumStart(client); +} /* SProcVncChromiumStart */ + +static int +SProcVncChromiumMonitor(ClientPtr client) +{ + REQUEST(xVncChromiumMonitorReq); + register char n; + + swaps(&stuff->length, n); + swaps(&stuff->windowid, n); + swaps(&stuff->cr_windowid, n); + REQUEST_SIZE_MATCH(xVncChromiumMonitorReq); + return ProcVncChromiumMonitor(client); +} /* SProcVncChromiumMonitor */ +#endif /* CHROMIUM */ + +static int +SProcVncConnection(ClientPtr client) +{ + REQUEST(xVncConnectionReq); + register char n; + + + swaps(&stuff->length, n); + REQUEST_SIZE_MATCH(xVncConnectionReq); + swaps(&stuff->sock, n); + swaps(&stuff->accept,n); + return ProcVncConnection(client); +} /* SProcVncConnection */ + +static int +SProcVncDispatch(ClientPtr client) +{ + REQUEST(xReq); + + switch (stuff->data) + { + case X_VncQueryVersion: + return SProcVncQueryVersion(client); + case X_VncSelectNotify: + return SProcVncSelectNotify(client); + case X_VncConnection: + return SProcVncConnection(client); + case X_VncListConnections: + return SProcVncListConnections(client); +#ifdef CHROMIUM + case X_VncChromiumStart: + return SProcVncChromiumStart(client); + case X_VncChromiumMonitor: + return SProcVncChromiumMonitor(client); +#endif + default: + return BadRequest; + } +} /* SProcVncDispatch */ + +static void +SwapVncConnectedEvent(xVncConnectedEvent *from, xVncConnectedEvent *to) +{ + to->type = from->type; + to->detail = from->detail; + cpswaps(from->sequenceNumber, to->sequenceNumber); + cpswapl(from->connected, to->connected); +} + +#ifdef CHROMIUM +static void +SwapVncChromiumConnectedEvent(xVncConnectedEvent *from, xVncConnectedEvent *to) +{ + to->type = from->type; + to->detail = from->detail; + cpswaps(from->sequenceNumber, to->sequenceNumber); + cpswapl(from->connected, to->connected); +} +#endif + +static void +SwapVncDisconnectedEvent(xVncConnectedEvent *from, xVncConnectedEvent *to) +{ + to->type = from->type; + to->detail = from->detail; + cpswaps(from->sequenceNumber, to->sequenceNumber); + cpswapl(from->connected, to->connected); +} + +int +GenerateVncConnectedEvent(int sock) +{ +#if USE_ORIG_RES_CODE + VncNotifyListPtr pn; + + pn = (VncNotifyListPtr)LookupIDByType(faked, VncNotifyList); +#else + VncClientRecPtr pn = ClientsList; +#endif + + while (pn) + { + if (pn->client) + { + xVncConnectedEvent conn; + SOCKLEN_T peer_len; + struct sockaddr_in peer; + + conn.type = VncEventBase + XVncConnected; + conn.sequenceNumber = pn->client->sequence; + conn.connected = sock; + + peer_len = sizeof(peer); + if (getpeername(sock, (struct sockaddr *) &peer, &peer_len) == -1) + conn.ipaddress = 0; + else + conn.ipaddress = (CARD32)peer.sin_addr.s_addr; + + (void) TryClientEvents(pn->client, (xEventPtr)&conn, 1, NoEventMask, + NoEventMask, NullGrab); + } + pn = pn->next; + } + + return 1; +} + + + +#ifdef CHROMIUM +int +GenerateVncChromiumConnectedEvent(int sock) +{ +#if USE_ORIG_RES_CODE + VncNotifyListPtr pn; + pn = (VncNotifyListPtr)LookupIDByType(faked, VncNotifyList); +#else + VncClientRecPtr pn = ClientsList; +#endif + rfbLog("Enter GenerateVncChromiumConnectedEvent\n"); + while (pn) + { + if (pn->client) + { + xVncConnectedEvent conn; + SOCKLEN_T peer_len; + struct sockaddr_in peer; + + rfbLog("Sending XVncChromiumConnected to client %p\n", + (void *) pn->client); + + conn.type = VncEventBase + XVncChromiumConnected; + conn.sequenceNumber = pn->client->sequence; + conn.connected = sock; + + peer_len = sizeof(peer); + if (getpeername(sock, (struct sockaddr *)&peer, &peer_len) == -1) + conn.ipaddress = 0; + else + conn.ipaddress = (CARD32)peer.sin_addr.s_addr; + + (void) TryClientEvents(pn->client, (xEventPtr)&conn, 1, NoEventMask, + NoEventMask, NullGrab); + } + pn = pn->next; + } + return 1; +} +#endif /* CHROMIUM */ + + +int +GenerateVncDisconnectedEvent(int sock) +{ +#if USE_ORIG_RES_CODE + VncNotifyListPtr pn; + + pn = (VncNotifyListPtr)LookupIDByType(faked, VncNotifyList); +#else + VncClientRecPtr pn = ClientsList; +#endif + rfbLog("GenerateVncDisconnectedEvent\n"); + while (pn) + { + if (pn->client) + { + xVncDisconnectedEvent conn; + conn.type = VncEventBase + XVncDisconnected; + conn.sequenceNumber = pn->client->sequence; + conn.connected = sock; + (void) TryClientEvents(pn->client, (xEventPtr)&conn, 1, NoEventMask, + NoEventMask, NullGrab); + } + pn = pn->next; + } + + return 1; +} + +static void +VncResetProc(ExtensionEntry *extEntry) +{ +} /* VncResetProc */ + + + +/** + * When the app calls libVnc's XVncSelectNotify() we wind up here. + * Either add or remove 'client' from the VncNotifyList depending on 'onoff'. + */ +int +VncSelectNotify(ClientPtr client, BOOL onoff) +{ +#if USE_ORIG_RES_CODE + VncNotifyListPtr head, newRec, tmp, freeRec = NULL; + + rfbLog("%s client %p onoff=%d\n", __func__, (void *) client, (int) onoff); + if (!faked) + faked = FakeClientID(client->index); +#else + /* ignore onoff param */ + VncSaveClientRecord(client); +#endif + +#if USE_ORIG_RES_CODE + /* Get the first VncNotifyListPtr */ + head = (VncNotifyListPtr) LookupIDByType(faked, VncNotifyList); + + /* search list for this client */ + tmp = head; + while (tmp) { + if (tmp->client == client) { + /* found client! */ + if (!onoff) + tmp->client = NULL; + return Success; + } + if (!tmp->client) + freeRec = tmp; /* save ptr to free record */ + tmp = tmp->next; + } + + /* if we get here, we didn't find client in the list */ + + if (!onoff) { + /* if turning off non-existing client, just return */ + return Success; + } + + /* OK, add new client to list now */ + + if (freeRec) { + /* re-using a free spot */ + freeRec->client = client; + return Success; + } + + /* need to allocate new node */ + if (!(newRec = (VncNotifyListPtr)xalloc(sizeof(VncNotifyListRec)))) + return BadAlloc; + /* insert at head, just as AddResource() does!!! */ + newRec->next = head; + newRec->client = client; + if (!AddResource(faked, VncNotifyList, newRec)) { + xfree(newRec); + return BadAlloc; + } +#endif + return Success; +} + + +#if USE_ORIG_RES_CODE +static int +VncDestroyNotifyList(pointer pn, XID id) +{ + VncNotifyListPtr cpn = (VncNotifyListPtr) pn; + rfbLog("%s client %p\n", __func__, (void *) cpn->client); + cpn->client = NULL; + return Success; +} +#endif + +static Bool +CreateResourceTypes(void) +{ +#if USE_ORIG_RES_CODE + if (VncResourceGeneration == serverGeneration) + return TRUE; + + VncResourceGeneration = serverGeneration; + + if (!(VncNotifyList = CreateNewResourceType(VncDestroyNotifyList))) { + ErrorF("CreateResourceTypes: failed to allocate vnc notify list resource.\n"); + return FALSE; + } +#endif + return TRUE; +} + + + +static unsigned long vncExtGeneration = 0; +#if XFREE86VNC +extern Bool VNCInit(ScreenPtr pScreen, unsigned char *FBStart); + +/* copied from miscrinit.c */ +typedef struct +{ + pointer pbits; /* pointer to framebuffer */ + int width; /* delta to add to a framebuffer addr to move one row down */ +} miScreenInitParmsRec, *miScreenInitParmsPtr; + + +static Bool +vncCreateScreenResources(ScreenPtr pScreen) +{ + int ret = TRUE; + CreateScreenResourcesProcPtr CreateScreenResources = + (CreateScreenResourcesProcPtr) + dixLookupPrivate(&pScreen->devPrivates, vncCreateScreenResourcesKey); + miScreenInitParmsPtr pScrInitParms; + + pScrInitParms = (miScreenInitParmsPtr)pScreen->devPrivate; + + if ( pScreen->CreateScreenResources != vncCreateScreenResources ) { + /* Can't find hook we are hung on */ + xf86DrvMsg(pScreen->myNum, X_WARNING /* X_ERROR */, + "vncCreateScreenResources %p called when not in " + "pScreen->CreateScreenResources %p n", + (void *) vncCreateScreenResources, + (void *) pScreen->CreateScreenResources ); + } + + /* Now do our stuff */ + VNCInit(pScreen, pScrInitParms->pbits); + + /* Unhook this function ... */ + pScreen->CreateScreenResources = CreateScreenResources; + dixSetPrivate(&pScreen->devPrivates, vncCreateScreenResourcesKey, NULL); + + /* ... and call the previous CreateScreenResources fuction, if any */ + if (pScreen->CreateScreenResources) { + ret = (*pScreen->CreateScreenResources)(pScreen); + } + +#ifdef DEBUG + ErrorF("vncCreateScreenResources() returns %d\n", ret); +#endif + return ret; +} +#endif + + + +void +VncExtensionInit(void) +{ + ExtensionEntry *extEntry; + + if (vncExtGeneration != serverGeneration) { + unsigned int i; + + vncExtGeneration = serverGeneration; + + // no allocation needed for screen privates + if (!dixRequestPrivate(rfbGCKey, sizeof(rfbGCRec))) + return; + +#if XFREE86VNC + for (i = 0 ; i < screenInfo.numScreens; i++) + { + dixSetPrivate(&screenInfo.screens[i]->devPrivates, + vncCreateScreenResourcesKey, + xf86Screens[i]->pScreen->CreateScreenResources); + xf86Screens[i]->pScreen->CreateScreenResources = vncCreateScreenResources; + } +#endif + + gethostname(rfbThisHost, 255); + } + + if (!CreateResourceTypes()) + return; + + extEntry = AddExtension(VNC_EXTENSION_NAME, + XVncNumberEvents, XVncNumberErrors, + ProcVncDispatch, SProcVncDispatch, + VncResetProc, StandardMinorOpcode); + + VncErrorBase = extEntry->errorBase; + VncEventBase = extEntry->eventBase; + + EventSwapVector[VncEventBase + XVncConnected] = + (EventSwapPtr)SwapVncConnectedEvent; + EventSwapVector[VncEventBase + XVncDisconnected] = + (EventSwapPtr)SwapVncDisconnectedEvent; +#ifdef CHROMIUM + EventSwapVector[VncEventBase + XVncChromiumConnected] = + (EventSwapPtr)SwapVncChromiumConnectedEvent; +#endif +} /* VncExtensionInit */ diff --git a/hw/vnc/xistubs.c b/hw/vnc/xistubs.c new file mode 100644 index 0000000..011ac9e --- /dev/null +++ b/hw/vnc/xistubs.c @@ -0,0 +1,323 @@ +/* $Xorg: stubs.c,v 1.4 2001/02/09 02:04:35 xorgcvs Exp $ */ + +/************************************************************ + +Copyright 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +Copyright 1989 by Hewlett-Packard Company, Palo Alto, California. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Hewlett-Packard not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +HEWLETT-PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +HEWLETT-PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +********************************************************/ +/* $XFree86: xc/programs/Xserver/Xi/stubs.c,v 3.3 2001/01/17 22:13:26 dawes Exp $ */ + +/* + * stubs.c -- stub routines for the X server side of the XINPUT + * extension. This file is mainly to be used only as documentation. + * There is not much code here, and you can't get a working XINPUT + * server just using this. + * The Xvfb server uses this file so it will compile with the same + * object files as the real X server for a platform that has XINPUT. + * Xnest could do the same thing. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#define NEED_EVENTS +#include +#include +#include +#include +#include "inputstr.h" +#include "XIstubs.h" + +/*********************************************************************** + * + * Caller: ProcXChangeKeyboardDevice + * + * This procedure does the implementation-dependent portion of the work + * needed to change the keyboard device. + * + * The X keyboard device has a FocusRec. If the device that has been + * made into the new X keyboard did not have a FocusRec, + * ProcXChangeKeyboardDevice will allocate one for it. + * + * If you do not want clients to be able to focus the old X keyboard + * device, call DeleteFocusClassDeviceStruct to free the FocusRec. + * + * If you support input devices with keys that you do not want to be + * used as the X keyboard, you need to check for them here and return + * a BadDevice error. + * + * The default implementation is to do nothing (assume you do want + * clients to be able to focus the old X keyboard). The commented-out + * sample code shows what you might do if you don't want the default. + * + */ + +int +ChangeKeyboardDevice (old_dev, new_dev) + DeviceIntPtr old_dev; + DeviceIntPtr new_dev; + { + /*********************************************************************** + DeleteFocusClassDeviceStruct(old_dev); * defined in xchgptr.c * + **********************************************************************/ + return BadMatch; + } + + +/*********************************************************************** + * + * Caller: ProcXChangePointerDevice + * + * This procedure does the implementation-dependent portion of the work + * needed to change the pointer device. + * + * The X pointer device does not have a FocusRec. If the device that + * has been made into the new X pointer had a FocusRec, + * ProcXChangePointerDevice will free it. + * + * If you want clients to be able to focus the old pointer device that + * has now become accessible through the input extension, you need to + * add a FocusRec to it here. + * + * The XChangePointerDevice protocol request also allows the client + * to choose which axes of the new pointer device are used to move + * the X cursor in the X- and Y- directions. If the axes are different + * than the default ones, you need to keep track of that here. + * + * If you support input devices with valuators that you do not want to be + * used as the X pointer, you need to check for them here and return a + * BadDevice error. + * + * The default implementation is to do nothing (assume you don't want + * clients to be able to focus the old X pointer). The commented-out + * sample code shows what you might do if you don't want the default. + * + */ + +int +#if NeedFunctionPrototypes +ChangePointerDevice ( + DeviceIntPtr old_dev, + DeviceIntPtr new_dev, + unsigned char x, + unsigned char y) +#else +ChangePointerDevice (old_dev, new_dev, x, y) + DeviceIntPtr old_dev, new_dev; + unsigned char x, y; +#endif + { + /*********************************************************************** + InitFocusClassDeviceStruct(old_dev); * allow focusing old ptr* + + x_axis = x; * keep track of new x-axis* + y_axis = y; * keep track of new y-axis* + if (x_axis != 0 || y_axis != 1) + axes_changed = TRUE; * remember axes have changed* + else + axes_changed = FALSE; + *************************************************************************/ + return BadMatch; + } + +/*********************************************************************** + * + * Caller: ProcXCloseDevice + * + * Take care of implementation-dependent details of closing a device. + * Some implementations may actually close the device, others may just + * remove this clients interest in that device. + * + * The default implementation is to do nothing (assume all input devices + * are initialized during X server initialization and kept open). + * + */ + +void +CloseInputDevice (d, client) + DeviceIntPtr d; + ClientPtr client; + { + } + +/*********************************************************************** + * + * Caller: ProcXListInputDevices + * + * This is the implementation-dependent routine to initialize an input + * device to the point that information about it can be listed. + * Some implementations open all input devices when the server is first + * initialized, and never close them. Other implementations open only + * the X pointer and keyboard devices during server initialization, + * and only open other input devices when some client makes an + * XOpenDevice request. If some other process has the device open, the + * server may not be able to get information about the device to list it. + * + * This procedure should be used by implementations that do not initialize + * all input devices at server startup. It should do device-dependent + * initialization for any devices not previously initialized, and call + * AddInputDevice for each of those devices so that a DeviceIntRec will be + * created for them. + * + * The default implementation is to do nothing (assume all input devices + * are initialized during X server initialization and kept open). + * The commented-out sample code shows what you might do if you don't want + * the default. + * + */ + +void +AddOtherInputDevices () + { + /********************************************************************** + for each uninitialized device, do something like: + + DeviceIntPtr dev; + DeviceProc deviceProc; + pointer private; + + dev = (DeviceIntPtr) AddInputDevice(deviceProc, TRUE); + dev->public.devicePrivate = private; + RegisterOtherDevice(dev); + dev->inited = ((*dev->deviceProc)(dev, DEVICE_INIT) == Success); + ************************************************************************/ + + } + +/*********************************************************************** + * + * Caller: ProcXOpenDevice + * + * This is the implementation-dependent routine to open an input device. + * Some implementations open all input devices when the server is first + * initialized, and never close them. Other implementations open only + * the X pointer and keyboard devices during server initialization, + * and only open other input devices when some client makes an + * XOpenDevice request. This entry point is for the latter type of + * implementation. + * + * If the physical device is not already open, do it here. In this case, + * you need to keep track of the fact that one or more clients has the + * device open, and physically close it when the last client that has + * it open does an XCloseDevice. + * + * The default implementation is to do nothing (assume all input devices + * are opened during X server initialization and kept open). + * + */ + +void +OpenInputDevice (dev, client, status) + DeviceIntPtr dev; + ClientPtr client; + int *status; + { + } + +/**************************************************************************** + * + * Caller: ProcXSetDeviceMode + * + * Change the mode of an extension device. + * This function is used to change the mode of a device from reporting + * relative motion to reporting absolute positional information, and + * vice versa. + * The default implementation below is that no such devices are supported. + * + */ + +int +SetDeviceMode (client, dev, mode) + register ClientPtr client; + DeviceIntPtr dev; + int mode; + { + return BadMatch; + } + +/**************************************************************************** + * + * Caller: ProcXSetDeviceValuators + * + * Set the value of valuators on an extension input device. + * This function is used to set the initial value of valuators on + * those input devices that are capable of reporting either relative + * motion or an absolute position, and allow an initial position to be set. + * The default implementation below is that no such devices are supported. + * + */ + +int +SetDeviceValuators (client, dev, valuators, first_valuator, num_valuators) + register ClientPtr client; + DeviceIntPtr dev; + int *valuators; + int first_valuator; + int num_valuators; + { + return BadMatch; + } + +/**************************************************************************** + * + * Caller: ProcXChangeDeviceControl + * + * Change the specified device controls on an extension input device. + * + */ + +int +ChangeDeviceControl (client, dev, control) + register ClientPtr client; + DeviceIntPtr dev; + xDeviceCtl *control; + { + switch (control->control) + { + case DEVICE_RESOLUTION: + return (BadMatch); + default: + return (BadMatch); + } + } diff --git a/hw/vnc/zlib.c b/hw/vnc/zlib.c new file mode 100644 index 0000000..4863fb4 --- /dev/null +++ b/hw/vnc/zlib.c @@ -0,0 +1,310 @@ +/* + * zlib.c + * + * Routines to implement zlib based encoding (deflate). + * + * Modified for XFree86 4.x by Alan Hourihane + */ + +/* + * 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. + * + * For the latest source code, please check: + * + * http://www.developVNC.org/ + * + * or send email to feedback@developvnc.org. + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + + +#include +#include "rfb.h" + +/* + * zlibBeforeBuf contains pixel data in the client's format. + * zlibAfterBuf contains the zlib (deflated) encoding version. + * If the zlib compressed/encoded version is + * larger than the raw data or if it exceeds zlibAfterBufSize then + * raw encoding is used instead. + */ + +static int zlibBeforeBufSize = 0; +static unsigned char *zlibBeforeBuf = NULL; + +static int zlibAfterBufSize = 0; +static unsigned char *zlibAfterBuf = NULL; +static int zlibAfterBufLen; + +/* + * rfbSendOneRectEncodingZlib - send a given rectangle using one Zlib + * rectangle encoding. + */ + +Bool +rfbSendOneRectEncodingZlib(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + VNCSCREENPTR(cl->pScreen); + rfbFramebufferUpdateRectHeader rect; + rfbZlibHeader hdr; + int deflateResult; + int previousOut; + int i; + int maxRawSize; + int maxCompSize; + + maxRawSize = (pVNC->width * pVNC->height + * (cl->format.bitsPerPixel / 8)); + + if (zlibBeforeBufSize < maxRawSize) { + zlibBeforeBufSize = maxRawSize; + if (zlibBeforeBuf == NULL) + zlibBeforeBuf = (unsigned char *)xalloc(zlibBeforeBufSize); + else + zlibBeforeBuf = (unsigned char *)xrealloc(zlibBeforeBuf, zlibBeforeBufSize); + } + + /* zlib compression is not useful for very small data sets. + * So, we just send these raw without any compression. + */ + if (( w * h * (pVNC->bitsPerPixel / 8)) < + VNC_ENCODE_ZLIB_MIN_COMP_SIZE ) { + + int result; + + /* The translation function (used also by the in raw encoding) + * requires 4/2/1 byte alignment in the output buffer (which is + * pVNC->updateBuf for the raw encoding) based on the bitsPerPixel of + * the viewer/client. This prevents SIGBUS errors on some + * architectures like SPARC, PARISC... + */ + if (( cl->format.bitsPerPixel > 8 ) && + ( pVNC->ublen % ( cl->format.bitsPerPixel / 8 )) != 0 ) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + result = rfbSendRectEncodingRaw(cl, x, y, w, h); + + return result; + + } + + /* + * zlib requires output buffer to be slightly larger than the input + * buffer, in the worst case. + */ + maxCompSize = maxRawSize + (( maxRawSize + 99 ) / 100 ) + 12; + + if (zlibAfterBufSize < maxCompSize) { + zlibAfterBufSize = maxCompSize; + if (zlibAfterBuf == NULL) + zlibAfterBuf = (unsigned char *)xalloc(zlibAfterBufSize); + else + zlibAfterBuf = (unsigned char *)xrealloc(zlibAfterBuf, zlibAfterBufSize); + } + + /* + * Convert pixel data to client format. + */ + (*cl->translateFn)(cl->pScreen, cl->translateLookupTable, + &pVNC->rfbServerFormat, + &cl->format, zlibBeforeBuf, + pVNC->paddedWidthInBytes, w, h, x, y); + + cl->compStream.next_in = ( Bytef * )zlibBeforeBuf; + cl->compStream.avail_in = w * h * (cl->format.bitsPerPixel / 8); + cl->compStream.next_out = ( Bytef * )zlibAfterBuf; + cl->compStream.avail_out = maxCompSize; + cl->compStream.data_type = Z_BINARY; + + /* Initialize the deflation state. */ + if ( cl->compStreamInited == FALSE ) { + + cl->compStream.total_in = 0; + cl->compStream.total_out = 0; + cl->compStream.zalloc = Z_NULL; + cl->compStream.zfree = Z_NULL; + cl->compStream.opaque = Z_NULL; + + deflateInit2( &(cl->compStream), + cl->zlibCompressLevel, + Z_DEFLATED, + MAX_WBITS, + MAX_MEM_LEVEL, + Z_DEFAULT_STRATEGY ); + /* deflateInit( &(cl->compStream), Z_BEST_COMPRESSION ); */ + /* deflateInit( &(cl->compStream), Z_BEST_SPEED ); */ + cl->compStreamInited = TRUE; + + } + + previousOut = cl->compStream.total_out; + + /* Perform the compression here. */ + deflateResult = deflate( &(cl->compStream), Z_SYNC_FLUSH ); + + /* Find the total size of the resulting compressed data. */ + zlibAfterBufLen = cl->compStream.total_out - previousOut; + + if ( deflateResult != Z_OK ) { + rfbLog("zlib deflation error: %s\n", cl->compStream.msg); + return FALSE; + } + + /* Note that it is not possible to switch zlib parameters based on + * the results of the compression pass. The reason is + * that we rely on the compressor and decompressor states being + * in sync. Compressing and then discarding the results would + * cause lose of synchronization. + */ + + /* Update statics */ + cl->rfbRectanglesSent[rfbEncodingZlib]++; + cl->rfbBytesSent[rfbEncodingZlib] += (sz_rfbFramebufferUpdateRectHeader + + sz_rfbZlibHeader + zlibAfterBufLen); + + if (pVNC->ublen + sz_rfbFramebufferUpdateRectHeader + sz_rfbZlibHeader + > UPDATE_BUF_SIZE) + { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + rect.r.x = Swap16IfLE(x); + rect.r.y = Swap16IfLE(y); + rect.r.w = Swap16IfLE(w); + rect.r.h = Swap16IfLE(h); + rect.encoding = Swap32IfLE(rfbEncodingZlib); + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&rect, + sz_rfbFramebufferUpdateRectHeader); + pVNC->ublen += sz_rfbFramebufferUpdateRectHeader; + + hdr.nBytes = Swap32IfLE(zlibAfterBufLen); + + memcpy(&pVNC->updateBuf[pVNC->ublen], (char *)&hdr, sz_rfbZlibHeader); + pVNC->ublen += sz_rfbZlibHeader; + + for (i = 0; i < zlibAfterBufLen;) { + + int bytesToCopy = UPDATE_BUF_SIZE - pVNC->ublen; + + if (i + bytesToCopy > zlibAfterBufLen) { + bytesToCopy = zlibAfterBufLen - i; + } + + memcpy(&pVNC->updateBuf[pVNC->ublen], &zlibAfterBuf[i], bytesToCopy); + + pVNC->ublen += bytesToCopy; + i += bytesToCopy; + + if (pVNC->ublen == UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + } + + return TRUE; + +} + + +/* + * rfbSendRectEncodingZlib - send a given rectangle using one or more + * Zlib encoding rectangles. + */ + +Bool +rfbSendRectEncodingZlib(cl, x, y, w, h) + rfbClientPtr cl; + int x, y, w, h; +{ + VNCSCREENPTR(cl->pScreen); + int maxLines; + int linesRemaining; + rfbRectangle partialRect; + + partialRect.x = x; + partialRect.y = y; + partialRect.w = w; + partialRect.h = h; + + /* Determine maximum pixel/scan lines allowed per rectangle. */ + maxLines = ( ZLIB_MAX_SIZE(w) / w ); + + /* Initialize number of scan lines left to do. */ + linesRemaining = h; + + /* Loop until all work is done. */ + while ( linesRemaining > 0 ) { + + int linesToComp; + + if ( maxLines < linesRemaining ) + linesToComp = maxLines; + else + linesToComp = linesRemaining; + + partialRect.h = linesToComp; + + /* Encode (compress) and send the next rectangle. */ + if ( ! rfbSendOneRectEncodingZlib( cl, + partialRect.x, + partialRect.y, + partialRect.w, + partialRect.h )) { + + return FALSE; + } + + /* Technically, flushing the buffer here is not extrememly + * efficient. However, this improves the overall throughput + * of the system over very slow networks. By flushing + * the buffer with every maximum size zlib rectangle, we + * improve the pipelining usage of the server CPU, network, + * and viewer CPU components. Insuring that these components + * are working in parallel actually improves the performance + * seen by the user. + * Since, zlib is most useful for slow networks, this flush + * is appropriate for the desired behavior of the zlib encoding. + */ + if (( pVNC->ublen > 0 ) && + ( linesToComp == maxLines )) { + if (!rfbSendUpdateBuf(cl)) { + + return FALSE; + } + } + + /* Update remaining and incremental rectangle location. */ + linesRemaining -= linesToComp; + partialRect.y += linesToComp; + + } + + return TRUE; + +} + + diff --git a/hw/xfree86/Makefile.am b/hw/xfree86/Makefile.am index 03c2c3a..06329a8 100644 diff --git a/hw/xfree86/dixmods/Makefile.am b/hw/xfree86/dixmods/Makefile.am index efc5f4a..98d7302 100644 diff --git a/hw/xfree86/vnc/.gitignore b/hw/xfree86/vnc/.gitignore new file mode 100644 index 0000000..d14621f --- /dev/null +++ b/hw/xfree86/vnc/.gitignore @@ -0,0 +1,36 @@ +auth.c +cmap.c +corre.c +cursor.c +cutpaste.c +d3des.c +d3des.h +dispcur.c +draw.c +hextile.c +httpd.c +kbdptr.c +keyboard.h +loginauth.c +rdp.c +rfb.h +rfbkeyb.c +rfbmouse.c +rfbproto.h +rfbserver.c +rre.c +sockets.c +sprite.c +sprite.h +spritest.h +stats.c +tableinitcmtemplate.c +tableinittctemplate.c +tabletranstemplate.c +tight.c +translate.c +vncauth.c +vncauth.h +vncext.c +xistubs.c +zlib.c diff --git a/hw/xfree86/vnc/Makefile.am b/hw/xfree86/vnc/Makefile.am new file mode 100644 index 0000000..6f5cd76 --- /dev/null +++ b/hw/xfree86/vnc/Makefile.am @@ -0,0 +1,51 @@ +AM_CFLAGS = $(DIX_CFLAGS) + +libvnc_la_LTLIBRARIES = libvnc.la +libvnc_la_CFLAGS = -I$(top_srcdir)/hw/xfree86/common \ + -I$(top_srcdir)/hw/xfree86/os-support \ + -I$(top_srcdir)/hw/xfree86/os-support/bus \ + -I$(top_srcdir)/mi \ + -I$(top_srcdir)/render \ + -I$(top_srcdir)/GL/glx \ + -I$(top_srcdir)/GL/include \ + -I$(top_builddir)/GL/include \ + -I@MESA_SOURCE@/include \ + -DHAVE_XORG_CONFIG_H \ + -DHAVE_DIX_CONFIG_H \ + -DXFree86LOADER \ + -DXFREE86VNC=1 \ + -DCHROMIUM=1 \ + $(AM_CFLAGS) + +libvnc_la_LIBADD = -ljpeg -lcrypt +libvnc_la_LDFLAGS = -module -avoid-version +libvnc_ladir = $(moduledir)/extensions +libvnc_la_SOURCES = \ + auth.c \ + cmap.c \ + corre.c \ + cursor.c \ + cutpaste.c \ + d3des.c \ + dispcur.c \ + draw.c \ + hextile.c \ + httpd.c \ + kbdptr.c \ + loginauth.c \ + rdp.c \ + rfbkeyb.c \ + rfbmouse.c \ + rfbserver.c \ + rre.c \ + sockets.c \ + sprite.c \ + stats.c \ + tight.c \ + translate.c \ + vncInit.c \ + vncauth.c \ + vncext.c \ + zlib.c + +#sdk_HEADERS = vncint.h diff --git a/hw/xfree86/vnc/README b/hw/xfree86/vnc/README new file mode 100644 index 0000000..64aa888 --- /dev/null +++ b/hw/xfree86/vnc/README @@ -0,0 +1 @@ +This directory contains the sources for building the vnc.so server extension module. diff --git a/hw/xfree86/vnc/vncInit.c b/hw/xfree86/vnc/vncInit.c new file mode 100644 index 0000000..e07ac8f --- /dev/null +++ b/hw/xfree86/vnc/vncInit.c @@ -0,0 +1,618 @@ +/* + * Copyright (C) 2002 Alan Hourihane. 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. + * + * Author: Alan Hourihane + */ +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "../ramdac/xf86CursorPriv.h" +#include "rfb.h" +#include "vncint.h" + +#include "xf86.h" +#include "xf86_OSproc.h" +#include "xf86Resources.h" +#include "xf86Version.h" + +int vncScreenPrivateIndex = -1; +int inetdSock = -1; +Atom VNC_LAST_CLIENT_ID = 0; +Atom VNC_CONNECT = 0; +char *desktopName = "x11"; +char rfbThisHost[256]; + +DevPrivateKey VNCScreenKey = &VNCScreenKey; + +extern void VncExtensionInit(void); + +Bool VNCInit(ScreenPtr pScreen, unsigned char *FBStart); + +#ifndef XFree86LOADER +static unsigned long VNCGeneration = 0; +#endif +static const OptionInfoRec *VNCAvailableOptions(void *unused); +static void rfbWakeupHandler (int i, pointer blockData, unsigned long err, pointer pReadmask); + +static Bool vncCursorRealizeCursor(ScreenPtr, CursorPtr); +static Bool vncCursorUnrealizeCursor(ScreenPtr, CursorPtr); +static void vncCursorSetCursor(ScreenPtr, CursorPtr, int, int); +static void vncCursorMoveCursor(ScreenPtr, int, int); +static Bool vncDisplayCursor(ScreenPtr, CursorPtr); + +static miPointerSpriteFuncRec vncCursorSpriteFuncs = { + vncCursorRealizeCursor, + vncCursorUnrealizeCursor, + vncCursorSetCursor, + vncCursorMoveCursor +}; + +/* + * VNC Config options + */ + +typedef enum { + OPTION_USEVNC, + OPTION_RFBPORT, + OPTION_HTTPPORT, + OPTION_ALWAYS_SHARED, + OPTION_NEVER_SHARED, + OPTION_DONT_DISCONNECT, + OPTION_HTTPDIR, + OPTION_PASSWD_FILE, + OPTION_USER_ACCEPT, + OPTION_LOCALHOST, + OPTION_INTERFACE, + OPTION_VIEWONLY, + OPTION_LOGIN_AUTH, +} VNCOpts; + +static const OptionInfoRec VNCOptions[] = { + {OPTION_USEVNC, "usevnc", OPTV_BOOLEAN, {0}, FALSE }, + {OPTION_RFBPORT, "rfbport", OPTV_INTEGER, {0}, FALSE }, + {OPTION_HTTPPORT, "httpport", OPTV_INTEGER, {0}, FALSE }, + {OPTION_ALWAYS_SHARED, "alwaysshared", OPTV_BOOLEAN, {0}, FALSE }, + {OPTION_NEVER_SHARED, "nevershared", OPTV_BOOLEAN, {0}, FALSE }, + {OPTION_DONT_DISCONNECT, "dontdisconnect", OPTV_BOOLEAN, {0}, FALSE }, + {OPTION_HTTPDIR, "httpdir", OPTV_STRING, {0}, FALSE }, + {OPTION_PASSWD_FILE, "rfbauth", OPTV_STRING, {0}, FALSE }, + {OPTION_USER_ACCEPT, "useraccept", OPTV_BOOLEAN, {0}, FALSE }, + {OPTION_LOCALHOST, "localhost", OPTV_BOOLEAN, {0}, FALSE }, + {OPTION_INTERFACE, "interface", OPTV_STRING, {0}, FALSE }, + {OPTION_VIEWONLY, "viewonly", OPTV_BOOLEAN, {0}, FALSE }, + {OPTION_LOGIN_AUTH, "loginauth", OPTV_BOOLEAN, {0}, FALSE }, + { -1, NULL, OPTV_NONE, {0}, FALSE } +}; + +/*ARGSUSED*/ +static const OptionInfoRec * +VNCAvailableOptions(void *unused) +{ + return (VNCOptions); +} + +/* + * rfbLog prints a time-stamped message to the log file (stderr). + */ + +void rfbLog(char *format, ...) +{ + va_list ap; + char buf[256]; + time_t clock; + + time(&clock); + strftime(buf, 255, "%d/%m/%Y %H:%M:%S ", localtime(&clock)); + xf86DrvMsgVerb(-1, X_INFO, 1, buf); + + va_start(ap, format); + xf86VDrvMsgVerb(-1, X_NONE, 1, format, ap); + va_end(ap); +} + +void rfbLogPerror(char *str) +{ + rfbLog(""); + perror(str); +} + +/* + * Called by vncCreateScreenResources() + */ +Bool +VNCInit(ScreenPtr pScreen, unsigned char *FBStart) +{ + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + VisualPtr visual; + vncScreenPtr pScreenPriv; + OptionInfoPtr options; + char *interface_str = NULL; + miPointerScreenPtr PointPriv; + xf86CursorScreenPtr xf86CursorPriv; +#ifdef RENDER + PictureScreenPtr ps; +#endif + + if (!FBStart) + return FALSE; + +#ifndef XFree86LOADER + if (VNCGeneration != serverGeneration) { + VncExtensionInit(); + VNCGeneration = serverGeneration; + } +#endif + + if (!(pScreenPriv = xalloc(sizeof(vncScreenRec)))) + return FALSE; + + dixSetPrivate(&pScreen->devPrivates, VNCScreenKey, pScreenPriv); + + options = xnfalloc(sizeof(VNCOptions)); + (void)memcpy(options, VNCOptions, sizeof(VNCOptions)); + xf86ProcessOptions(pScrn->scrnIndex, pScrn->options, options); + + if (xf86ReturnOptValBool(options, OPTION_USEVNC, FALSE)) { + xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "VNC enabled\n"); + } else { + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "VNC disabled\n"); + xfree(options); + return FALSE; + } + + pScreenPriv->rfbAuthTries = 0; + pScreenPriv->rfbAuthTooManyTries = FALSE; + pScreenPriv->timer = NULL; + pScreenPriv->udpPort = 0; + pScreenPriv->rfbListenSock = -1; + pScreenPriv->udpSock = -1; + pScreenPriv->udpSockConnected = FALSE; + pScreenPriv->httpListenSock = -1; + pScreenPriv->httpSock = -1; + pScreenPriv->maxFd = 0; + pScreenPriv->rfbAuthPasswdFile = NULL; + pScreenPriv->httpDir = NULL; + pScreenPriv->rfbInstalledColormap = NULL; + pScreenPriv->interface.s_addr = htonl (INADDR_ANY); + + pScreenPriv->rfbPort = 0; + xf86GetOptValInteger(options, OPTION_RFBPORT, &pScreenPriv->rfbPort); + pScreenPriv->httpPort = 0; + xf86GetOptValInteger(options, OPTION_HTTPPORT, &pScreenPriv->httpPort); + pScreenPriv->rfbAuthPasswdFile = + xf86GetOptValString(options, OPTION_PASSWD_FILE); + pScreenPriv->httpDir = + xf86GetOptValString(options, OPTION_HTTPDIR); + pScreenPriv->rfbAlwaysShared = FALSE; + xf86GetOptValBool(options, OPTION_ALWAYS_SHARED, + &pScreenPriv->rfbAlwaysShared); + pScreenPriv->rfbNeverShared = FALSE; + xf86GetOptValBool(options, OPTION_NEVER_SHARED, + &pScreenPriv->rfbNeverShared); + pScreenPriv->rfbUserAccept = FALSE; + xf86GetOptValBool(options, OPTION_USER_ACCEPT, + &pScreenPriv->rfbUserAccept); + pScreenPriv->rfbViewOnly = FALSE; + xf86GetOptValBool(options, OPTION_VIEWONLY, + &pScreenPriv->rfbViewOnly); + pScreenPriv->rfbDontDisconnect = FALSE; + xf86GetOptValBool(options, OPTION_DONT_DISCONNECT, + &pScreenPriv->rfbDontDisconnect); + pScreenPriv->loginAuthEnabled = FALSE; + xf86GetOptValBool(options, OPTION_LOGIN_AUTH, + &pScreenPriv->loginAuthEnabled); + + if (xf86ReturnOptValBool(options, OPTION_LOCALHOST, FALSE)) + pScreenPriv->interface.s_addr = htonl (INADDR_LOOPBACK); + + interface_str = xf86GetOptValString(options, OPTION_INTERFACE); + + if (interface_str && pScreenPriv->interface.s_addr == htonl(INADDR_ANY)) { + Bool failed = FALSE; + struct in_addr got; + unsigned long octet; + char *p = interface_str, *end; + int q; + + for (q = 0; q < 4; q++) { + octet = strtoul (p, &end, 10); + + if (p == end || octet > 255) + failed = TRUE; + + if ((q < 3 && *end != '.') || + (q == 3 && *end != '\0')) + failed = TRUE; + + got.s_addr = (got.s_addr << 8) | octet; + p = end + 1; + } + + if (!failed) + pScreenPriv->interface.s_addr = htonl (got.s_addr); + else + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "VNC interface option malformed, not using.\n"); + } + + xfree(options); + + if (!VNC_LAST_CLIENT_ID) + VNC_LAST_CLIENT_ID = MakeAtom("VNC_LAST_CLIENT_ID", + strlen("VNC_LAST_CLIENT_ID"), TRUE); + if (!VNC_CONNECT) + VNC_CONNECT = MakeAtom("VNC_CONNECT", strlen("VNC_CONNECT"), TRUE); + + rfbInitSockets(pScreen); + if (inetdSock == -1) + httpInitSockets(pScreen); + +#ifdef CORBA + initialiseCORBA(argc, argv, desktopName); +#endif + + pScreenPriv->width = pScrn->virtualX; + pScreenPriv->height = pScrn->virtualY; + pScreenPriv->depth = pScrn->depth; + pScreenPriv->paddedWidthInBytes = PixmapBytePad(pScrn->displayWidth, pScrn->depth); + pScreenPriv->bitsPerPixel = rfbBitsPerPixel(pScrn->depth); + pScreenPriv->pfbMemory = FBStart; + pScreenPriv->oldpfbMemory = FBStart; + + pScreenPriv->cursorIsDrawn = TRUE; + pScreenPriv->dontSendFramebufferUpdate = FALSE; + + pScreenPriv->CloseScreen = pScreen->CloseScreen; + pScreenPriv->CreateGC = pScreen->CreateGC; + pScreenPriv->PaintWindowBackground = pScreen->PaintWindowBackground; + pScreenPriv->PaintWindowBorder = pScreen->PaintWindowBorder; + pScreenPriv->CopyWindow = pScreen->CopyWindow; + pScreenPriv->ClearToBackground = pScreen->ClearToBackground; + pScreenPriv->RestoreAreas = pScreen->RestoreAreas; + pScreenPriv->WakeupHandler = pScreen->WakeupHandler; + pScreenPriv->EnableDisableFBAccess = pScrn->EnableDisableFBAccess; + pScreenPriv->InstallColormap = pScreen->InstallColormap; + pScreenPriv->UninstallColormap = pScreen->UninstallColormap; + pScreenPriv->ListInstalledColormaps = pScreen->ListInstalledColormaps; + pScreenPriv->StoreColors = pScreen->StoreColors; + pScreenPriv->DisplayCursor = pScreen->DisplayCursor; +#ifdef CHROMIUM + pScreenPriv->RealizeWindow = pScreen->RealizeWindow; + pScreenPriv->UnrealizeWindow = pScreen->UnrealizeWindow; + pScreenPriv->DestroyWindow = pScreen->DestroyWindow; + pScreenPriv->PositionWindow = pScreen->PositionWindow; + pScreenPriv->ResizeWindow = pScreen->ResizeWindow; + pScreenPriv->ClipNotify = pScreen->ClipNotify; +#endif +#ifdef RENDER + ps = GetPictureScreenIfSet(pScreen); + if (ps) + pScreenPriv->Composite = ps->Composite; +#endif + pScreen->CloseScreen = rfbCloseScreen; + pScreen->CreateGC = rfbCreateGC; + pScreen->PaintWindowBackground = rfbPaintWindowBackground; + pScreen->PaintWindowBorder = rfbPaintWindowBorder; + pScreen->CopyWindow = rfbCopyWindow; + pScreen->ClearToBackground = rfbClearToBackground; + pScreen->RestoreAreas = rfbRestoreAreas; + pScreen->WakeupHandler = rfbWakeupHandler; + pScrn->EnableDisableFBAccess = rfbEnableDisableFBAccess; + pScreen->InstallColormap = rfbInstallColormap; + pScreen->UninstallColormap = rfbUninstallColormap; + pScreen->ListInstalledColormaps = rfbListInstalledColormaps; + pScreen->StoreColors = rfbStoreColors; + pScreen->DisplayCursor = vncDisplayCursor; /* it's defined in here */ + +#ifdef CHROMIUM + pScreen->RealizeWindow = rfbRealizeWindow; + pScreen->UnrealizeWindow = rfbUnrealizeWindow; + pScreen->DestroyWindow = rfbDestroyWindow; + pScreen->PositionWindow = rfbPositionWindow; + pScreen->ResizeWindow = rfbResizeWindow; + pScreen->ClipNotify = rfbClipNotify; +#endif +#ifdef RENDER + if (ps) + ps->Composite = rfbComposite; +#endif + + for (visual = pScreen->visuals; visual->vid != pScreen->rootVisual; visual++) + ; + + if (!visual) { + ErrorF("rfbScreenInit: couldn't find root visual\n"); + return FALSE; + } + + pScreenPriv->rfbServerFormat.bitsPerPixel = pScrn->bitsPerPixel; + pScreenPriv->rfbServerFormat.depth = pScrn->depth; + pScreenPriv->rfbServerFormat.bigEndian = !(*(char *)&rfbEndianTest); + pScreenPriv->rfbServerFormat.trueColour = (visual->class == TrueColor); + if (pScreenPriv->rfbServerFormat.trueColour) { + pScreenPriv->rfbServerFormat.redMax = visual->redMask >> visual->offsetRed; + pScreenPriv->rfbServerFormat.greenMax = visual->greenMask >> visual->offsetGreen; + pScreenPriv->rfbServerFormat.blueMax = visual->blueMask >> visual->offsetBlue; + pScreenPriv->rfbServerFormat.redShift = visual->offsetRed; + pScreenPriv->rfbServerFormat.greenShift = visual->offsetGreen; + pScreenPriv->rfbServerFormat.blueShift = visual->offsetBlue; + } else { + pScreenPriv->rfbServerFormat.redMax + = pScreenPriv->rfbServerFormat.greenMax + = pScreenPriv->rfbServerFormat.blueMax = 0; + pScreenPriv->rfbServerFormat.redShift + = pScreenPriv->rfbServerFormat.greenShift + = pScreenPriv->rfbServerFormat.blueShift = 0; + } + + PointPriv = dixLookupPrivate(&(pScreen)->devPrivates, miPointerScreenKey); + + pScreenPriv->spriteFuncs = PointPriv->spriteFuncs; + PointPriv->spriteFuncs = &vncCursorSpriteFuncs; + + xf86CursorPriv = dixLookupPrivate(&(pScreen)->devPrivates, xf86CursorScreenKey); + + if (xf86CursorPriv) { + pScreenPriv->UseHWCursor = xf86CursorPriv->CursorInfoPtr->UseHWCursor; + xf86CursorPriv->CursorInfoPtr->UseHWCursor = vncUseHWCursor; +#ifdef ARGB_CURSOR + pScreenPriv->UseHWCursorARGB = xf86CursorPriv->CursorInfoPtr->UseHWCursorARGB; + xf86CursorPriv->CursorInfoPtr->UseHWCursorARGB = vncUseHWCursorARGB; +#endif + pScreenPriv->SWCursor = &xf86CursorPriv->SWCursor; + } + + return TRUE; +} + +/****** miPointerSpriteFunctions *******/ + +static Bool +vncCursorRealizeCursor(ScreenPtr pScreen, CursorPtr pCurs) +{ + vncScreenPtr pScreenPriv = VNCPTR(pScreen); + + return (*pScreenPriv->spriteFuncs->RealizeCursor)(pScreen, pCurs); +} + +static Bool +vncCursorUnrealizeCursor(ScreenPtr pScreen, CursorPtr pCurs) +{ + vncScreenPtr pScreenPriv = VNCPTR(pScreen); + + return (*pScreenPriv->spriteFuncs->UnrealizeCursor)(pScreen, pCurs); +} + +static void +vncCursorSetCursor(ScreenPtr pScreen, CursorPtr pCurs, int x, int y) +{ + vncScreenPtr pScreenPriv = VNCPTR(pScreen); + + pScreenPriv->pCurs = pCurs; + + /* Without this call, cursor changes only appear in the viewer when + * some other drawing has occured. Added by BrianP. + */ + rfbScheduleUpdate(pScreen); + +#if 0 + if (pCurs == NullCursor) { /* means we're supposed to remove the cursor */ + if (pScreenPriv->cursorIsDrawn) + pScreenPriv->cursorIsDrawn = FALSE; + return; + } + + pScreenPriv->cursorIsDrawn = TRUE; +#endif + + (*pScreenPriv->spriteFuncs->SetCursor)(pScreen, pCurs, x, y); +} + +static void +vncCursorMoveCursor(ScreenPtr pScreen, int x, int y) +{ + vncScreenPtr pScreenPriv = VNCPTR(pScreen); + rfbClientPtr cl; + + for (cl = rfbClientHead; cl ; cl = cl->next) { + if (cl->enableCursorPosUpdates) + cl->cursorWasMoved = TRUE; + } + + (*pScreenPriv->spriteFuncs->MoveCursor)(pScreen, x, y); +} + +Bool +vncUseHWCursor(pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + vncScreenPtr pScreenPriv = VNCPTR(pScreen); + rfbClientPtr cl; + + if (!*pScreenPriv->UseHWCursor) { + /* If the driver doesn't have a UseHWCursor function we're + * basically saying we can have the HWCursor on all the time + */ + pScreenPriv->SWCursor = (Bool *)FALSE; + return TRUE; + } + + pScreenPriv->SWCursor = (Bool *)FALSE; + + /* If someone's connected, we revert to software cursor */ + for (cl = rfbClientHead; cl ; cl = cl->next) { + if (!cl->enableCursorShapeUpdates) + pScreenPriv->SWCursor = (Bool *)TRUE; + } + + if (pScreenPriv->SWCursor == (Bool *)TRUE) + return FALSE; + + return (*pScreenPriv->UseHWCursor)(pScreen, pCursor); +} + +#ifdef ARGB_CURSOR +#include "cursorstr.h" + +Bool +vncUseHWCursorARGB(pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + vncScreenPtr pScreenPriv = VNCPTR(pScreen); + rfbClientPtr cl; + + if (!*pScreenPriv->UseHWCursorARGB) { + pScreenPriv->SWCursor = (Bool *)TRUE; + return FALSE; + } + + pScreenPriv->SWCursor = (Bool *)FALSE; + + /* If someone's connected, we revert to software cursor */ + for (cl = rfbClientHead; cl ; cl = cl->next) { + if (!cl->enableCursorShapeUpdates) + pScreenPriv->SWCursor = (Bool *)TRUE; + } + + if (pScreenPriv->SWCursor == (Bool *)TRUE) + return FALSE; + + return (*pScreenPriv->UseHWCursorARGB)(pScreen, pCursor); +} +#endif + +static Bool +vncDisplayCursor(pScreen, pCursor) + ScreenPtr pScreen; + CursorPtr pCursor; +{ + vncScreenPtr pScreenPriv = VNCPTR(pScreen); + rfbClientPtr cl; + Bool ret; + + pScreen->DisplayCursor = pScreenPriv->DisplayCursor; + + for (cl = rfbClientHead; cl ; cl = cl->next) { + if (cl->enableCursorShapeUpdates) + cl->cursorWasChanged = TRUE; + } + + ret = (*pScreen->DisplayCursor)(pScreen, pCursor); + + pScreen->DisplayCursor = vncDisplayCursor; + + return ret; +} + +static void +rfbWakeupHandler ( + int i, + pointer blockData, + unsigned long err, + pointer pReadmask +){ + ScreenPtr pScreen = screenInfo.screens[i]; + vncScreenPtr pScreenPriv = VNCPTR(pScreen); + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + int sigstate = xf86BlockSIGIO(); + + rfbRootPropertyChange(pScreen); /* Check clipboard */ + + if (pScrn->vtSema) { + rfbCheckFds(pScreen); + httpCheckFds(pScreen); +#ifdef CORBA + corbaCheckFds(); +#endif + } else { + rfbCheckFds(pScreen); + } + + xf86UnblockSIGIO(sigstate); + + pScreen->WakeupHandler = pScreenPriv->WakeupHandler; + (*pScreen->WakeupHandler) (i, blockData, err, pReadmask); + pScreen->WakeupHandler = rfbWakeupHandler; +} + +#ifdef XFree86LOADER +static MODULESETUPPROTO(vncSetup); + +static XF86ModuleVersionInfo vncVersRec = +{ + "vnc", + "xf4vnc Project, see http://xf4vnc.sf.net (based on modular X.org)", + MODINFOSTRING1, + MODINFOSTRING2, + XORG_VERSION_CURRENT, + 1, 1, 0, + ABI_CLASS_EXTENSION, +#if 0 + ABI_EXTENSION_VERSION, +#else + /* Hack to allow module to work with more servers (vs. 0.3 above) */ + SET_ABI_VERSION(0, 2), +#endif + MOD_CLASS_EXTENSION, + {0,0,0,0} +}; + +XF86ModuleData vncModuleData = { + &vncVersRec, /* vers */ + vncSetup, /* ModuleSetupProc */ + NULL /* ModuleTearDownProc */ +}; + +ModuleInfoRec VNC = { + 1, /* moduleVersion */ + "VNC", /* moduleName */ + NULL, /* module pointer */ + 0, /* refCount */ + VNCAvailableOptions, /* function returning array of OptionsInfoRec */ +}; + +ExtensionModule vncExtensionModule = { + VncExtensionInit, /* initFunc */ + "VNC", /* name */ + NULL, /* disablePtr */ + NULL, /* setupFunc */ + NULL /* initDependencies */ +}; + + +/*ARGSUSED*/ +static pointer +vncSetup(pointer Module, pointer Options, int *ErrorMajor, int *ErrorMinor) +{ + LoadExtension(&vncExtensionModule, FALSE); + /* add mouse/kbd input drivers */ + vncInitMouse(); + vncInitKeyb(); + xf86Msg(X_INFO, "Ignore errors regarding the loading of the rfbmouse & rfbkeyb drivers\n"); + + return (pointer)TRUE; +} +#endif diff --git a/hw/xfree86/vnc/vncint.h b/hw/xfree86/vnc/vncint.h new file mode 100644 index 0000000..43aeaae --- /dev/null +++ b/hw/xfree86/vnc/vncint.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2002 Alan Hourihane. 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. + * + * Author: Alan Hourihane + */ + +#ifndef _VNC_H_ +#define _VNC_H_ + +#include <../ramdac/xf86Cursor.h> + +#define VNCPTR(pScreen)\ + (vncScreenPtr)(dixLookupPrivate(&(pScreen)->devPrivates, VNCScreenKey)) + +typedef struct { + int rfbPort; + int rdpPort; + int udpPort; + int rfbListenSock; + int rdpListenSock; + int udpSock; + int httpPort; + int httpListenSock; + int httpSock; + char * httpDir; + char buf[HTTP_BUF_SIZE]; + Bool udpSockConnected; + char * rfbAuthPasswdFile; + size_t buf_filled; + int maxFd; + fd_set allFds; + unsigned char * oldpfbMemory; + Bool rfbAlwaysShared; + Bool rfbNeverShared; + Bool rfbDontDisconnect; + Bool rfbUserAccept; + Bool rfbViewOnly; + unsigned char * pfbMemory; + int paddedWidthInBytes; + ColormapPtr rfbInstalledColormap; + ColormapPtr savedColormap; + rfbPixelFormat rfbServerFormat; + Bool rfbAuthTooManyTries; + int rfbAuthTries; + Bool loginAuthEnabled; + struct in_addr interface; + OsTimerPtr timer; + unsigned char updateBuf[UPDATE_BUF_SIZE]; + int ublen; + int width; + int height; + int depth; + int bitsPerPixel; + + /* The following two members are used to minimise the amount of unnecessary + drawing caused by cursor movement. Whenever any drawing affects the + part of the screen where the cursor is, the cursor is removed first and + then the drawing is done (this is what the sprite routines test for). + Afterwards, however, we do not replace the cursor, even when the cursor + is logically being moved across the screen. We only draw the cursor + again just as we are about to send the client a framebuffer update. + + We need to be careful when removing and drawing the cursor because of + their relationship with the normal drawing routines. The drawing + routines can invoke the cursor routines, but also the cursor routines + themselves end up invoking drawing routines. + + Removing the cursor (rfbSpriteRemoveCursor) is eventually achieved by + doing a CopyArea from a pixmap to the screen, where the pixmap contains + the saved contents of the screen under the cursor. Before doing this, + however, we set cursorIsDrawn to FALSE. Then, when CopyArea is called, + it sees that cursorIsDrawn is FALSE and so doesn't feel the need to + (recursively!) remove the cursor before doing it. + + Putting up the cursor (rfbSpriteRestoreCursor) involves a call to + PushPixels. While this is happening, cursorIsDrawn must be FALSE so + that PushPixels doesn't think it has to remove the cursor first. + Obviously cursorIsDrawn is set to TRUE afterwards. + + Another problem we face is that drawing routines sometimes cause a + framebuffer update to be sent to the RFB client. When the RFB client is + already waiting for a framebuffer update and some drawing to the + framebuffer then happens, the drawing routine sees that the client is + ready, so it calls rfbSendFramebufferUpdate. If the cursor is not drawn + at this stage, it must be put up, and so rfbSpriteRestoreCursor is + called. However, if the original drawing routine was actually called + from within rfbSpriteRestoreCursor or rfbSpriteRemoveCursor we don't + want this to happen. So both the cursor routines set + dontSendFramebufferUpdate to TRUE, and all the drawing routines check + this before calling rfbSendFramebufferUpdate. */ + + Bool cursorIsDrawn; /* TRUE if the cursor is currently drawn */ + Bool dontSendFramebufferUpdate; /* TRUE while removing or drawing the + cursor */ + + /* wrapped screen functions */ + + CloseScreenProcPtr CloseScreen; + CreateGCProcPtr CreateGC; + PaintWindowBackgroundProcPtr PaintWindowBackground; + PaintWindowBorderProcPtr PaintWindowBorder; + CopyWindowProcPtr CopyWindow; + ClearToBackgroundProcPtr ClearToBackground; + RestoreAreasProcPtr RestoreAreas; + ScreenWakeupHandlerProcPtr WakeupHandler; + InstallColormapProcPtr InstallColormap; + UninstallColormapProcPtr UninstallColormap; + ListInstalledColormapsProcPtr ListInstalledColormaps; + StoreColorsProcPtr StoreColors; + xf86EnableDisableFBAccessProc *EnableDisableFBAccess; + miPointerSpriteFuncPtr spriteFuncs; + DisplayCursorProcPtr DisplayCursor; + CursorPtr pCurs; + Bool (*UseHWCursor)(ScreenPtr, CursorPtr); + Bool (*UseHWCursorARGB)(ScreenPtr, CursorPtr); + Bool *SWCursor; +#ifdef CHROMIUM + RealizeWindowProcPtr RealizeWindow; + UnrealizeWindowProcPtr UnrealizeWindow; + DestroyWindowProcPtr DestroyWindow; + ResizeWindowProcPtr ResizeWindow; + PositionWindowProcPtr PositionWindow; + ClipNotifyProcPtr ClipNotify; +#endif +#ifdef RENDER + CompositeProcPtr Composite; +#endif + +} vncScreenRec, *vncScreenPtr; + +extern Bool vncUseHWCursor(ScreenPtr pScreen, CursorPtr pCurs); +extern Bool vncUseHWCursorARGB(ScreenPtr pScreen, CursorPtr pCurs); +extern void rfbEnableDisableFBAccess (int index, Bool enable); + +#endif /* _VNC_H_ */ + diff --git a/mi/miinitext.c b/mi/miinitext.c index 3c55eeb..4cb6584 100644 --- a/mi/miinitext.c +++ b/mi/miinitext.c @@ -79,6 +79,18 @@ SOFTWARE. #undef XF86VIDMODE #endif +#ifdef VNCSERVER +#undef COMPOSITE +#undef DAMAGE +#undef DBE +#undef RANDR +#undef XF86MISC +#undef XFreeXDGA +#undef XF86DRI +#undef XF86VIDMODE +#undef XFIXES +#endif + #include "misc.h" #include "extension.h" #include "micmap.h" diff --git a/xcliplist/Makefile.am b/xcliplist/Makefile.am new file mode 100644 index 0000000..3f13acc --- /dev/null +++ b/xcliplist/Makefile.am @@ -0,0 +1,18 @@ +#noinst_LTLIBRARIES = libxcliplist.la +libxcliplist_la_LTLIBRARIES = libxcliplist.la + +#AM_CFLAGS = $(DIX_CFLAGS) @SERVER_DEFINES@ @LOADER_DEFINES@ +AM_CFLAGS = $(DIX_CFLAGS) + +libxcliplist_la_LDFLAGS = -module -avoid-version +libxcliplist_ladir = $(moduledir)/extensions + +libxcliplist_la_SOURCES = \ + cliplist.c \ + cliplistmod.c + +## cliplistmod.c + +if XORG +sdk_HEADERS = +endif diff --git a/xcliplist/cliplist.c b/xcliplist/cliplist.c new file mode 100644 index 0000000..290eb79 --- /dev/null +++ b/xcliplist/cliplist.c @@ -0,0 +1,128 @@ +/* + * Server-side code for the Xcliplist extension + */ + +#if HAVE_DIX_CONFIG_H +#include "dix-config.h" +#endif + +#include +#include + +#include "dixstruct.h" +#include "extnsionst.h" +#include "windowstr.h" + +#define _XCLIPLIST_SERVER_ +#include + + +static int XClipListErrorBase; +static unsigned char XClipListReqCode = 0; + +static void +XClipListResetProc(ExtensionEntry* extEntry) +{ + (void)extEntry; +} + +static int +ProcXClipListQueryVersion(ClientPtr client) +{ + xXClipListQueryVersionReply rep; + + REQUEST_SIZE_MATCH(xXClipListQueryVersionReq); + rep.type = X_Reply; + rep.length = 0; + rep.sequenceNumber = client->sequence; + rep.majorVersion = XCLIPLIST_MAJOR_VERSION; + rep.minorVersion = XCLIPLIST_MINOR_VERSION; + rep.patchVersion = XCLIPLIST_PATCH_VERSION; + + WriteToClient(client, sizeof(xXClipListQueryVersionReply), (char *)&rep); + return (client->noClientException); +} + +static int +ProcXGetClipList(ClientPtr client) +{ + xXGetClipListReply rep; + WindowPtr pWin; + int i; + BoxPtr pClipRects = NULL; + short xorig, yorig; + REQUEST(xXGetClipListReq); + REQUEST_SIZE_MATCH(xXGetClipListReq); + + rep.type = X_Reply; + rep.length = 0; + rep.sequenceNumber = client->sequence; + + if(!(pWin = (WindowPtr)LOOKUP_DRAWABLE(stuff->windowid, client) )) + { + client->errorValue = stuff->windowid; + return (BadWindow); + } + + rep.num_entries = REGION_NUM_RECTS(&pWin->clipList); + + WriteToClient(client, sizeof(xXGetClipListReply), (char *)&rep); + + pClipRects = REGION_RECTS(&pWin->clipList); + + xorig = pWin->drawable.x; + yorig = pWin->drawable.y; + + for (i = 0; i < rep.num_entries; i++) { + BoxRec box; + /* translate clip rect from screen coords to window coords */ + box.x1 = pClipRects[i].x1 - xorig; + box.y1 = pClipRects[i].y1 - yorig; + box.x2 = pClipRects[i].x2 - xorig; + box.y2 = pClipRects[i].y2 - yorig; + /*memcpy(&box, &pClipRects[i], sizeof(BoxRec));*/ + WriteToClient(client, sizeof(BoxRec), (char *)&box); + } + + return (client->noClientException); +} + +static int +ProcXClipListDispatch (ClientPtr client) +{ + REQUEST(xReq); + + switch (stuff->data) { + case X_XClipListQueryVersion: + return ProcXClipListQueryVersion(client); + case X_XGetClipList: + return ProcXGetClipList(client); + default: + return BadRequest; + } +} + +static int +SProcXClipListDispatch (ClientPtr client) +{ + REQUEST(xReq); + (void)stuff; + return XClipListErrorBase + XClipListClientNotLocal; +} + +void +XClipListExtensionInit(void) +{ + ExtensionEntry* extEntry; + + if ((extEntry = AddExtension(XCLIPLISTNAME, + XClipListNumberEvents, + XClipListNumberErrors, + ProcXClipListDispatch, + SProcXClipListDispatch, + XClipListResetProc, + StandardMinorOpcode))) { + XClipListReqCode = (unsigned char)extEntry->base; + XClipListErrorBase = extEntry->errorBase; + } +} diff --git a/xcliplist/cliplistmod.c b/xcliplist/cliplistmod.c new file mode 100644 index 0000000..c365ca2 --- /dev/null +++ b/xcliplist/cliplistmod.c @@ -0,0 +1,46 @@ + +#include "../hw/xfree86/common/xf86Module.h" + +extern Bool noTestExtensions; + +static MODULESETUPPROTO(xcliplistSetup); + +extern void XClipListExtensionInit(INITARGS); + +ExtensionModule xcliplistExt = { + XClipListExtensionInit, + "XClipList", + &noTestExtensions, + NULL, + NULL +}; + +static XF86ModuleVersionInfo VersRec = { + "XClipList", + MODULEVENDORSTRING, + MODINFOSTRING1, + MODINFOSTRING2, + XF86_VERSION_CURRENT, /* XXX fix? */ + 1, 13, 0, + ABI_CLASS_EXTENSION, +#if 0 + ABI_EXTENSION_VERSION, +#else + /* Hack to allow module to work with more servers (vs. 0.3 above) */ + SET_ABI_VERSION(0, 2), +#endif + MOD_CLASS_EXTENSION, + {0,0,0,0} +}; + +XF86ModuleData xcliplistModuleData = { &VersRec, xcliplistSetup, NULL }; + +static pointer +xcliplistSetup(pointer module, pointer opts, int *errmaj, int *errmin) +{ + LoadExtension(&xcliplistExt, FALSE); + + /* Need a non-NULL return value to indicate success */ + return (pointer)1; +} + --- a/configure.ac.orig 2009-02-25 21:04:51.000000000 +0100 +++ a/configure.ac 2009-03-02 22:15:25.000000000 +0100 @@ -553,6 +553,8 @@ AC_ARG_ENABLE(xfree86-utils, AS_HELP dnl DDXes. AC_ARG_ENABLE(xorg, AS_HELP_STRING([--enable-xorg], [Build Xorg server (default: auto)]), [XORG=$enableval], [XORG=auto]) +AC_ARG_ENABLE(xcliplist, AS_HELP_STRING([--enable-xcliplist], [Build XClipList extension (default: auto)]), [XCLIPLIST=$enableval], [XCLIPLIST=auto]) +AC_ARG_ENABLE(vnc, AS_HELP_STRING([--enable-vnc], [Build Xvnc server (default: yes)]), [VNC=$enableval], [VNC=auto]) AC_ARG_ENABLE(dmx, AS_HELP_STRING([--enable-dmx], [Build DMX server (default: no)]), [DMX=$enableval], [DMX=no]) AC_ARG_ENABLE(xvfb, AS_HELP_STRING([--enable-xvfb], [Build Xvfb server (default: yes)]), [XVFB=$enableval], [XVFB=yes]) AC_ARG_ENABLE(xnest, AS_HELP_STRING([--enable-xnest], [Build Xnest server (default: auto)]), [XNEST=$enableval], [XNEST=auto]) @@ -987,6 +989,20 @@ XI_INC='-I$(top_srcdir)/Xi' AM_CONDITIONAL(XF86UTILS, test "x$XF86UTILS" = xyes) +if test "x$XCLIPLIST" = xauto; then + if test "x$XORG" = xno; then + XCLIPLIST=no + else + PKG_CHECK_MODULES([XCLIPLIST], [xcliplistproto xcliplist], [XCLIPLIST=yes], [XCLIPLIST=no]) + fi +fi +AM_CONDITIONAL(XCLIPLIST, [test "x$XCLIPLIST" = xyes]) +if test "x$XCLIPLIST" = xyes; then + AC_DEFINE(XCLIPLIST, 1, [Support XClipList extension]) + REQUIRED_MODULES="$REQUIRED_MODULES xcliplistproto" + XCLIPLIST_LIB='$(top_builddir)/xcliplist/libxcliplist.la' +fi + AC_DEFINE(SHAPE, 1, [Support SHAPE extension]) AC_DEFINE(XKB, 1, [Build XKB]) @@ -1182,6 +1198,40 @@ dnl ------------------------------------ dnl DDX section. dnl --------------------------------------------------------------------------- +dnl VNC DDX + +AC_MSG_CHECKING([whether to build Xvnc DDX]) +PKG_CHECK_MODULES([VNCMODULES], [xmuu xext x11 xrender xfont xi vncproto xau $XDMCP_MODULES $PIXMAN], [have_vnc=yes], [have_vnc=no]) +if test "x$VNC" = xauto; then + VNC="$have_vnc" +fi +AC_MSG_RESULT([$VNC]) +AM_CONDITIONAL(VNC, [test "x$VNC" = xyes]) + +if test "x$VNC" = xyes; then + if test "x$have_vnc" = xno; then + AC_MSG_ERROR([Xvnc build explicitly requested, but required + modules not found.]) + fi + XVNC_CFLAGS="-DVNCSERVER -DHAVE_XVNC_CONFIG_H" + AC_SUBST([XVNC_CFLAGS]) + VNC_INCLUDES="$XEXT_INC $RENDER_INC $XTRAP_INC $RECORD_INC" + XVNC_LIBS="$CONFIG_LIB $XSERVER_LIBS $FB_LIB $MI_LIB $FIXES_LIB $XEXT_LIB $DBE_LIB $XTRAP_LIB $RECORD_LIB $GLX_LIBS $RENDER_LIB $RANDR_LIB $DAMAGE_LIB $MIEXT_DAMAGE_LIB $MIEXT_SHADOW_LIB $XI_LIB $XKB_LIB $XKB_STUB_LIB $COMPOSITE_LIB $XPSTUBS_LIB $CWRAP_LIB $OS_LIB $LIBS" + AC_SUBST([XVNC_LIBS]) + + if test "x$GLX" = xyes; then + PKG_CHECK_MODULES([GL], [glproto]) + fi + PKG_CHECK_MODULES([XVNCCONFIG_DEP], [xaw7 xmu xt xpm x11]) + AC_SUBST(XVNCCONFIG_DEP_CFLAGS) + AC_SUBST(XVNCCONFIG_DEP_LIBS) + ${CONFIG_SHELL-/bin/sh} $srcdir/hw/vnc/symlink-vnc.sh \ + $srcdir/hw/vnc $srcdir/hw/xfree86/vnc && \ + ${CONFIG_SHELL-/bin/sh} $srcdir/hw/vnc/symlink-vnc.sh \ + $srcdir/hw/vnc $srcdir/hw/dmx/vnc || \ + AC_MSG_ERROR([Failed symlinking VNC sources]) +fi + dnl Xvfb DDX AC_MSG_CHECKING([whether to build Xvfb DDX]) @@ -1824,6 +1874,7 @@ Xext/Makefile Xi/Makefile xfixes/Makefile exa/Makefile +xcliplist/Makefile hw/Makefile hw/xfree86/Makefile hw/xfree86/common/Makefile @@ -1858,6 +1909,7 @@ hw/xfree86/ramdac/Makefile hw/xfree86/shadowfb/Makefile hw/xfree86/vbe/Makefile hw/xfree86/vgahw/Makefile +hw/xfree86/vnc/Makefile hw/xfree86/x86emu/Makefile hw/xfree86/xaa/Makefile hw/xfree86/xf8_16bpp/Makefile @@ -1869,8 +1921,10 @@ hw/dmx/doc/Makefile hw/dmx/examples/Makefile hw/dmx/input/Makefile hw/dmx/glxProxy/Makefile +hw/dmx/vnc/Makefile hw/dmx/Makefile hw/vfb/Makefile +hw/vnc/Makefile hw/xnest/Makefile hw/xwin/Makefile hw/xquartz/Makefile --- a/hw/Makefile.am.orig 2008-10-13 01:18:43.000000000 +0200 +++ a/hw/Makefile.am 2009-03-02 22:17:39.000000000 +0100 @@ -22,6 +22,10 @@ if KDRIVE KDRIVE_SUBDIRS = kdrive endif +if VNC +VNC_SUBDIRS = vnc +endif + if XQUARTZ XQUARTZ_SUBDIRS = xquartz endif @@ -32,10 +36,11 @@ SUBDIRS = \ $(XVFB_SUBDIRS) \ $(XNEST_SUBDIRS) \ $(DMX_SUBDIRS) \ + $(VNC_SUBDIRS) \ $(KDRIVE_SUBDIRS) \ $(XQUARTZ_SUBDIRS) -DIST_SUBDIRS = dmx xfree86 vfb xnest xwin xquartz kdrive +DIST_SUBDIRS = dmx xfree86 vfb xnest xwin xquartz kdrive vnc relink: for i in $(SUBDIRS) ; do $(MAKE) -C $$i relink ; done --- a/hw/dmx/input/dmxinputinit.c.orig 2009-02-25 20:12:11.000000000 +0100 +++ a/hw/dmx/input/dmxinputinit.c 2009-03-02 22:21:09.000000000 +0100 @@ -391,6 +391,10 @@ static int dmxKeyboardOn(DeviceIntPtr pD DevicePtr pDev = &pDevice->public; #endif +#ifdef DMXVNC + vncSetKeyboardDevice(pDevice); +#endif + #ifdef XKB if (noXkbExtension) { #endif @@ -476,6 +480,9 @@ static int dmxDeviceOnOff(DeviceIntPtr p break; } if (info.keyClass) { +#ifdef DMXVNC + vncSetKeyboardDevice(pDevice); +#endif DevicePtr pDev = (DevicePtr) pDevice; InitKeyboardDeviceStruct(pDev, &info.keySyms, @@ -512,6 +519,9 @@ static int dmxDeviceOnOff(DeviceIntPtr p info.res[i+1], info.minres[i+1], info.maxres[i+1]); } +#ifdef DMXVNC + vncSetPointerDevice(pDevice); +#endif } if (info.focusClass) InitFocusClassDeviceStruct(pDevice); if (info.proximityClass) InitProximityClassDeviceStruct(pDevice); --- a/hw/xfree86/Makefile.am.orig 2009-02-25 20:12:11.000000000 +0100 +++ a/hw/xfree86/Makefile.am 2009-03-02 22:22:26.000000000 +0100 @@ -4,6 +4,10 @@ if DRI DRI_SUBDIR = dri endif +if VNC +VNC_SUBDIR = vnc +endif + if DRI2 DRI2_SUBDIR = dri2 endif @@ -16,13 +20,13 @@ DOC_SUBDIR = doc SUBDIRS = common ddc dummylib i2c x86emu int10 fbdevhw os-support parser \ ramdac shadowfb vbe vgahw xaa \ - xf8_16bpp loader dixmods exa modes \ + xf8_16bpp loader dixmods exa modes $(VNC_SUBDIR) \ $(DRI_SUBDIR) $(DRI2_SUBDIR) $(XF86UTILS_SUBDIR) $(DOC_SUBDIR) DIST_SUBDIRS = common ddc dummylib i2c x86emu int10 fbdevhw os-support \ parser ramdac shadowfb vbe vgahw xaa \ xf8_16bpp loader dixmods dri dri2 exa modes \ - utils doc + utils doc vnc bin_PROGRAMS = Xorg --- a/hw/xfree86/dixmods/Makefile.am.orig 2008-11-14 22:27:05.000000000 +0100 +++ a/hw/xfree86/dixmods/Makefile.am 2009-03-02 22:24:14.000000000 +0100 @@ -10,6 +10,10 @@ if DBE DBEMOD = libdbe.la endif +if XCLIPLIST +XCLIPLISTMOD = libxcliplist.la +endif + if RECORD RECORDMOD = librecord.la endif @@ -21,11 +25,13 @@ module_LTLIBRARIES = libfb.la \ extsmoduledir = $(moduledir)/extensions extsmodule_LTLIBRARIES = $(RECORDMOD) \ $(DBEMOD) \ + $(XCLIPLISTMOD) \ $(GLXMODS) AM_CFLAGS = @XORG_CFLAGS@ @DIX_CFLAGS@ INCLUDES = @XORG_INCS@ \ -I$(top_srcdir)/dbe \ + -I$(top_srcdir)/xcliplist \ -I$(top_srcdir)/hw/xfree86/loader \ -I$(top_srcdir)/miext/shadow \ -I$(top_srcdir)/glx @@ -34,6 +40,10 @@ libdbe_la_LDFLAGS = -avoid-version libdbe_la_LIBADD = $(top_builddir)/dbe/libdbe.la libdbe_la_SOURCES = dbemodule.c +libxcliplist_la_LDFLAGS = -avoid-version +libxcliplist_la_LIBADD = $(top_builddir)/xcliplist/libxcliplist.la +libxcliplist_la_SOURCES = $(top_srcdir)/xcliplist/cliplistmod.c + libfb_la_LDFLAGS = -avoid-version libfb_la_LIBADD = $(top_builddir)/fb/libfb.la libfb_la_SOURCES = $(top_builddir)/fb/fbcmap_mi.c fbmodule.c