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.

CMsgReader.cxx 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright 2009-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. #ifdef HAVE_CONFIG_H
  20. #include <config.h>
  21. #endif
  22. #include <assert.h>
  23. #include <stdio.h>
  24. #include <rdr/InStream.h>
  25. #include <rdr/ZlibInStream.h>
  26. #include <rfb/msgTypes.h>
  27. #include <rfb/clipboardTypes.h>
  28. #include <rfb/Exception.h>
  29. #include <rfb/LogWriter.h>
  30. #include <rfb/util.h>
  31. #include <rfb/CMsgHandler.h>
  32. #include <rfb/CMsgReader.h>
  33. static rfb::LogWriter vlog("CMsgReader");
  34. static rfb::IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024);
  35. using namespace rfb;
  36. CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_)
  37. : imageBufIdealSize(0), handler(handler_), is(is_),
  38. state(MSGSTATE_IDLE), cursorEncoding(-1)
  39. {
  40. }
  41. CMsgReader::~CMsgReader()
  42. {
  43. }
  44. bool CMsgReader::readServerInit()
  45. {
  46. int width, height;
  47. rdr::U32 len;
  48. if (!is->hasData(2 + 2 + 16 + 4))
  49. return false;
  50. is->setRestorePoint();
  51. width = is->readU16();
  52. height = is->readU16();
  53. PixelFormat pf;
  54. pf.read(is);
  55. len = is->readU32();
  56. if (!is->hasDataOrRestore(len))
  57. return false;
  58. is->clearRestorePoint();
  59. CharArray name(len + 1);
  60. is->readBytes(name.buf, len);
  61. name.buf[len] = '\0';
  62. handler->serverInit(width, height, pf, name.buf);
  63. return true;
  64. }
  65. bool CMsgReader::readMsg()
  66. {
  67. if (state == MSGSTATE_IDLE) {
  68. if (!is->hasData(1))
  69. return false;
  70. currentMsgType = is->readU8();
  71. state = MSGSTATE_MESSAGE;
  72. }
  73. if (currentMsgType != msgTypeFramebufferUpdate) {
  74. bool ret;
  75. switch (currentMsgType) {
  76. case msgTypeSetColourMapEntries:
  77. ret = readSetColourMapEntries();
  78. break;
  79. case msgTypeBell:
  80. ret = readBell();
  81. break;
  82. case msgTypeServerCutText:
  83. ret = readServerCutText();
  84. break;
  85. case msgTypeFramebufferUpdate:
  86. ret = readFramebufferUpdate();
  87. break;
  88. case msgTypeServerFence:
  89. ret = readFence();
  90. break;
  91. case msgTypeEndOfContinuousUpdates:
  92. ret = readEndOfContinuousUpdates();
  93. break;
  94. default:
  95. throw Exception("Unknown message type %d", currentMsgType);
  96. }
  97. if (ret)
  98. state = MSGSTATE_IDLE;
  99. return ret;
  100. } else {
  101. if (state == MSGSTATE_MESSAGE) {
  102. if (!readFramebufferUpdate())
  103. return false;
  104. // Empty update?
  105. if (nUpdateRectsLeft == 0) {
  106. state = MSGSTATE_IDLE;
  107. handler->framebufferUpdateEnd();
  108. return true;
  109. }
  110. state = MSGSTATE_RECT_HEADER;
  111. }
  112. if (state == MSGSTATE_RECT_HEADER) {
  113. if (!is->hasData(2 + 2 + 2 + 2 + 4))
  114. return false;
  115. int x = is->readU16();
  116. int y = is->readU16();
  117. int w = is->readU16();
  118. int h = is->readU16();
  119. dataRect.setXYWH(x, y, w, h);
  120. rectEncoding = is->readS32();
  121. state = MSGSTATE_RECT_DATA;
  122. }
  123. bool ret;
  124. switch (rectEncoding) {
  125. case pseudoEncodingLastRect:
  126. nUpdateRectsLeft = 1; // this rectangle is the last one
  127. ret = true;
  128. break;
  129. case pseudoEncodingXCursor:
  130. ret = readSetXCursor(dataRect.width(), dataRect.height(), dataRect.tl);
  131. break;
  132. case pseudoEncodingCursor:
  133. ret = readSetCursor(dataRect.width(), dataRect.height(), dataRect.tl);
  134. break;
  135. case pseudoEncodingCursorWithAlpha:
  136. ret = readSetCursorWithAlpha(dataRect.width(), dataRect.height(), dataRect.tl);
  137. break;
  138. case pseudoEncodingVMwareCursor:
  139. ret = readSetVMwareCursor(dataRect.width(), dataRect.height(), dataRect.tl);
  140. break;
  141. case pseudoEncodingVMwareCursorPosition:
  142. handler->setCursorPos(dataRect.tl);
  143. ret = true;
  144. break;
  145. case pseudoEncodingDesktopName:
  146. ret = readSetDesktopName(dataRect.tl.x, dataRect.tl.y,
  147. dataRect.width(), dataRect.height());
  148. break;
  149. case pseudoEncodingDesktopSize:
  150. handler->setDesktopSize(dataRect.width(), dataRect.height());
  151. ret = true;
  152. break;
  153. case pseudoEncodingExtendedDesktopSize:
  154. ret = readExtendedDesktopSize(dataRect.tl.x, dataRect.tl.y,
  155. dataRect.width(), dataRect.height());
  156. break;
  157. case pseudoEncodingLEDState:
  158. ret = readLEDState();
  159. break;
  160. case pseudoEncodingVMwareLEDState:
  161. ret = readVMwareLEDState();
  162. break;
  163. case pseudoEncodingQEMUKeyEvent:
  164. handler->supportsQEMUKeyEvent();
  165. ret = true;
  166. break;
  167. default:
  168. ret = readRect(dataRect, rectEncoding);
  169. break;
  170. };
  171. if (ret) {
  172. state = MSGSTATE_RECT_HEADER;
  173. nUpdateRectsLeft--;
  174. if (nUpdateRectsLeft == 0) {
  175. state = MSGSTATE_IDLE;
  176. handler->framebufferUpdateEnd();
  177. }
  178. }
  179. return ret;
  180. }
  181. }
  182. bool CMsgReader::readSetColourMapEntries()
  183. {
  184. if (!is->hasData(1 + 2 + 2))
  185. return false;
  186. is->setRestorePoint();
  187. is->skip(1);
  188. int firstColour = is->readU16();
  189. int nColours = is->readU16();
  190. if (!is->hasDataOrRestore(nColours * 3 * 2))
  191. return false;
  192. is->clearRestorePoint();
  193. rdr::U16Array rgbs(nColours * 3);
  194. for (int i = 0; i < nColours * 3; i++)
  195. rgbs.buf[i] = is->readU16();
  196. handler->setColourMapEntries(firstColour, nColours, rgbs.buf);
  197. return true;
  198. }
  199. bool CMsgReader::readBell()
  200. {
  201. handler->bell();
  202. return true;
  203. }
  204. bool CMsgReader::readServerCutText()
  205. {
  206. if (!is->hasData(3 + 4))
  207. return false;
  208. is->setRestorePoint();
  209. is->skip(3);
  210. rdr::U32 len = is->readU32();
  211. if (len & 0x80000000) {
  212. rdr::S32 slen = len;
  213. slen = -slen;
  214. if (readExtendedClipboard(slen)) {
  215. is->clearRestorePoint();
  216. return true;
  217. } else {
  218. is->gotoRestorePoint();
  219. return false;
  220. }
  221. }
  222. if (!is->hasDataOrRestore(len))
  223. return false;
  224. is->clearRestorePoint();
  225. if (len > (size_t)maxCutText) {
  226. is->skip(len);
  227. vlog.error("cut text too long (%d bytes) - ignoring",len);
  228. return true;
  229. }
  230. CharArray ca(len);
  231. is->readBytes(ca.buf, len);
  232. CharArray filtered(convertLF(ca.buf, len));
  233. handler->serverCutText(filtered.buf);
  234. return true;
  235. }
  236. bool CMsgReader::readExtendedClipboard(rdr::S32 len)
  237. {
  238. rdr::U32 flags;
  239. rdr::U32 action;
  240. if (!is->hasData(len))
  241. return false;
  242. if (len < 4)
  243. throw Exception("Invalid extended clipboard message");
  244. if (len > maxCutText) {
  245. vlog.error("Extended clipboard message too long (%d bytes) - ignoring", len);
  246. is->skip(len);
  247. return true;
  248. }
  249. flags = is->readU32();
  250. action = flags & clipboardActionMask;
  251. if (action & clipboardCaps) {
  252. int i;
  253. size_t num;
  254. rdr::U32 lengths[16];
  255. num = 0;
  256. for (i = 0;i < 16;i++) {
  257. if (flags & (1 << i))
  258. num++;
  259. }
  260. if (len < (rdr::S32)(4 + 4*num))
  261. throw Exception("Invalid extended clipboard message");
  262. num = 0;
  263. for (i = 0;i < 16;i++) {
  264. if (flags & (1 << i))
  265. lengths[num++] = is->readU32();
  266. }
  267. handler->handleClipboardCaps(flags, lengths);
  268. } else if (action == clipboardProvide) {
  269. rdr::ZlibInStream zis;
  270. int i;
  271. size_t num;
  272. size_t lengths[16];
  273. rdr::U8* buffers[16];
  274. zis.setUnderlying(is, len - 4);
  275. num = 0;
  276. for (i = 0;i < 16;i++) {
  277. if (!(flags & 1 << i))
  278. continue;
  279. if (!zis.hasData(4))
  280. throw Exception("Extended clipboard decode error");
  281. lengths[num] = zis.readU32();
  282. if (!zis.hasData(lengths[num]))
  283. throw Exception("Extended clipboard decode error");
  284. if (lengths[num] > (size_t)maxCutText) {
  285. vlog.error("Extended clipboard data too long (%d bytes) - ignoring",
  286. (unsigned)lengths[num]);
  287. zis.skip(lengths[num]);
  288. flags &= ~(1 << i);
  289. continue;
  290. }
  291. buffers[num] = new rdr::U8[lengths[num]];
  292. zis.readBytes(buffers[num], lengths[num]);
  293. num++;
  294. }
  295. zis.flushUnderlying();
  296. zis.setUnderlying(NULL, 0);
  297. handler->handleClipboardProvide(flags, lengths, buffers);
  298. num = 0;
  299. for (i = 0;i < 16;i++) {
  300. if (!(flags & 1 << i))
  301. continue;
  302. delete [] buffers[num++];
  303. }
  304. } else {
  305. switch (action) {
  306. case clipboardRequest:
  307. handler->handleClipboardRequest(flags);
  308. break;
  309. case clipboardPeek:
  310. handler->handleClipboardPeek(flags);
  311. break;
  312. case clipboardNotify:
  313. handler->handleClipboardNotify(flags);
  314. break;
  315. default:
  316. throw Exception("Invalid extended clipboard action");
  317. }
  318. }
  319. return true;
  320. }
  321. bool CMsgReader::readFence()
  322. {
  323. rdr::U32 flags;
  324. rdr::U8 len;
  325. char data[64];
  326. if (!is->hasData(3 + 4 + 1))
  327. return false;
  328. is->setRestorePoint();
  329. is->skip(3);
  330. flags = is->readU32();
  331. len = is->readU8();
  332. if (!is->hasDataOrRestore(len))
  333. return false;
  334. is->clearRestorePoint();
  335. if (len > sizeof(data)) {
  336. vlog.error("Ignoring fence with too large payload");
  337. is->skip(len);
  338. return true;
  339. }
  340. is->readBytes(data, len);
  341. handler->fence(flags, len, data);
  342. return true;
  343. }
  344. bool CMsgReader::readEndOfContinuousUpdates()
  345. {
  346. handler->endOfContinuousUpdates();
  347. return true;
  348. }
  349. bool CMsgReader::readFramebufferUpdate()
  350. {
  351. if (!is->hasData(1 + 2))
  352. return false;
  353. is->skip(1);
  354. nUpdateRectsLeft = is->readU16();
  355. handler->framebufferUpdateStart();
  356. return true;
  357. }
  358. bool CMsgReader::readRect(const Rect& r, int encoding)
  359. {
  360. if ((r.br.x > handler->server.width()) ||
  361. (r.br.y > handler->server.height())) {
  362. vlog.error("Rect too big: %dx%d at %d,%d exceeds %dx%d",
  363. r.width(), r.height(), r.tl.x, r.tl.y,
  364. handler->server.width(), handler->server.height());
  365. throw Exception("Rect too big");
  366. }
  367. if (r.is_empty())
  368. vlog.error("zero size rect");
  369. return handler->dataRect(r, encoding);
  370. }
  371. bool CMsgReader::readSetXCursor(int width, int height, const Point& hotspot)
  372. {
  373. if (width > maxCursorSize || height > maxCursorSize)
  374. throw Exception("Too big cursor");
  375. rdr::U8Array rgba(width*height*4);
  376. if (width * height > 0) {
  377. rdr::U8 pr, pg, pb;
  378. rdr::U8 sr, sg, sb;
  379. int data_len = ((width+7)/8) * height;
  380. int mask_len = ((width+7)/8) * height;
  381. rdr::U8Array data(data_len);
  382. rdr::U8Array mask(mask_len);
  383. int x, y;
  384. rdr::U8* out;
  385. if (!is->hasData(3 + 3 + data_len + mask_len))
  386. return false;
  387. pr = is->readU8();
  388. pg = is->readU8();
  389. pb = is->readU8();
  390. sr = is->readU8();
  391. sg = is->readU8();
  392. sb = is->readU8();
  393. is->readBytes(data.buf, data_len);
  394. is->readBytes(mask.buf, mask_len);
  395. int maskBytesPerRow = (width+7)/8;
  396. out = rgba.buf;
  397. for (y = 0;y < height;y++) {
  398. for (x = 0;x < width;x++) {
  399. int byte = y * maskBytesPerRow + x / 8;
  400. int bit = 7 - x % 8;
  401. if (data.buf[byte] & (1 << bit)) {
  402. out[0] = pr;
  403. out[1] = pg;
  404. out[2] = pb;
  405. } else {
  406. out[0] = sr;
  407. out[1] = sg;
  408. out[2] = sb;
  409. }
  410. if (mask.buf[byte] & (1 << bit))
  411. out[3] = 255;
  412. else
  413. out[3] = 0;
  414. out += 4;
  415. }
  416. }
  417. }
  418. handler->setCursor(width, height, hotspot, rgba.buf);
  419. return true;
  420. }
  421. bool CMsgReader::readSetCursor(int width, int height, const Point& hotspot)
  422. {
  423. if (width > maxCursorSize || height > maxCursorSize)
  424. throw Exception("Too big cursor");
  425. int data_len = width * height * (handler->server.pf().bpp/8);
  426. int mask_len = ((width+7)/8) * height;
  427. rdr::U8Array data(data_len);
  428. rdr::U8Array mask(mask_len);
  429. int x, y;
  430. rdr::U8Array rgba(width*height*4);
  431. rdr::U8* in;
  432. rdr::U8* out;
  433. if (!is->hasData(data_len + mask_len))
  434. return false;
  435. is->readBytes(data.buf, data_len);
  436. is->readBytes(mask.buf, mask_len);
  437. int maskBytesPerRow = (width+7)/8;
  438. in = data.buf;
  439. out = rgba.buf;
  440. for (y = 0;y < height;y++) {
  441. for (x = 0;x < width;x++) {
  442. int byte = y * maskBytesPerRow + x / 8;
  443. int bit = 7 - x % 8;
  444. handler->server.pf().rgbFromBuffer(out, in, 1);
  445. if (mask.buf[byte] & (1 << bit))
  446. out[3] = 255;
  447. else
  448. out[3] = 0;
  449. in += handler->server.pf().bpp/8;
  450. out += 4;
  451. }
  452. }
  453. handler->setCursor(width, height, hotspot, rgba.buf);
  454. return true;
  455. }
  456. bool CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hotspot)
  457. {
  458. if (width > maxCursorSize || height > maxCursorSize)
  459. throw Exception("Too big cursor");
  460. const PixelFormat rgbaPF(32, 32, false, true, 255, 255, 255, 16, 8, 0);
  461. ManagedPixelBuffer pb(rgbaPF, width, height);
  462. PixelFormat origPF;
  463. bool ret;
  464. rdr::U8* buf;
  465. int stride;
  466. // We can't use restore points as the decoder likely wants to as well, so
  467. // we need to keep track of the read encoding
  468. if (cursorEncoding == -1) {
  469. if (!is->hasData(4))
  470. return false;
  471. cursorEncoding = is->readS32();
  472. }
  473. origPF = handler->server.pf();
  474. handler->server.setPF(rgbaPF);
  475. ret = handler->readAndDecodeRect(pb.getRect(), cursorEncoding, &pb);
  476. handler->server.setPF(origPF);
  477. if (!ret)
  478. return false;
  479. cursorEncoding = -1;
  480. // On-wire data has pre-multiplied alpha, but we store it
  481. // non-pre-multiplied
  482. buf = pb.getBufferRW(pb.getRect(), &stride);
  483. assert(stride == width);
  484. for (int i = 0;i < pb.area();i++) {
  485. rdr::U8 alpha;
  486. alpha = buf[3];
  487. if (alpha == 0)
  488. alpha = 1; // Avoid division by zero
  489. buf[0] = (unsigned)buf[0] * 255/alpha;
  490. buf[1] = (unsigned)buf[1] * 255/alpha;
  491. buf[2] = (unsigned)buf[2] * 255/alpha;
  492. buf += 4;
  493. }
  494. pb.commitBufferRW(pb.getRect());
  495. handler->setCursor(width, height, hotspot,
  496. pb.getBuffer(pb.getRect(), &stride));
  497. return true;
  498. }
  499. bool CMsgReader::readSetVMwareCursor(int width, int height, const Point& hotspot)
  500. {
  501. if (width > maxCursorSize || height > maxCursorSize)
  502. throw Exception("Too big cursor");
  503. rdr::U8 type;
  504. if (!is->hasData(1 + 1))
  505. return false;
  506. is->setRestorePoint();
  507. type = is->readU8();
  508. is->skip(1);
  509. if (type == 0) {
  510. int len = width * height * (handler->server.pf().bpp/8);
  511. rdr::U8Array andMask(len);
  512. rdr::U8Array xorMask(len);
  513. rdr::U8Array data(width*height*4);
  514. rdr::U8* andIn;
  515. rdr::U8* xorIn;
  516. rdr::U8* out;
  517. int Bpp;
  518. if (!is->hasDataOrRestore(len + len))
  519. return false;
  520. is->clearRestorePoint();
  521. is->readBytes(andMask.buf, len);
  522. is->readBytes(xorMask.buf, len);
  523. andIn = andMask.buf;
  524. xorIn = xorMask.buf;
  525. out = data.buf;
  526. Bpp = handler->server.pf().bpp/8;
  527. for (int y = 0;y < height;y++) {
  528. for (int x = 0;x < width;x++) {
  529. Pixel andPixel, xorPixel;
  530. andPixel = handler->server.pf().pixelFromBuffer(andIn);
  531. xorPixel = handler->server.pf().pixelFromBuffer(xorIn);
  532. andIn += Bpp;
  533. xorIn += Bpp;
  534. if (andPixel == 0) {
  535. rdr::U8 r, g, b;
  536. // Opaque pixel
  537. handler->server.pf().rgbFromPixel(xorPixel, &r, &g, &b);
  538. *out++ = r;
  539. *out++ = g;
  540. *out++ = b;
  541. *out++ = 0xff;
  542. } else if (xorPixel == 0) {
  543. // Fully transparent pixel
  544. *out++ = 0;
  545. *out++ = 0;
  546. *out++ = 0;
  547. *out++ = 0;
  548. } else if (andPixel == xorPixel) {
  549. // Inverted pixel
  550. // We don't really support this, so just turn the pixel black
  551. // FIXME: Do an outline like WinVNC does?
  552. *out++ = 0;
  553. *out++ = 0;
  554. *out++ = 0;
  555. *out++ = 0xff;
  556. } else {
  557. // Partially transparent/inverted pixel
  558. // We _really_ can't handle this, just make it black
  559. *out++ = 0;
  560. *out++ = 0;
  561. *out++ = 0;
  562. *out++ = 0xff;
  563. }
  564. }
  565. }
  566. handler->setCursor(width, height, hotspot, data.buf);
  567. } else if (type == 1) {
  568. rdr::U8Array data(width*height*4);
  569. if (!is->hasDataOrRestore(width*height*4))
  570. return false;
  571. is->clearRestorePoint();
  572. // FIXME: Is alpha premultiplied?
  573. is->readBytes(data.buf, width*height*4);
  574. handler->setCursor(width, height, hotspot, data.buf);
  575. } else {
  576. throw Exception("Unknown cursor type");
  577. }
  578. return true;
  579. }
  580. bool CMsgReader::readSetDesktopName(int x, int y, int w, int h)
  581. {
  582. rdr::U32 len;
  583. if (!is->hasData(4))
  584. return false;
  585. is->setRestorePoint();
  586. len = is->readU32();
  587. if (!is->hasDataOrRestore(len))
  588. return false;
  589. is->clearRestorePoint();
  590. CharArray name(len + 1);
  591. is->readBytes(name.buf, len);
  592. name.buf[len] = '\0';
  593. if (x || y || w || h) {
  594. vlog.error("Ignoring DesktopName rect with non-zero position/size");
  595. } else {
  596. handler->setName(name.buf);
  597. }
  598. return true;
  599. }
  600. bool CMsgReader::readExtendedDesktopSize(int x, int y, int w, int h)
  601. {
  602. unsigned int screens, i;
  603. rdr::U32 id, flags;
  604. int sx, sy, sw, sh;
  605. ScreenSet layout;
  606. if (!is->hasData(1 + 3))
  607. return false;
  608. is->setRestorePoint();
  609. screens = is->readU8();
  610. is->skip(3);
  611. if (!is->hasDataOrRestore(16 * screens))
  612. return false;
  613. is->clearRestorePoint();
  614. for (i = 0;i < screens;i++) {
  615. id = is->readU32();
  616. sx = is->readU16();
  617. sy = is->readU16();
  618. sw = is->readU16();
  619. sh = is->readU16();
  620. flags = is->readU32();
  621. layout.add_screen(Screen(id, sx, sy, sw, sh, flags));
  622. }
  623. handler->setExtendedDesktopSize(x, y, w, h, layout);
  624. return true;
  625. }
  626. bool CMsgReader::readLEDState()
  627. {
  628. rdr::U8 state;
  629. if (!is->hasData(1))
  630. return false;
  631. state = is->readU8();
  632. handler->setLEDState(state);
  633. return true;
  634. }
  635. bool CMsgReader::readVMwareLEDState()
  636. {
  637. rdr::U32 state;
  638. if (!is->hasData(4))
  639. return false;
  640. state = is->readU32();
  641. // As luck has it, this extension uses the same bit definitions,
  642. // so no conversion required
  643. handler->setLEDState(state);
  644. return true;
  645. }