1 /*
2 * This file is called main.c, because it contains most of the new functions
3 * for use with LibVNCServer.
4 *
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.
8 * All Rights Reserved.
9 *
10 * see GPL (latest version) for full details
11 */
12
13 #ifdef __STRICT_ANSI__
14 #define _BSD_SOURCE
15 #endif
16 #include <rfb/rfb.h>
17 #include <rfb/rfbregion.h>
18 #include "private.h"
19
20 #include <stdarg.h>
21 #include <errno.h>
22
23 #if defined(__DragonFly__)
24 #include <sys/socket.h> /* For sockaddr_storage */
25 #endif
26
27 #ifndef false
28 #define false 0
29 #define true -1
30 #endif
31
32 #ifdef LIBVNCSERVER_HAVE_SYS_TIME_H
33 #include <sys/time.h>
34 #endif
35
36 #ifdef LIBVNCSERVER_HAVE_SYS_TYPES_H
37 #include <sys/types.h>
38 #endif
39
40 #ifdef LIBVNCSERVER_HAVE_FCNTL_H
41 #include <fcntl.h>
42 #endif
43
44 #include <signal.h>
45 #include <time.h>
46
47 static int extMutex_initialized = 0;
48 static int logMutex_initialized = 0;
49 #if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) || defined(LIBVNCSERVER_HAVE_WIN32THREADS)
50 static MUTEX(logMutex);
51 static MUTEX(extMutex);
52 #endif
53
54 static int rfbEnableLogging=1;
55
56 #ifdef LIBVNCSERVER_WORDS_BIGENDIAN
57 char rfbEndianTest = (1==0);
58 #else
59 char rfbEndianTest = (1==1);
60 #endif
61
62 /*
63 * Protocol extensions
64 */
65
66 static rfbProtocolExtension* rfbExtensionHead = NULL;
67
68 /*
69 * This method registers a list of new extensions.
70 * It avoids same extension getting registered multiple times.
71 * The order is not preserved if multiple extensions are
72 * registered at one-go.
73 */
74 void
rfbRegisterProtocolExtension(rfbProtocolExtension * extension)75 rfbRegisterProtocolExtension(rfbProtocolExtension* extension)
76 {
77 rfbProtocolExtension *head = rfbExtensionHead, *next = NULL;
78
79 if(extension == NULL)
80 return;
81
82 next = extension->next;
83
84 if (! extMutex_initialized) {
85 INIT_MUTEX(extMutex);
86 extMutex_initialized = 1;
87 }
88
89 LOCK(extMutex);
90
91 while(head != NULL) {
92 if(head == extension) {
93 UNLOCK(extMutex);
94 rfbRegisterProtocolExtension(next);
95 return;
96 }
97
98 head = head->next;
99 }
100
101 extension->next = rfbExtensionHead;
102 rfbExtensionHead = extension;
103
104 UNLOCK(extMutex);
105 rfbRegisterProtocolExtension(next);
106 }
107
108 /*
109 * This method unregisters a list of extensions.
110 * These extensions won't be available for any new
111 * client connection.
112 */
113 void
rfbUnregisterProtocolExtension(rfbProtocolExtension * extension)114 rfbUnregisterProtocolExtension(rfbProtocolExtension* extension)
115 {
116
117 rfbProtocolExtension *cur = NULL, *pre = NULL;
118
119 if(extension == NULL)
120 return;
121
122 if (! extMutex_initialized) {
123 INIT_MUTEX(extMutex);
124 extMutex_initialized = 1;
125 }
126
127 LOCK(extMutex);
128
129 if(rfbExtensionHead == extension) {
130 rfbExtensionHead = rfbExtensionHead->next;
131 UNLOCK(extMutex);
132 rfbUnregisterProtocolExtension(extension->next);
133 return;
134 }
135
136 cur = pre = rfbExtensionHead;
137
138 while(cur) {
139 if(cur == extension) {
140 pre->next = cur->next;
141 break;
142 }
143 pre = cur;
144 cur = cur->next;
145 }
146
147 UNLOCK(extMutex);
148
149 rfbUnregisterProtocolExtension(extension->next);
150 }
151
rfbGetExtensionIterator()152 rfbProtocolExtension* rfbGetExtensionIterator()
153 {
154 if (! extMutex_initialized) {
155 INIT_MUTEX(extMutex);
156 extMutex_initialized = 1;
157 }
158
159 LOCK(extMutex);
160 return rfbExtensionHead;
161 }
162
rfbReleaseExtensionIterator()163 void rfbReleaseExtensionIterator()
164 {
165 UNLOCK(extMutex);
166 }
167
rfbEnableExtension(rfbClientPtr cl,rfbProtocolExtension * extension,void * data)168 rfbBool rfbEnableExtension(rfbClientPtr cl, rfbProtocolExtension* extension,
169 void* data)
170 {
171 rfbExtensionData* extData;
172
173 /* make sure extension is not yet enabled. */
174 for(extData = cl->extensions; extData; extData = extData->next)
175 if(extData->extension == extension)
176 return FALSE;
177
178 extData = calloc(sizeof(rfbExtensionData),1);
179 if(!extData)
180 return FALSE;
181 extData->extension = extension;
182 extData->data = data;
183 extData->next = cl->extensions;
184 cl->extensions = extData;
185
186 return TRUE;
187 }
188
rfbDisableExtension(rfbClientPtr cl,rfbProtocolExtension * extension)189 rfbBool rfbDisableExtension(rfbClientPtr cl, rfbProtocolExtension* extension)
190 {
191 rfbExtensionData* extData;
192 rfbExtensionData* prevData = NULL;
193
194 for(extData = cl->extensions; extData; extData = extData->next) {
195 if(extData->extension == extension) {
196 if(extData->data)
197 free(extData->data);
198 if(prevData == NULL)
199 cl->extensions = extData->next;
200 else
201 prevData->next = extData->next;
202 return TRUE;
203 }
204 prevData = extData;
205 }
206
207 return FALSE;
208 }
209
rfbGetExtensionClientData(rfbClientPtr cl,rfbProtocolExtension * extension)210 void* rfbGetExtensionClientData(rfbClientPtr cl, rfbProtocolExtension* extension)
211 {
212 rfbExtensionData* data = cl->extensions;
213
214 while(data && data->extension != extension)
215 data = data->next;
216
217 if(data == NULL) {
218 rfbLog("Extension is not enabled !\n");
219 /* rfbCloseClient(cl); */
220 return NULL;
221 }
222
223 return data->data;
224 }
225
226 /*
227 * Logging
228 */
229
rfbLogEnable(int enabled)230 void rfbLogEnable(int enabled) {
231 rfbEnableLogging=enabled;
232 }
233
234 /*
235 * rfbLog prints a time-stamped message to the log file (stderr).
236 */
237
238 static void
rfbDefaultLog(const char * format,...)239 rfbDefaultLog(const char *format, ...)
240 {
241 va_list args;
242 char buf[256];
243 time_t log_clock;
244
245 if(!rfbEnableLogging)
246 return;
247
248 if (! logMutex_initialized) {
249 INIT_MUTEX(logMutex);
250 logMutex_initialized = 1;
251 }
252
253 LOCK(logMutex);
254 va_start(args, format);
255
256 time(&log_clock);
257 strftime(buf, 255, "%d/%m/%Y %X ", localtime(&log_clock));
258 fprintf(stderr, "%s", buf);
259
260 vfprintf(stderr, format, args);
261 fflush(stderr);
262
263 va_end(args);
264 UNLOCK(logMutex);
265 }
266
267 rfbLogProc rfbLog=rfbDefaultLog;
268 rfbLogProc rfbErr=rfbDefaultLog;
269
rfbLogPerror(const char * str)270 void rfbLogPerror(const char *str)
271 {
272 #ifdef WIN32
273 wchar_t *s = NULL;
274 FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
275 NULL, errno, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
276 (LPWSTR)&s, 0, NULL);
277 rfbErr("%s: %S\n", str, s);
278 LocalFree(s);
279 #else
280 rfbErr("%s: %s\n", str, strerror(errno));
281 #endif
282 }
283
rfbScheduleCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy)284 void rfbScheduleCopyRegion(rfbScreenInfoPtr rfbScreen,sraRegionPtr copyRegion,int dx,int dy)
285 {
286 rfbClientIteratorPtr iterator;
287 rfbClientPtr cl;
288
289 iterator=rfbGetClientIterator(rfbScreen);
290 while((cl=rfbClientIteratorNext(iterator))) {
291 LOCK(cl->updateMutex);
292 if(cl->useCopyRect) {
293 sraRegionPtr modifiedRegionBackup;
294 if(!sraRgnEmpty(cl->copyRegion)) {
295 if(cl->copyDX!=dx || cl->copyDY!=dy) {
296 /* if a copyRegion was not yet executed, treat it as a
297 * modifiedRegion. The idea: in this case it could be
298 * source of the new copyRect or modified anyway. */
299 sraRgnOr(cl->modifiedRegion,cl->copyRegion);
300 sraRgnMakeEmpty(cl->copyRegion);
301 } else {
302 /* we have to set the intersection of the source of the copy
303 * and the old copy to modified. */
304 modifiedRegionBackup=sraRgnCreateRgn(copyRegion);
305 sraRgnOffset(modifiedRegionBackup,-dx,-dy);
306 sraRgnAnd(modifiedRegionBackup,cl->copyRegion);
307 sraRgnOr(cl->modifiedRegion,modifiedRegionBackup);
308 sraRgnDestroy(modifiedRegionBackup);
309 }
310 }
311
312 sraRgnOr(cl->copyRegion,copyRegion);
313 cl->copyDX = dx;
314 cl->copyDY = dy;
315
316 /* if there were modified regions, which are now copied,
317 * mark them as modified, because the source of these can be overlapped
318 * either by new modified or now copied regions. */
319 modifiedRegionBackup=sraRgnCreateRgn(cl->modifiedRegion);
320 sraRgnOffset(modifiedRegionBackup,dx,dy);
321 sraRgnAnd(modifiedRegionBackup,cl->copyRegion);
322 sraRgnOr(cl->modifiedRegion,modifiedRegionBackup);
323 sraRgnDestroy(modifiedRegionBackup);
324
325 if(!cl->enableCursorShapeUpdates) {
326 /*
327 * n.b. (dx, dy) is the vector pointing in the direction the
328 * copyrect displacement will take place. copyRegion is the
329 * destination rectangle (say), not the source rectangle.
330 */
331 sraRegionPtr cursorRegion;
332 int x = cl->cursorX - cl->screen->cursor->xhot;
333 int y = cl->cursorY - cl->screen->cursor->yhot;
334 int w = cl->screen->cursor->width;
335 int h = cl->screen->cursor->height;
336
337 cursorRegion = sraRgnCreateRect(x, y, x + w, y + h);
338 sraRgnAnd(cursorRegion, cl->copyRegion);
339 if(!sraRgnEmpty(cursorRegion)) {
340 /*
341 * current cursor rect overlaps with the copy region *dest*,
342 * mark it as modified since we won't copy-rect stuff to it.
343 */
344 sraRgnOr(cl->modifiedRegion, cursorRegion);
345 }
346 sraRgnDestroy(cursorRegion);
347
348 cursorRegion = sraRgnCreateRect(x, y, x + w, y + h);
349 /* displace it to check for overlap with copy region source: */
350 sraRgnOffset(cursorRegion, dx, dy);
351 sraRgnAnd(cursorRegion, cl->copyRegion);
352 if(!sraRgnEmpty(cursorRegion)) {
353 /*
354 * current cursor rect overlaps with the copy region *source*,
355 * mark the *displaced* cursorRegion as modified since we
356 * won't copyrect stuff to it.
357 */
358 sraRgnOr(cl->modifiedRegion, cursorRegion);
359 }
360 sraRgnDestroy(cursorRegion);
361 }
362
363 } else {
364 sraRgnOr(cl->modifiedRegion,copyRegion);
365 }
366 TSIGNAL(cl->updateCond);
367 UNLOCK(cl->updateMutex);
368 }
369
370 rfbReleaseClientIterator(iterator);
371 }
372
rfbDoCopyRegion(rfbScreenInfoPtr screen,sraRegionPtr copyRegion,int dx,int dy)373 void rfbDoCopyRegion(rfbScreenInfoPtr screen,sraRegionPtr copyRegion,int dx,int dy)
374 {
375 sraRectangleIterator* i;
376 sraRect rect;
377 int j,widthInBytes,bpp=screen->serverFormat.bitsPerPixel/8,
378 rowstride=screen->paddedWidthInBytes;
379 char *in,*out;
380
381 /* copy it, really */
382 i = sraRgnGetReverseIterator(copyRegion,dx<0,dy<0);
383 while(sraRgnIteratorNext(i,&rect)) {
384 widthInBytes = (rect.x2-rect.x1)*bpp;
385 out = screen->frameBuffer+rect.x1*bpp+rect.y1*rowstride;
386 in = screen->frameBuffer+(rect.x1-dx)*bpp+(rect.y1-dy)*rowstride;
387 if(dy<0)
388 for(j=rect.y1;j<rect.y2;j++,out+=rowstride,in+=rowstride)
389 memmove(out,in,widthInBytes);
390 else {
391 out += rowstride*(rect.y2-rect.y1-1);
392 in += rowstride*(rect.y2-rect.y1-1);
393 for(j=rect.y2-1;j>=rect.y1;j--,out-=rowstride,in-=rowstride)
394 memmove(out,in,widthInBytes);
395 }
396 }
397 sraRgnReleaseIterator(i);
398
399 rfbScheduleCopyRegion(screen,copyRegion,dx,dy);
400 }
401
rfbDoCopyRect(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2,int dx,int dy)402 void rfbDoCopyRect(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2,int dx,int dy)
403 {
404 sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2);
405 rfbDoCopyRegion(screen,region,dx,dy);
406 sraRgnDestroy(region);
407 }
408
rfbScheduleCopyRect(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2,int dx,int dy)409 void rfbScheduleCopyRect(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2,int dx,int dy)
410 {
411 sraRegionPtr region = sraRgnCreateRect(x1,y1,x2,y2);
412 rfbScheduleCopyRegion(screen,region,dx,dy);
413 sraRgnDestroy(region);
414 }
415
rfbMarkRegionAsModified(rfbScreenInfoPtr screen,sraRegionPtr modRegion)416 void rfbMarkRegionAsModified(rfbScreenInfoPtr screen,sraRegionPtr modRegion)
417 {
418 rfbClientIteratorPtr iterator;
419 rfbClientPtr cl;
420
421 iterator=rfbGetClientIterator(screen);
422 while((cl=rfbClientIteratorNext(iterator))) {
423 LOCK(cl->updateMutex);
424 sraRgnOr(cl->modifiedRegion,modRegion);
425 TSIGNAL(cl->updateCond);
426 UNLOCK(cl->updateMutex);
427 }
428
429 rfbReleaseClientIterator(iterator);
430 }
431
432 void rfbScaledScreenUpdate(rfbScreenInfoPtr screen, int x1, int y1, int x2, int y2);
rfbMarkRectAsModified(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2)433 void rfbMarkRectAsModified(rfbScreenInfoPtr screen,int x1,int y1,int x2,int y2)
434 {
435 sraRegionPtr region;
436 int i;
437
438 if(x1>x2) { i=x1; x1=x2; x2=i; }
439 if(x1<0) x1=0;
440 if(x2>screen->width) x2=screen->width;
441 if(x1==x2) return;
442
443 if(y1>y2) { i=y1; y1=y2; y2=i; }
444 if(y1<0) y1=0;
445 if(y2>screen->height) y2=screen->height;
446 if(y1==y2) return;
447
448 /* update scaled copies for this rectangle */
449 rfbScaledScreenUpdate(screen,x1,y1,x2,y2);
450
451 region = sraRgnCreateRect(x1,y1,x2,y2);
452 rfbMarkRegionAsModified(screen,region);
453 sraRgnDestroy(region);
454 }
455
456 #if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) || defined(LIBVNCSERVER_HAVE_WIN32THREADS)
457
458 static THREAD_ROUTINE_RETURN_TYPE
clientOutput(void * data)459 clientOutput(void *data)
460 {
461 rfbClientPtr cl = (rfbClientPtr)data;
462 rfbBool haveUpdate;
463 sraRegion* updateRegion;
464
465 while (1) {
466 haveUpdate = false;
467 while (!haveUpdate) {
468 if (cl->sock == RFB_INVALID_SOCKET) {
469 /* Client has disconnected. */
470 return THREAD_ROUTINE_RETURN_VALUE;
471 }
472 if (cl->state != RFB_NORMAL || cl->onHold) {
473 /* just sleep until things get normal */
474 THREAD_SLEEP_MS(cl->screen->deferUpdateTime);
475 continue;
476 }
477
478 LOCK(cl->updateMutex);
479
480 if (sraRgnEmpty(cl->requestedRegion)) {
481 ; /* always require a FB Update Request (otherwise can crash.) */
482 } else {
483 haveUpdate = FB_UPDATE_PENDING(cl);
484 if(!haveUpdate) {
485 updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
486 haveUpdate = sraRgnAnd(updateRegion,cl->requestedRegion);
487 sraRgnDestroy(updateRegion);
488 }
489 }
490
491 if (!haveUpdate) {
492 WAIT(cl->updateCond, cl->updateMutex);
493 }
494
495 UNLOCK(cl->updateMutex);
496 }
497
498 /* OK, now, to save bandwidth, wait a little while for more
499 updates to come along. */
500 THREAD_SLEEP_MS(cl->screen->deferUpdateTime);
501
502 /* Now, get the region we're going to update, and remove
503 it from cl->modifiedRegion _before_ we send the update.
504 That way, if anything that overlaps the region we're sending
505 is updated, we'll be sure to do another update later. */
506 LOCK(cl->updateMutex);
507 updateRegion = sraRgnCreateRgn(cl->modifiedRegion);
508 UNLOCK(cl->updateMutex);
509
510 /* Now actually send the update. */
511 rfbIncrClientRef(cl);
512 LOCK(cl->sendMutex);
513 rfbSendFramebufferUpdate(cl, updateRegion);
514 UNLOCK(cl->sendMutex);
515 rfbDecrClientRef(cl);
516
517 sraRgnDestroy(updateRegion);
518 }
519
520 /* Not reached. */
521 return THREAD_ROUTINE_RETURN_VALUE;
522 }
523
524 static THREAD_ROUTINE_RETURN_TYPE
clientInput(void * data)525 clientInput(void *data)
526 {
527 rfbClientPtr cl = (rfbClientPtr)data;
528 #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
529 pthread_t output_thread;
530 pthread_create(&output_thread, NULL, clientOutput, (void *)cl);
531 #elif defined(LIBVNCSERVER_HAVE_WIN32THREADS)
532 uintptr_t output_thread = _beginthread(clientOutput, 0, cl);
533 #endif
534
535 while (1) {
536 fd_set rfds, wfds, efds;
537 struct timeval tv;
538 int n;
539
540 if (cl->sock == RFB_INVALID_SOCKET) {
541 /* Client has disconnected. */
542 break;
543 }
544
545 FD_ZERO(&rfds);
546 FD_SET(cl->sock, &rfds);
547 #ifndef WIN32
548 FD_SET(cl->pipe_notify_client_thread[0], &rfds);
549 #endif
550 FD_ZERO(&efds);
551 FD_SET(cl->sock, &efds);
552
553 /* Are we transferring a file in the background? */
554 FD_ZERO(&wfds);
555 if ((cl->fileTransfer.fd!=-1) && (cl->fileTransfer.sending==1))
556 FD_SET(cl->sock, &wfds);
557
558 #ifndef WIN32
559 int nfds = cl->pipe_notify_client_thread[0] > cl->sock ? cl->pipe_notify_client_thread[0] : cl->sock;
560 #else
561 int nfds = cl->sock;
562 #endif
563
564 tv.tv_sec = 60; /* 1 minute */
565 tv.tv_usec = 0;
566
567 n = select(nfds + 1, &rfds, &wfds, &efds, &tv);
568
569 if (n < 0) {
570 rfbLogPerror("ReadExact: select");
571 break;
572 }
573 if (n == 0) /* timeout */
574 {
575 rfbSendFileTransferChunk(cl);
576 continue;
577 }
578
579 /* We have some space on the transmit queue, send some data */
580 if (FD_ISSET(cl->sock, &wfds))
581 rfbSendFileTransferChunk(cl);
582
583 #ifndef WIN32
584 if (FD_ISSET(cl->pipe_notify_client_thread[0], &rfds))
585 {
586 /* Reset the pipe */
587 char buf;
588 while (read(cl->pipe_notify_client_thread[0], &buf, sizeof(buf)) == sizeof(buf));
589 }
590 #endif
591
592 if (FD_ISSET(cl->sock, &rfds) || FD_ISSET(cl->sock, &efds))
593 {
594 #ifdef LIBVNCSERVER_WITH_WEBSOCKETS
595 do {
596 rfbProcessClientMessage(cl);
597 } while (webSocketsHasDataInBuffer(cl));
598 #else
599 rfbProcessClientMessage(cl);
600 #endif
601 }
602 }
603
604 /* Get rid of the output thread. */
605 LOCK(cl->updateMutex);
606 TSIGNAL(cl->updateCond);
607 UNLOCK(cl->updateMutex);
608 THREAD_JOIN(output_thread);
609
610 rfbClientConnectionGone(cl);
611
612 return THREAD_ROUTINE_RETURN_VALUE;
613 }
614
615
616 static THREAD_ROUTINE_RETURN_TYPE
listenerRun(void * data)617 listenerRun(void *data)
618 {
619 rfbScreenInfoPtr screen=(rfbScreenInfoPtr)data;
620 int client_fd;
621 struct sockaddr_storage peer;
622 rfbClientPtr cl = NULL;
623 socklen_t len;
624 fd_set listen_fds; /* temp file descriptor list for select() */
625
626 /* TODO: this thread won't die by restarting the server */
627 /* TODO: HTTP is not handled */
628 while (1) {
629 client_fd = -1;
630 FD_ZERO(&listen_fds);
631 if(screen->listenSock != RFB_INVALID_SOCKET)
632 FD_SET(screen->listenSock, &listen_fds);
633 if(screen->listen6Sock != RFB_INVALID_SOCKET)
634 FD_SET(screen->listen6Sock, &listen_fds);
635
636 if (select(screen->maxFd+1, &listen_fds, NULL, NULL, NULL) == -1) {
637 rfbLogPerror("listenerRun: error in select");
638 return THREAD_ROUTINE_RETURN_VALUE;
639 }
640
641 /* there is something on the listening sockets, handle new connections */
642 len = sizeof (peer);
643 if (FD_ISSET(screen->listenSock, &listen_fds))
644 client_fd = accept(screen->listenSock, (struct sockaddr*)&peer, &len);
645 else if (FD_ISSET(screen->listen6Sock, &listen_fds))
646 client_fd = accept(screen->listen6Sock, (struct sockaddr*)&peer, &len);
647
648 if(client_fd >= 0)
649 cl = rfbNewClient(screen,client_fd);
650 if (cl && !cl->onHold )
651 rfbStartOnHoldClient(cl);
652 }
653 return THREAD_ROUTINE_RETURN_VALUE;
654 }
655
656 #endif
657
658 void
rfbStartOnHoldClient(rfbClientPtr cl)659 rfbStartOnHoldClient(rfbClientPtr cl)
660 {
661 cl->onHold = FALSE;
662 #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
663 if(cl->screen->backgroundLoop) {
664 #ifndef WIN32
665 if (pipe(cl->pipe_notify_client_thread) == -1) {
666 cl->pipe_notify_client_thread[0] = -1;
667 cl->pipe_notify_client_thread[1] = -1;
668 }
669 fcntl(cl->pipe_notify_client_thread[0], F_SETFL, O_NONBLOCK);
670 #endif
671 pthread_create(&cl->client_thread, NULL, clientInput, (void *)cl);
672 }
673 #elif defined(LIBVNCSERVER_HAVE_WIN32THREADS)
674 if(cl->screen->backgroundLoop) {
675 cl->client_thread = _beginthread(clientInput, 0, cl);
676 }
677 #endif
678 }
679
680
681 void
rfbRefuseOnHoldClient(rfbClientPtr cl)682 rfbRefuseOnHoldClient(rfbClientPtr cl)
683 {
684 rfbCloseClient(cl);
685 rfbClientConnectionGone(cl);
686 }
687
688 static void
rfbDefaultKbdAddEvent(rfbBool down,rfbKeySym keySym,rfbClientPtr cl)689 rfbDefaultKbdAddEvent(rfbBool down, rfbKeySym keySym, rfbClientPtr cl)
690 {
691 }
692
693 void
rfbDefaultPtrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl)694 rfbDefaultPtrAddEvent(int buttonMask, int x, int y, rfbClientPtr cl)
695 {
696 rfbClientIteratorPtr iterator;
697 rfbClientPtr other_client;
698 rfbScreenInfoPtr s = cl->screen;
699
700 if (x != s->cursorX || y != s->cursorY) {
701 LOCK(s->cursorMutex);
702 s->cursorX = x;
703 s->cursorY = y;
704 UNLOCK(s->cursorMutex);
705
706 /* The cursor was moved by this client, so don't send CursorPos. */
707 if (cl->enableCursorPosUpdates)
708 cl->cursorWasMoved = FALSE;
709
710 /* But inform all remaining clients about this cursor movement. */
711 iterator = rfbGetClientIterator(s);
712 while ((other_client = rfbClientIteratorNext(iterator)) != NULL) {
713 if (other_client != cl && other_client->enableCursorPosUpdates) {
714 other_client->cursorWasMoved = TRUE;
715 }
716 }
717 rfbReleaseClientIterator(iterator);
718 }
719 }
720
rfbDefaultSetXCutText(char * text,int len,rfbClientPtr cl)721 static void rfbDefaultSetXCutText(char* text, int len, rfbClientPtr cl)
722 {
723 }
724
725 /* TODO: add a nice VNC or RFB cursor */
726
727 #if defined(WIN32) || defined(sparc) || !defined(NO_STRICT_ANSI)
728 static rfbCursor myCursor =
729 {
730 FALSE, FALSE, FALSE, FALSE,
731 (unsigned char*)"\000\102\044\030\044\102\000",
732 (unsigned char*)"\347\347\176\074\176\347\347",
733 8, 7, 3, 3,
734 0, 0, 0,
735 0xffff, 0xffff, 0xffff,
736 NULL
737 };
738 #else
739 static rfbCursor myCursor =
740 {
741 cleanup: FALSE,
742 cleanupSource: FALSE,
743 cleanupMask: FALSE,
744 cleanupRichSource: FALSE,
745 source: "\000\102\044\030\044\102\000",
746 mask: "\347\347\176\074\176\347\347",
747 width: 8, height: 7, xhot: 3, yhot: 3,
748 foreRed: 0, foreGreen: 0, foreBlue: 0,
749 backRed: 0xffff, backGreen: 0xffff, backBlue: 0xffff,
750 richSource: NULL
751 };
752 #endif
753
rfbDefaultGetCursorPtr(rfbClientPtr cl)754 static rfbCursorPtr rfbDefaultGetCursorPtr(rfbClientPtr cl)
755 {
756 return(cl->screen->cursor);
757 }
758
759 /* response is cl->authChallenge vncEncrypted with passwd */
rfbDefaultPasswordCheck(rfbClientPtr cl,const char * response,int len)760 static rfbBool rfbDefaultPasswordCheck(rfbClientPtr cl,const char* response,int len)
761 {
762 int i;
763 char *passwd=rfbDecryptPasswdFromFile(cl->screen->authPasswdData);
764
765 if(!passwd) {
766 rfbErr("Couldn't read password file: %s\n",cl->screen->authPasswdData);
767 return(FALSE);
768 }
769
770 rfbEncryptBytes(cl->authChallenge, passwd);
771
772 /* Lose the password from memory */
773 for (i = strlen(passwd); i >= 0; i--) {
774 passwd[i] = '\0';
775 }
776
777 free(passwd);
778
779 if (memcmp(cl->authChallenge, response, len) != 0) {
780 rfbErr("authProcessClientMessage: authentication failed from %s\n",
781 cl->host);
782 return(FALSE);
783 }
784
785 return(TRUE);
786 }
787
788 /* for this method, authPasswdData is really a pointer to an array
789 of char*'s, where the last pointer is 0. */
rfbCheckPasswordByList(rfbClientPtr cl,const char * response,int len)790 rfbBool rfbCheckPasswordByList(rfbClientPtr cl,const char* response,int len)
791 {
792 char **passwds;
793 int i=0;
794
795 for(passwds=(char**)cl->screen->authPasswdData;*passwds;passwds++,i++) {
796 uint8_t auth_tmp[CHALLENGESIZE];
797 memcpy((char *)auth_tmp, (char *)cl->authChallenge, CHALLENGESIZE);
798 rfbEncryptBytes(auth_tmp, *passwds);
799
800 if (memcmp(auth_tmp, response, len) == 0) {
801 if(i>=cl->screen->authPasswdFirstViewOnly)
802 cl->viewOnly=TRUE;
803 return(TRUE);
804 }
805 }
806
807 rfbErr("authProcessClientMessage: authentication failed from %s\n",
808 cl->host);
809 return(FALSE);
810 }
811
rfbDoNothingWithClient(rfbClientPtr cl)812 void rfbDoNothingWithClient(rfbClientPtr cl)
813 {
814 }
815
rfbDefaultNewClientHook(rfbClientPtr cl)816 static enum rfbNewClientAction rfbDefaultNewClientHook(rfbClientPtr cl)
817 {
818 return RFB_CLIENT_ACCEPT;
819 }
820
rfbDefaultNumberOfExtDesktopScreens(rfbClientPtr cl)821 static int rfbDefaultNumberOfExtDesktopScreens(rfbClientPtr cl)
822 {
823 return 1;
824 }
825
rfbDefaultGetExtDesktopScreen(int seqnumber,rfbExtDesktopScreen * s,rfbClientPtr cl)826 static rfbBool rfbDefaultGetExtDesktopScreen(int seqnumber, rfbExtDesktopScreen* s, rfbClientPtr cl)
827 {
828 if (seqnumber != 0)
829 return FALSE;
830
831 /* Populate the provided rfbExtDesktopScreen structure */
832 s->id = 1;
833 s->width = cl->scaledScreen->width;
834 s->height = cl->scaledScreen->height;
835 s->x = 0;
836 s->y = 0;
837 s->flags = 0;
838
839 return TRUE;
840 }
841
rfbDefaultSetDesktopSize(int width,int height,int numScreens,rfbExtDesktopScreen * extDesktopScreens,rfbClientPtr cl)842 static int rfbDefaultSetDesktopSize(int width, int height, int numScreens, rfbExtDesktopScreen* extDesktopScreens, rfbClientPtr cl)
843 {
844 return rfbExtDesktopSize_ResizeProhibited;
845 }
846
847 /*
848 * Update server's pixel format in screenInfo structure. This
849 * function is called from rfbGetScreen() and rfbNewFramebuffer().
850 */
851
rfbInitServerFormat(rfbScreenInfoPtr screen,int bitsPerSample)852 static void rfbInitServerFormat(rfbScreenInfoPtr screen, int bitsPerSample)
853 {
854 rfbPixelFormat* format=&screen->serverFormat;
855
856 format->bitsPerPixel = screen->bitsPerPixel;
857 format->depth = screen->depth;
858 format->bigEndian = rfbEndianTest?FALSE:TRUE;
859 format->trueColour = TRUE;
860 screen->colourMap.count = 0;
861 screen->colourMap.is16 = 0;
862 screen->colourMap.data.bytes = NULL;
863
864 if (format->bitsPerPixel == 8) {
865 format->redMax = 7;
866 format->greenMax = 7;
867 format->blueMax = 3;
868 format->redShift = 0;
869 format->greenShift = 3;
870 format->blueShift = 6;
871 } else {
872 format->redMax = (1 << bitsPerSample) - 1;
873 format->greenMax = (1 << bitsPerSample) - 1;
874 format->blueMax = (1 << bitsPerSample) - 1;
875 if(rfbEndianTest) {
876 format->redShift = 0;
877 format->greenShift = bitsPerSample;
878 format->blueShift = bitsPerSample * 2;
879 } else {
880 if(format->bitsPerPixel==8*3) {
881 format->redShift = bitsPerSample*2;
882 format->greenShift = bitsPerSample*1;
883 format->blueShift = 0;
884 } else {
885 format->redShift = bitsPerSample*3;
886 format->greenShift = bitsPerSample*2;
887 format->blueShift = bitsPerSample;
888 }
889 }
890 }
891 }
892
rfbGetScreen(int * argc,char ** argv,int width,int height,int bitsPerSample,int samplesPerPixel,int bytesPerPixel)893 rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv,
894 int width,int height,int bitsPerSample,int samplesPerPixel,
895 int bytesPerPixel)
896 {
897 rfbScreenInfoPtr screen=calloc(sizeof(rfbScreenInfo),1);
898 if (!screen)
899 return NULL;
900
901 if (! logMutex_initialized) {
902 INIT_MUTEX(logMutex);
903 logMutex_initialized = 1;
904 }
905
906
907 if(width&3)
908 rfbErr("WARNING: Width (%d) is not a multiple of 4. VncViewer has problems with that.\n",width);
909
910 screen->autoPort=FALSE;
911 screen->clientHead=NULL;
912 screen->pointerClient=NULL;
913 screen->port=5900;
914 screen->ipv6port=5900;
915 screen->socketState=RFB_SOCKET_INIT;
916
917 screen->inetdInitDone = FALSE;
918 screen->inetdSock=RFB_INVALID_SOCKET;
919
920 screen->udpSock=RFB_INVALID_SOCKET;
921 screen->udpSockConnected=FALSE;
922 screen->udpPort=0;
923 screen->udpClient=NULL;
924
925 screen->maxFd=0;
926 screen->listenSock=RFB_INVALID_SOCKET;
927 screen->listen6Sock=RFB_INVALID_SOCKET;
928
929 screen->fdQuota = 0.5;
930
931 screen->httpInitDone=FALSE;
932 screen->httpEnableProxyConnect=FALSE;
933 screen->httpPort=0;
934 screen->http6Port=0;
935 screen->httpDir=NULL;
936 screen->httpListenSock=RFB_INVALID_SOCKET;
937 screen->httpListen6Sock=RFB_INVALID_SOCKET;
938 screen->httpSock=RFB_INVALID_SOCKET;
939
940 screen->desktopName = "LibVNCServer";
941 screen->alwaysShared = FALSE;
942 screen->neverShared = FALSE;
943 screen->dontDisconnect = FALSE;
944 screen->authPasswdData = NULL;
945 screen->authPasswdFirstViewOnly = 1;
946
947 screen->width = width;
948 screen->height = height;
949 screen->bitsPerPixel = screen->depth = 8*bytesPerPixel;
950
951 screen->passwordCheck = rfbDefaultPasswordCheck;
952
953 screen->ignoreSIGPIPE = TRUE;
954
955 /* disable progressive updating per default */
956 screen->progressiveSliceHeight = 0;
957
958 screen->listenInterface = htonl(INADDR_ANY);
959
960 screen->deferUpdateTime=5;
961 screen->maxRectsPerUpdate=50;
962
963 screen->handleEventsEagerly = FALSE;
964
965 screen->protocolMajorVersion = rfbProtocolMajorVersion;
966 screen->protocolMinorVersion = rfbProtocolMinorVersion;
967
968 screen->permitFileTransfer = FALSE;
969
970 if(!rfbProcessArguments(screen,argc,argv)) {
971 free(screen);
972 return NULL;
973 }
974
975 #ifdef WIN32
976 {
977 DWORD dummy=255;
978 GetComputerName(screen->thisHost,&dummy);
979 }
980 #else
981 gethostname(screen->thisHost, 255);
982 #endif
983
984 screen->paddedWidthInBytes = width*bytesPerPixel;
985
986 /* format */
987
988 rfbInitServerFormat(screen, bitsPerSample);
989
990 /* cursor */
991
992 screen->cursorX=screen->cursorY=screen->underCursorBufferLen=0;
993 screen->underCursorBuffer=NULL;
994 screen->dontConvertRichCursorToXCursor = FALSE;
995 screen->cursor = &myCursor;
996 INIT_MUTEX(screen->cursorMutex);
997
998 #if defined(LIBVNCSERVER_HAVE_LIBPTHREAD) || defined(LIBVNCSERVER_HAVE_WIN32THREADS)
999 screen->backgroundLoop = FALSE;
1000 #endif
1001
1002 /* proc's and hook's */
1003
1004 screen->kbdAddEvent = rfbDefaultKbdAddEvent;
1005 screen->kbdReleaseAllKeys = rfbDoNothingWithClient;
1006 screen->ptrAddEvent = rfbDefaultPtrAddEvent;
1007 screen->setXCutText = rfbDefaultSetXCutText;
1008 screen->getCursorPtr = rfbDefaultGetCursorPtr;
1009 screen->setTranslateFunction = rfbSetTranslateFunction;
1010 screen->newClientHook = rfbDefaultNewClientHook;
1011 screen->displayHook = NULL;
1012 screen->displayFinishedHook = NULL;
1013 screen->getKeyboardLedStateHook = NULL;
1014 screen->xvpHook = NULL;
1015 screen->setDesktopSizeHook = rfbDefaultSetDesktopSize;
1016 screen->numberOfExtDesktopScreensHook = rfbDefaultNumberOfExtDesktopScreens;
1017 screen->getExtDesktopScreenHook = rfbDefaultGetExtDesktopScreen;
1018
1019 /* initialize client list and iterator mutex */
1020 rfbClientListInit(screen);
1021
1022 return(screen);
1023 }
1024
1025 /*
1026 * Switch to another framebuffer (maybe of different size and color
1027 * format). Clients supporting NewFBSize pseudo-encoding will change
1028 * their local framebuffer dimensions if necessary.
1029 * NOTE: Rich cursor data should be converted to new pixel format by
1030 * the caller.
1031 */
1032
rfbNewFramebuffer(rfbScreenInfoPtr screen,char * framebuffer,int width,int height,int bitsPerSample,int samplesPerPixel,int bytesPerPixel)1033 void rfbNewFramebuffer(rfbScreenInfoPtr screen, char *framebuffer,
1034 int width, int height,
1035 int bitsPerSample, int samplesPerPixel,
1036 int bytesPerPixel)
1037 {
1038 rfbPixelFormat old_format;
1039 rfbBool format_changed = FALSE;
1040 rfbClientIteratorPtr iterator;
1041 rfbClientPtr cl;
1042
1043 /* Update information in the screenInfo structure */
1044
1045 old_format = screen->serverFormat;
1046
1047 if (width & 3)
1048 rfbErr("WARNING: New width (%d) is not a multiple of 4.\n", width);
1049
1050 screen->width = width;
1051 screen->height = height;
1052 screen->bitsPerPixel = screen->depth = 8*bytesPerPixel;
1053 screen->paddedWidthInBytes = width*bytesPerPixel;
1054
1055 rfbInitServerFormat(screen, bitsPerSample);
1056
1057 if (memcmp(&screen->serverFormat, &old_format,
1058 sizeof(rfbPixelFormat)) != 0) {
1059 format_changed = TRUE;
1060 }
1061
1062 screen->frameBuffer = framebuffer;
1063
1064 /* Adjust pointer position if necessary */
1065
1066 if (screen->cursorX >= width)
1067 screen->cursorX = width - 1;
1068 if (screen->cursorY >= height)
1069 screen->cursorY = height - 1;
1070
1071 /* For each client: */
1072 iterator = rfbGetClientIterator(screen);
1073 while ((cl = rfbClientIteratorNext(iterator)) != NULL) {
1074
1075 /* Re-install color translation tables if necessary */
1076
1077 if (format_changed)
1078 screen->setTranslateFunction(cl);
1079
1080 /* Mark the screen contents as changed, and schedule sending
1081 NewFBSize message if supported by this client. */
1082
1083 LOCK(cl->updateMutex);
1084 sraRgnDestroy(cl->modifiedRegion);
1085 cl->modifiedRegion = sraRgnCreateRect(0, 0, width, height);
1086 sraRgnMakeEmpty(cl->copyRegion);
1087 cl->copyDX = 0;
1088 cl->copyDY = 0;
1089
1090 if (cl->useNewFBSize)
1091 cl->newFBSizePending = TRUE;
1092
1093 TSIGNAL(cl->updateCond);
1094 UNLOCK(cl->updateMutex);
1095 }
1096 rfbReleaseClientIterator(iterator);
1097 }
1098
1099 /* hang up on all clients and free all reserved memory */
1100
rfbScreenCleanup(rfbScreenInfoPtr screen)1101 void rfbScreenCleanup(rfbScreenInfoPtr screen)
1102 {
1103 rfbClientIteratorPtr i=rfbGetClientIterator(screen);
1104 rfbClientPtr cl,cl1=rfbClientIteratorNext(i);
1105 while(cl1) {
1106 cl=rfbClientIteratorNext(i);
1107 rfbClientConnectionGone(cl1);
1108 cl1=cl;
1109 }
1110 rfbReleaseClientIterator(i);
1111
1112 #define FREE_IF(x) if(screen->x) free(screen->x)
1113 FREE_IF(colourMap.data.bytes);
1114 FREE_IF(underCursorBuffer);
1115 TINI_MUTEX(screen->cursorMutex);
1116
1117 rfbFreeCursor(screen->cursor);
1118
1119 #ifdef LIBVNCSERVER_HAVE_LIBZ
1120 rfbZlibCleanup(screen);
1121 #ifdef LIBVNCSERVER_HAVE_LIBJPEG
1122 rfbTightCleanup(screen);
1123 #endif
1124
1125 /* free all 'scaled' versions of this screen */
1126 while (screen->scaledScreenNext!=NULL)
1127 {
1128 rfbScreenInfoPtr ptr;
1129 ptr = screen->scaledScreenNext;
1130 screen->scaledScreenNext = ptr->scaledScreenNext;
1131 free(ptr->frameBuffer);
1132 free(ptr);
1133 }
1134
1135 #endif
1136 free(screen);
1137 }
1138
rfbInitServer(rfbScreenInfoPtr screen)1139 void rfbInitServer(rfbScreenInfoPtr screen)
1140 {
1141 rfbInitSockets(screen);
1142 rfbHttpInitSockets(screen);
1143 #ifndef WIN32
1144 if(screen->ignoreSIGPIPE)
1145 signal(SIGPIPE,SIG_IGN);
1146 #endif
1147 }
1148
rfbShutdownServer(rfbScreenInfoPtr screen,rfbBool disconnectClients)1149 void rfbShutdownServer(rfbScreenInfoPtr screen,rfbBool disconnectClients) {
1150 if(disconnectClients) {
1151 rfbClientIteratorPtr iter = rfbGetClientIterator(screen);
1152 rfbClientPtr nextCl, currentCl = rfbClientIteratorNext(iter);
1153
1154 while(currentCl) {
1155 nextCl = rfbClientIteratorNext(iter);
1156 if (currentCl->sock != RFB_INVALID_SOCKET) {
1157 /* we don't care about maxfd here, because the server goes away */
1158 rfbCloseClient(currentCl);
1159 }
1160
1161 #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
1162 if(currentCl->screen->backgroundLoop) {
1163 /*
1164 Notify the thread. This simply writes a NULL byte to the notify pipe in order to get past the select()
1165 in clientInput(), the loop in there will then break because the rfbCloseClient() above has set
1166 currentCl->sock to -1.
1167 */
1168 write(currentCl->pipe_notify_client_thread[1], "\x00", 1);
1169 /* And wait for it to finish. */
1170 pthread_join(currentCl->client_thread, NULL);
1171 } else {
1172 rfbClientConnectionGone(currentCl);
1173 }
1174 #else
1175 rfbClientConnectionGone(currentCl);
1176 #endif
1177
1178 currentCl = nextCl;
1179 }
1180
1181 rfbReleaseClientIterator(iter);
1182 }
1183
1184 rfbHttpShutdownSockets(screen);
1185 rfbShutdownSockets(screen);
1186 }
1187
1188 #if !defined LIBVNCSERVER_HAVE_GETTIMEOFDAY && defined WIN32
1189 #include <fcntl.h>
1190 #include <conio.h>
1191 #include <sys/timeb.h>
1192
gettimeofday(struct timeval * tv,char * dummy)1193 static void gettimeofday(struct timeval* tv,char* dummy)
1194 {
1195 SYSTEMTIME t;
1196 GetSystemTime(&t);
1197 tv->tv_sec=t.wHour*3600+t.wMinute*60+t.wSecond;
1198 tv->tv_usec=t.wMilliseconds*1000;
1199 }
1200 #endif
1201
1202 rfbBool
rfbProcessEvents(rfbScreenInfoPtr screen,long usec)1203 rfbProcessEvents(rfbScreenInfoPtr screen,long usec)
1204 {
1205 rfbClientIteratorPtr i;
1206 rfbClientPtr cl,clPrev;
1207 rfbBool result=FALSE;
1208 extern rfbClientIteratorPtr
1209 rfbGetClientIteratorWithClosed(rfbScreenInfoPtr rfbScreen);
1210
1211 if(usec<0)
1212 usec=screen->deferUpdateTime*1000;
1213
1214 rfbCheckFds(screen,usec);
1215 rfbHttpCheckFds(screen);
1216
1217 i = rfbGetClientIteratorWithClosed(screen);
1218 cl=rfbClientIteratorHead(i);
1219 while(cl) {
1220 result = rfbUpdateClient(cl);
1221 clPrev=cl;
1222 cl=rfbClientIteratorNext(i);
1223 if(clPrev->sock==RFB_INVALID_SOCKET) {
1224 rfbClientConnectionGone(clPrev);
1225 result=TRUE;
1226 }
1227 }
1228 rfbReleaseClientIterator(i);
1229
1230 return result;
1231 }
1232
1233 rfbBool
rfbUpdateClient(rfbClientPtr cl)1234 rfbUpdateClient(rfbClientPtr cl)
1235 {
1236 struct timeval tv;
1237 rfbBool result=FALSE;
1238 rfbScreenInfoPtr screen = cl->screen;
1239
1240 if (cl->sock != RFB_INVALID_SOCKET && !cl->onHold && FB_UPDATE_PENDING(cl) &&
1241 !sraRgnEmpty(cl->requestedRegion)) {
1242 result=TRUE;
1243 if(screen->deferUpdateTime == 0) {
1244 rfbSendFramebufferUpdate(cl,cl->modifiedRegion);
1245 } else if(cl->startDeferring.tv_usec == 0) {
1246 gettimeofday(&cl->startDeferring,NULL);
1247 if(cl->startDeferring.tv_usec == 0)
1248 cl->startDeferring.tv_usec++;
1249 } else {
1250 gettimeofday(&tv,NULL);
1251 if(tv.tv_sec < cl->startDeferring.tv_sec /* at midnight */
1252 || ((tv.tv_sec-cl->startDeferring.tv_sec)*1000
1253 +(tv.tv_usec-cl->startDeferring.tv_usec)/1000)
1254 > screen->deferUpdateTime) {
1255 cl->startDeferring.tv_usec = 0;
1256 rfbSendFramebufferUpdate(cl,cl->modifiedRegion);
1257 }
1258 }
1259 }
1260
1261 if (!cl->viewOnly && cl->lastPtrX >= 0) {
1262 if(cl->startPtrDeferring.tv_usec == 0) {
1263 gettimeofday(&cl->startPtrDeferring,NULL);
1264 if(cl->startPtrDeferring.tv_usec == 0)
1265 cl->startPtrDeferring.tv_usec++;
1266 } else {
1267 struct timeval tv;
1268 gettimeofday(&tv,NULL);
1269 if(tv.tv_sec < cl->startPtrDeferring.tv_sec /* at midnight */
1270 || ((tv.tv_sec-cl->startPtrDeferring.tv_sec)*1000
1271 +(tv.tv_usec-cl->startPtrDeferring.tv_usec)/1000)
1272 > cl->screen->deferPtrUpdateTime) {
1273 cl->startPtrDeferring.tv_usec = 0;
1274 cl->screen->ptrAddEvent(cl->lastPtrButtons,
1275 cl->lastPtrX,
1276 cl->lastPtrY, cl);
1277 cl->lastPtrX = -1;
1278 }
1279 }
1280 }
1281
1282 return result;
1283 }
1284
rfbIsActive(rfbScreenInfoPtr screenInfo)1285 rfbBool rfbIsActive(rfbScreenInfoPtr screenInfo) {
1286 return screenInfo->socketState!=RFB_SOCKET_SHUTDOWN || screenInfo->clientHead!=NULL;
1287 }
1288
rfbRunEventLoop(rfbScreenInfoPtr screen,long usec,rfbBool runInBackground)1289 void rfbRunEventLoop(rfbScreenInfoPtr screen, long usec, rfbBool runInBackground)
1290 {
1291 if(runInBackground) {
1292 #ifdef LIBVNCSERVER_HAVE_LIBPTHREAD
1293 pthread_t listener_thread;
1294
1295 screen->backgroundLoop = TRUE;
1296
1297 pthread_create(&listener_thread, NULL, listenerRun, screen);
1298 return;
1299 #elif defined(LIBVNCSERVER_HAVE_WIN32THREADS)
1300 screen->backgroundLoop = TRUE;
1301 _beginthread(listenerRun, 0, screen);
1302 return;
1303 #else
1304 rfbErr("Can't run in background, because I don't have PThreads!\n");
1305 return;
1306 #endif
1307 }
1308
1309 if(usec<0)
1310 usec=screen->deferUpdateTime*1000;
1311
1312 while(rfbIsActive(screen))
1313 rfbProcessEvents(screen,usec);
1314 }
1315