blob: d5010854f35d0b9263d27eb82cb54ba679630959 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossmanff9eb5a2014-01-30 17:47:31 +01002 * Copyright 2009-2014 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
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000054#include <rfb/ServerCore.h>
55#include <rfb/VNCServerST.h>
56#include <rfb/VNCSConnectionST.h>
57#include <rfb/ComparingUpdateTracker.h>
Adam Tkaca6578bf2010-04-23 14:07:41 +000058#include <rfb/Security.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000059#include <rfb/KeyRemapper.h>
60#include <rfb/util.h>
61
62#include <rdr/types.h>
63
64using namespace rfb;
65
66static LogWriter slog("VNCServerST");
67LogWriter VNCServerST::connectionsLog("Connections");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000068
Pierre Ossmanbbf955e2011-11-08 12:44:10 +000069rfb::IntParameter deferUpdateTime("DeferUpdate",
DRC4548f302011-12-22 15:57:59 +000070 "Time in milliseconds to defer updates",1);
Pierre Ossmanbbf955e2011-11-08 12:44:10 +000071
72rfb::BoolParameter alwaysSetDeferUpdateTimer("AlwaysSetDeferUpdateTimer",
73 "Always reset the defer update timer on every change",false);
74
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000075//
76// -=- VNCServerST Implementation
77//
78
79// -=- Constructors/Destructor
80
Adam Tkaca6578bf2010-04-23 14:07:41 +000081VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
Pierre Ossman559a2e82012-01-23 15:54:11 +000082 : blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
83 blockCounter(0), pb(0),
Adam Tkacd36b6262009-09-04 10:57:20 +000084 name(strDup(name_)), pointerClient(0), comparer(0),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000085 renderedCursorInvalid(false),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000086 queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
Pierre Ossmanbbf955e2011-11-08 12:44:10 +000087 lastConnectionTime(0), disableclients(false),
88 deferTimer(this), deferPending(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000089{
90 lastUserInputTime = lastDisconnectTime = time(0);
91 slog.debug("creating single-threaded server %s", name.buf);
92}
93
94VNCServerST::~VNCServerST()
95{
96 slog.debug("shutting down server %s", name.buf);
97
98 // Close any active clients, with appropriate logging & cleanup
99 closeClients("Server shutdown");
100
101 // 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
113 delete comparer;
114}
115
116
117// SocketServer methods
118
119void VNCServerST::addSocket(network::Socket* sock, bool outgoing)
120{
121 // - Check the connection isn't black-marked
122 // *** do this in getSecurity instead?
123 CharArray address(sock->getPeerAddress());
124 if (blHosts->isBlackmarked(address.buf)) {
125 connectionsLog.error("blacklisted: %s", address.buf);
126 try {
127 SConnection::writeConnFailedFromScratch("Too many security failures",
128 &sock->outStream());
129 } catch (rdr::Exception&) {
130 }
131 sock->shutdown();
132 closingSockets.push_back(sock);
133 return;
134 }
135
136 if (clients.empty()) {
137 lastConnectionTime = time(0);
138 }
139
140 VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing);
141 client->init();
142}
143
144void VNCServerST::removeSocket(network::Socket* sock) {
145 // - If the socket has resources allocated to it, delete them
146 std::list<VNCSConnectionST*>::iterator ci;
147 for (ci = clients.begin(); ci != clients.end(); ci++) {
148 if ((*ci)->getSock() == sock) {
149 // - Delete the per-Socket resources
150 delete *ci;
151
152 // - Check that the desktop object is still required
153 if (authClientCount() == 0 && desktopStarted) {
154 slog.debug("no authenticated clients - stopping desktop");
155 desktopStarted = false;
156 desktop->stop();
157 }
158 return;
159 }
160 }
161
162 // - If the Socket has no resources, it may have been a closingSocket
163 closingSockets.remove(sock);
164}
165
Pierre Ossmand408ca52016-04-29 14:26:05 +0200166void VNCServerST::processSocketReadEvent(network::Socket* sock)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000167{
168 // - Find the appropriate VNCSConnectionST and process the event
169 std::list<VNCSConnectionST*>::iterator ci;
170 for (ci = clients.begin(); ci != clients.end(); ci++) {
171 if ((*ci)->getSock() == sock) {
172 (*ci)->processMessages();
173 return;
174 }
175 }
176 throw rdr::Exception("invalid Socket in VNCServerST");
177}
178
Pierre Ossmand408ca52016-04-29 14:26:05 +0200179void VNCServerST::processSocketWriteEvent(network::Socket* sock)
180{
181 // - Find the appropriate VNCSConnectionST and process the event
182 std::list<VNCSConnectionST*>::iterator ci;
183 for (ci = clients.begin(); ci != clients.end(); ci++) {
184 if ((*ci)->getSock() == sock) {
185 (*ci)->flushSocket();
186 return;
187 }
188 }
189 throw rdr::Exception("invalid Socket in VNCServerST");
190}
191
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000192int VNCServerST::checkTimeouts()
193{
194 int timeout = 0;
195 std::list<VNCSConnectionST*>::iterator ci, ci_next;
Pierre Ossman2d61deb2011-10-25 15:18:53 +0000196
197 soonestTimeout(&timeout, Timer::checkTimeouts());
198
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000199 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
200 ci_next = ci; ci_next++;
201 soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
202 }
203
204 int timeLeft;
Constantin Kaplinsky8499d0c2008-08-21 05:51:29 +0000205 time_t now = time(0);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000206
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000207 // Check MaxDisconnectionTime
208 if (rfb::Server::maxDisconnectionTime && clients.empty()) {
209 if (now < lastDisconnectTime) {
210 // Someone must have set the time backwards.
211 slog.info("Time has gone backwards - resetting lastDisconnectTime");
212 lastDisconnectTime = now;
213 }
214 timeLeft = lastDisconnectTime + rfb::Server::maxDisconnectionTime - now;
215 if (timeLeft < -60) {
216 // Someone must have set the time forwards.
217 slog.info("Time has gone forwards - resetting lastDisconnectTime");
218 lastDisconnectTime = now;
219 timeLeft = rfb::Server::maxDisconnectionTime;
220 }
221 if (timeLeft <= 0) {
222 slog.info("MaxDisconnectionTime reached, exiting");
223 exit(0);
224 }
225 soonestTimeout(&timeout, timeLeft * 1000);
226 }
227
228 // Check MaxConnectionTime
229 if (rfb::Server::maxConnectionTime && lastConnectionTime && !clients.empty()) {
230 if (now < lastConnectionTime) {
231 // Someone must have set the time backwards.
232 slog.info("Time has gone backwards - resetting lastConnectionTime");
233 lastConnectionTime = now;
234 }
235 timeLeft = lastConnectionTime + rfb::Server::maxConnectionTime - now;
236 if (timeLeft < -60) {
237 // Someone must have set the time forwards.
238 slog.info("Time has gone forwards - resetting lastConnectionTime");
239 lastConnectionTime = now;
240 timeLeft = rfb::Server::maxConnectionTime;
241 }
242 if (timeLeft <= 0) {
243 slog.info("MaxConnectionTime reached, exiting");
244 exit(0);
245 }
246 soonestTimeout(&timeout, timeLeft * 1000);
247 }
248
249
250 // Check MaxIdleTime
251 if (rfb::Server::maxIdleTime) {
252 if (now < lastUserInputTime) {
253 // Someone must have set the time backwards.
254 slog.info("Time has gone backwards - resetting lastUserInputTime");
255 lastUserInputTime = now;
256 }
257 timeLeft = lastUserInputTime + rfb::Server::maxIdleTime - now;
258 if (timeLeft < -60) {
259 // Someone must have set the time forwards.
260 slog.info("Time has gone forwards - resetting lastUserInputTime");
261 lastUserInputTime = now;
262 timeLeft = rfb::Server::maxIdleTime;
263 }
264 if (timeLeft <= 0) {
265 slog.info("MaxIdleTime reached, exiting");
266 exit(0);
267 }
268 soonestTimeout(&timeout, timeLeft * 1000);
269 }
270
271 return timeout;
272}
273
274
275// VNCServer methods
276
Pierre Ossman559a2e82012-01-23 15:54:11 +0000277void VNCServerST::blockUpdates()
278{
279 blockCounter++;
280}
281
282void VNCServerST::unblockUpdates()
283{
284 assert(blockCounter > 0);
285
286 blockCounter--;
287
288 // Flush out any updates we might have blocked
289 if (blockCounter == 0)
290 tryUpdate();
291}
292
Pierre Ossman04e62db2009-03-23 16:57:07 +0000293void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000294{
295 pb = pb_;
296 delete comparer;
297 comparer = 0;
298
Pierre Ossman04e62db2009-03-23 16:57:07 +0000299 screenLayout = layout;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000300
Pierre Ossman04e62db2009-03-23 16:57:07 +0000301 if (!pb) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000302 if (desktopStarted)
303 throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
Pierre Ossman04e62db2009-03-23 16:57:07 +0000304 return;
305 }
306
307 comparer = new ComparingUpdateTracker(pb);
308 cursor.setPF(pb->getPF());
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100309 renderedCursorInvalid = true;
Pierre Ossman04e62db2009-03-23 16:57:07 +0000310
311 // Make sure that we have at least one screen
312 if (screenLayout.num_screens() == 0)
313 screenLayout.add_screen(Screen(0, 0, 0, pb->width(), pb->height(), 0));
314
315 std::list<VNCSConnectionST*>::iterator ci, ci_next;
316 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
317 ci_next = ci; ci_next++;
318 (*ci)->pixelBufferChange();
319 // Since the new pixel buffer means an ExtendedDesktopSize needs to
320 // be sent anyway, we don't need to call screenLayoutChange.
321 }
322}
323
324void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
325{
326 ScreenSet layout;
327
328 if (!pb_) {
329 if (desktopStarted)
330 throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
331 return;
332 }
333
334 layout = screenLayout;
335
336 // Check that the screen layout is still valid
337 if (!layout.validate(pb_->width(), pb_->height())) {
338 Rect fbRect;
339 ScreenSet::iterator iter, iter_next;
340
341 fbRect.setXYWH(0, 0, pb_->width(), pb_->height());
342
343 for (iter = layout.begin();iter != layout.end();iter = iter_next) {
344 iter_next = iter; ++iter_next;
345 if (iter->dimensions.enclosed_by(fbRect))
346 continue;
347 iter->dimensions = iter->dimensions.intersect(fbRect);
348 if (iter->dimensions.is_empty()) {
349 slog.info("Removing screen %d (%x) as it is completely outside the new framebuffer",
350 (int)iter->id, (unsigned)iter->id);
351 layout.remove_screen(iter->id);
352 }
353 }
354 }
355
356 setPixelBuffer(pb_, layout);
357}
358
359void VNCServerST::setScreenLayout(const ScreenSet& layout)
360{
361 if (!pb)
362 throw Exception("setScreenLayout: new screen layout without a PixelBuffer");
363 if (!layout.validate(pb->width(), pb->height()))
364 throw Exception("setScreenLayout: invalid screen layout");
365
Pierre Ossmandf453202009-04-02 14:26:45 +0000366 screenLayout = layout;
367
Pierre Ossman04e62db2009-03-23 16:57:07 +0000368 std::list<VNCSConnectionST*>::iterator ci, ci_next;
369 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
370 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000371 (*ci)->screenLayoutChangeOrClose(reasonServer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000372 }
373}
374
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000375void VNCServerST::bell()
376{
377 std::list<VNCSConnectionST*>::iterator ci, ci_next;
378 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
379 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000380 (*ci)->bellOrClose();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000381 }
382}
383
384void VNCServerST::serverCutText(const char* str, int len)
385{
386 std::list<VNCSConnectionST*>::iterator ci, ci_next;
387 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
388 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000389 (*ci)->serverCutTextOrClose(str, len);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000390 }
391}
392
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000393void VNCServerST::setName(const char* name_)
394{
Adam Tkacd36b6262009-09-04 10:57:20 +0000395 name.replaceBuf(strDup(name_));
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000396 std::list<VNCSConnectionST*>::iterator ci, ci_next;
397 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
398 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000399 (*ci)->setDesktopNameOrClose(name_);
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000400 }
401}
402
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000403void VNCServerST::add_changed(const Region& region)
404{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000405 if (comparer == NULL)
406 return;
407
408 comparer->add_changed(region);
409 startDefer();
410 tryUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000411}
412
413void VNCServerST::add_copied(const Region& dest, const Point& delta)
414{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000415 if (comparer == NULL)
416 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000417
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000418 comparer->add_copied(dest, delta);
419 startDefer();
420 tryUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000421}
422
423void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
Pierre Ossmanff9eb5a2014-01-30 17:47:31 +0100424 const void* data, const void* mask)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000425{
426 cursor.hotspot = newHotspot;
427 cursor.setSize(width, height);
Pierre Ossmanff9eb5a2014-01-30 17:47:31 +0100428 cursor.imageRect(cursor.getRect(), data);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000429 memcpy(cursor.mask.buf, mask, cursor.maskLen());
430
431 cursor.crop();
432
433 renderedCursorInvalid = true;
434
435 std::list<VNCSConnectionST*>::iterator ci, ci_next;
436 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
437 ci_next = ci; ci_next++;
438 (*ci)->renderedCursorChange();
439 (*ci)->setCursorOrClose();
440 }
441}
442
443void VNCServerST::setCursorPos(const Point& pos)
444{
445 if (!cursorPos.equals(pos)) {
446 cursorPos = pos;
447 renderedCursorInvalid = true;
448 std::list<VNCSConnectionST*>::iterator ci;
449 for (ci = clients.begin(); ci != clients.end(); ci++)
450 (*ci)->renderedCursorChange();
451 }
452}
453
454// Other public methods
455
456void VNCServerST::approveConnection(network::Socket* sock, bool accept,
457 const char* reason)
458{
459 std::list<VNCSConnectionST*>::iterator ci;
460 for (ci = clients.begin(); ci != clients.end(); ci++) {
461 if ((*ci)->getSock() == sock) {
462 (*ci)->approveConnectionOrClose(accept, reason);
463 return;
464 }
465 }
466}
467
468void VNCServerST::closeClients(const char* reason, network::Socket* except)
469{
470 std::list<VNCSConnectionST*>::iterator i, next_i;
471 for (i=clients.begin(); i!=clients.end(); i=next_i) {
472 next_i = i; next_i++;
473 if ((*i)->getSock() != except)
474 (*i)->close(reason);
475 }
476}
477
478void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
479{
480 sockets->clear();
481 std::list<VNCSConnectionST*>::iterator ci;
482 for (ci = clients.begin(); ci != clients.end(); ci++) {
483 sockets->push_back((*ci)->getSock());
484 }
485 std::list<network::Socket*>::iterator si;
486 for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
487 sockets->push_back(*si);
488 }
489}
490
491SConnection* VNCServerST::getSConnection(network::Socket* sock) {
492 std::list<VNCSConnectionST*>::iterator ci;
493 for (ci = clients.begin(); ci != clients.end(); ci++) {
494 if ((*ci)->getSock() == sock)
495 return *ci;
496 }
497 return 0;
498}
499
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000500bool VNCServerST::handleTimeout(Timer* t)
501{
502 if (t != &deferTimer)
503 return false;
504
505 tryUpdate();
506
507 return false;
508}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000509
510// -=- Internal methods
511
512void VNCServerST::startDesktop()
513{
514 if (!desktopStarted) {
515 slog.debug("starting desktop");
516 desktop->start(this);
517 desktopStarted = true;
518 if (!pb)
519 throw Exception("SDesktop::start() did not set a valid PixelBuffer");
520 }
521}
522
523int VNCServerST::authClientCount() {
524 int count = 0;
525 std::list<VNCSConnectionST*>::iterator ci;
526 for (ci = clients.begin(); ci != clients.end(); ci++) {
527 if ((*ci)->authenticated())
528 count++;
529 }
530 return count;
531}
532
533inline bool VNCServerST::needRenderedCursor()
534{
535 std::list<VNCSConnectionST*>::iterator ci;
536 for (ci = clients.begin(); ci != clients.end(); ci++)
537 if ((*ci)->needRenderedCursor()) return true;
538 return false;
539}
540
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000541inline void VNCServerST::startDefer()
542{
543 if (deferUpdateTime == 0)
544 return;
545
546 if (deferPending && !alwaysSetDeferUpdateTimer)
547 return;
548
549 gettimeofday(&deferStart, NULL);
550 deferTimer.start(deferUpdateTime);
551
552 deferPending = true;
553}
554
555inline bool VNCServerST::checkDefer()
556{
557 if (!deferPending)
558 return true;
559
Pierre Ossman5c23b9e2015-03-03 16:26:03 +0100560 if (msSince(&deferStart) >= (unsigned)deferUpdateTime)
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000561 return true;
562
563 return false;
564}
565
566void VNCServerST::tryUpdate()
567{
568 std::list<VNCSConnectionST*>::iterator ci, ci_next;
569
Pierre Ossman559a2e82012-01-23 15:54:11 +0000570 if (blockCounter > 0)
571 return;
572
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000573 if (!checkDefer())
574 return;
575
576 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
577 ci_next = ci; ci_next++;
578 (*ci)->writeFramebufferUpdateOrClose();
579 }
580}
581
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000582// checkUpdate() is called just before sending an update. It checks to see
583// what updates are pending and propagates them to the update tracker for each
584// client. It uses the ComparingUpdateTracker's compare() method to filter out
585// areas of the screen which haven't actually changed. It also checks the
586// state of the (server-side) rendered cursor, if necessary rendering it again
587// with the correct background.
588
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000589bool VNCServerST::checkUpdate()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000590{
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000591 UpdateInfo ui;
592 comparer->getUpdateInfo(&ui, pb->getRect());
593
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000594 bool renderCursor = needRenderedCursor();
595
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000596 if (ui.is_empty() && !(renderCursor && renderedCursorInvalid))
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000597 return true;
598
Pierre Ossman559a2e82012-01-23 15:54:11 +0000599 // Block clients as the frame buffer cannot be safely accessed
600 if (blockCounter > 0)
601 return false;
602
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000603 // Block client from updating if we are currently deferring updates
604 if (!checkDefer())
605 return false;
606
607 deferPending = false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000608
Pierre Ossman02e43d72009-03-05 11:57:11 +0000609 Region toCheck = ui.changed.union_(ui.copied);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000610
611 if (renderCursor) {
612 Rect clippedCursorRect
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100613 = cursor.getRect(cursorPos.subtract(cursor.hotspot)).intersect(pb->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000614
615 if (!renderedCursorInvalid && (toCheck.intersect(clippedCursorRect)
616 .is_empty())) {
617 renderCursor = false;
618 } else {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000619 toCheck.assign_union(clippedCursorRect);
620 }
621 }
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
633 if (renderCursor) {
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100634 renderedCursor.update(pb, &cursor, cursorPos);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000635 renderedCursorInvalid = false;
636 }
637
638 std::list<VNCSConnectionST*>::iterator ci, ci_next;
639 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
640 ci_next = ci; ci_next++;
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000641 (*ci)->add_copied(ui.copied, ui.copy_delta);
642 (*ci)->add_changed(ui.changed);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000643 }
644
645 comparer->clear();
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000646
647 return true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000648}
649
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000650void VNCServerST::getConnInfo(ListConnInfo * listConn)
651{
652 listConn->Clear();
653 listConn->setDisable(getDisable());
654 if (clients.empty())
655 return;
656 std::list<VNCSConnectionST*>::iterator i;
657 for (i = clients.begin(); i != clients.end(); i++)
658 listConn->addInfo((void*)(*i), (*i)->getSock()->getPeerAddress(),
659 (*i)->getStartTime(), (*i)->getStatus());
660}
661
662void VNCServerST::setConnStatus(ListConnInfo* listConn)
663{
664 setDisable(listConn->getDisable());
665 if (listConn->Empty() || clients.empty()) return;
666 for (listConn->iBegin(); !listConn->iEnd(); listConn->iNext()) {
667 VNCSConnectionST* conn = (VNCSConnectionST*)listConn->iGetConn();
668 std::list<VNCSConnectionST*>::iterator i;
669 for (i = clients.begin(); i != clients.end(); i++) {
670 if ((*i) == conn) {
671 int status = listConn->iGetStatus();
672 if (status == 3) {
673 (*i)->close(0);
674 } else {
675 (*i)->setStatus(status);
676 }
677 break;
678 }
679 }
680 }
681}
Constantin Kaplinsky9d1fc6c2008-06-14 05:23:10 +0000682
Pierre Ossman04e62db2009-03-23 16:57:07 +0000683void VNCServerST::notifyScreenLayoutChange(VNCSConnectionST* requester)
684{
685 std::list<VNCSConnectionST*>::iterator ci, ci_next;
686 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
687 ci_next = ci; ci_next++;
688 if ((*ci) == requester)
689 continue;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000690 (*ci)->screenLayoutChangeOrClose(reasonOtherClient);
Pierre Ossman04e62db2009-03-23 16:57:07 +0000691 }
692}
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000693
694bool VNCServerST::getComparerState()
695{
696 if (rfb::Server::compareFB == 0)
697 return false;
698 if (rfb::Server::compareFB != 2)
699 return true;
700
701 std::list<VNCSConnectionST*>::iterator ci, ci_next;
702 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
703 ci_next = ci; ci_next++;
704 if ((*ci)->getComparerState())
705 return true;
706 }
707 return false;
708}