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 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright 2009-2014 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/ServerCore.h>
  49. #include <rfb/VNCServerST.h>
  50. #include <rfb/VNCSConnectionST.h>
  51. #include <rfb/ComparingUpdateTracker.h>
  52. #include <rfb/Security.h>
  53. #include <rfb/KeyRemapper.h>
  54. #include <rfb/util.h>
  55. #include <rdr/types.h>
  56. using namespace rfb;
  57. static LogWriter slog("VNCServerST");
  58. LogWriter VNCServerST::connectionsLog("Connections");
  59. rfb::IntParameter deferUpdateTime("DeferUpdate",
  60. "Time in milliseconds to defer updates",1);
  61. rfb::BoolParameter alwaysSetDeferUpdateTimer("AlwaysSetDeferUpdateTimer",
  62. "Always reset the defer update timer on every change",false);
  63. //
  64. // -=- VNCServerST Implementation
  65. //
  66. // -=- Constructors/Destructor
  67. VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
  68. : blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
  69. blockCounter(0), pb(0),
  70. name(strDup(name_)), pointerClient(0), comparer(0),
  71. renderedCursorInvalid(false),
  72. queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
  73. lastConnectionTime(0), disableclients(false),
  74. deferTimer(this), deferPending(false)
  75. {
  76. lastUserInputTime = lastDisconnectTime = time(0);
  77. slog.debug("creating single-threaded server %s", name.buf);
  78. }
  79. VNCServerST::~VNCServerST()
  80. {
  81. slog.debug("shutting down server %s", name.buf);
  82. // Close any active clients, with appropriate logging & cleanup
  83. closeClients("Server shutdown");
  84. // Delete all the clients, and their sockets, and any closing sockets
  85. // NB: Deleting a client implicitly removes it from the clients list
  86. while (!clients.empty()) {
  87. delete clients.front();
  88. }
  89. // Stop the desktop object if active, *only* after deleting all clients!
  90. if (desktopStarted) {
  91. desktopStarted = false;
  92. desktop->stop();
  93. }
  94. delete comparer;
  95. }
  96. // SocketServer methods
  97. void VNCServerST::addSocket(network::Socket* sock, bool outgoing)
  98. {
  99. // - Check the connection isn't black-marked
  100. // *** do this in getSecurity instead?
  101. CharArray address(sock->getPeerAddress());
  102. if (blHosts->isBlackmarked(address.buf)) {
  103. connectionsLog.error("blacklisted: %s", address.buf);
  104. try {
  105. SConnection::writeConnFailedFromScratch("Too many security failures",
  106. &sock->outStream());
  107. } catch (rdr::Exception&) {
  108. }
  109. sock->shutdown();
  110. closingSockets.push_back(sock);
  111. return;
  112. }
  113. if (clients.empty()) {
  114. lastConnectionTime = time(0);
  115. }
  116. VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing);
  117. client->init();
  118. }
  119. void VNCServerST::removeSocket(network::Socket* sock) {
  120. // - If the socket has resources allocated to it, delete them
  121. std::list<VNCSConnectionST*>::iterator ci;
  122. for (ci = clients.begin(); ci != clients.end(); ci++) {
  123. if ((*ci)->getSock() == sock) {
  124. // - Delete the per-Socket resources
  125. delete *ci;
  126. // - Check that the desktop object is still required
  127. if (authClientCount() == 0 && desktopStarted) {
  128. slog.debug("no authenticated clients - stopping desktop");
  129. desktopStarted = false;
  130. desktop->stop();
  131. }
  132. return;
  133. }
  134. }
  135. // - If the Socket has no resources, it may have been a closingSocket
  136. closingSockets.remove(sock);
  137. }
  138. void VNCServerST::processSocketEvent(network::Socket* sock)
  139. {
  140. // - Find the appropriate VNCSConnectionST and process the event
  141. std::list<VNCSConnectionST*>::iterator ci;
  142. for (ci = clients.begin(); ci != clients.end(); ci++) {
  143. if ((*ci)->getSock() == sock) {
  144. (*ci)->processMessages();
  145. return;
  146. }
  147. }
  148. throw rdr::Exception("invalid Socket in VNCServerST");
  149. }
  150. int VNCServerST::checkTimeouts()
  151. {
  152. int timeout = 0;
  153. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  154. soonestTimeout(&timeout, Timer::checkTimeouts());
  155. for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
  156. ci_next = ci; ci_next++;
  157. soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
  158. }
  159. int timeLeft;
  160. time_t now = time(0);
  161. // Check MaxDisconnectionTime
  162. if (rfb::Server::maxDisconnectionTime && clients.empty()) {
  163. if (now < lastDisconnectTime) {
  164. // Someone must have set the time backwards.
  165. slog.info("Time has gone backwards - resetting lastDisconnectTime");
  166. lastDisconnectTime = now;
  167. }
  168. timeLeft = lastDisconnectTime + rfb::Server::maxDisconnectionTime - now;
  169. if (timeLeft < -60) {
  170. // Someone must have set the time forwards.
  171. slog.info("Time has gone forwards - resetting lastDisconnectTime");
  172. lastDisconnectTime = now;
  173. timeLeft = rfb::Server::maxDisconnectionTime;
  174. }
  175. if (timeLeft <= 0) {
  176. slog.info("MaxDisconnectionTime reached, exiting");
  177. exit(0);
  178. }
  179. soonestTimeout(&timeout, timeLeft * 1000);
  180. }
  181. // Check MaxConnectionTime
  182. if (rfb::Server::maxConnectionTime && lastConnectionTime && !clients.empty()) {
  183. if (now < lastConnectionTime) {
  184. // Someone must have set the time backwards.
  185. slog.info("Time has gone backwards - resetting lastConnectionTime");
  186. lastConnectionTime = now;
  187. }
  188. timeLeft = lastConnectionTime + rfb::Server::maxConnectionTime - now;
  189. if (timeLeft < -60) {
  190. // Someone must have set the time forwards.
  191. slog.info("Time has gone forwards - resetting lastConnectionTime");
  192. lastConnectionTime = now;
  193. timeLeft = rfb::Server::maxConnectionTime;
  194. }
  195. if (timeLeft <= 0) {
  196. slog.info("MaxConnectionTime reached, exiting");
  197. exit(0);
  198. }
  199. soonestTimeout(&timeout, timeLeft * 1000);
  200. }
  201. // Check MaxIdleTime
  202. if (rfb::Server::maxIdleTime) {
  203. if (now < lastUserInputTime) {
  204. // Someone must have set the time backwards.
  205. slog.info("Time has gone backwards - resetting lastUserInputTime");
  206. lastUserInputTime = now;
  207. }
  208. timeLeft = lastUserInputTime + rfb::Server::maxIdleTime - now;
  209. if (timeLeft < -60) {
  210. // Someone must have set the time forwards.
  211. slog.info("Time has gone forwards - resetting lastUserInputTime");
  212. lastUserInputTime = now;
  213. timeLeft = rfb::Server::maxIdleTime;
  214. }
  215. if (timeLeft <= 0) {
  216. slog.info("MaxIdleTime reached, exiting");
  217. exit(0);
  218. }
  219. soonestTimeout(&timeout, timeLeft * 1000);
  220. }
  221. return timeout;
  222. }
  223. // VNCServer methods
  224. void VNCServerST::blockUpdates()
  225. {
  226. blockCounter++;
  227. }
  228. void VNCServerST::unblockUpdates()
  229. {
  230. assert(blockCounter > 0);
  231. blockCounter--;
  232. // Flush out any updates we might have blocked
  233. if (blockCounter == 0)
  234. tryUpdate();
  235. }
  236. void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
  237. {
  238. pb = pb_;
  239. delete comparer;
  240. comparer = 0;
  241. screenLayout = layout;
  242. if (!pb) {
  243. if (desktopStarted)
  244. throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
  245. return;
  246. }
  247. comparer = new ComparingUpdateTracker(pb);
  248. cursor.setPF(pb->getPF());
  249. renderedCursorInvalid = true;
  250. // Make sure that we have at least one screen
  251. if (screenLayout.num_screens() == 0)
  252. screenLayout.add_screen(Screen(0, 0, 0, pb->width(), pb->height(), 0));
  253. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  254. for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
  255. ci_next = ci; ci_next++;
  256. (*ci)->pixelBufferChange();
  257. // Since the new pixel buffer means an ExtendedDesktopSize needs to
  258. // be sent anyway, we don't need to call screenLayoutChange.
  259. }
  260. }
  261. void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
  262. {
  263. ScreenSet layout;
  264. if (!pb_) {
  265. if (desktopStarted)
  266. throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
  267. return;
  268. }
  269. layout = screenLayout;
  270. // Check that the screen layout is still valid
  271. if (!layout.validate(pb_->width(), pb_->height())) {
  272. Rect fbRect;
  273. ScreenSet::iterator iter, iter_next;
  274. fbRect.setXYWH(0, 0, pb_->width(), pb_->height());
  275. for (iter = layout.begin();iter != layout.end();iter = iter_next) {
  276. iter_next = iter; ++iter_next;
  277. if (iter->dimensions.enclosed_by(fbRect))
  278. continue;
  279. iter->dimensions = iter->dimensions.intersect(fbRect);
  280. if (iter->dimensions.is_empty()) {
  281. slog.info("Removing screen %d (%x) as it is completely outside the new framebuffer",
  282. (int)iter->id, (unsigned)iter->id);
  283. layout.remove_screen(iter->id);
  284. }
  285. }
  286. }
  287. setPixelBuffer(pb_, layout);
  288. }
  289. void VNCServerST::setScreenLayout(const ScreenSet& layout)
  290. {
  291. if (!pb)
  292. throw Exception("setScreenLayout: new screen layout without a PixelBuffer");
  293. if (!layout.validate(pb->width(), pb->height()))
  294. throw Exception("setScreenLayout: invalid screen layout");
  295. screenLayout = layout;
  296. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  297. for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
  298. ci_next = ci; ci_next++;
  299. (*ci)->screenLayoutChangeOrClose(reasonServer);
  300. }
  301. }
  302. void VNCServerST::bell()
  303. {
  304. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  305. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  306. ci_next = ci; ci_next++;
  307. (*ci)->bellOrClose();
  308. }
  309. }
  310. void VNCServerST::serverCutText(const char* str, int len)
  311. {
  312. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  313. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  314. ci_next = ci; ci_next++;
  315. (*ci)->serverCutTextOrClose(str, len);
  316. }
  317. }
  318. void VNCServerST::setName(const char* name_)
  319. {
  320. name.replaceBuf(strDup(name_));
  321. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  322. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  323. ci_next = ci; ci_next++;
  324. (*ci)->setDesktopNameOrClose(name_);
  325. }
  326. }
  327. void VNCServerST::add_changed(const Region& region)
  328. {
  329. if (comparer == NULL)
  330. return;
  331. comparer->add_changed(region);
  332. startDefer();
  333. tryUpdate();
  334. }
  335. void VNCServerST::add_copied(const Region& dest, const Point& delta)
  336. {
  337. if (comparer == NULL)
  338. return;
  339. comparer->add_copied(dest, delta);
  340. startDefer();
  341. tryUpdate();
  342. }
  343. void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
  344. const void* data, const void* mask)
  345. {
  346. cursor.hotspot = newHotspot;
  347. cursor.setSize(width, height);
  348. cursor.imageRect(cursor.getRect(), data);
  349. memcpy(cursor.mask.buf, mask, cursor.maskLen());
  350. cursor.crop();
  351. renderedCursorInvalid = true;
  352. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  353. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  354. ci_next = ci; ci_next++;
  355. (*ci)->renderedCursorChange();
  356. (*ci)->setCursorOrClose();
  357. }
  358. }
  359. void VNCServerST::setCursorPos(const Point& pos)
  360. {
  361. if (!cursorPos.equals(pos)) {
  362. cursorPos = pos;
  363. renderedCursorInvalid = true;
  364. std::list<VNCSConnectionST*>::iterator ci;
  365. for (ci = clients.begin(); ci != clients.end(); ci++)
  366. (*ci)->renderedCursorChange();
  367. }
  368. }
  369. // Other public methods
  370. void VNCServerST::approveConnection(network::Socket* sock, bool accept,
  371. const char* reason)
  372. {
  373. std::list<VNCSConnectionST*>::iterator ci;
  374. for (ci = clients.begin(); ci != clients.end(); ci++) {
  375. if ((*ci)->getSock() == sock) {
  376. (*ci)->approveConnectionOrClose(accept, reason);
  377. return;
  378. }
  379. }
  380. }
  381. void VNCServerST::closeClients(const char* reason, network::Socket* except)
  382. {
  383. std::list<VNCSConnectionST*>::iterator i, next_i;
  384. for (i=clients.begin(); i!=clients.end(); i=next_i) {
  385. next_i = i; next_i++;
  386. if ((*i)->getSock() != except)
  387. (*i)->close(reason);
  388. }
  389. }
  390. void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
  391. {
  392. sockets->clear();
  393. std::list<VNCSConnectionST*>::iterator ci;
  394. for (ci = clients.begin(); ci != clients.end(); ci++) {
  395. sockets->push_back((*ci)->getSock());
  396. }
  397. std::list<network::Socket*>::iterator si;
  398. for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
  399. sockets->push_back(*si);
  400. }
  401. }
  402. SConnection* VNCServerST::getSConnection(network::Socket* sock) {
  403. std::list<VNCSConnectionST*>::iterator ci;
  404. for (ci = clients.begin(); ci != clients.end(); ci++) {
  405. if ((*ci)->getSock() == sock)
  406. return *ci;
  407. }
  408. return 0;
  409. }
  410. bool VNCServerST::handleTimeout(Timer* t)
  411. {
  412. if (t != &deferTimer)
  413. return false;
  414. tryUpdate();
  415. return false;
  416. }
  417. // -=- Internal methods
  418. void VNCServerST::startDesktop()
  419. {
  420. if (!desktopStarted) {
  421. slog.debug("starting desktop");
  422. desktop->start(this);
  423. desktopStarted = true;
  424. if (!pb)
  425. throw Exception("SDesktop::start() did not set a valid PixelBuffer");
  426. }
  427. }
  428. int VNCServerST::authClientCount() {
  429. int count = 0;
  430. std::list<VNCSConnectionST*>::iterator ci;
  431. for (ci = clients.begin(); ci != clients.end(); ci++) {
  432. if ((*ci)->authenticated())
  433. count++;
  434. }
  435. return count;
  436. }
  437. inline bool VNCServerST::needRenderedCursor()
  438. {
  439. std::list<VNCSConnectionST*>::iterator ci;
  440. for (ci = clients.begin(); ci != clients.end(); ci++)
  441. if ((*ci)->needRenderedCursor()) return true;
  442. return false;
  443. }
  444. inline void VNCServerST::startDefer()
  445. {
  446. if (deferUpdateTime == 0)
  447. return;
  448. if (deferPending && !alwaysSetDeferUpdateTimer)
  449. return;
  450. gettimeofday(&deferStart, NULL);
  451. deferTimer.start(deferUpdateTime);
  452. deferPending = true;
  453. }
  454. inline bool VNCServerST::checkDefer()
  455. {
  456. if (!deferPending)
  457. return true;
  458. if (msSince(&deferStart) >= deferUpdateTime)
  459. return true;
  460. return false;
  461. }
  462. void VNCServerST::tryUpdate()
  463. {
  464. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  465. if (blockCounter > 0)
  466. return;
  467. if (!checkDefer())
  468. return;
  469. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  470. ci_next = ci; ci_next++;
  471. (*ci)->writeFramebufferUpdateOrClose();
  472. }
  473. }
  474. // checkUpdate() is called just before sending an update. It checks to see
  475. // what updates are pending and propagates them to the update tracker for each
  476. // client. It uses the ComparingUpdateTracker's compare() method to filter out
  477. // areas of the screen which haven't actually changed. It also checks the
  478. // state of the (server-side) rendered cursor, if necessary rendering it again
  479. // with the correct background.
  480. bool VNCServerST::checkUpdate()
  481. {
  482. UpdateInfo ui;
  483. comparer->getUpdateInfo(&ui, pb->getRect());
  484. bool renderCursor = needRenderedCursor();
  485. if (ui.is_empty() && !(renderCursor && renderedCursorInvalid))
  486. return true;
  487. // Block clients as the frame buffer cannot be safely accessed
  488. if (blockCounter > 0)
  489. return false;
  490. // Block client from updating if we are currently deferring updates
  491. if (!checkDefer())
  492. return false;
  493. deferPending = false;
  494. Region toCheck = ui.changed.union_(ui.copied);
  495. if (renderCursor) {
  496. Rect clippedCursorRect
  497. = cursor.getRect(cursorPos.subtract(cursor.hotspot)).intersect(pb->getRect());
  498. if (!renderedCursorInvalid && (toCheck.intersect(clippedCursorRect)
  499. .is_empty())) {
  500. renderCursor = false;
  501. } else {
  502. toCheck.assign_union(clippedCursorRect);
  503. }
  504. }
  505. pb->grabRegion(toCheck);
  506. if (getComparerState())
  507. comparer->enable();
  508. else
  509. comparer->disable();
  510. if (comparer->compare())
  511. comparer->getUpdateInfo(&ui, pb->getRect());
  512. if (renderCursor) {
  513. renderedCursor.update(pb, &cursor, cursorPos);
  514. renderedCursorInvalid = false;
  515. }
  516. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  517. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  518. ci_next = ci; ci_next++;
  519. (*ci)->add_copied(ui.copied, ui.copy_delta);
  520. (*ci)->add_changed(ui.changed);
  521. }
  522. comparer->clear();
  523. return true;
  524. }
  525. void VNCServerST::getConnInfo(ListConnInfo * listConn)
  526. {
  527. listConn->Clear();
  528. listConn->setDisable(getDisable());
  529. if (clients.empty())
  530. return;
  531. std::list<VNCSConnectionST*>::iterator i;
  532. for (i = clients.begin(); i != clients.end(); i++)
  533. listConn->addInfo((void*)(*i), (*i)->getSock()->getPeerAddress(),
  534. (*i)->getStartTime(), (*i)->getStatus());
  535. }
  536. void VNCServerST::setConnStatus(ListConnInfo* listConn)
  537. {
  538. setDisable(listConn->getDisable());
  539. if (listConn->Empty() || clients.empty()) return;
  540. for (listConn->iBegin(); !listConn->iEnd(); listConn->iNext()) {
  541. VNCSConnectionST* conn = (VNCSConnectionST*)listConn->iGetConn();
  542. std::list<VNCSConnectionST*>::iterator i;
  543. for (i = clients.begin(); i != clients.end(); i++) {
  544. if ((*i) == conn) {
  545. int status = listConn->iGetStatus();
  546. if (status == 3) {
  547. (*i)->close(0);
  548. } else {
  549. (*i)->setStatus(status);
  550. }
  551. break;
  552. }
  553. }
  554. }
  555. }
  556. void VNCServerST::notifyScreenLayoutChange(VNCSConnectionST* requester)
  557. {
  558. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  559. for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
  560. ci_next = ci; ci_next++;
  561. if ((*ci) == requester)
  562. continue;
  563. (*ci)->screenLayoutChangeOrClose(reasonOtherClient);
  564. }
  565. }
  566. bool VNCServerST::getComparerState()
  567. {
  568. if (rfb::Server::compareFB == 0)
  569. return false;
  570. if (rfb::Server::compareFB != 2)
  571. return true;
  572. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  573. for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
  574. ci_next = ci; ci_next++;
  575. if ((*ci)->getComparerState())
  576. return true;
  577. }
  578. return false;
  579. }