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.

VNCSConnectionST.cxx 23KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright 2009 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. #include <rfb/VNCSConnectionST.h>
  20. #include <rfb/LogWriter.h>
  21. #include <rfb/secTypes.h>
  22. #include <rfb/screenTypes.h>
  23. #include <rfb/ServerCore.h>
  24. #include <rfb/ComparingUpdateTracker.h>
  25. #include <rfb/KeyRemapper.h>
  26. #define XK_MISCELLANY
  27. #define XK_XKB_KEYS
  28. #include <rfb/keysymdef.h>
  29. using namespace rfb;
  30. static LogWriter vlog("VNCSConnST");
  31. VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
  32. bool reverse)
  33. : SConnection(server_->securityFactory, reverse), sock(s), server(server_),
  34. updates(false), image_getter(server->useEconomicTranslate),
  35. drawRenderedCursor(false), removeRenderedCursor(false),
  36. pointerEventTime(0), accessRights(AccessDefault),
  37. startTime(time(0))
  38. {
  39. setStreams(&sock->inStream(), &sock->outStream());
  40. peerEndpoint.buf = sock->getPeerEndpoint();
  41. VNCServerST::connectionsLog.write(1,"accepted: %s", peerEndpoint.buf);
  42. // Configure the socket
  43. setSocketTimeouts();
  44. lastEventTime = time(0);
  45. server->clients.push_front(this);
  46. }
  47. VNCSConnectionST::~VNCSConnectionST()
  48. {
  49. // If we reach here then VNCServerST is deleting us!
  50. VNCServerST::connectionsLog.write(1,"closed: %s (%s)",
  51. peerEndpoint.buf,
  52. (closeReason.buf) ? closeReason.buf : "");
  53. // Release any keys the client still had pressed
  54. std::set<rdr::U32>::iterator i;
  55. for (i=pressedKeys.begin(); i!=pressedKeys.end(); i++)
  56. server->desktop->keyEvent(*i, false);
  57. if (server->pointerClient == this)
  58. server->pointerClient = 0;
  59. // Remove this client from the server
  60. server->clients.remove(this);
  61. }
  62. // Methods called from VNCServerST
  63. bool VNCSConnectionST::init()
  64. {
  65. try {
  66. initialiseProtocol();
  67. } catch (rdr::Exception& e) {
  68. close(e.str());
  69. return false;
  70. }
  71. return true;
  72. }
  73. void VNCSConnectionST::close(const char* reason)
  74. {
  75. // Log the reason for the close
  76. if (!closeReason.buf)
  77. closeReason.buf = strDup(reason);
  78. else
  79. vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
  80. if (authenticated()) {
  81. server->lastDisconnectTime = time(0);
  82. }
  83. // Just shutdown the socket and mark our state as closing. Eventually the
  84. // calling code will call VNCServerST's removeSocket() method causing us to
  85. // be deleted.
  86. sock->shutdown();
  87. setState(RFBSTATE_CLOSING);
  88. }
  89. void VNCSConnectionST::processMessages()
  90. {
  91. if (state() == RFBSTATE_CLOSING) return;
  92. try {
  93. // - Now set appropriate socket timeouts and process data
  94. setSocketTimeouts();
  95. bool clientsReadyBefore = server->clientsReadyForUpdate();
  96. while (getInStream()->checkNoWait(1)) {
  97. processMsg();
  98. }
  99. // If there were update requests, try to send a framebuffer update.
  100. // We don't send updates immediately on requests as this way, we
  101. // give higher priority to user actions such as keyboard and
  102. // pointer events.
  103. if (!requested.is_empty()) {
  104. writeFramebufferUpdate();
  105. }
  106. if (!clientsReadyBefore && !requested.is_empty())
  107. server->desktop->framebufferUpdateRequest();
  108. } catch (rdr::EndOfStream&) {
  109. close("Clean disconnection");
  110. } catch (rdr::Exception &e) {
  111. close(e.str());
  112. }
  113. }
  114. void VNCSConnectionST::writeFramebufferUpdateOrClose()
  115. {
  116. try {
  117. writeFramebufferUpdate();
  118. } catch(rdr::Exception &e) {
  119. close(e.str());
  120. }
  121. }
  122. void VNCSConnectionST::pixelBufferChange()
  123. {
  124. try {
  125. if (!authenticated()) return;
  126. if (cp.width && cp.height && (server->pb->width() != cp.width ||
  127. server->pb->height() != cp.height))
  128. {
  129. // We need to clip the next update to the new size, but also add any
  130. // extra bits if it's bigger. If we wanted to do this exactly, something
  131. // like the code below would do it, but at the moment we just update the
  132. // entire new size. However, we do need to clip the renderedCursorRect
  133. // because that might be added to updates in writeFramebufferUpdate().
  134. //updates.intersect(server->pb->getRect());
  135. //
  136. //if (server->pb->width() > cp.width)
  137. // updates.add_changed(Rect(cp.width, 0, server->pb->width(),
  138. // server->pb->height()));
  139. //if (server->pb->height() > cp.height)
  140. // updates.add_changed(Rect(0, cp.height, cp.width,
  141. // server->pb->height()));
  142. renderedCursorRect = renderedCursorRect.intersect(server->pb->getRect());
  143. cp.width = server->pb->width();
  144. cp.height = server->pb->height();
  145. cp.screenLayout = server->screenLayout;
  146. if (state() == RFBSTATE_NORMAL) {
  147. if (!writer()->writeSetDesktopSize() &&
  148. !writer()->writeExtendedDesktopSize()) {
  149. close("Client does not support desktop resize");
  150. return;
  151. }
  152. }
  153. }
  154. // Just update the whole screen at the moment because we're too lazy to
  155. // work out what's actually changed.
  156. updates.clear();
  157. updates.add_changed(server->pb->getRect());
  158. vlog.debug("pixel buffer changed - re-initialising image getter");
  159. image_getter.init(server->pb, cp.pf(), writer());
  160. if (writer()->needFakeUpdate())
  161. writeFramebufferUpdate();
  162. } catch(rdr::Exception &e) {
  163. close(e.str());
  164. }
  165. }
  166. void VNCSConnectionST::setColourMapEntriesOrClose(int firstColour,int nColours)
  167. {
  168. try {
  169. setColourMapEntries(firstColour, nColours);
  170. } catch(rdr::Exception& e) {
  171. close(e.str());
  172. }
  173. }
  174. void VNCSConnectionST::bell()
  175. {
  176. try {
  177. if (state() == RFBSTATE_NORMAL) writer()->writeBell();
  178. } catch(rdr::Exception& e) {
  179. close(e.str());
  180. }
  181. }
  182. void VNCSConnectionST::serverCutText(const char *str, int len)
  183. {
  184. try {
  185. if (!(accessRights & AccessCutText)) return;
  186. if (!rfb::Server::sendCutText) return;
  187. if (state() == RFBSTATE_NORMAL)
  188. writer()->writeServerCutText(str, len);
  189. } catch(rdr::Exception& e) {
  190. close(e.str());
  191. }
  192. }
  193. void VNCSConnectionST::setDesktopName(const char *name)
  194. {
  195. cp.setName(name);
  196. try {
  197. if (state() == RFBSTATE_NORMAL) {
  198. if (!writer()->writeSetDesktopName()) {
  199. fprintf(stderr, "Client does not support desktop rename\n");
  200. }
  201. }
  202. } catch(rdr::Exception& e) {
  203. close(e.str());
  204. }
  205. }
  206. void VNCSConnectionST::setCursorOrClose()
  207. {
  208. try {
  209. setCursor();
  210. } catch(rdr::Exception& e) {
  211. close(e.str());
  212. }
  213. }
  214. int VNCSConnectionST::checkIdleTimeout()
  215. {
  216. int idleTimeout = rfb::Server::idleTimeout;
  217. if (idleTimeout == 0) return 0;
  218. if (state() != RFBSTATE_NORMAL && idleTimeout < 15)
  219. idleTimeout = 15; // minimum of 15 seconds while authenticating
  220. time_t now = time(0);
  221. if (now < lastEventTime) {
  222. // Someone must have set the time backwards. Set lastEventTime so that the
  223. // idleTimeout will count from now.
  224. vlog.info("Time has gone backwards - resetting idle timeout");
  225. lastEventTime = now;
  226. }
  227. int timeLeft = lastEventTime + idleTimeout - now;
  228. if (timeLeft < -60) {
  229. // Our callback is over a minute late - someone must have set the time
  230. // forwards. Set lastEventTime so that the idleTimeout will count from
  231. // now.
  232. vlog.info("Time has gone forwards - resetting idle timeout");
  233. lastEventTime = now;
  234. return secsToMillis(idleTimeout);
  235. }
  236. if (timeLeft <= 0) {
  237. close("Idle timeout");
  238. return 0;
  239. }
  240. return secsToMillis(timeLeft);
  241. }
  242. // renderedCursorChange() is called whenever the server-side rendered cursor
  243. // changes shape or position. It ensures that the next update will clean up
  244. // the old rendered cursor and if necessary draw the new rendered cursor.
  245. void VNCSConnectionST::renderedCursorChange()
  246. {
  247. if (state() != RFBSTATE_NORMAL) return;
  248. removeRenderedCursor = true;
  249. if (needRenderedCursor())
  250. drawRenderedCursor = true;
  251. }
  252. // needRenderedCursor() returns true if this client needs the server-side
  253. // rendered cursor. This may be because it does not support local cursor or
  254. // because the current cursor position has not been set by this client.
  255. // Unfortunately we can't know for sure when the current cursor position has
  256. // been set by this client. We guess that this is the case when the current
  257. // cursor position is the same as the last pointer event from this client, or
  258. // if it is a very short time since this client's last pointer event (up to a
  259. // second). [ Ideally we should do finer-grained timing here and make the time
  260. // configurable, but I don't think it's that important. ]
  261. bool VNCSConnectionST::needRenderedCursor()
  262. {
  263. return (state() == RFBSTATE_NORMAL
  264. && (!cp.supportsLocalCursor && !cp.supportsLocalXCursor
  265. || (!server->cursorPos.equals(pointerEventPos) &&
  266. (time(0) - pointerEventTime) > 0)));
  267. }
  268. void VNCSConnectionST::approveConnectionOrClose(bool accept,
  269. const char* reason)
  270. {
  271. try {
  272. approveConnection(accept, reason);
  273. } catch (rdr::Exception& e) {
  274. close(e.str());
  275. }
  276. }
  277. // -=- Callbacks from SConnection
  278. void VNCSConnectionST::authSuccess()
  279. {
  280. lastEventTime = time(0);
  281. server->startDesktop();
  282. // - Set the connection parameters appropriately
  283. cp.width = server->pb->width();
  284. cp.height = server->pb->height();
  285. cp.screenLayout = server->screenLayout;
  286. cp.setName(server->getName());
  287. // - Set the default pixel format
  288. cp.setPF(server->pb->getPF());
  289. char buffer[256];
  290. cp.pf().print(buffer, 256);
  291. vlog.info("Server default pixel format %s", buffer);
  292. image_getter.init(server->pb, cp.pf(), 0);
  293. // - Mark the entire display as "dirty"
  294. updates.add_changed(server->pb->getRect());
  295. startTime = time(0);
  296. }
  297. void VNCSConnectionST::queryConnection(const char* userName)
  298. {
  299. // - Authentication succeeded - clear from blacklist
  300. CharArray name; name.buf = sock->getPeerAddress();
  301. server->blHosts->clearBlackmark(name.buf);
  302. // - Special case to provide a more useful error message
  303. if (rfb::Server::neverShared && !rfb::Server::disconnectClients &&
  304. server->authClientCount() > 0) {
  305. approveConnection(false, "The server is already in use");
  306. return;
  307. }
  308. // - Does the client have the right to bypass the query?
  309. if (reverseConnection ||
  310. !(rfb::Server::queryConnect || sock->requiresQuery()) ||
  311. (accessRights & AccessNoQuery))
  312. {
  313. approveConnection(true);
  314. return;
  315. }
  316. // - Get the server to display an Accept/Reject dialog, if required
  317. // If a dialog is displayed, the result will be PENDING, and the
  318. // server will call approveConnection at a later time
  319. CharArray reason;
  320. VNCServerST::queryResult qr = server->queryConnection(sock, userName,
  321. &reason.buf);
  322. if (qr == VNCServerST::PENDING)
  323. return;
  324. // - If server returns ACCEPT/REJECT then pass result to SConnection
  325. approveConnection(qr == VNCServerST::ACCEPT, reason.buf);
  326. }
  327. void VNCSConnectionST::clientInit(bool shared)
  328. {
  329. lastEventTime = time(0);
  330. if (rfb::Server::alwaysShared || reverseConnection) shared = true;
  331. if (rfb::Server::neverShared) shared = false;
  332. if (!shared) {
  333. if (rfb::Server::disconnectClients) {
  334. // - Close all the other connected clients
  335. vlog.debug("non-shared connection - closing clients");
  336. server->closeClients("Non-shared connection requested", getSock());
  337. } else {
  338. // - Refuse this connection if there are existing clients, in addition to
  339. // this one
  340. if (server->authClientCount() > 1) {
  341. close("Server is already in use");
  342. return;
  343. }
  344. }
  345. }
  346. SConnection::clientInit(shared);
  347. }
  348. void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
  349. {
  350. SConnection::setPixelFormat(pf);
  351. char buffer[256];
  352. pf.print(buffer, 256);
  353. vlog.info("Client pixel format %s", buffer);
  354. image_getter.init(server->pb, pf, writer());
  355. setCursor();
  356. }
  357. void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask)
  358. {
  359. pointerEventTime = lastEventTime = time(0);
  360. server->lastUserInputTime = lastEventTime;
  361. if (!(accessRights & AccessPtrEvents)) return;
  362. if (!rfb::Server::acceptPointerEvents) return;
  363. if (!server->pointerClient || server->pointerClient == this) {
  364. pointerEventPos = pos;
  365. if (buttonMask)
  366. server->pointerClient = this;
  367. else
  368. server->pointerClient = 0;
  369. server->desktop->pointerEvent(pointerEventPos, buttonMask);
  370. }
  371. }
  372. class VNCSConnectionSTShiftPresser {
  373. public:
  374. VNCSConnectionSTShiftPresser(SDesktop* desktop_)
  375. : desktop(desktop_), pressed(false) {}
  376. ~VNCSConnectionSTShiftPresser() {
  377. if (pressed) { desktop->keyEvent(XK_Shift_L, false); }
  378. }
  379. void press() {
  380. desktop->keyEvent(XK_Shift_L, true);
  381. pressed = true;
  382. }
  383. SDesktop* desktop;
  384. bool pressed;
  385. };
  386. // keyEvent() - record in the pressedKeys which keys were pressed. Allow
  387. // multiple down events (for autorepeat), but only allow a single up event.
  388. void VNCSConnectionST::keyEvent(rdr::U32 key, bool down) {
  389. lastEventTime = time(0);
  390. server->lastUserInputTime = lastEventTime;
  391. if (!(accessRights & AccessKeyEvents)) return;
  392. if (!rfb::Server::acceptKeyEvents) return;
  393. // Remap the key if required
  394. if (server->keyRemapper)
  395. key = server->keyRemapper->remapKey(key);
  396. // Turn ISO_Left_Tab into shifted Tab.
  397. VNCSConnectionSTShiftPresser shiftPresser(server->desktop);
  398. if (key == XK_ISO_Left_Tab) {
  399. if (pressedKeys.find(XK_Shift_L) == pressedKeys.end() &&
  400. pressedKeys.find(XK_Shift_R) == pressedKeys.end())
  401. shiftPresser.press();
  402. key = XK_Tab;
  403. }
  404. if (down) {
  405. pressedKeys.insert(key);
  406. } else {
  407. if (!pressedKeys.erase(key)) return;
  408. }
  409. server->desktop->keyEvent(key, down);
  410. }
  411. void VNCSConnectionST::clientCutText(const char* str, int len)
  412. {
  413. if (!(accessRights & AccessCutText)) return;
  414. if (!rfb::Server::acceptCutText) return;
  415. server->desktop->clientCutText(str, len);
  416. }
  417. void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
  418. {
  419. if (!(accessRights & AccessView)) return;
  420. SConnection::framebufferUpdateRequest(r, incremental);
  421. // Check that the client isn't sending crappy requests
  422. if (!r.enclosed_by(Rect(0, 0, cp.width, cp.height))) {
  423. vlog.error("FramebufferUpdateRequest %dx%d at %d,%d exceeds framebuffer %dx%d",
  424. r.width(), r.height(), r.tl.x, r.tl.y, cp.width, cp.height);
  425. // We crop the size later in writeFramebufferUpdate() so no need to
  426. // do so now.
  427. }
  428. // Just update the requested region.
  429. // Framebuffer update will be sent a bit later, see processMessages().
  430. Region reqRgn(r);
  431. requested.assign_union(reqRgn);
  432. if (!incremental) {
  433. // Non-incremental update - treat as if area requested has changed
  434. updates.add_changed(reqRgn);
  435. server->comparer->add_changed(reqRgn);
  436. // And update the clients view of screen layout
  437. writer()->writeSetDesktopSize();
  438. writer()->writeExtendedDesktopSize();
  439. }
  440. }
  441. void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height)
  442. {
  443. vlog.info("Rejecting client request to change desktop size");
  444. writer()->writeExtendedDesktopSize(resultProhibited);
  445. }
  446. void VNCSConnectionST::setInitialColourMap()
  447. {
  448. setColourMapEntries(0, 0);
  449. }
  450. // supportsLocalCursor() is called whenever the status of
  451. // cp.supportsLocalCursor has changed. If the client does now support local
  452. // cursor, we make sure that the old server-side rendered cursor is cleaned up
  453. // and the cursor is sent to the client.
  454. void VNCSConnectionST::supportsLocalCursor()
  455. {
  456. if (cp.supportsLocalCursor || cp.supportsLocalXCursor) {
  457. removeRenderedCursor = true;
  458. drawRenderedCursor = false;
  459. setCursor();
  460. }
  461. }
  462. void VNCSConnectionST::writeSetCursorCallback()
  463. {
  464. if (cp.supportsLocalXCursor) {
  465. Pixel pix0, pix1;
  466. rdr::U8Array bitmap(server->cursor.getBitmap(&pix0, &pix1));
  467. if (bitmap.buf) {
  468. // The client supports XCursor and the cursor only has two
  469. // colors. Use the XCursor encoding.
  470. writer()->writeSetXCursor(server->cursor.width(),
  471. server->cursor.height(),
  472. server->cursor.hotspot.x,
  473. server->cursor.hotspot.y,
  474. bitmap.buf, server->cursor.mask.buf);
  475. return;
  476. } else {
  477. // More than two colors
  478. if (!cp.supportsLocalCursor) {
  479. // FIXME: We could reduce to two colors.
  480. vlog.info("Unable to send multicolor cursor: RichCursor not supported by client");
  481. return;
  482. }
  483. }
  484. }
  485. // Use RichCursor
  486. rdr::U8* transData = writer()->getImageBuf(server->cursor.area());
  487. image_getter.translatePixels(server->cursor.data, transData,
  488. server->cursor.area());
  489. writer()->writeSetCursor(server->cursor.width(),
  490. server->cursor.height(),
  491. server->cursor.hotspot,
  492. transData, server->cursor.mask.buf);
  493. }
  494. void VNCSConnectionST::writeFramebufferUpdate()
  495. {
  496. // The framebuffer might have changed size since the
  497. // FramebufferUpdateRequest message was received. Clip it to the current
  498. // size of the framebuffer.
  499. requested = requested.intersect(Region(Rect(0, 0, cp.width, cp.height)));
  500. if (state() != RFBSTATE_NORMAL || requested.is_empty()) return;
  501. updates.enable_copyrect(cp.useCopyRect);
  502. server->checkUpdate();
  503. // Get the lists of updates. Prior to exporting the data to the `ui' object,
  504. // getUpdateInfo() will normalize the `updates' object such way that its
  505. // `changed' and `copied' regions would not intersect.
  506. UpdateInfo ui;
  507. updates.getUpdateInfo(&ui, requested);
  508. bool needNewUpdateInfo = false;
  509. // If the previous position of the rendered cursor overlaps the source of the
  510. // copy, then when the copy happens the corresponding rectangle in the
  511. // destination will be wrong, so add it to the changed region.
  512. if (!ui.copied.is_empty() && !renderedCursorRect.is_empty()) {
  513. Rect bogusCopiedCursor = (renderedCursorRect.translate(ui.copy_delta)
  514. .intersect(server->pb->getRect()));
  515. if (!ui.copied.intersect(bogusCopiedCursor).is_empty()) {
  516. updates.add_changed(bogusCopiedCursor);
  517. needNewUpdateInfo = true;
  518. }
  519. }
  520. // If we need to remove the old rendered cursor, just add the rectangle to
  521. // the changed region.
  522. if (removeRenderedCursor) {
  523. updates.add_changed(renderedCursorRect);
  524. needNewUpdateInfo = true;
  525. renderedCursorRect.clear();
  526. removeRenderedCursor = false;
  527. }
  528. // Return if there is nothing to send the client.
  529. if (updates.is_empty() && !writer()->needFakeUpdate() && !drawRenderedCursor)
  530. return;
  531. // The `updates' object could change, make sure we have valid update info.
  532. if (needNewUpdateInfo)
  533. updates.getUpdateInfo(&ui, requested);
  534. // If the client needs a server-side rendered cursor, work out the cursor
  535. // rectangle. If it's empty then don't bother drawing it, but if it overlaps
  536. // with the update region, we need to draw the rendered cursor regardless of
  537. // whether it has changed.
  538. if (needRenderedCursor()) {
  539. renderedCursorRect
  540. = (server->renderedCursor.getRect(server->renderedCursorTL)
  541. .intersect(requested.get_bounding_rect()));
  542. if (renderedCursorRect.is_empty()) {
  543. drawRenderedCursor = false;
  544. } else if (!ui.changed.union_(ui.copied)
  545. .intersect(renderedCursorRect).is_empty()) {
  546. drawRenderedCursor = true;
  547. }
  548. // We could remove the new cursor rect from updates here. It's not clear
  549. // whether this is worth it. If we do remove it, then we won't draw over
  550. // the same bit of screen twice, but we have the overhead of a more complex
  551. // region.
  552. //if (drawRenderedCursor) {
  553. // updates.subtract(renderedCursorRect);
  554. // updates.getUpdateInfo(&ui, requested);
  555. //}
  556. }
  557. if (!ui.is_empty() || writer()->needFakeUpdate() || drawRenderedCursor) {
  558. // Compute the number of rectangles. Tight encoder makes the things more
  559. // complicated as compared to the original VNC4.
  560. writer()->setupCurrentEncoder();
  561. int nRects = (ui.copied.numRects() +
  562. (drawRenderedCursor ? 1 : 0));
  563. std::vector<Rect> rects;
  564. std::vector<Rect>::const_iterator i;
  565. ui.changed.get_rects(&rects);
  566. for (i = rects.begin(); i != rects.end(); i++) {
  567. if (i->width() && i->height())
  568. nRects += writer()->getNumRects(*i);
  569. }
  570. writer()->writeFramebufferUpdateStart(nRects);
  571. Region updatedRegion;
  572. writer()->writeRects(ui, &image_getter, &updatedRegion);
  573. updates.subtract(updatedRegion);
  574. if (drawRenderedCursor)
  575. writeRenderedCursorRect();
  576. writer()->writeFramebufferUpdateEnd();
  577. requested.clear();
  578. }
  579. }
  580. // writeRenderedCursorRect() writes a single rectangle drawing the rendered
  581. // cursor on the client.
  582. void VNCSConnectionST::writeRenderedCursorRect()
  583. {
  584. image_getter.setPixelBuffer(&server->renderedCursor);
  585. image_getter.setOffset(server->renderedCursorTL);
  586. Rect actual;
  587. writer()->writeRect(renderedCursorRect, &image_getter, &actual);
  588. image_getter.setPixelBuffer(server->pb);
  589. image_getter.setOffset(Point(0,0));
  590. drawRenderedCursor = false;
  591. }
  592. void VNCSConnectionST::setColourMapEntries(int firstColour, int nColours)
  593. {
  594. if (!readyForSetColourMapEntries) return;
  595. if (server->pb->getPF().trueColour) return;
  596. image_getter.setColourMapEntries(firstColour, nColours, writer());
  597. if (cp.pf().trueColour) {
  598. updates.add_changed(server->pb->getRect());
  599. }
  600. }
  601. // setCursor() is called whenever the cursor has changed shape or pixel format.
  602. // If the client supports local cursor then it will arrange for the cursor to
  603. // be sent to the client.
  604. void VNCSConnectionST::setCursor()
  605. {
  606. if (state() != RFBSTATE_NORMAL || !cp.supportsLocalCursor) return;
  607. writer()->cursorChange(this);
  608. if (writer()->needFakeUpdate())
  609. writeFramebufferUpdate();
  610. }
  611. void VNCSConnectionST::setSocketTimeouts()
  612. {
  613. int timeoutms = rfb::Server::clientWaitTimeMillis;
  614. soonestTimeout(&timeoutms, secsToMillis(rfb::Server::idleTimeout));
  615. if (timeoutms == 0)
  616. timeoutms = -1;
  617. sock->inStream().setTimeout(timeoutms);
  618. sock->outStream().setTimeout(timeoutms);
  619. }
  620. char* VNCSConnectionST::getStartTime()
  621. {
  622. char* result = ctime(&startTime);
  623. result[24] = '\0';
  624. return result;
  625. }
  626. void VNCSConnectionST::setStatus(int status)
  627. {
  628. switch (status) {
  629. case 0:
  630. accessRights = accessRights | AccessPtrEvents | AccessKeyEvents | AccessView;
  631. break;
  632. case 1:
  633. accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents) | AccessView;
  634. break;
  635. case 2:
  636. accessRights = accessRights & !(AccessPtrEvents | AccessKeyEvents | AccessView);
  637. break;
  638. }
  639. framebufferUpdateRequest(server->pb->getRect(), false);
  640. }
  641. int VNCSConnectionST::getStatus()
  642. {
  643. if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0007)
  644. return 0;
  645. if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0001)
  646. return 1;
  647. if ((accessRights & (AccessPtrEvents | AccessKeyEvents | AccessView)) == 0x0000)
  648. return 2;
  649. return 4;
  650. }