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

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