You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

VNCServerST.cxx 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright 2009-2018 Pierre Ossman for Cendio AB
  3. *
  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. // -=- Single-Threaded VNC Server implementation
  20. // Note about how sockets get closed:
  21. //
  22. // Closing sockets to clients is non-trivial because the code which calls
  23. // VNCServerST must explicitly know about all the sockets (so that it can block
  24. // on them appropriately). However, VNCServerST may want to close clients for
  25. // a number of reasons, and from a variety of entry points. The simplest is
  26. // when processSocketEvent() is called for a client, and the remote end has
  27. // closed its socket. A more complex reason is when processSocketEvent() is
  28. // called for a client which has just sent a ClientInit with the shared flag
  29. // set to false - in this case we want to close all other clients. Yet another
  30. // reason for disconnecting clients is when the desktop size has changed as a
  31. // result of a call to setPixelBuffer().
  32. //
  33. // The responsibility for creating and deleting sockets is entirely with the
  34. // calling code. When VNCServerST wants to close a connection to a client it
  35. // calls the VNCSConnectionST's close() method which calls shutdown() on the
  36. // socket. Eventually the calling code will notice that the socket has been
  37. // shut down and call removeSocket() so that we can delete the
  38. // VNCSConnectionST. Note that the socket must not be deleted by the calling
  39. // code until after removeSocket() has been called.
  40. //
  41. // One minor complication is that we don't allocate a VNCSConnectionST object
  42. // for a blacklisted host (since we want to minimise the resources used for
  43. // dealing with such a connection). In order to properly implement the
  44. // getSockets function, we must maintain a separate closingSockets list,
  45. // otherwise blacklisted connections might be "forgotten".
  46. #include <assert.h>
  47. #include <stdlib.h>
  48. #include <rfb/ComparingUpdateTracker.h>
  49. #include <rfb/KeyRemapper.h>
  50. #include <rfb/LogWriter.h>
  51. #include <rfb/Security.h>
  52. #include <rfb/ServerCore.h>
  53. #include <rfb/VNCServerST.h>
  54. #include <rfb/VNCSConnectionST.h>
  55. #include <rfb/util.h>
  56. #include <rfb/ledStates.h>
  57. #include <rdr/types.h>
  58. using namespace rfb;
  59. static LogWriter slog("VNCServerST");
  60. static LogWriter connectionsLog("Connections");
  61. //
  62. // -=- VNCServerST Implementation
  63. //
  64. // -=- Constructors/Destructor
  65. VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
  66. : blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
  67. blockCounter(0), pb(0), ledState(ledUnknown),
  68. name(strDup(name_)), pointerClient(0), comparer(0),
  69. cursor(new Cursor(0, 0, Point(), NULL)),
  70. renderedCursorInvalid(false),
  71. keyRemapper(&KeyRemapper::defInstance),
  72. idleTimer(this), disconnectTimer(this), connectTimer(this),
  73. frameTimer(this)
  74. {
  75. slog.debug("creating single-threaded server %s", name.buf);
  76. // FIXME: Do we really want to kick off these right away?
  77. if (rfb::Server::maxIdleTime)
  78. idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
  79. if (rfb::Server::maxDisconnectionTime)
  80. disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime));
  81. }
  82. VNCServerST::~VNCServerST()
  83. {
  84. slog.debug("shutting down server %s", name.buf);
  85. // Close any active clients, with appropriate logging & cleanup
  86. closeClients("Server shutdown");
  87. // Stop trying to render things
  88. stopFrameClock();
  89. // Delete all the clients, and their sockets, and any closing sockets
  90. while (!clients.empty()) {
  91. VNCSConnectionST* client;
  92. client = clients.front();
  93. clients.pop_front();
  94. delete client;
  95. }
  96. // Stop the desktop object if active, *only* after deleting all clients!
  97. stopDesktop();
  98. if (comparer)
  99. comparer->logStats();
  100. delete comparer;
  101. delete cursor;
  102. }
  103. // SocketServer methods
  104. void VNCServerST::addSocket(network::Socket* sock, bool outgoing)
  105. {
  106. // - Check the connection isn't black-marked
  107. // *** do this in getSecurity instead?
  108. CharArray address(sock->getPeerAddress());
  109. if (blHosts->isBlackmarked(address.buf)) {
  110. connectionsLog.error("blacklisted: %s", address.buf);
  111. try {
  112. rdr::OutStream& os = sock->outStream();
  113. // Shortest possible way to tell a client it is not welcome
  114. os.writeBytes("RFB 003.003\n", 12);
  115. os.writeU32(0);
  116. os.writeString("Too many security failures");
  117. os.flush();
  118. } catch (rdr::Exception&) {
  119. }
  120. sock->shutdown();
  121. closingSockets.push_back(sock);
  122. return;
  123. }
  124. CharArray name;
  125. name.buf = sock->getPeerEndpoint();
  126. connectionsLog.status("accepted: %s", name.buf);
  127. // Adjust the exit timers
  128. if (rfb::Server::maxConnectionTime && clients.empty())
  129. connectTimer.start(secsToMillis(rfb::Server::maxConnectionTime));
  130. disconnectTimer.stop();
  131. VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing);
  132. clients.push_front(client);
  133. client->init();
  134. }
  135. void VNCServerST::removeSocket(network::Socket* sock) {
  136. // - If the socket has resources allocated to it, delete them
  137. std::list<VNCSConnectionST*>::iterator ci;
  138. for (ci = clients.begin(); ci != clients.end(); ci++) {
  139. if ((*ci)->getSock() == sock) {
  140. clients.remove(*ci);
  141. // - Release the cursor if this client owns it
  142. if (pointerClient == *ci)
  143. pointerClient = NULL;
  144. // Adjust the exit timers
  145. connectTimer.stop();
  146. if (rfb::Server::maxDisconnectionTime && clients.empty())
  147. disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime));
  148. // - Delete the per-Socket resources
  149. delete *ci;
  150. CharArray name;
  151. name.buf = sock->getPeerEndpoint();
  152. connectionsLog.status("closed: %s", name.buf);
  153. // - Check that the desktop object is still required
  154. if (authClientCount() == 0)
  155. stopDesktop();
  156. if (comparer)
  157. comparer->logStats();
  158. return;
  159. }
  160. }
  161. // - If the Socket has no resources, it may have been a closingSocket
  162. closingSockets.remove(sock);
  163. }
  164. void VNCServerST::processSocketReadEvent(network::Socket* sock)
  165. {
  166. // - Find the appropriate VNCSConnectionST and process the event
  167. std::list<VNCSConnectionST*>::iterator ci;
  168. for (ci = clients.begin(); ci != clients.end(); ci++) {
  169. if ((*ci)->getSock() == sock) {
  170. (*ci)->processMessages();
  171. return;
  172. }
  173. }
  174. throw rdr::Exception("invalid Socket in VNCServerST");
  175. }
  176. void VNCServerST::processSocketWriteEvent(network::Socket* sock)
  177. {
  178. // - Find the appropriate VNCSConnectionST and process the event
  179. std::list<VNCSConnectionST*>::iterator ci;
  180. for (ci = clients.begin(); ci != clients.end(); ci++) {
  181. if ((*ci)->getSock() == sock) {
  182. (*ci)->flushSocket();
  183. return;
  184. }
  185. }
  186. throw rdr::Exception("invalid Socket in VNCServerST");
  187. }
  188. // VNCServer methods
  189. void VNCServerST::blockUpdates()
  190. {
  191. blockCounter++;
  192. stopFrameClock();
  193. }
  194. void VNCServerST::unblockUpdates()
  195. {
  196. assert(blockCounter > 0);
  197. blockCounter--;
  198. // Restart the frame clock if we have updates
  199. if (blockCounter == 0) {
  200. if (!comparer->is_empty())
  201. startFrameClock();
  202. }
  203. }
  204. void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
  205. {
  206. if (comparer)
  207. comparer->logStats();
  208. pb = pb_;
  209. delete comparer;
  210. comparer = 0;
  211. if (!pb) {
  212. screenLayout = ScreenSet();
  213. if (desktopStarted)
  214. throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
  215. return;
  216. }
  217. if (!layout.validate(pb->width(), pb->height()))
  218. throw Exception("setPixelBuffer: invalid screen layout");
  219. screenLayout = layout;
  220. // Assume the framebuffer contents wasn't saved and reset everything
  221. // that tracks its contents
  222. comparer = new ComparingUpdateTracker(pb);
  223. renderedCursorInvalid = true;
  224. add_changed(pb->getRect());
  225. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  226. for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
  227. ci_next = ci; ci_next++;
  228. (*ci)->pixelBufferChange();
  229. // Since the new pixel buffer means an ExtendedDesktopSize needs to
  230. // be sent anyway, we don't need to call screenLayoutChange.
  231. }
  232. }
  233. void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
  234. {
  235. ScreenSet layout = screenLayout;
  236. // Check that the screen layout is still valid
  237. if (pb_ && !layout.validate(pb_->width(), pb_->height())) {
  238. Rect fbRect;
  239. ScreenSet::iterator iter, iter_next;
  240. fbRect.setXYWH(0, 0, pb_->width(), pb_->height());
  241. for (iter = layout.begin();iter != layout.end();iter = iter_next) {
  242. iter_next = iter; ++iter_next;
  243. if (iter->dimensions.enclosed_by(fbRect))
  244. continue;
  245. iter->dimensions = iter->dimensions.intersect(fbRect);
  246. if (iter->dimensions.is_empty()) {
  247. slog.info("Removing screen %d (%x) as it is completely outside the new framebuffer",
  248. (int)iter->id, (unsigned)iter->id);
  249. layout.remove_screen(iter->id);
  250. }
  251. }
  252. }
  253. // Make sure that we have at least one screen
  254. if (layout.num_screens() == 0)
  255. layout.add_screen(Screen(0, 0, 0, pb->width(), pb->height(), 0));
  256. setPixelBuffer(pb_, layout);
  257. }
  258. void VNCServerST::setScreenLayout(const ScreenSet& layout)
  259. {
  260. if (!pb)
  261. throw Exception("setScreenLayout: new screen layout without a PixelBuffer");
  262. if (!layout.validate(pb->width(), pb->height()))
  263. throw Exception("setScreenLayout: invalid screen layout");
  264. screenLayout = layout;
  265. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  266. for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
  267. ci_next = ci; ci_next++;
  268. (*ci)->screenLayoutChangeOrClose(reasonServer);
  269. }
  270. }
  271. void VNCServerST::bell()
  272. {
  273. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  274. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  275. ci_next = ci; ci_next++;
  276. (*ci)->bellOrClose();
  277. }
  278. }
  279. void VNCServerST::serverCutText(const char* str)
  280. {
  281. if (strchr(str, '\r') != NULL)
  282. throw Exception("Invalid carriage return in clipboard data");
  283. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  284. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  285. ci_next = ci; ci_next++;
  286. (*ci)->serverCutTextOrClose(str);
  287. }
  288. }
  289. void VNCServerST::setName(const char* name_)
  290. {
  291. name.replaceBuf(strDup(name_));
  292. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  293. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  294. ci_next = ci; ci_next++;
  295. (*ci)->setDesktopNameOrClose(name_);
  296. }
  297. }
  298. void VNCServerST::add_changed(const Region& region)
  299. {
  300. if (comparer == NULL)
  301. return;
  302. comparer->add_changed(region);
  303. startFrameClock();
  304. }
  305. void VNCServerST::add_copied(const Region& dest, const Point& delta)
  306. {
  307. if (comparer == NULL)
  308. return;
  309. comparer->add_copied(dest, delta);
  310. startFrameClock();
  311. }
  312. void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
  313. const rdr::U8* data)
  314. {
  315. delete cursor;
  316. cursor = new Cursor(width, height, newHotspot, data);
  317. cursor->crop();
  318. renderedCursorInvalid = true;
  319. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  320. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  321. ci_next = ci; ci_next++;
  322. (*ci)->renderedCursorChange();
  323. (*ci)->setCursorOrClose();
  324. }
  325. }
  326. void VNCServerST::setCursorPos(const Point& pos)
  327. {
  328. if (!cursorPos.equals(pos)) {
  329. cursorPos = pos;
  330. renderedCursorInvalid = true;
  331. std::list<VNCSConnectionST*>::iterator ci;
  332. for (ci = clients.begin(); ci != clients.end(); ci++)
  333. (*ci)->renderedCursorChange();
  334. }
  335. }
  336. void VNCServerST::setLEDState(unsigned int state)
  337. {
  338. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  339. if (state == ledState)
  340. return;
  341. ledState = state;
  342. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  343. ci_next = ci; ci_next++;
  344. (*ci)->setLEDStateOrClose(state);
  345. }
  346. }
  347. // Event handlers
  348. void VNCServerST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
  349. {
  350. if (rfb::Server::maxIdleTime)
  351. idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
  352. // Remap the key if required
  353. if (keyRemapper) {
  354. rdr::U32 newkey;
  355. newkey = keyRemapper->remapKey(keysym);
  356. if (newkey != keysym) {
  357. slog.debug("Key remapped to 0x%x", newkey);
  358. keysym = newkey;
  359. }
  360. }
  361. desktop->keyEvent(keysym, keycode, down);
  362. }
  363. void VNCServerST::pointerEvent(VNCSConnectionST* client,
  364. const Point& pos, int buttonMask)
  365. {
  366. if (rfb::Server::maxIdleTime)
  367. idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
  368. // Let one client own the cursor whilst buttons are pressed in order
  369. // to provide a bit more sane user experience
  370. if ((pointerClient != NULL) && (pointerClient != client))
  371. return;
  372. if (buttonMask)
  373. pointerClient = client;
  374. else
  375. pointerClient = NULL;
  376. desktop->pointerEvent(pos, buttonMask);
  377. }
  378. void VNCServerST::clientCutText(const char* str)
  379. {
  380. desktop->clientCutText(str);
  381. }
  382. unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester,
  383. int fb_width, int fb_height,
  384. const ScreenSet& layout)
  385. {
  386. unsigned int result;
  387. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  388. // Don't bother the desktop with an invalid configuration
  389. if (!layout.validate(fb_width, fb_height))
  390. return resultInvalid;
  391. // FIXME: the desktop will call back to VNCServerST and an extra set
  392. // of ExtendedDesktopSize messages will be sent. This is okay
  393. // protocol-wise, but unnecessary.
  394. result = desktop->setScreenLayout(fb_width, fb_height, layout);
  395. if (result != resultSuccess)
  396. return result;
  397. // Sanity check
  398. if (screenLayout != layout)
  399. throw Exception("Desktop configured a different screen layout than requested");
  400. // Notify other clients
  401. for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
  402. ci_next = ci; ci_next++;
  403. if ((*ci) == requester)
  404. continue;
  405. (*ci)->screenLayoutChangeOrClose(reasonOtherClient);
  406. }
  407. return resultSuccess;
  408. }
  409. // Other public methods
  410. void VNCServerST::approveConnection(network::Socket* sock, bool accept,
  411. const char* reason)
  412. {
  413. std::list<VNCSConnectionST*>::iterator ci;
  414. for (ci = clients.begin(); ci != clients.end(); ci++) {
  415. if ((*ci)->getSock() == sock) {
  416. (*ci)->approveConnectionOrClose(accept, reason);
  417. return;
  418. }
  419. }
  420. }
  421. void VNCServerST::closeClients(const char* reason, network::Socket* except)
  422. {
  423. std::list<VNCSConnectionST*>::iterator i, next_i;
  424. for (i=clients.begin(); i!=clients.end(); i=next_i) {
  425. next_i = i; next_i++;
  426. if ((*i)->getSock() != except)
  427. (*i)->close(reason);
  428. }
  429. }
  430. void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
  431. {
  432. sockets->clear();
  433. std::list<VNCSConnectionST*>::iterator ci;
  434. for (ci = clients.begin(); ci != clients.end(); ci++) {
  435. sockets->push_back((*ci)->getSock());
  436. }
  437. std::list<network::Socket*>::iterator si;
  438. for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
  439. sockets->push_back(*si);
  440. }
  441. }
  442. SConnection* VNCServerST::getConnection(network::Socket* sock) {
  443. std::list<VNCSConnectionST*>::iterator ci;
  444. for (ci = clients.begin(); ci != clients.end(); ci++) {
  445. if ((*ci)->getSock() == sock)
  446. return *ci;
  447. }
  448. return 0;
  449. }
  450. bool VNCServerST::handleTimeout(Timer* t)
  451. {
  452. if (t == &frameTimer) {
  453. // We keep running until we go a full interval without any updates
  454. if (comparer->is_empty())
  455. return false;
  456. writeUpdate();
  457. // If this is the first iteration then we need to adjust the timeout
  458. if (frameTimer.getTimeoutMs() != 1000/rfb::Server::frameRate) {
  459. frameTimer.start(1000/rfb::Server::frameRate);
  460. return false;
  461. }
  462. return true;
  463. } else if (t == &idleTimer) {
  464. slog.info("MaxIdleTime reached, exiting");
  465. desktop->terminate();
  466. } else if (t == &disconnectTimer) {
  467. slog.info("MaxDisconnectionTime reached, exiting");
  468. desktop->terminate();
  469. } else if (t == &connectTimer) {
  470. slog.info("MaxConnectionTime reached, exiting");
  471. desktop->terminate();
  472. }
  473. return false;
  474. }
  475. void VNCServerST::queryConnection(VNCSConnectionST* client,
  476. const char* userName)
  477. {
  478. // - Authentication succeeded - clear from blacklist
  479. CharArray name;
  480. name.buf = client->getSock()->getPeerAddress();
  481. blHosts->clearBlackmark(name.buf);
  482. // - Prepare the desktop for that the client will start requiring
  483. // resources after this
  484. startDesktop();
  485. // - Special case to provide a more useful error message
  486. if (rfb::Server::neverShared &&
  487. !rfb::Server::disconnectClients &&
  488. authClientCount() > 0) {
  489. approveConnection(client->getSock(), false,
  490. "The server is already in use");
  491. return;
  492. }
  493. // - Are we configured to do queries?
  494. if (!rfb::Server::queryConnect &&
  495. !client->getSock()->requiresQuery()) {
  496. approveConnection(client->getSock(), true, NULL);
  497. return;
  498. }
  499. // - Does the client have the right to bypass the query?
  500. if (client->accessCheck(SConnection::AccessNoQuery))
  501. {
  502. approveConnection(client->getSock(), true, NULL);
  503. return;
  504. }
  505. desktop->queryConnection(client->getSock(), userName);
  506. }
  507. void VNCServerST::clientReady(VNCSConnectionST* client, bool shared)
  508. {
  509. if (!shared) {
  510. if (rfb::Server::disconnectClients &&
  511. client->accessCheck(SConnection::AccessNonShared)) {
  512. // - Close all the other connected clients
  513. slog.debug("non-shared connection - closing clients");
  514. closeClients("Non-shared connection requested", client->getSock());
  515. } else {
  516. // - Refuse this connection if there are existing clients, in addition to
  517. // this one
  518. if (authClientCount() > 1) {
  519. client->close("Server is already in use");
  520. return;
  521. }
  522. }
  523. }
  524. }
  525. // -=- Internal methods
  526. void VNCServerST::startDesktop()
  527. {
  528. if (!desktopStarted) {
  529. slog.debug("starting desktop");
  530. desktop->start(this);
  531. if (!pb)
  532. throw Exception("SDesktop::start() did not set a valid PixelBuffer");
  533. desktopStarted = true;
  534. // The tracker might have accumulated changes whilst we were
  535. // stopped, so flush those out
  536. if (!comparer->is_empty())
  537. writeUpdate();
  538. }
  539. }
  540. void VNCServerST::stopDesktop()
  541. {
  542. if (desktopStarted) {
  543. slog.debug("stopping desktop");
  544. desktopStarted = false;
  545. desktop->stop();
  546. stopFrameClock();
  547. }
  548. }
  549. int VNCServerST::authClientCount() {
  550. int count = 0;
  551. std::list<VNCSConnectionST*>::iterator ci;
  552. for (ci = clients.begin(); ci != clients.end(); ci++) {
  553. if ((*ci)->authenticated())
  554. count++;
  555. }
  556. return count;
  557. }
  558. inline bool VNCServerST::needRenderedCursor()
  559. {
  560. std::list<VNCSConnectionST*>::iterator ci;
  561. for (ci = clients.begin(); ci != clients.end(); ci++)
  562. if ((*ci)->needRenderedCursor()) return true;
  563. return false;
  564. }
  565. void VNCServerST::startFrameClock()
  566. {
  567. if (frameTimer.isStarted())
  568. return;
  569. if (blockCounter > 0)
  570. return;
  571. if (!desktopStarted)
  572. return;
  573. // The first iteration will be just half a frame as we get a very
  574. // unstable update rate if we happen to be perfectly in sync with
  575. // the application's update rate
  576. frameTimer.start(1000/rfb::Server::frameRate/2);
  577. }
  578. void VNCServerST::stopFrameClock()
  579. {
  580. frameTimer.stop();
  581. }
  582. int VNCServerST::msToNextUpdate()
  583. {
  584. // FIXME: If the application is updating slower than frameRate then
  585. // we could allow the clients more time here
  586. if (!frameTimer.isStarted())
  587. return 1000/rfb::Server::frameRate/2;
  588. else
  589. return frameTimer.getRemainingMs();
  590. }
  591. // writeUpdate() is called on a regular interval in order to see what
  592. // updates are pending and propagates them to the update tracker for
  593. // each client. It uses the ComparingUpdateTracker's compare() method
  594. // to filter out areas of the screen which haven't actually changed. It
  595. // also checks the state of the (server-side) rendered cursor, if
  596. // necessary rendering it again with the correct background.
  597. void VNCServerST::writeUpdate()
  598. {
  599. UpdateInfo ui;
  600. Region toCheck;
  601. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  602. assert(blockCounter == 0);
  603. assert(desktopStarted);
  604. comparer->getUpdateInfo(&ui, pb->getRect());
  605. toCheck = ui.changed.union_(ui.copied);
  606. if (needRenderedCursor()) {
  607. Rect clippedCursorRect = Rect(0, 0, cursor->width(), cursor->height())
  608. .translate(cursorPos.subtract(cursor->hotspot()))
  609. .intersect(pb->getRect());
  610. if (!toCheck.intersect(clippedCursorRect).is_empty())
  611. renderedCursorInvalid = true;
  612. }
  613. pb->grabRegion(toCheck);
  614. if (getComparerState())
  615. comparer->enable();
  616. else
  617. comparer->disable();
  618. if (comparer->compare())
  619. comparer->getUpdateInfo(&ui, pb->getRect());
  620. comparer->clear();
  621. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  622. ci_next = ci; ci_next++;
  623. (*ci)->add_copied(ui.copied, ui.copy_delta);
  624. (*ci)->add_changed(ui.changed);
  625. (*ci)->writeFramebufferUpdateOrClose();
  626. }
  627. }
  628. // checkUpdate() is called by clients to see if it is safe to read from
  629. // the framebuffer at this time.
  630. Region VNCServerST::getPendingRegion()
  631. {
  632. UpdateInfo ui;
  633. // Block clients as the frame buffer cannot be safely accessed
  634. if (blockCounter > 0)
  635. return pb->getRect();
  636. // Block client from updating if there are pending updates
  637. if (comparer->is_empty())
  638. return Region();
  639. comparer->getUpdateInfo(&ui, pb->getRect());
  640. return ui.changed.union_(ui.copied);
  641. }
  642. const RenderedCursor* VNCServerST::getRenderedCursor()
  643. {
  644. if (renderedCursorInvalid) {
  645. renderedCursor.update(pb, cursor, cursorPos);
  646. renderedCursorInvalid = false;
  647. }
  648. return &renderedCursor;
  649. }
  650. bool VNCServerST::getComparerState()
  651. {
  652. if (rfb::Server::compareFB == 0)
  653. return false;
  654. if (rfb::Server::compareFB != 2)
  655. return true;
  656. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  657. for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
  658. ci_next = ci; ci_next++;
  659. if ((*ci)->getComparerState())
  660. return true;
  661. }
  662. return false;
  663. }