blob: fc649fa133e71b80954d63b37ee139f2ef76c52a [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman6a1a0d02017-02-19 15:48:17 +01002 * Copyright 2009-2017 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19
20// -=- Single-Threaded VNC Server implementation
21
22
23// Note about how sockets get closed:
24//
25// Closing sockets to clients is non-trivial because the code which calls
26// VNCServerST must explicitly know about all the sockets (so that it can block
27// on them appropriately). However, VNCServerST may want to close clients for
28// a number of reasons, and from a variety of entry points. The simplest is
29// when processSocketEvent() is called for a client, and the remote end has
30// closed its socket. A more complex reason is when processSocketEvent() is
31// called for a client which has just sent a ClientInit with the shared flag
32// set to false - in this case we want to close all other clients. Yet another
33// reason for disconnecting clients is when the desktop size has changed as a
34// result of a call to setPixelBuffer().
35//
36// The responsibility for creating and deleting sockets is entirely with the
37// calling code. When VNCServerST wants to close a connection to a client it
38// calls the VNCSConnectionST's close() method which calls shutdown() on the
39// socket. Eventually the calling code will notice that the socket has been
40// shut down and call removeSocket() so that we can delete the
41// VNCSConnectionST. Note that the socket must not be deleted by the calling
42// code until after removeSocket() has been called.
43//
44// One minor complication is that we don't allocate a VNCSConnectionST object
45// for a blacklisted host (since we want to minimise the resources used for
46// dealing with such a connection). In order to properly implement the
47// getSockets function, we must maintain a separate closingSockets list,
48// otherwise blacklisted connections might be "forgotten".
49
50
Pierre Ossman559a2e82012-01-23 15:54:11 +000051#include <assert.h>
Pierre Ossmanf99c5712009-03-13 14:41:27 +000052#include <stdlib.h>
53
Pierre Ossman707fa122015-12-11 20:21:20 +010054#include <rfb/ComparingUpdateTracker.h>
55#include <rfb/KeyRemapper.h>
56#include <rfb/ListConnInfo.h>
57#include <rfb/Security.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000058#include <rfb/ServerCore.h>
59#include <rfb/VNCServerST.h>
60#include <rfb/VNCSConnectionST.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000061#include <rfb/util.h>
Pierre Ossmanbb305ca2016-12-11 12:41:26 +010062#include <rfb/ledStates.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000063
64#include <rdr/types.h>
65
66using namespace rfb;
67
68static LogWriter slog("VNCServerST");
69LogWriter VNCServerST::connectionsLog("Connections");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000070
71//
72// -=- VNCServerST Implementation
73//
74
75// -=- Constructors/Destructor
76
Adam Tkaca6578bf2010-04-23 14:07:41 +000077VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
Pierre Ossman559a2e82012-01-23 15:54:11 +000078 : blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
Pierre Ossmanbb305ca2016-12-11 12:41:26 +010079 blockCounter(0), pb(0), ledState(ledUnknown),
Adam Tkacd36b6262009-09-04 10:57:20 +000080 name(strDup(name_)), pointerClient(0), comparer(0),
Pierre Ossman6a1a0d02017-02-19 15:48:17 +010081 cursor(new Cursor(0, 0, Point(), NULL)),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000082 renderedCursorInvalid(false),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000083 queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
Pierre Ossmanbbf955e2011-11-08 12:44:10 +000084 lastConnectionTime(0), disableclients(false),
Pierre Ossman6e49e952016-10-07 15:59:38 +020085 frameTimer(this)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000086{
87 lastUserInputTime = lastDisconnectTime = time(0);
88 slog.debug("creating single-threaded server %s", name.buf);
89}
90
91VNCServerST::~VNCServerST()
92{
93 slog.debug("shutting down server %s", name.buf);
94
95 // Close any active clients, with appropriate logging & cleanup
96 closeClients("Server shutdown");
97
Pierre Ossman6e49e952016-10-07 15:59:38 +020098 // Stop trying to render things
99 stopFrameClock();
100
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000101 // Delete all the clients, and their sockets, and any closing sockets
102 // NB: Deleting a client implicitly removes it from the clients list
103 while (!clients.empty()) {
104 delete clients.front();
105 }
106
107 // Stop the desktop object if active, *only* after deleting all clients!
108 if (desktopStarted) {
109 desktopStarted = false;
110 desktop->stop();
111 }
112
Pierre Ossman05338bc2016-11-08 14:57:11 +0100113 if (comparer)
114 comparer->logStats();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000115 delete comparer;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100116
117 delete cursor;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000118}
119
120
121// SocketServer methods
122
123void VNCServerST::addSocket(network::Socket* sock, bool outgoing)
124{
125 // - Check the connection isn't black-marked
126 // *** do this in getSecurity instead?
127 CharArray address(sock->getPeerAddress());
128 if (blHosts->isBlackmarked(address.buf)) {
129 connectionsLog.error("blacklisted: %s", address.buf);
130 try {
131 SConnection::writeConnFailedFromScratch("Too many security failures",
132 &sock->outStream());
133 } catch (rdr::Exception&) {
134 }
135 sock->shutdown();
136 closingSockets.push_back(sock);
137 return;
138 }
139
140 if (clients.empty()) {
141 lastConnectionTime = time(0);
142 }
143
144 VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing);
145 client->init();
146}
147
148void VNCServerST::removeSocket(network::Socket* sock) {
149 // - If the socket has resources allocated to it, delete them
150 std::list<VNCSConnectionST*>::iterator ci;
151 for (ci = clients.begin(); ci != clients.end(); ci++) {
152 if ((*ci)->getSock() == sock) {
153 // - Delete the per-Socket resources
154 delete *ci;
155
156 // - Check that the desktop object is still required
157 if (authClientCount() == 0 && desktopStarted) {
158 slog.debug("no authenticated clients - stopping desktop");
159 desktopStarted = false;
160 desktop->stop();
161 }
Pierre Ossman05338bc2016-11-08 14:57:11 +0100162
163 if (comparer)
164 comparer->logStats();
165
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000166 return;
167 }
168 }
169
170 // - If the Socket has no resources, it may have been a closingSocket
171 closingSockets.remove(sock);
172}
173
Pierre Ossmand408ca52016-04-29 14:26:05 +0200174void VNCServerST::processSocketReadEvent(network::Socket* sock)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000175{
176 // - Find the appropriate VNCSConnectionST and process the event
177 std::list<VNCSConnectionST*>::iterator ci;
178 for (ci = clients.begin(); ci != clients.end(); ci++) {
179 if ((*ci)->getSock() == sock) {
180 (*ci)->processMessages();
181 return;
182 }
183 }
184 throw rdr::Exception("invalid Socket in VNCServerST");
185}
186
Pierre Ossmand408ca52016-04-29 14:26:05 +0200187void VNCServerST::processSocketWriteEvent(network::Socket* sock)
188{
189 // - Find the appropriate VNCSConnectionST and process the event
190 std::list<VNCSConnectionST*>::iterator ci;
191 for (ci = clients.begin(); ci != clients.end(); ci++) {
192 if ((*ci)->getSock() == sock) {
193 (*ci)->flushSocket();
194 return;
195 }
196 }
197 throw rdr::Exception("invalid Socket in VNCServerST");
198}
199
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000200int VNCServerST::checkTimeouts()
201{
202 int timeout = 0;
203 std::list<VNCSConnectionST*>::iterator ci, ci_next;
Pierre Ossman2d61deb2011-10-25 15:18:53 +0000204
205 soonestTimeout(&timeout, Timer::checkTimeouts());
206
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000207 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
208 ci_next = ci; ci_next++;
209 soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
210 }
211
212 int timeLeft;
Constantin Kaplinsky8499d0c2008-08-21 05:51:29 +0000213 time_t now = time(0);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000214
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000215 // Check MaxDisconnectionTime
216 if (rfb::Server::maxDisconnectionTime && clients.empty()) {
217 if (now < lastDisconnectTime) {
218 // Someone must have set the time backwards.
219 slog.info("Time has gone backwards - resetting lastDisconnectTime");
220 lastDisconnectTime = now;
221 }
222 timeLeft = lastDisconnectTime + rfb::Server::maxDisconnectionTime - now;
223 if (timeLeft < -60) {
224 // Someone must have set the time forwards.
225 slog.info("Time has gone forwards - resetting lastDisconnectTime");
226 lastDisconnectTime = now;
227 timeLeft = rfb::Server::maxDisconnectionTime;
228 }
229 if (timeLeft <= 0) {
230 slog.info("MaxDisconnectionTime reached, exiting");
231 exit(0);
232 }
233 soonestTimeout(&timeout, timeLeft * 1000);
234 }
235
236 // Check MaxConnectionTime
237 if (rfb::Server::maxConnectionTime && lastConnectionTime && !clients.empty()) {
238 if (now < lastConnectionTime) {
239 // Someone must have set the time backwards.
240 slog.info("Time has gone backwards - resetting lastConnectionTime");
241 lastConnectionTime = now;
242 }
243 timeLeft = lastConnectionTime + rfb::Server::maxConnectionTime - now;
244 if (timeLeft < -60) {
245 // Someone must have set the time forwards.
246 slog.info("Time has gone forwards - resetting lastConnectionTime");
247 lastConnectionTime = now;
248 timeLeft = rfb::Server::maxConnectionTime;
249 }
250 if (timeLeft <= 0) {
251 slog.info("MaxConnectionTime reached, exiting");
252 exit(0);
253 }
254 soonestTimeout(&timeout, timeLeft * 1000);
255 }
256
257
258 // Check MaxIdleTime
259 if (rfb::Server::maxIdleTime) {
260 if (now < lastUserInputTime) {
261 // Someone must have set the time backwards.
262 slog.info("Time has gone backwards - resetting lastUserInputTime");
263 lastUserInputTime = now;
264 }
265 timeLeft = lastUserInputTime + rfb::Server::maxIdleTime - now;
266 if (timeLeft < -60) {
267 // Someone must have set the time forwards.
268 slog.info("Time has gone forwards - resetting lastUserInputTime");
269 lastUserInputTime = now;
270 timeLeft = rfb::Server::maxIdleTime;
271 }
272 if (timeLeft <= 0) {
273 slog.info("MaxIdleTime reached, exiting");
274 exit(0);
275 }
276 soonestTimeout(&timeout, timeLeft * 1000);
277 }
278
279 return timeout;
280}
281
282
283// VNCServer methods
284
Pierre Ossman559a2e82012-01-23 15:54:11 +0000285void VNCServerST::blockUpdates()
286{
287 blockCounter++;
Pierre Ossman6e49e952016-10-07 15:59:38 +0200288
289 stopFrameClock();
Pierre Ossman559a2e82012-01-23 15:54:11 +0000290}
291
292void VNCServerST::unblockUpdates()
293{
294 assert(blockCounter > 0);
295
296 blockCounter--;
297
Pierre Ossman6e49e952016-10-07 15:59:38 +0200298 // Restart the frame clock if we have updates
299 if (blockCounter == 0) {
300 if (!comparer->is_empty())
301 startFrameClock();
302 }
Pierre Ossman559a2e82012-01-23 15:54:11 +0000303}
304
Pierre Ossman04e62db2009-03-23 16:57:07 +0000305void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000306{
Pierre Ossman05338bc2016-11-08 14:57:11 +0100307 if (comparer)
308 comparer->logStats();
309
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000310 pb = pb_;
311 delete comparer;
312 comparer = 0;
313
Pierre Ossman04e62db2009-03-23 16:57:07 +0000314 screenLayout = layout;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000315
Pierre Ossman04e62db2009-03-23 16:57:07 +0000316 if (!pb) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000317 if (desktopStarted)
318 throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
Pierre Ossman04e62db2009-03-23 16:57:07 +0000319 return;
320 }
321
322 comparer = new ComparingUpdateTracker(pb);
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100323 renderedCursorInvalid = true;
Pierre Ossmanb1e80f72017-09-22 16:48:14 +0200324 startFrameClock();
Pierre Ossman04e62db2009-03-23 16:57:07 +0000325
326 // Make sure that we have at least one screen
327 if (screenLayout.num_screens() == 0)
328 screenLayout.add_screen(Screen(0, 0, 0, pb->width(), pb->height(), 0));
329
330 std::list<VNCSConnectionST*>::iterator ci, ci_next;
331 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
332 ci_next = ci; ci_next++;
333 (*ci)->pixelBufferChange();
334 // Since the new pixel buffer means an ExtendedDesktopSize needs to
335 // be sent anyway, we don't need to call screenLayoutChange.
336 }
337}
338
339void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
340{
341 ScreenSet layout;
342
343 if (!pb_) {
344 if (desktopStarted)
345 throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
346 return;
347 }
348
349 layout = screenLayout;
350
351 // Check that the screen layout is still valid
352 if (!layout.validate(pb_->width(), pb_->height())) {
353 Rect fbRect;
354 ScreenSet::iterator iter, iter_next;
355
356 fbRect.setXYWH(0, 0, pb_->width(), pb_->height());
357
358 for (iter = layout.begin();iter != layout.end();iter = iter_next) {
359 iter_next = iter; ++iter_next;
360 if (iter->dimensions.enclosed_by(fbRect))
361 continue;
362 iter->dimensions = iter->dimensions.intersect(fbRect);
363 if (iter->dimensions.is_empty()) {
364 slog.info("Removing screen %d (%x) as it is completely outside the new framebuffer",
365 (int)iter->id, (unsigned)iter->id);
366 layout.remove_screen(iter->id);
367 }
368 }
369 }
370
371 setPixelBuffer(pb_, layout);
372}
373
374void VNCServerST::setScreenLayout(const ScreenSet& layout)
375{
376 if (!pb)
377 throw Exception("setScreenLayout: new screen layout without a PixelBuffer");
378 if (!layout.validate(pb->width(), pb->height()))
379 throw Exception("setScreenLayout: invalid screen layout");
380
Pierre Ossmandf453202009-04-02 14:26:45 +0000381 screenLayout = layout;
382
Pierre Ossman04e62db2009-03-23 16:57:07 +0000383 std::list<VNCSConnectionST*>::iterator ci, ci_next;
384 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
385 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000386 (*ci)->screenLayoutChangeOrClose(reasonServer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000387 }
388}
389
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000390void VNCServerST::bell()
391{
392 std::list<VNCSConnectionST*>::iterator ci, ci_next;
393 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
394 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000395 (*ci)->bellOrClose();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000396 }
397}
398
399void VNCServerST::serverCutText(const char* str, int len)
400{
401 std::list<VNCSConnectionST*>::iterator ci, ci_next;
402 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
403 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000404 (*ci)->serverCutTextOrClose(str, len);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000405 }
406}
407
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000408void VNCServerST::setName(const char* name_)
409{
Adam Tkacd36b6262009-09-04 10:57:20 +0000410 name.replaceBuf(strDup(name_));
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000411 std::list<VNCSConnectionST*>::iterator ci, ci_next;
412 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
413 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000414 (*ci)->setDesktopNameOrClose(name_);
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000415 }
416}
417
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000418void VNCServerST::add_changed(const Region& region)
419{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000420 if (comparer == NULL)
421 return;
422
423 comparer->add_changed(region);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200424 startFrameClock();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000425}
426
427void VNCServerST::add_copied(const Region& dest, const Point& delta)
428{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000429 if (comparer == NULL)
430 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000431
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000432 comparer->add_copied(dest, delta);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200433 startFrameClock();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000434}
435
436void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100437 const rdr::U8* data)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000438{
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100439 delete cursor;
440 cursor = new Cursor(width, height, newHotspot, data);
441 cursor->crop();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000442
443 renderedCursorInvalid = true;
444
445 std::list<VNCSConnectionST*>::iterator ci, ci_next;
446 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
447 ci_next = ci; ci_next++;
448 (*ci)->renderedCursorChange();
449 (*ci)->setCursorOrClose();
450 }
451}
452
453void VNCServerST::setCursorPos(const Point& pos)
454{
455 if (!cursorPos.equals(pos)) {
456 cursorPos = pos;
457 renderedCursorInvalid = true;
458 std::list<VNCSConnectionST*>::iterator ci;
459 for (ci = clients.begin(); ci != clients.end(); ci++)
460 (*ci)->renderedCursorChange();
461 }
462}
463
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100464void VNCServerST::setLEDState(unsigned int state)
465{
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100466 std::list<VNCSConnectionST*>::iterator ci, ci_next;
467
468 if (state == ledState)
469 return;
470
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100471 ledState = state;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100472
473 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
474 ci_next = ci; ci_next++;
475 (*ci)->setLEDStateOrClose(state);
476 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100477}
478
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000479// Other public methods
480
481void VNCServerST::approveConnection(network::Socket* sock, bool accept,
482 const char* reason)
483{
484 std::list<VNCSConnectionST*>::iterator ci;
485 for (ci = clients.begin(); ci != clients.end(); ci++) {
486 if ((*ci)->getSock() == sock) {
487 (*ci)->approveConnectionOrClose(accept, reason);
488 return;
489 }
490 }
491}
492
493void VNCServerST::closeClients(const char* reason, network::Socket* except)
494{
495 std::list<VNCSConnectionST*>::iterator i, next_i;
496 for (i=clients.begin(); i!=clients.end(); i=next_i) {
497 next_i = i; next_i++;
498 if ((*i)->getSock() != except)
499 (*i)->close(reason);
500 }
501}
502
503void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
504{
505 sockets->clear();
506 std::list<VNCSConnectionST*>::iterator ci;
507 for (ci = clients.begin(); ci != clients.end(); ci++) {
508 sockets->push_back((*ci)->getSock());
509 }
510 std::list<network::Socket*>::iterator si;
511 for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
512 sockets->push_back(*si);
513 }
514}
515
516SConnection* VNCServerST::getSConnection(network::Socket* sock) {
517 std::list<VNCSConnectionST*>::iterator ci;
518 for (ci = clients.begin(); ci != clients.end(); ci++) {
519 if ((*ci)->getSock() == sock)
520 return *ci;
521 }
522 return 0;
523}
524
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000525bool VNCServerST::handleTimeout(Timer* t)
526{
Pierre Ossman6e49e952016-10-07 15:59:38 +0200527 if (t == &frameTimer) {
528 // We keep running until we go a full interval without any updates
529 if (comparer->is_empty())
530 return false;
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000531
Pierre Ossman6e49e952016-10-07 15:59:38 +0200532 writeUpdate();
Pierre Ossman7be73d72017-11-06 13:16:35 +0100533
534 // If this is the first iteration then we need to adjust the timeout
535 if (frameTimer.getTimeoutMs() != 1000/rfb::Server::frameRate) {
536 frameTimer.start(1000/rfb::Server::frameRate);
537 return false;
538 }
539
Pierre Ossman6e49e952016-10-07 15:59:38 +0200540 return true;
541 }
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000542
543 return false;
544}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000545
546// -=- Internal methods
547
548void VNCServerST::startDesktop()
549{
550 if (!desktopStarted) {
551 slog.debug("starting desktop");
552 desktop->start(this);
553 desktopStarted = true;
554 if (!pb)
555 throw Exception("SDesktop::start() did not set a valid PixelBuffer");
556 }
557}
558
559int VNCServerST::authClientCount() {
560 int count = 0;
561 std::list<VNCSConnectionST*>::iterator ci;
562 for (ci = clients.begin(); ci != clients.end(); ci++) {
563 if ((*ci)->authenticated())
564 count++;
565 }
566 return count;
567}
568
569inline bool VNCServerST::needRenderedCursor()
570{
571 std::list<VNCSConnectionST*>::iterator ci;
572 for (ci = clients.begin(); ci != clients.end(); ci++)
573 if ((*ci)->needRenderedCursor()) return true;
574 return false;
575}
576
Pierre Ossman6e49e952016-10-07 15:59:38 +0200577void VNCServerST::startFrameClock()
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000578{
Pierre Ossman6e49e952016-10-07 15:59:38 +0200579 if (frameTimer.isStarted())
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000580 return;
Pierre Ossman559a2e82012-01-23 15:54:11 +0000581 if (blockCounter > 0)
582 return;
583
Pierre Ossman7be73d72017-11-06 13:16:35 +0100584 // The first iteration will be just half a frame as we get a very
585 // unstable update rate if we happen to be perfectly in sync with
586 // the application's update rate
587 frameTimer.start(1000/rfb::Server::frameRate/2);
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000588}
589
Pierre Ossman6e49e952016-10-07 15:59:38 +0200590void VNCServerST::stopFrameClock()
591{
592 frameTimer.stop();
593}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000594
Pierre Ossman6e49e952016-10-07 15:59:38 +0200595// writeUpdate() is called on a regular interval in order to see what
596// updates are pending and propagates them to the update tracker for
597// each client. It uses the ComparingUpdateTracker's compare() method
598// to filter out areas of the screen which haven't actually changed. It
599// also checks the state of the (server-side) rendered cursor, if
600// necessary rendering it again with the correct background.
601
602void VNCServerST::writeUpdate()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000603{
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000604 UpdateInfo ui;
Pierre Ossman6e49e952016-10-07 15:59:38 +0200605 Region toCheck;
606
607 std::list<VNCSConnectionST*>::iterator ci, ci_next;
608
609 assert(blockCounter == 0);
610
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000611 comparer->getUpdateInfo(&ui, pb->getRect());
Pierre Ossman6e49e952016-10-07 15:59:38 +0200612 toCheck = ui.changed.union_(ui.copied);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000613
Pierre Ossman6e49e952016-10-07 15:59:38 +0200614 if (needRenderedCursor()) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100615 Rect clippedCursorRect = Rect(0, 0, cursor->width(), cursor->height())
616 .translate(cursorPos.subtract(cursor->hotspot()))
617 .intersect(pb->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000618
Pierre Ossman24684e52016-12-05 16:58:19 +0100619 if (!toCheck.intersect(clippedCursorRect).is_empty())
620 renderedCursorInvalid = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000621 }
622
623 pb->grabRegion(toCheck);
624
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000625 if (getComparerState())
626 comparer->enable();
627 else
628 comparer->disable();
629
630 if (comparer->compare())
Constantin Kaplinskyf0b3be72008-08-21 05:22:04 +0000631 comparer->getUpdateInfo(&ui, pb->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000632
Pierre Ossman6e49e952016-10-07 15:59:38 +0200633 comparer->clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000634
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000635 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
636 ci_next = ci; ci_next++;
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000637 (*ci)->add_copied(ui.copied, ui.copy_delta);
638 (*ci)->add_changed(ui.changed);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200639 (*ci)->writeFramebufferUpdateOrClose();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000640 }
Pierre Ossman6e49e952016-10-07 15:59:38 +0200641}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000642
Pierre Ossman6e49e952016-10-07 15:59:38 +0200643// checkUpdate() is called by clients to see if it is safe to read from
644// the framebuffer at this time.
645
646bool VNCServerST::checkUpdate()
647{
648 // Block clients as the frame buffer cannot be safely accessed
649 if (blockCounter > 0)
650 return false;
651
652 // Block client from updating if there are pending updates
653 if (!comparer->is_empty())
654 return false;
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000655
656 return true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000657}
658
Pierre Ossman24684e52016-12-05 16:58:19 +0100659const RenderedCursor* VNCServerST::getRenderedCursor()
660{
661 if (renderedCursorInvalid) {
Pierre Ossman7cb4f312017-02-24 13:25:00 +0100662 renderedCursor.update(pb, cursor, cursorPos);
Pierre Ossman24684e52016-12-05 16:58:19 +0100663 renderedCursorInvalid = false;
664 }
665
666 return &renderedCursor;
667}
668
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000669void VNCServerST::getConnInfo(ListConnInfo * listConn)
670{
671 listConn->Clear();
672 listConn->setDisable(getDisable());
673 if (clients.empty())
674 return;
675 std::list<VNCSConnectionST*>::iterator i;
676 for (i = clients.begin(); i != clients.end(); i++)
677 listConn->addInfo((void*)(*i), (*i)->getSock()->getPeerAddress(),
678 (*i)->getStartTime(), (*i)->getStatus());
679}
680
681void VNCServerST::setConnStatus(ListConnInfo* listConn)
682{
683 setDisable(listConn->getDisable());
684 if (listConn->Empty() || clients.empty()) return;
685 for (listConn->iBegin(); !listConn->iEnd(); listConn->iNext()) {
686 VNCSConnectionST* conn = (VNCSConnectionST*)listConn->iGetConn();
687 std::list<VNCSConnectionST*>::iterator i;
688 for (i = clients.begin(); i != clients.end(); i++) {
689 if ((*i) == conn) {
690 int status = listConn->iGetStatus();
691 if (status == 3) {
692 (*i)->close(0);
693 } else {
694 (*i)->setStatus(status);
695 }
696 break;
697 }
698 }
699 }
700}
Constantin Kaplinsky9d1fc6c2008-06-14 05:23:10 +0000701
Pierre Ossman04e62db2009-03-23 16:57:07 +0000702void VNCServerST::notifyScreenLayoutChange(VNCSConnectionST* requester)
703{
704 std::list<VNCSConnectionST*>::iterator ci, ci_next;
705 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
706 ci_next = ci; ci_next++;
707 if ((*ci) == requester)
708 continue;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000709 (*ci)->screenLayoutChangeOrClose(reasonOtherClient);
Pierre Ossman04e62db2009-03-23 16:57:07 +0000710 }
711}
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000712
713bool VNCServerST::getComparerState()
714{
715 if (rfb::Server::compareFB == 0)
716 return false;
717 if (rfb::Server::compareFB != 2)
718 return true;
719
720 std::list<VNCSConnectionST*>::iterator ci, ci_next;
721 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
722 ci_next = ci; ci_next++;
723 if ((*ci)->getComparerState())
724 return true;
725 }
726 return false;
727}