2
* This file is called main.c, because it contains most of the new functions
3
* for use with LibVNCServer.
5
* LibVNCServer (C) 2001 Johannes E. Schindelin <Johannes.Schindelin@gmx.de>
6
* Original OSXvnc (C) 2001 Dan McGuirk <mcguirk@incompleteness.net>.
7
* Original Xvnc (C) 1999 AT&T Laboratories Cambridge.
10
* see GPL (latest version) for full details
14
#include <rfb/rfbregion.h>
24
#ifdef HAVE_SYS_TYPES_H
25
#include <sys/types.h>
29
#include <sys/socket.h>
30
#include <netinet/in.h>
37
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
41
int rfbEnableLogging=1;
43
#ifdef WORDS_BIGENDIAN
44
char rfbEndianTest = 0;
46
char rfbEndianTest = -1;
49
void rfbLogEnable(int enabled) {
50
rfbEnableLogging=enabled;
54
* rfbLog prints a time-stamped message to the log file (stderr).
58
rfbDefaultLog(const char *format, ...)
68
va_start(args, format);
71
strftime(buf, 255, "%d/%m/%Y %X ", localtime(&log_clock));
74
vfprintf(stderr, format, args);
81
rfbLogProc rfbLog=rfbDefaultLog;
82
rfbLogProc rfbErr=rfbDefaultLog;
84
void rfbLogPerror(const char *str)
86
rfbErr("%s: %s\n", str, strerror(errno));
89
void rfbScheduleCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy)
91
rfbClientIteratorPtr iterator;
94
iterator=rfbGetClientIterator(rfbScreen);
95
while((cl=rfbClientIteratorNext(iterator))) {
96
LOCK(cl->updateMutex);
98
sraRegionPtr modifiedRegionBackup;
99
if(!sraRgnEmpty(cl->copyRegion)) {
100
if(cl->copyDX!=dx || cl->copyDY!=dy) {
101
/* if a copyRegion was not yet executed, treat it as a
102
* modifiedRegion. The idea: in this case it could be
103
* source of the new copyRect or modified anyway. */
104
sraRgnOr(cl->modifiedRegion,cl->copyRegion);
105
sraRgnMakeEmpty(cl->copyRegion);
107
/* we have to set the intersection of the source of the copy
108
* and the old copy to modified. */
109
modifiedRegionBackup=sraRgnCreateRgn(copyRegion);
110
sraRgnOffset(modifiedRegionBackup,-dx,-dy);
111
sraRgnAnd(modifiedRegionBackup,cl->copyRegion);
112
sraRgnOr(cl->modifiedRegion,modifiedRegionBackup);
113
sraRgnDestroy(modifiedRegionBackup);
117
sraRgnOr(cl->copyRegion,copyRegion);
121
/* if there were modified regions, which are now copied,
122
* mark them as modified, because the source of these can be overlapped
123
* either by new modified or now copied regions. */
124
modifiedRegionBackup=sraRgnCreateRgn(cl->modifiedRegion);
125
sraRgnOffset(modifiedRegionBackup,dx,dy);
126
sraRgnAnd(modifiedRegionBackup,cl->copyRegion);
127
sraRgnOr(cl->modifiedRegion,modifiedRegionBackup);
128
sraRgnDestroy(modifiedRegionBackup);
131
/* TODO: is this needed? Or does it mess up deferring? */
132
/* while(!sraRgnEmpty(cl->copyRegion)) */ {
133
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
134
if(!cl->screen->backgroundLoop)
137
sraRegionPtr updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
138
sraRgnOr(updateRegion,cl->copyRegion);
139
UNLOCK(cl->updateMutex);
140
rfbSendFramebufferUpdate(cl,updateRegion);
141
sraRgnDestroy(updateRegion);
147
sraRgnOr(cl->modifiedRegion,copyRegion);
149
TSIGNAL(cl->updateCond);
150
UNLOCK(cl->updateMutex);
153
rfbReleaseClientIterator(iterator);
156
void rfbDoCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy)
158
sraRectangleIterator* i;
160
int j,widthInBytes,bpp=rfbScreen->rfbServerFormat.bitsPerPixel/8,
161
rowstride=rfbScreen->paddedWidthInBytes;
164
/* copy it, really */
165
i = sraRgnGetReverseIterator(copyRegion,dx<0,dy<0);
166
while(sraRgnIteratorNext(i,&rect)) {
167
widthInBytes = (rect.x2-rect.x1)*bpp;
168
out = rfbScreen->frameBuffer+rect.x1*bpp+rect.y1*rowstride;
169
in = rfbScreen->frameBuffer+(rect.x1-dx)*bpp+(rect.y1-dy)*rowstride;
171
for(j=rect.y1;j<rect.y2;j++,out+=rowstride,in+=rowstride)
172
memmove(out,in,widthInBytes);
174
out += rowstride*(rect.y2-rect.y1-1);
175
in += rowstride*(rect.y2-rect.y1-1);
176
for(j=rect.y2-1;j>=rect.y1;j--,out-=rowstride,in-=rowstride)
177
memmove(out,in,widthInBytes);
181
rfbScheduleCopyRegion(rfbScreen,copyRegion,dx,dy);
184
void rfbDoCopyRect(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2,int dx,int dy)
186
sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2);
187
rfbDoCopyRegion(rfbScreen,region,dx,dy);
190
void rfbScheduleCopyRect(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2,int dx,int dy)
192
sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2);
193
rfbScheduleCopyRegion(rfbScreen,region,dx,dy);
196
void rfbMarkRegionAsModified(rfbScreenInfoPtr rfbScreen,sraRegionPtr modRegion)
198
rfbClientIteratorPtr iterator;
201
iterator=rfbGetClientIterator(rfbScreen);
202
while((cl=rfbClientIteratorNext(iterator))) {
203
LOCK(cl->updateMutex);
204
sraRgnOr(cl->modifiedRegion,modRegion);
205
TSIGNAL(cl->updateCond);
206
UNLOCK(cl->updateMutex);
209
rfbReleaseClientIterator(iterator);
212
void rfbMarkRectAsModified(rfbScreenInfoPtr rfbScreen,int x1,int y1,int x2,int y2)
217
if(x1>x2) { i=x1; x1=x2; x2=i; }
219
if(x2>=rfbScreen->width) x2=rfbScreen->width-1;
222
if(y1>y2) { i=y1; y1=y2; y2=i; }
224
if(y2>=rfbScreen->height) y2=rfbScreen->height-1;
227
region = sraRgnCreateRect(x1,y1,x2,y2);
228
rfbMarkRegionAsModified(rfbScreen,region);
229
sraRgnDestroy(region);
232
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
234
clientOutput(void *data)
236
rfbClientPtr cl = (rfbClientPtr)data;
238
sraRegion* updateRegion;
242
while (!haveUpdate) {
243
if (cl->sock == -1) {
244
/* Client has disconnected. */
247
LOCK(cl->updateMutex);
248
haveUpdate = FB_UPDATE_PENDING(cl);
250
updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
251
haveUpdate = sraRgnAnd(updateRegion,cl->requestedRegion);
252
sraRgnDestroy(updateRegion);
254
UNLOCK(cl->updateMutex);
257
WAIT(cl->updateCond, cl->updateMutex);
258
UNLOCK(cl->updateMutex); /* we really needn't lock now. */
262
/* OK, now, to save bandwidth, wait a little while for more
263
updates to come along. */
264
usleep(cl->screen->rfbDeferUpdateTime * 1000);
266
/* Now, get the region we're going to update, and remove
267
it from cl->modifiedRegion _before_ we send the update.
268
That way, if anything that overlaps the region we're sending
269
is updated, we'll be sure to do another update later. */
270
LOCK(cl->updateMutex);
271
updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
272
UNLOCK(cl->updateMutex);
274
/* Now actually send the update. */
275
rfbIncrClientRef(cl);
276
rfbSendFramebufferUpdate(cl, updateRegion);
277
rfbDecrClientRef(cl);
279
sraRgnDestroy(updateRegion);
286
clientInput(void *data)
288
rfbClientPtr cl = (rfbClientPtr)data;
289
pthread_t output_thread;
290
pthread_create(&output_thread, NULL, clientOutput, (void *)cl);
293
rfbProcessClientMessage(cl);
294
if (cl->sock == -1) {
295
/* Client has disconnected. */
300
/* Get rid of the output thread. */
301
LOCK(cl->updateMutex);
302
TSIGNAL(cl->updateCond);
303
UNLOCK(cl->updateMutex);
304
IF_PTHREADS(pthread_join(output_thread, NULL));
306
rfbClientConnectionGone(cl);
312
listenerRun(void *data)
314
rfbScreenInfoPtr rfbScreen=(rfbScreenInfoPtr)data;
316
struct sockaddr_in peer;
322
/* TODO: this thread wont die by restarting the server */
323
while ((client_fd = accept(rfbScreen->rfbListenSock,
324
(struct sockaddr*)&peer, &len)) >= 0) {
325
cl = rfbNewClient(rfbScreen,client_fd);
328
if (cl && !cl->onHold )
329
rfbStartOnHoldClient(cl);
335
rfbStartOnHoldClient(rfbClientPtr cl)
337
pthread_create(&cl->client_thread, NULL, clientInput, (void *)cl);
343
rfbStartOnHoldClient(rfbClientPtr cl)
351
rfbRefuseOnHoldClient(rfbClientPtr cl)
354
rfbClientConnectionGone(cl);
358
defaultPtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl)
360
rfbSetCursorPosition(cl->screen, cl, x, y);
363
/* TODO: add a nice VNC or RFB cursor */
365
#if defined(WIN32) || defined(sparc) || !defined(NO_STRICT_ANSI)
366
static rfbCursor myCursor =
368
FALSE, FALSE, FALSE, FALSE,
369
(unsigned char*)"\000\102\044\030\044\102\000",
370
(unsigned char*)"\347\347\176\074\176\347\347",
373
0xffff, 0xffff, 0xffff,
377
static rfbCursor myCursor =
380
cleanupSource: FALSE,
382
cleanupRichSource: FALSE,
383
source: "\000\102\044\030\044\102\000",
384
mask: "\347\347\176\074\176\347\347",
385
width: 8, height: 7, xhot: 3, yhot: 3,
386
foreRed: 0, foreGreen: 0, foreBlue: 0,
387
backRed: 0xffff, backGreen: 0xffff, backBlue: 0xffff,
393
* Update server's pixel format in rfbScreenInfo structure. This
394
* function is called from rfbGetScreen() and rfbNewFramebuffer().
397
static void rfbInitServerFormat(rfbScreenInfoPtr rfbScreen, int bitsPerSample)
399
rfbPixelFormat* format=&rfbScreen->rfbServerFormat;
401
format->bitsPerPixel = rfbScreen->bitsPerPixel;
402
format->depth = rfbScreen->depth;
403
format->bigEndian = rfbEndianTest?FALSE:TRUE;
404
format->trueColour = TRUE;
405
rfbScreen->colourMap.count = 0;
406
rfbScreen->colourMap.is16 = 0;
407
rfbScreen->colourMap.data.bytes = NULL;
409
if (format->bitsPerPixel == 8) {
411
format->greenMax = 7;
413
format->redShift = 0;
414
format->greenShift = 3;
415
format->blueShift = 6;
417
format->redMax = (1 << bitsPerSample) - 1;
418
format->greenMax = (1 << bitsPerSample) - 1;
419
format->blueMax = (1 << bitsPerSample) - 1;
421
format->redShift = 0;
422
format->greenShift = bitsPerSample;
423
format->blueShift = bitsPerSample * 2;
425
if(format->bitsPerPixel==8*3) {
426
format->redShift = bitsPerSample*2;
427
format->greenShift = bitsPerSample*1;
428
format->blueShift = 0;
430
format->redShift = bitsPerSample*3;
431
format->greenShift = bitsPerSample*2;
432
format->blueShift = bitsPerSample;
438
rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv,
439
int width,int height,int bitsPerSample,int samplesPerPixel,
442
rfbScreenInfoPtr rfbScreen=malloc(sizeof(rfbScreenInfo));
444
INIT_MUTEX(logMutex);
447
rfbErr("WARNING: Width (%d) is not a multiple of 4. VncViewer has problems with that.\n",width);
449
rfbScreen->autoPort=FALSE;
450
rfbScreen->rfbClientHead=0;
451
rfbScreen->rfbPort=5900;
452
rfbScreen->socketInitDone=FALSE;
454
rfbScreen->inetdInitDone = FALSE;
455
rfbScreen->inetdSock=-1;
458
rfbScreen->rfbListenSock=-1;
460
rfbScreen->desktopName = "LibVNCServer";
461
rfbScreen->rfbAlwaysShared = FALSE;
462
rfbScreen->rfbNeverShared = FALSE;
463
rfbScreen->rfbDontDisconnect = FALSE;
465
rfbScreen->width = width;
466
rfbScreen->height = height;
467
rfbScreen->bitsPerPixel = rfbScreen->depth = 8*bytesPerPixel;
469
rfbScreen->securityTypes[0] = rfbNoAuth;
470
rfbScreen->nSecurityTypes = 0;
471
rfbScreen->authTypes[0] = rfbNoAuth;
472
rfbScreen->nAuthTypes = 0;
473
rfbScreen->passwordCheck = NULL;
478
GetComputerName(rfbScreen->rfbThisHost,&dummy);
481
gethostname(rfbScreen->rfbThisHost, 255);
484
rfbScreen->paddedWidthInBytes = width*bytesPerPixel;
488
rfbInitServerFormat(rfbScreen, bitsPerSample);
492
rfbScreen->cursorX=rfbScreen->cursorY=rfbScreen->underCursorBufferLen=0;
493
rfbScreen->underCursorBuffer=NULL;
494
rfbScreen->cursor = &myCursor;
495
INIT_MUTEX(rfbScreen->cursorMutex);
497
IF_PTHREADS(rfbScreen->backgroundLoop = FALSE);
499
rfbScreen->rfbDeferUpdateTime=5;
500
rfbScreen->maxRectsPerUpdate=50;
502
/* proc's and hook's */
504
rfbScreen->kbdAddEvent = NULL;
505
rfbScreen->ptrAddEvent = defaultPtrAddEvent;
506
rfbScreen->setXCutText = NULL;
507
rfbScreen->newClientHook = NULL;
508
rfbScreen->authenticatedClientHook = NULL;
510
/* initialize client list and iterator mutex */
511
rfbClientListInit(rfbScreen);
513
rfbAuthInitScreen(rfbScreen);
519
* Switch to another framebuffer (maybe of different size and color
520
* format). Clients supporting NewFBSize pseudo-encoding will change
521
* their local framebuffer dimensions if necessary.
522
* NOTE: Rich cursor data should be converted to new pixel format by
526
void rfbNewFramebuffer(rfbScreenInfoPtr rfbScreen, char *framebuffer,
527
int width, int height,
528
int bitsPerSample, int samplesPerPixel,
531
rfbPixelFormat old_format;
532
rfbBool format_changed = FALSE;
533
rfbClientIteratorPtr iterator;
536
/* Update information in the rfbScreenInfo structure */
538
old_format = rfbScreen->rfbServerFormat;
541
rfbErr("WARNING: New width (%d) is not a multiple of 4.\n", width);
543
rfbScreen->width = width;
544
rfbScreen->height = height;
545
rfbScreen->bitsPerPixel = rfbScreen->depth = 8*bytesPerPixel;
546
rfbScreen->paddedWidthInBytes = width*bytesPerPixel;
548
rfbInitServerFormat(rfbScreen, bitsPerSample);
550
if (memcmp(&rfbScreen->rfbServerFormat, &old_format,
551
sizeof(rfbPixelFormat)) != 0) {
552
format_changed = TRUE;
555
rfbScreen->frameBuffer = framebuffer;
557
/* Adjust pointer position if necessary */
559
if (rfbScreen->cursorX >= width)
560
rfbScreen->cursorX = width - 1;
561
if (rfbScreen->cursorY >= height)
562
rfbScreen->cursorY = height - 1;
564
/* For each client: */
565
iterator = rfbGetClientIterator(rfbScreen);
566
while ((cl = rfbClientIteratorNext(iterator)) != NULL) {
568
/* Re-install color translation tables if necessary */
571
rfbSetTranslateFunction(cl);
573
/* Mark the screen contents as changed, and schedule sending
574
NewFBSize message if supported by this client. */
576
LOCK(cl->updateMutex);
577
sraRgnDestroy(cl->modifiedRegion);
578
cl->modifiedRegion = sraRgnCreateRect(0, 0, width, height);
579
sraRgnMakeEmpty(cl->copyRegion);
583
if (cl->useNewFBSize)
584
cl->newFBSizePending = TRUE;
586
TSIGNAL(cl->updateCond);
587
UNLOCK(cl->updateMutex);
589
rfbReleaseClientIterator(iterator);
592
void rfbScreenCleanup(rfbScreenInfoPtr rfbScreen)
594
rfbClientIteratorPtr i=rfbGetClientIterator(rfbScreen);
595
rfbClientPtr cl,cl1=rfbClientIteratorNext(i);
597
cl=rfbClientIteratorNext(i);
598
rfbClientConnectionGone(cl1);
601
rfbReleaseClientIterator(i);
603
rfbAuthCleanupScreen(rfbScreen);
605
/* TODO: hang up on all clients and free all reserved memory */
606
#define FREE_IF(x) if(rfbScreen->x) free(rfbScreen->x)
607
FREE_IF(colourMap.data.bytes);
608
FREE_IF(underCursorBuffer);
609
TINI_MUTEX(rfbScreen->cursorMutex);
610
if(rfbScreen->cursor)
611
rfbFreeCursor(rfbScreen->cursor);
618
void rfbInitServer(rfbScreenInfoPtr rfbScreen)
622
int i=WSAStartup(MAKEWORD(2,2),&trash);
624
rfbInitSockets(rfbScreen);
627
#ifndef HAVE_GETTIMEOFDAY
630
#include <sys/timeb.h>
632
void gettimeofday(struct timeval* tv,char* dummy)
636
tv->tv_sec=t.wHour*3600+t.wMinute*60+t.wSecond;
637
tv->tv_usec=t.wMilliseconds*1000;
642
rfbUpdateClient(rfbClientPtr cl)
646
if (cl->sock >= 0 && !cl->onHold && FB_UPDATE_PENDING(cl) &&
647
!sraRgnEmpty(cl->requestedRegion)) {
648
if(cl->screen->rfbDeferUpdateTime == 0) {
649
rfbSendFramebufferUpdate(cl,cl->modifiedRegion);
650
} else if(cl->startDeferring.tv_usec == 0) {
651
gettimeofday(&cl->startDeferring,NULL);
652
if(cl->startDeferring.tv_usec == 0)
653
cl->startDeferring.tv_usec++;
655
gettimeofday(&tv,NULL);
656
if(tv.tv_sec < cl->startDeferring.tv_sec /* at midnight */
657
|| ((tv.tv_sec-cl->startDeferring.tv_sec)*1000
658
+(tv.tv_usec-cl->startDeferring.tv_usec)/1000)
659
> cl->screen->rfbDeferUpdateTime) {
660
cl->startDeferring.tv_usec = 0;
661
rfbSendFramebufferUpdate(cl,cl->modifiedRegion);
668
rfbProcessEvents(rfbScreenInfoPtr rfbScreen,long usec)
670
rfbClientIteratorPtr i;
671
rfbClientPtr cl,clPrev;
674
usec=rfbScreen->rfbDeferUpdateTime*1000;
676
rfbCheckFds(rfbScreen,usec);
678
i = rfbGetClientIterator(rfbScreen);
679
cl=rfbClientIteratorHead(i);
683
cl=rfbClientIteratorNext(i);
685
rfbClientConnectionGone(clPrev);
687
rfbReleaseClientIterator(i);
690
void rfbRunEventLoop(rfbScreenInfoPtr rfbScreen, long usec, rfbBool runInBackground)
692
if(runInBackground) {
693
#ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
694
pthread_t listener_thread;
696
rfbScreen->backgroundLoop = TRUE;
698
pthread_create(&listener_thread, NULL, listenerRun, rfbScreen);
701
rfbErr("Can't run in background, because I don't have PThreads!\n");
707
usec=rfbScreen->rfbDeferUpdateTime*1000;
710
rfbProcessEvents(rfbScreen,usec);
714
securityTypeToName(int securityType)
716
switch (securityType) {
718
return "No Authentication";
720
return "VNC Authentication";
730
void rfbAddSecurityType(rfbScreenInfoPtr rfbScreen, int securityType)
732
if (rfbScreen->nSecurityTypes >= RFB_MAX_N_SECURITY_TYPES)
735
rfbLog("Advertising security type: '%s' (%d)\n",
736
securityTypeToName(securityType), securityType);
738
switch (securityType) {
744
rfbScreen->securityTypes[rfbScreen->nSecurityTypes] = securityType;
745
rfbScreen->nSecurityTypes++;
752
void rfbClearSecurityTypes(rfbScreenInfoPtr rfbScreen)
754
if (rfbScreen->nSecurityTypes > 0) {
755
rfbLog("Clearing securityTypes\n");
757
memset (&rfbScreen->securityTypes, 0, sizeof (rfbScreen->securityTypes));
758
rfbScreen->securityTypes [0] = rfbNoAuth;
759
rfbScreen->nSecurityTypes = 0;
764
authTypeToName(int authType)
768
return "No Authentication";
770
return "VNC Authentication";
776
void rfbAddAuthType(rfbScreenInfoPtr rfbScreen, int authType)
778
if (rfbScreen->nAuthTypes >= RFB_MAX_N_AUTH_TYPES)
781
rfbLog("Advertising authentication type: '%s' (%d)\n",
782
authTypeToName(authType), authType);
787
rfbScreen->authTypes[rfbScreen->nAuthTypes] = authType;
788
rfbScreen->nAuthTypes++;
795
void rfbClearAuthTypes(rfbScreenInfoPtr rfbScreen)
797
if (rfbScreen->nAuthTypes > 0) {
798
rfbLog("Clearing authTypes\n");
800
memset (&rfbScreen->authTypes, 0, sizeof (rfbScreen->authTypes));
801
rfbScreen->authTypes [0] = rfbNoAuth;
802
rfbScreen->nAuthTypes = 0;