6 * Copyright (C) 2002-2003 RealVNC Ltd.
7 * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
9 * This is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This software is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this software; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
27 Copyright (c) 1993 X Consortium
29 Permission is hereby granted, free of charge, to any person obtaining
30 a copy of this software and associated documentation files (the
31 "Software"), to deal in the Software without restriction, including
32 without limitation the rights to use, copy, modify, merge, publish,
33 distribute, sublicense, and/or sell copies of the Software, and to
34 permit persons to whom the Software is furnished to do so, subject to
35 the following conditions:
37 The above copyright notice and this permission notice shall be included
38 in all copies or substantial portions of the Software.
40 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
41 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
42 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
43 IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR
44 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
45 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
46 OTHER DEALINGS IN THE SOFTWARE.
48 Except as contained in this notice, the name of the X Consortium shall
49 not be used in advertising or otherwise to promote the sale, use or
50 other dealings in this Software without prior written authorization
51 from the X Consortium.
58 #include <sys/types.h>
59 #include <sys/socket.h>
60 #include <netinet/in.h>
64 #include "X11/Xproto.h"
66 #include "scrnintstr.h"
72 #include "colormapst.h"
75 #include "mipointer.h"
76 #include "dixstruct.h"
77 #include "propertyst.h"
80 #include <sys/param.h>
89 Bool rfbLocalhostOnly;
91 extern Bool cfb16ScreenInit(ScreenPtr, pointer, int, int, int, int, int);
92 extern Bool cfb32ScreenInit(ScreenPtr, pointer, int, int, int, int, int);
94 #define RFB_DEFAULT_WIDTH 640
95 #define RFB_DEFAULT_HEIGHT 480
96 #define RFB_DEFAULT_DEPTH 8
97 #define RFB_DEFAULT_WHITEPIXEL 0
98 #define RFB_DEFAULT_BLACKPIXEL 1
100 rfbScreenInfo rfbScreen;
103 static Bool initOutputCalled = FALSE;
104 static Bool noCursor = FALSE;
105 char *desktopName = "x11";
106 Bool rfbTrace = FALSE;
108 char rfbThisHost[256];
110 Atom VNC_LAST_CLIENT_ID;
112 Atom VNC_DEFER_UPDATE;
114 static HWEventQueueType alwaysCheckForInput[2] = { 0, 1 };
115 static HWEventQueueType *mieqCheckForInput[2];
117 static char primaryOrder[4] = "";
118 static int redBits, greenBits, blueBits;
121 static Bool rfbScreenInit(int index, ScreenPtr pScreen, int argc,
123 static int rfbKeybdProc(DeviceIntPtr pDevice, int onoff);
124 static int rfbMouseProc(DeviceIntPtr pDevice, int onoff);
125 static Bool CheckDisplayNumber(int n);
127 static Bool rfbAlwaysTrue();
128 static char *rfbAllocateFramebufferMemory(rfbScreenInfoPtr prfb);
129 static Bool rfbCursorOffScreen(ScreenPtr *ppScreen, int *x, int *y);
130 static void rfbCrossScreen(ScreenPtr pScreen, Bool entering);
131 static void rfbClientStateChange(CallbackListPtr *, pointer myData,
134 static miPointerScreenFuncRec rfbPointerCursorFuncs = {
142 static char inetdDisplayNumStr[10];
144 extern char buildtime[];
148 * ddxProcessArgument is our first entry point and will be called at the
149 * very start for each argument. It is not called again on server reset.
153 ddxProcessArgument (argc, argv, i)
158 static Bool firstTime = TRUE;
162 rfbScreen.width = RFB_DEFAULT_WIDTH;
163 rfbScreen.height = RFB_DEFAULT_HEIGHT;
164 rfbScreen.depth = RFB_DEFAULT_DEPTH;
165 rfbScreen.blackPixel = RFB_DEFAULT_BLACKPIXEL;
166 rfbScreen.whitePixel = RFB_DEFAULT_WHITEPIXEL;
167 rfbScreen.pfbMemory = NULL;
168 gethostname(rfbThisHost, 255);
172 if (strcasecmp (argv[i], "-geometry") == 0) /* -geometry WxH */
174 if (i + 1 >= argc) UseMsg();
175 if (sscanf(argv[i+1],"%dx%d",
176 &rfbScreen.width,&rfbScreen.height) != 2) {
177 ErrorF("Invalid geometry %s\n", argv[i+1]);
183 if (strcasecmp (argv[i], "-depth") == 0) /* -depth D */
185 if (i + 1 >= argc) UseMsg();
186 rfbScreen.depth = atoi(argv[i+1]);
190 if (strcasecmp (argv[i], "-pixelformat") == 0) {
191 if (i + 1 >= argc) UseMsg();
192 if (sscanf(argv[i+1], "%3s%1d%1d%1d", primaryOrder,
193 &redBits, &greenBits, &blueBits) < 4) {
194 ErrorF("Invalid pixel format %s\n", argv[i+1]);
198 if (strcasecmp(primaryOrder, "bgr") == 0) {
202 } else if (strcasecmp(primaryOrder, "rgb") != 0) {
203 ErrorF("Invalid pixel format %s\n", argv[i+1]);
210 if (strcasecmp (argv[i], "-blackpixel") == 0) { /* -blackpixel n */
211 if (i + 1 >= argc) UseMsg();
212 rfbScreen.blackPixel = atoi(argv[i+1]);
216 if (strcasecmp (argv[i], "-whitepixel") == 0) { /* -whitepixel n */
217 if (i + 1 >= argc) UseMsg();
218 rfbScreen.whitePixel = atoi(argv[i+1]);
222 if (strcasecmp(argv[i], "-nocursor") == 0) {
227 if (strcasecmp(argv[i], "-desktop") == 0) { /* -desktop desktop-name */
228 if (i + 1 >= argc) UseMsg();
229 desktopName = argv[i+1];
233 if (strcasecmp(argv[i], "-inetd") == 0) { /* -inetd */
235 for (n = 1; n < 100; n++) {
236 if (CheckDisplayNumber(n))
241 FatalError("-inetd: couldn't find free display number");
243 sprintf(inetdDisplayNumStr, "%d", n);
244 display = inetdDisplayNumStr;
246 /* fds 0, 1 and 2 (stdin, out and err) are all the same socket to the
247 RFB client. OsInit() closes stdout and stdin, and we don't want
248 stderr to go to the RFB client, so make the client socket 3 and
249 close stderr. OsInit() will redirect stderr logging to an
250 appropriate log file or /dev/null if that doesn't work. */
259 if (inetdSock != -1 && argv[i][0] == ':') {
260 FatalError("can't specify both -inetd and :displaynumber");
268 * InitOutput is called every time the server resets. It should call
269 * AddScreen for each screen (but we only ever have one), and in turn this
270 * will call rfbScreenInit.
274 InitOutput(screenInfo, argc, argv)
275 ScreenInfo *screenInfo;
279 initOutputCalled = TRUE;
281 rfbLog("rdpsrv version 0.1 - built %s\n", buildtime);
282 rfbLog("Copyright (C) 2005 Steinar H. Gunderson\n");
283 rfbLog("Copyright (C) 2002-2003 RealVNC Ltd.\n");
284 rfbLog("Copyright (C) 1994-2000 AT&T Laboratories Cambridge.\n");
285 rfbLog("All Rights Reserved.\n");
286 rfbLog("See http://www.realvnc.com for information on VNC\n");
287 rfbLog("Desktop name '%s' (%s:%s)\n",desktopName,rfbThisHost,display);
289 VNC_LAST_CLIENT_ID = MakeAtom("VNC_LAST_CLIENT_ID",
290 strlen("VNC_LAST_CLIENT_ID"), TRUE);
291 VNC_CONNECT = MakeAtom("VNC_CONNECT", strlen("VNC_CONNECT"), TRUE);
292 VNC_DEFER_UPDATE = MakeAtom("VNC_DEFER_UPDATE",
293 strlen("VNC_DEFER_UPDATE"), TRUE);
296 httpInitSockets(); */
299 /* initialize pixmap formats */
301 screenInfo->imageByteOrder = IMAGE_BYTE_ORDER;
302 screenInfo->bitmapScanlineUnit = BITMAP_SCANLINE_UNIT;
303 screenInfo->bitmapScanlinePad = BITMAP_SCANLINE_PAD;
304 screenInfo->bitmapBitOrder = BITMAP_BIT_ORDER;
305 screenInfo->numPixmapFormats = 2;
307 screenInfo->formats[0].depth = 1;
308 screenInfo->formats[0].bitsPerPixel = 1;
309 screenInfo->formats[0].scanlinePad = BITMAP_SCANLINE_PAD;
311 screenInfo->formats[1].depth = rfbScreen.depth;
312 screenInfo->formats[1].bitsPerPixel = rfbBitsPerPixel(rfbScreen.depth);
313 screenInfo->formats[1].scanlinePad = BITMAP_SCANLINE_PAD;
315 rfbGCIndex = AllocateGCPrivateIndex();
316 if (rfbGCIndex < 0) {
317 FatalError("InitOutput: AllocateGCPrivateIndex failed\n");
320 if (!AddCallback(&ClientStateCallback, rfbClientStateChange, NULL)) {
321 rfbLog("InitOutput: AddCallback failed\n");
325 /* initialize screen */
327 if (AddScreen(rfbScreenInit, argc, argv) == -1) {
328 FatalError("Couldn't add screen");
334 rfbScreenInit(index, pScreen, argc, argv)
340 rfbScreenInfoPtr prfb = &rfbScreen;
341 int dpix = 75, dpiy = 75;
345 extern int monitorResolution;
347 if (monitorResolution != 0) {
348 dpix = monitorResolution;
349 dpiy = monitorResolution;
352 prfb->paddedWidthInBytes = PixmapBytePad(prfb->width, prfb->depth);
353 prfb->bitsPerPixel = rfbBitsPerPixel(prfb->depth);
354 pbits = rfbAllocateFramebufferMemory(prfb);
355 if (!pbits) return FALSE;
357 if (prfb->bitsPerPixel > 1) {
358 extern int defaultColorVisualClass;
359 if (defaultColorVisualClass != -1) {
360 cfbSetVisualTypes(prfb->depth, (1 << defaultColorVisualClass), 8);
362 cfbSetVisualTypes(prfb->depth, (1 << TrueColor), 8);
366 switch (prfb->bitsPerPixel)
369 ret = mfbScreenInit(pScreen, pbits, prfb->width, prfb->height,
370 dpix, dpiy, prfb->paddedWidthInBytes * 8);
373 ret = cfbScreenInit(pScreen, pbits, prfb->width, prfb->height,
374 dpix, dpiy, prfb->paddedWidthInBytes);
377 ret = cfb16ScreenInit(pScreen, pbits, prfb->width, prfb->height,
378 dpix, dpiy, prfb->paddedWidthInBytes / 2);
381 ret = cfb32ScreenInit(pScreen, pbits, prfb->width, prfb->height,
382 dpix, dpiy, prfb->paddedWidthInBytes / 4);
388 if (!ret) return FALSE;
390 if (!AllocateGCPrivate(pScreen, rfbGCIndex, sizeof(rfbGCRec))) {
391 FatalError("rfbScreenInit: AllocateGCPrivate failed\n");
394 prfb->cursorIsDrawn = FALSE;
395 prfb->dontSendFramebufferUpdate = FALSE;
397 prfb->CloseScreen = pScreen->CloseScreen;
398 prfb->CreateGC = pScreen->CreateGC;
399 prfb->PaintWindowBackground = pScreen->PaintWindowBackground;
400 prfb->PaintWindowBorder = pScreen->PaintWindowBorder;
401 prfb->CopyWindow = pScreen->CopyWindow;
402 prfb->ClearToBackground = pScreen->ClearToBackground;
403 prfb->RestoreAreas = pScreen->RestoreAreas;
405 /* pScreen->CloseScreen = rfbCloseScreen;
406 pScreen->CreateGC = rfbCreateGC;
407 pScreen->PaintWindowBackground = rfbPaintWindowBackground;
408 pScreen->PaintWindowBorder = rfbPaintWindowBorder;
409 pScreen->CopyWindow = rfbCopyWindow;
410 pScreen->ClearToBackground = rfbClearToBackground;
411 pScreen->RestoreAreas = rfbRestoreAreas;
413 pScreen->InstallColormap = rfbInstallColormap;
414 pScreen->UninstallColormap = rfbUninstallColormap;
415 pScreen->ListInstalledColormaps = rfbListInstalledColormaps;
416 pScreen->StoreColors = rfbStoreColors; */
418 pScreen->SaveScreen = rfbAlwaysTrue;
420 //rfbDCInitialize(pScreen, &rfbPointerCursorFuncs);
423 pScreen->DisplayCursor = rfbAlwaysTrue;
424 prfb->cursorIsDrawn = TRUE;
427 pScreen->blackPixel = prfb->blackPixel;
428 pScreen->whitePixel = prfb->whitePixel;
430 for (vis = pScreen->visuals; vis->vid != pScreen->rootVisual; vis++)
434 rfbLog("rfbScreenInit: couldn't find root visual\n");
438 if (strcmp(primaryOrder, "") == 0) {
439 if (prfb->depth == 16) { /* use rgb565 for depth 16 */
440 strcpy(primaryOrder, "rgb");
444 } else if (prfb->depth == 24) { /* use rgb888 for depth 24 */
445 strcpy(primaryOrder, "rgb");
452 if (strcasecmp(primaryOrder, "rgb") == 0) {
454 vis->blueMask = (1 << blueBits) - 1;
455 vis->offsetGreen = blueBits;
456 vis->greenMask = ((1 << greenBits) - 1) << vis->offsetGreen;
457 vis->offsetRed = vis->offsetGreen + greenBits;
458 vis->redMask = ((1 << redBits) - 1) << vis->offsetRed;
459 } else if (strcasecmp(primaryOrder, "bgr") == 0) {
460 rfbLog("BGR format %d %d %d\n", blueBits, greenBits, redBits);
462 vis->redMask = (1 << redBits) - 1;
463 vis->offsetGreen = redBits;
464 vis->greenMask = ((1 << greenBits) - 1) << vis->offsetGreen;
465 vis->offsetBlue = vis->offsetGreen + greenBits;
466 vis->blueMask = ((1 << blueBits) - 1) << vis->offsetBlue;
470 rfbServerFormat.bitsPerPixel = prfb->bitsPerPixel;
471 rfbServerFormat.depth = prfb->depth;
472 rfbServerFormat.bigEndian = !(*(char *)&rfbEndianTest);
473 rfbServerFormat.trueColour = (vis->class == TrueColor);
474 if (rfbServerFormat.trueColour) {
475 rfbServerFormat.redMax = vis->redMask >> vis->offsetRed;
476 rfbServerFormat.greenMax = vis->greenMask >> vis->offsetGreen;
477 rfbServerFormat.blueMax = vis->blueMask >> vis->offsetBlue;
478 rfbServerFormat.redShift = vis->offsetRed;
479 rfbServerFormat.greenShift = vis->offsetGreen;
480 rfbServerFormat.blueShift = vis->offsetBlue;
482 rfbServerFormat.redMax
483 = rfbServerFormat.greenMax = rfbServerFormat.blueMax = 0;
484 rfbServerFormat.redShift
485 = rfbServerFormat.greenShift = rfbServerFormat.blueShift = 0;
489 if (prfb->bitsPerPixel == 1)
491 ret = mfbCreateDefColormap(pScreen);
495 ret = cfbCreateDefColormap(pScreen);
500 } /* end rfbScreenInit */
505 * InitInput is also called every time the server resets. It is called after
506 * InitOutput so we can assume that rfbInitSockets has already been called.
510 InitInput(argc, argv)
515 k = AddInputDevice(rfbKeybdProc, TRUE);
516 p = AddInputDevice(rfbMouseProc, TRUE);
517 RegisterKeyboardDevice(k);
518 RegisterPointerDevice(p);
519 miRegisterPointerDevice(screenInfo.screens[0], p);
520 mieqInit((DevicePtr)k, (DevicePtr)p);
521 mieqCheckForInput[0] = checkForInput[0];
522 mieqCheckForInput[1] = checkForInput[1];
523 SetInputCheck(&alwaysCheckForInput[0], &alwaysCheckForInput[1]);
528 rfbKeybdProc(pDevice, onoff)
529 DeviceIntPtr pDevice;
533 CARD8 modMap[MAP_LENGTH];
534 DevicePtr pDev = (DevicePtr)pDevice;
539 //KbdDeviceInit(pDevice, &keySyms, modMap);
540 /* InitKeyboardDeviceStruct(pDev, &keySyms, modMap,
541 (BellProcPtr)rfbSendBell,
542 (KbdCtrlProcPtr)NoopDDA); */
561 rfbMouseProc(pDevice, onoff)
562 DeviceIntPtr pDevice;
566 DevicePtr pDev = (DevicePtr)pDevice;
577 /* InitPointerDeviceStruct(pDev, map, 5, miPointerGetMotionEvents,
579 miPointerGetMotionBufferSize()); */
584 // PtrDeviceOn(pDevice);
602 LegalModifier(key, pDev)
615 if (*mieqCheckForInput[0] != *mieqCheckForInput[1]) {
616 mieqProcessInputEvents();
622 static Bool CheckDisplayNumber(int n)
626 struct sockaddr_in addr;
628 sock = socket(AF_INET, SOCK_STREAM, 0);
629 addr.sin_family = AF_INET;
630 addr.sin_addr.s_addr = htonl(INADDR_ANY);
631 addr.sin_port = htons(6000+n);
632 if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
638 sprintf(fname, "/tmp/.X%d-lock", n);
639 if (access(fname, F_OK) == 0)
642 sprintf(fname, "/tmp/.X11-unix/X%d", n);
643 if (access(fname, F_OK) == 0)
646 sprintf(fname, "/usr/spool/sockets/X11/%d", n);
647 if (access(fname, F_OK) == 0)
655 rfbRootPropertyChange(PropertyPtr pProp)
657 /* if ((pProp->propertyName == XA_CUT_BUFFER0) && (pProp->type == XA_STRING)
658 && (pProp->format == 8))
660 rfbGotXCutText(pProp->data, pProp->size);
662 else */ if ((pProp->propertyName == VNC_CONNECT) && (pProp->type == XA_STRING)
663 && (pProp->format == 8))
665 if (pProp->size == 0) {
666 rfbClientPtr cl, nextCl;
667 rfbLog("VNC_CONNECT message: disconnecting all clients\n");
668 /* for (cl = rfbClientHead; cl; cl = nextCl) {
670 rfbCloseSock(cl->sock);
676 char *host = (char *)Xalloc(pProp->size+1);
677 memcpy(host, pProp->data, pProp->size);
678 host[pProp->size] = 0;
679 for (i = 0; i < pProp->size; i++) {
680 if (host[i] == ':') {
681 port = atoi(&host[i+1]);
686 // cl = rfbReverseConnection(host, port);
692 else if ((pProp->propertyName == VNC_DEFER_UPDATE) &&
693 (pProp->type == XA_STRING) && (pProp->format == 8))
695 char *str = (char *)Xalloc(pProp->size+1);
696 memcpy(str, pProp->data, pProp->size);
697 str[pProp->size] = 0;
698 //rfbDeferUpdateTime = atoi(str);
705 rfbBitsPerPixel(depth)
708 if (depth == 1) return 1;
709 else if (depth <= 8) return 8;
710 else if (depth <= 16) return 16;
723 rfbAllocateFramebufferMemory(prfb)
724 rfbScreenInfoPtr prfb;
726 if (prfb->pfbMemory) return prfb->pfbMemory; /* already done */
728 prfb->sizeInBytes = (prfb->paddedWidthInBytes * prfb->height);
730 prfb->pfbMemory = (char *)Xalloc(prfb->sizeInBytes);
732 return prfb->pfbMemory;
737 rfbCursorOffScreen (ppScreen, x, y)
745 rfbCrossScreen (pScreen, entering)
752 rfbClientStateChange(cbl, myData, clt)
753 CallbackListPtr *cbl;
757 dispatchException &= ~DE_RESET; /* hack - force server not to reset */
763 Xfree(rfbScreen.pfbMemory);
764 if (initOutputCalled) {
765 char unixSocketName[256];
766 sprintf(unixSocketName,"/tmp/.X11-unix/X%s",display);
767 unlink(unixSocketName);
768 sprintf(unixSocketName,"/usr/spool/sockets/X11/%s",display);
769 unlink(unixSocketName);
789 #ifdef DDXTIME /* from ServerOSDefines */
796 return(tp.tv_sec * 1000) + (tp.tv_usec / 1000);
803 ErrorF("\nXvnc version %s - built %s\n\n", XVNCRELEASE, buildtime);
804 ErrorF("-geometry WxH set framebuffer width & height\n");
805 ErrorF("-depth D set framebuffer depth\n");
806 ErrorF("-pixelformat format set pixel format (BGRnnn or RGBnnn)\n");
807 ErrorF("-rfbport port TCP port for RFB protocol\n");
808 ErrorF("-rfbwait time max time in ms to wait for RFB client\n");
809 ErrorF("-nocursor don't put up a cursor\n");
810 ErrorF("-rfbauth passwd-file use authentication on RFB protocol\n");
811 ErrorF("-httpd dir serve files via HTTP from here\n");
812 ErrorF("-httpport port port for HTTP\n");
813 ErrorF("-deferupdate time time in ms to defer updates "
815 ErrorF("-economictranslate less memory-hungry translation\n");
816 ErrorF("-maxrects num max number of rectangles in an update "
818 ErrorF("-desktop name VNC desktop name (default x11)\n");
819 ErrorF("-alwaysshared always treat new clients as shared\n");
820 ErrorF("-nevershared never treat new clients as shared\n");
821 ErrorF("-dontdisconnect don't disconnect existing clients when a "
823 " connection comes in (refuse new connection "
825 ErrorF("-localhost only allow connections from localhost\n");
826 ErrorF("-inetd Xvnc is launched by inetd\n");
831 * rfbLog prints a time-stamped message to the log file (stderr).
834 void rfbLog(char *format, ...)
840 va_start(args, format);
843 strftime(buf, 255, "%d/%m/%y %T ", localtime(&clock));
844 fprintf(stderr, buf);
846 vfprintf(stderr, format, args);
852 void rfbLogPerror(char *str)