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.

CConnection.cxx 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright 2011-2019 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 <assert.h>
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <rfb/Exception.h>
  23. #include <rfb/clipboardTypes.h>
  24. #include <rfb/fenceTypes.h>
  25. #include <rfb/CMsgReader.h>
  26. #include <rfb/CMsgWriter.h>
  27. #include <rfb/CSecurity.h>
  28. #include <rfb/Decoder.h>
  29. #include <rfb/Security.h>
  30. #include <rfb/SecurityClient.h>
  31. #include <rfb/CConnection.h>
  32. #include <rfb/util.h>
  33. #include <rfb/LogWriter.h>
  34. #include <rdr/InStream.h>
  35. #include <rdr/OutStream.h>
  36. using namespace rfb;
  37. static LogWriter vlog("CConnection");
  38. CConnection::CConnection()
  39. : csecurity(0),
  40. supportsLocalCursor(false), supportsDesktopResize(false),
  41. supportsLEDState(false),
  42. is(0), os(0), reader_(0), writer_(0),
  43. shared(false),
  44. state_(RFBSTATE_UNINITIALISED),
  45. pendingPFChange(false), preferredEncoding(encodingTight),
  46. compressLevel(2), qualityLevel(-1),
  47. formatChange(false), encodingChange(false),
  48. firstUpdate(true), pendingUpdate(false), continuousUpdates(false),
  49. forceNonincremental(true),
  50. framebuffer(NULL), decoder(this),
  51. serverClipboard(NULL), hasLocalClipboard(false)
  52. {
  53. }
  54. CConnection::~CConnection()
  55. {
  56. setFramebuffer(NULL);
  57. if (csecurity)
  58. delete csecurity;
  59. delete reader_;
  60. reader_ = 0;
  61. delete writer_;
  62. writer_ = 0;
  63. strFree(serverClipboard);
  64. }
  65. void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
  66. {
  67. is = is_;
  68. os = os_;
  69. }
  70. void CConnection::setFramebuffer(ModifiablePixelBuffer* fb)
  71. {
  72. decoder.flush();
  73. if (fb) {
  74. assert(fb->width() == server.width());
  75. assert(fb->height() == server.height());
  76. }
  77. if ((framebuffer != NULL) && (fb != NULL)) {
  78. Rect rect;
  79. const rdr::U8* data;
  80. int stride;
  81. const rdr::U8 black[4] = { 0, 0, 0, 0 };
  82. // Copy still valid area
  83. rect.setXYWH(0, 0,
  84. __rfbmin(fb->width(), framebuffer->width()),
  85. __rfbmin(fb->height(), framebuffer->height()));
  86. data = framebuffer->getBuffer(framebuffer->getRect(), &stride);
  87. fb->imageRect(rect, data, stride);
  88. // Black out any new areas
  89. if (fb->width() > framebuffer->width()) {
  90. rect.setXYWH(framebuffer->width(), 0,
  91. fb->width() - framebuffer->width(),
  92. fb->height());
  93. fb->fillRect(rect, black);
  94. }
  95. if (fb->height() > framebuffer->height()) {
  96. rect.setXYWH(0, framebuffer->height(),
  97. fb->width(),
  98. fb->height() - framebuffer->height());
  99. fb->fillRect(rect, black);
  100. }
  101. }
  102. delete framebuffer;
  103. framebuffer = fb;
  104. }
  105. void CConnection::initialiseProtocol()
  106. {
  107. state_ = RFBSTATE_PROTOCOL_VERSION;
  108. }
  109. void CConnection::processMsg()
  110. {
  111. switch (state_) {
  112. case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
  113. case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
  114. case RFBSTATE_SECURITY: processSecurityMsg(); break;
  115. case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
  116. case RFBSTATE_INITIALISATION: processInitMsg(); break;
  117. case RFBSTATE_NORMAL: reader_->readMsg(); break;
  118. case RFBSTATE_UNINITIALISED:
  119. throw Exception("CConnection::processMsg: not initialised yet?");
  120. default:
  121. throw Exception("CConnection::processMsg: invalid state");
  122. }
  123. }
  124. void CConnection::processVersionMsg()
  125. {
  126. char verStr[27]; // FIXME: gcc has some bug in format-overflow
  127. int majorVersion;
  128. int minorVersion;
  129. vlog.debug("reading protocol version");
  130. if (!is->checkNoWait(12))
  131. return;
  132. is->readBytes(verStr, 12);
  133. verStr[12] = '\0';
  134. if (sscanf(verStr, "RFB %03d.%03d\n",
  135. &majorVersion, &minorVersion) != 2) {
  136. state_ = RFBSTATE_INVALID;
  137. throw Exception("reading version failed: not an RFB server?");
  138. }
  139. server.setVersion(majorVersion, minorVersion);
  140. vlog.info("Server supports RFB protocol version %d.%d",
  141. server.majorVersion, server.minorVersion);
  142. // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
  143. if (server.beforeVersion(3,3)) {
  144. vlog.error("Server gave unsupported RFB protocol version %d.%d",
  145. server.majorVersion, server.minorVersion);
  146. state_ = RFBSTATE_INVALID;
  147. throw Exception("Server gave unsupported RFB protocol version %d.%d",
  148. server.majorVersion, server.minorVersion);
  149. } else if (server.beforeVersion(3,7)) {
  150. server.setVersion(3,3);
  151. } else if (server.afterVersion(3,8)) {
  152. server.setVersion(3,8);
  153. }
  154. sprintf(verStr, "RFB %03d.%03d\n",
  155. server.majorVersion, server.minorVersion);
  156. os->writeBytes(verStr, 12);
  157. os->flush();
  158. state_ = RFBSTATE_SECURITY_TYPES;
  159. vlog.info("Using RFB protocol version %d.%d",
  160. server.majorVersion, server.minorVersion);
  161. }
  162. void CConnection::processSecurityTypesMsg()
  163. {
  164. vlog.debug("processing security types message");
  165. int secType = secTypeInvalid;
  166. std::list<rdr::U8> secTypes;
  167. secTypes = security.GetEnabledSecTypes();
  168. if (server.isVersion(3,3)) {
  169. // legacy 3.3 server may only offer "vnc authentication" or "none"
  170. secType = is->readU32();
  171. if (secType == secTypeInvalid) {
  172. throwConnFailedException();
  173. } else if (secType == secTypeNone || secType == secTypeVncAuth) {
  174. std::list<rdr::U8>::iterator i;
  175. for (i = secTypes.begin(); i != secTypes.end(); i++)
  176. if (*i == secType) {
  177. secType = *i;
  178. break;
  179. }
  180. if (i == secTypes.end())
  181. secType = secTypeInvalid;
  182. } else {
  183. vlog.error("Unknown 3.3 security type %d", secType);
  184. throw Exception("Unknown 3.3 security type");
  185. }
  186. } else {
  187. // >=3.7 server will offer us a list
  188. int nServerSecTypes = is->readU8();
  189. if (nServerSecTypes == 0)
  190. throwConnFailedException();
  191. std::list<rdr::U8>::iterator j;
  192. for (int i = 0; i < nServerSecTypes; i++) {
  193. rdr::U8 serverSecType = is->readU8();
  194. vlog.debug("Server offers security type %s(%d)",
  195. secTypeName(serverSecType), serverSecType);
  196. /*
  197. * Use the first type sent by server which matches client's type.
  198. * It means server's order specifies priority.
  199. */
  200. if (secType == secTypeInvalid) {
  201. for (j = secTypes.begin(); j != secTypes.end(); j++)
  202. if (*j == serverSecType) {
  203. secType = *j;
  204. break;
  205. }
  206. }
  207. }
  208. // Inform the server of our decision
  209. if (secType != secTypeInvalid) {
  210. os->writeU8(secType);
  211. os->flush();
  212. vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType);
  213. }
  214. }
  215. if (secType == secTypeInvalid) {
  216. state_ = RFBSTATE_INVALID;
  217. vlog.error("No matching security types");
  218. throw Exception("No matching security types");
  219. }
  220. state_ = RFBSTATE_SECURITY;
  221. csecurity = security.GetCSecurity(this, secType);
  222. processSecurityMsg();
  223. }
  224. void CConnection::processSecurityMsg()
  225. {
  226. vlog.debug("processing security message");
  227. if (csecurity->processMsg()) {
  228. state_ = RFBSTATE_SECURITY_RESULT;
  229. processSecurityResultMsg();
  230. }
  231. }
  232. void CConnection::processSecurityResultMsg()
  233. {
  234. vlog.debug("processing security result message");
  235. int result;
  236. if (server.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
  237. result = secResultOK;
  238. } else {
  239. if (!is->checkNoWait(1)) return;
  240. result = is->readU32();
  241. }
  242. switch (result) {
  243. case secResultOK:
  244. securityCompleted();
  245. return;
  246. case secResultFailed:
  247. vlog.debug("auth failed");
  248. break;
  249. case secResultTooMany:
  250. vlog.debug("auth failed - too many tries");
  251. break;
  252. default:
  253. throw Exception("Unknown security result from server");
  254. }
  255. state_ = RFBSTATE_INVALID;
  256. if (server.beforeVersion(3,8))
  257. throw AuthFailureException();
  258. CharArray reason(is->readString());
  259. throw AuthFailureException(reason.buf);
  260. }
  261. void CConnection::processInitMsg()
  262. {
  263. vlog.debug("reading server initialisation");
  264. reader_->readServerInit();
  265. }
  266. void CConnection::throwConnFailedException()
  267. {
  268. state_ = RFBSTATE_INVALID;
  269. CharArray reason;
  270. reason.buf = is->readString();
  271. throw ConnFailedException(reason.buf);
  272. }
  273. void CConnection::securityCompleted()
  274. {
  275. state_ = RFBSTATE_INITIALISATION;
  276. reader_ = new CMsgReader(this, is);
  277. writer_ = new CMsgWriter(&server, os);
  278. vlog.debug("Authentication success!");
  279. authSuccess();
  280. writer_->writeClientInit(shared);
  281. }
  282. void CConnection::setDesktopSize(int w, int h)
  283. {
  284. decoder.flush();
  285. CMsgHandler::setDesktopSize(w,h);
  286. if (continuousUpdates)
  287. writer()->writeEnableContinuousUpdates(true, 0, 0,
  288. server.width(),
  289. server.height());
  290. resizeFramebuffer();
  291. assert(framebuffer != NULL);
  292. assert(framebuffer->width() == server.width());
  293. assert(framebuffer->height() == server.height());
  294. }
  295. void CConnection::setExtendedDesktopSize(unsigned reason,
  296. unsigned result,
  297. int w, int h,
  298. const ScreenSet& layout)
  299. {
  300. decoder.flush();
  301. CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout);
  302. if (continuousUpdates)
  303. writer()->writeEnableContinuousUpdates(true, 0, 0,
  304. server.width(),
  305. server.height());
  306. resizeFramebuffer();
  307. assert(framebuffer != NULL);
  308. assert(framebuffer->width() == server.width());
  309. assert(framebuffer->height() == server.height());
  310. }
  311. void CConnection::endOfContinuousUpdates()
  312. {
  313. CMsgHandler::endOfContinuousUpdates();
  314. // We've gotten the marker for a format change, so make the pending
  315. // one active
  316. if (pendingPFChange) {
  317. server.setPF(pendingPF);
  318. pendingPFChange = false;
  319. // We might have another change pending
  320. if (formatChange)
  321. requestNewUpdate();
  322. }
  323. }
  324. void CConnection::serverInit(int width, int height,
  325. const PixelFormat& pf,
  326. const char* name)
  327. {
  328. CMsgHandler::serverInit(width, height, pf, name);
  329. state_ = RFBSTATE_NORMAL;
  330. vlog.debug("initialisation done");
  331. initDone();
  332. assert(framebuffer != NULL);
  333. assert(framebuffer->width() == server.width());
  334. assert(framebuffer->height() == server.height());
  335. // We want to make sure we call SetEncodings at least once
  336. encodingChange = true;
  337. requestNewUpdate();
  338. // This initial update request is a bit of a corner case, so we need
  339. // to help out setting the correct format here.
  340. if (pendingPFChange) {
  341. server.setPF(pendingPF);
  342. pendingPFChange = false;
  343. }
  344. }
  345. void CConnection::readAndDecodeRect(const Rect& r, int encoding,
  346. ModifiablePixelBuffer* pb)
  347. {
  348. decoder.decodeRect(r, encoding, pb);
  349. decoder.flush();
  350. }
  351. void CConnection::framebufferUpdateStart()
  352. {
  353. CMsgHandler::framebufferUpdateStart();
  354. assert(framebuffer != NULL);
  355. // Note: This might not be true if continuous updates are supported
  356. pendingUpdate = false;
  357. requestNewUpdate();
  358. }
  359. void CConnection::framebufferUpdateEnd()
  360. {
  361. decoder.flush();
  362. CMsgHandler::framebufferUpdateEnd();
  363. // A format change has been scheduled and we are now past the update
  364. // with the old format. Time to active the new one.
  365. if (pendingPFChange && !continuousUpdates) {
  366. server.setPF(pendingPF);
  367. pendingPFChange = false;
  368. }
  369. if (firstUpdate) {
  370. if (server.supportsContinuousUpdates) {
  371. vlog.info("Enabling continuous updates");
  372. continuousUpdates = true;
  373. writer()->writeEnableContinuousUpdates(true, 0, 0,
  374. server.width(),
  375. server.height());
  376. }
  377. firstUpdate = false;
  378. }
  379. }
  380. void CConnection::dataRect(const Rect& r, int encoding)
  381. {
  382. decoder.decodeRect(r, encoding, framebuffer);
  383. }
  384. void CConnection::serverCutText(const char* str)
  385. {
  386. hasLocalClipboard = false;
  387. strFree(serverClipboard);
  388. serverClipboard = NULL;
  389. serverClipboard = latin1ToUTF8(str);
  390. handleClipboardAnnounce(true);
  391. }
  392. void CConnection::handleClipboardCaps(rdr::U32 flags,
  393. const rdr::U32* lengths)
  394. {
  395. rdr::U32 sizes[] = { 0 };
  396. CMsgHandler::handleClipboardCaps(flags, lengths);
  397. writer()->writeClipboardCaps(rfb::clipboardUTF8 |
  398. rfb::clipboardRequest |
  399. rfb::clipboardPeek |
  400. rfb::clipboardNotify |
  401. rfb::clipboardProvide,
  402. sizes);
  403. }
  404. void CConnection::handleClipboardRequest(rdr::U32 flags)
  405. {
  406. if (!(flags & rfb::clipboardUTF8))
  407. return;
  408. if (!hasLocalClipboard)
  409. return;
  410. handleClipboardRequest();
  411. }
  412. void CConnection::handleClipboardPeek(rdr::U32 flags)
  413. {
  414. if (!hasLocalClipboard)
  415. return;
  416. if (server.clipboardFlags() & rfb::clipboardNotify)
  417. writer()->writeClipboardNotify(rfb::clipboardUTF8);
  418. }
  419. void CConnection::handleClipboardNotify(rdr::U32 flags)
  420. {
  421. strFree(serverClipboard);
  422. serverClipboard = NULL;
  423. if (flags & rfb::clipboardUTF8) {
  424. hasLocalClipboard = false;
  425. handleClipboardAnnounce(true);
  426. } else {
  427. handleClipboardAnnounce(false);
  428. }
  429. }
  430. void CConnection::handleClipboardProvide(rdr::U32 flags,
  431. const size_t* lengths,
  432. const rdr::U8* const* data)
  433. {
  434. if (!(flags & rfb::clipboardUTF8))
  435. return;
  436. strFree(serverClipboard);
  437. serverClipboard = NULL;
  438. serverClipboard = convertLF((const char*)data[0], lengths[0]);
  439. // FIXME: Should probably verify that this data was actually requested
  440. handleClipboardData(serverClipboard);
  441. }
  442. void CConnection::authSuccess()
  443. {
  444. }
  445. void CConnection::initDone()
  446. {
  447. }
  448. void CConnection::resizeFramebuffer()
  449. {
  450. assert(false);
  451. }
  452. void CConnection::handleClipboardRequest()
  453. {
  454. }
  455. void CConnection::handleClipboardAnnounce(bool available)
  456. {
  457. }
  458. void CConnection::handleClipboardData(const char* data)
  459. {
  460. }
  461. void CConnection::requestClipboard()
  462. {
  463. if (serverClipboard != NULL) {
  464. handleClipboardData(serverClipboard);
  465. return;
  466. }
  467. if (server.clipboardFlags() & rfb::clipboardRequest)
  468. writer()->writeClipboardRequest(rfb::clipboardUTF8);
  469. }
  470. void CConnection::announceClipboard(bool available)
  471. {
  472. hasLocalClipboard = available;
  473. if (server.clipboardFlags() & rfb::clipboardNotify)
  474. writer()->writeClipboardNotify(available ? rfb::clipboardUTF8 : 0);
  475. else {
  476. if (available)
  477. handleClipboardRequest();
  478. }
  479. }
  480. void CConnection::sendClipboardData(const char* data)
  481. {
  482. if (server.clipboardFlags() & rfb::clipboardProvide) {
  483. CharArray filtered(convertCRLF(data));
  484. size_t sizes[1] = { strlen(filtered.buf) + 1 };
  485. const rdr::U8* data[1] = { (const rdr::U8*)filtered.buf };
  486. writer()->writeClipboardProvide(rfb::clipboardUTF8, sizes, data);
  487. } else {
  488. CharArray latin1(utf8ToLatin1(data));
  489. writer()->writeClientCutText(latin1.buf);
  490. }
  491. }
  492. void CConnection::refreshFramebuffer()
  493. {
  494. forceNonincremental = true;
  495. // Without continuous updates we have to make sure we only have a
  496. // single update in flight, so we'll have to wait to do the refresh
  497. if (continuousUpdates)
  498. requestNewUpdate();
  499. }
  500. void CConnection::setPreferredEncoding(int encoding)
  501. {
  502. if (preferredEncoding == encoding)
  503. return;
  504. preferredEncoding = encoding;
  505. encodingChange = true;
  506. }
  507. int CConnection::getPreferredEncoding()
  508. {
  509. return preferredEncoding;
  510. }
  511. void CConnection::setCompressLevel(int level)
  512. {
  513. if (compressLevel == level)
  514. return;
  515. compressLevel = level;
  516. encodingChange = true;
  517. }
  518. void CConnection::setQualityLevel(int level)
  519. {
  520. if (qualityLevel == level)
  521. return;
  522. qualityLevel = level;
  523. encodingChange = true;
  524. }
  525. void CConnection::setPF(const PixelFormat& pf)
  526. {
  527. if (server.pf().equal(pf) && !formatChange)
  528. return;
  529. nextPF = pf;
  530. formatChange = true;
  531. }
  532. void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
  533. {
  534. CMsgHandler::fence(flags, len, data);
  535. if (!(flags & fenceFlagRequest))
  536. return;
  537. // We cannot guarantee any synchronisation at this level
  538. flags = 0;
  539. writer()->writeFence(flags, len, data);
  540. }
  541. // requestNewUpdate() requests an update from the server, having set the
  542. // format and encoding appropriately.
  543. void CConnection::requestNewUpdate()
  544. {
  545. if (formatChange && !pendingPFChange) {
  546. /* Catch incorrect requestNewUpdate calls */
  547. assert(!pendingUpdate || continuousUpdates);
  548. // We have to make sure we switch the internal format at a safe
  549. // time. For continuous updates we temporarily disable updates and
  550. // look for a EndOfContinuousUpdates message to see when to switch.
  551. // For classical updates we just got a new update right before this
  552. // function was called, so we need to make sure we finish that
  553. // update before we can switch.
  554. pendingPFChange = true;
  555. pendingPF = nextPF;
  556. if (continuousUpdates)
  557. writer()->writeEnableContinuousUpdates(false, 0, 0, 0, 0);
  558. writer()->writeSetPixelFormat(pendingPF);
  559. if (continuousUpdates)
  560. writer()->writeEnableContinuousUpdates(true, 0, 0,
  561. server.width(),
  562. server.height());
  563. formatChange = false;
  564. }
  565. if (encodingChange) {
  566. updateEncodings();
  567. encodingChange = false;
  568. }
  569. if (forceNonincremental || !continuousUpdates) {
  570. pendingUpdate = true;
  571. writer()->writeFramebufferUpdateRequest(Rect(0, 0,
  572. server.width(),
  573. server.height()),
  574. !forceNonincremental);
  575. }
  576. forceNonincremental = false;
  577. }
  578. // Ask for encodings based on which decoders are supported. Assumes higher
  579. // encoding numbers are more desirable.
  580. void CConnection::updateEncodings()
  581. {
  582. std::list<rdr::U32> encodings;
  583. if (supportsLocalCursor) {
  584. encodings.push_back(pseudoEncodingCursorWithAlpha);
  585. encodings.push_back(pseudoEncodingVMwareCursor);
  586. encodings.push_back(pseudoEncodingCursor);
  587. encodings.push_back(pseudoEncodingXCursor);
  588. }
  589. if (supportsDesktopResize) {
  590. encodings.push_back(pseudoEncodingDesktopSize);
  591. encodings.push_back(pseudoEncodingExtendedDesktopSize);
  592. }
  593. if (supportsLEDState) {
  594. encodings.push_back(pseudoEncodingLEDState);
  595. encodings.push_back(pseudoEncodingVMwareLEDState);
  596. }
  597. encodings.push_back(pseudoEncodingDesktopName);
  598. encodings.push_back(pseudoEncodingLastRect);
  599. encodings.push_back(pseudoEncodingExtendedClipboard);
  600. encodings.push_back(pseudoEncodingContinuousUpdates);
  601. encodings.push_back(pseudoEncodingFence);
  602. encodings.push_back(pseudoEncodingQEMUKeyEvent);
  603. if (Decoder::supported(preferredEncoding)) {
  604. encodings.push_back(preferredEncoding);
  605. }
  606. encodings.push_back(encodingCopyRect);
  607. for (int i = encodingMax; i >= 0; i--) {
  608. if ((i != preferredEncoding) && Decoder::supported(i))
  609. encodings.push_back(i);
  610. }
  611. if (compressLevel >= 0 && compressLevel <= 9)
  612. encodings.push_back(pseudoEncodingCompressLevel0 + compressLevel);
  613. if (qualityLevel >= 0 && qualityLevel <= 9)
  614. encodings.push_back(pseudoEncodingQualityLevel0 + qualityLevel);
  615. writer()->writeSetEncodings(encodings);
  616. }