Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
  3. * Copyright 2009-2019 Pierre Ossman for Cendio AB
  4. *
  5. * This is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This software is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this software; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  18. * USA.
  19. */
  20. #include <stdio.h>
  21. #include <rdr/OutStream.h>
  22. #include <rdr/MemOutStream.h>
  23. #include <rdr/ZlibOutStream.h>
  24. #include <rfb/msgTypes.h>
  25. #include <rfb/fenceTypes.h>
  26. #include <rfb/clipboardTypes.h>
  27. #include <rfb/Exception.h>
  28. #include <rfb/ClientParams.h>
  29. #include <rfb/UpdateTracker.h>
  30. #include <rfb/Encoder.h>
  31. #include <rfb/SMsgWriter.h>
  32. #include <rfb/LogWriter.h>
  33. #include <rfb/ledStates.h>
  34. using namespace rfb;
  35. static LogWriter vlog("SMsgWriter");
  36. SMsgWriter::SMsgWriter(ClientParams* client_, rdr::OutStream* os_)
  37. : client(client_), os(os_),
  38. nRectsInUpdate(0), nRectsInHeader(0),
  39. needSetDesktopName(false), needCursor(false),
  40. needLEDState(false), needQEMUKeyEvent(false)
  41. {
  42. }
  43. SMsgWriter::~SMsgWriter()
  44. {
  45. }
  46. void SMsgWriter::writeServerInit(rdr::U16 width, rdr::U16 height,
  47. const PixelFormat& pf, const char* name)
  48. {
  49. os->writeU16(width);
  50. os->writeU16(height);
  51. pf.write(os);
  52. os->writeU32(strlen(name));
  53. os->writeBytes(name, strlen(name));
  54. endMsg();
  55. }
  56. void SMsgWriter::writeSetColourMapEntries(int firstColour, int nColours,
  57. const rdr::U16 red[],
  58. const rdr::U16 green[],
  59. const rdr::U16 blue[])
  60. {
  61. startMsg(msgTypeSetColourMapEntries);
  62. os->pad(1);
  63. os->writeU16(firstColour);
  64. os->writeU16(nColours);
  65. for (int i = firstColour; i < firstColour+nColours; i++) {
  66. os->writeU16(red[i]);
  67. os->writeU16(green[i]);
  68. os->writeU16(blue[i]);
  69. }
  70. endMsg();
  71. }
  72. void SMsgWriter::writeBell()
  73. {
  74. startMsg(msgTypeBell);
  75. endMsg();
  76. }
  77. void SMsgWriter::writeServerCutText(const char* str)
  78. {
  79. size_t len;
  80. if (strchr(str, '\r') != NULL)
  81. throw Exception("Invalid carriage return in clipboard data");
  82. len = strlen(str);
  83. startMsg(msgTypeServerCutText);
  84. os->pad(3);
  85. os->writeU32(len);
  86. os->writeBytes(str, len);
  87. endMsg();
  88. }
  89. void SMsgWriter::writeClipboardCaps(rdr::U32 caps,
  90. const rdr::U32* lengths)
  91. {
  92. size_t i, count;
  93. if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
  94. throw Exception("Client does not support extended clipboard");
  95. count = 0;
  96. for (i = 0;i < 16;i++) {
  97. if (caps & (1 << i))
  98. count++;
  99. }
  100. startMsg(msgTypeServerCutText);
  101. os->pad(3);
  102. os->writeS32(-(4 + 4 * count));
  103. os->writeU32(caps | clipboardCaps);
  104. count = 0;
  105. for (i = 0;i < 16;i++) {
  106. if (caps & (1 << i))
  107. os->writeU32(lengths[count++]);
  108. }
  109. endMsg();
  110. }
  111. void SMsgWriter::writeClipboardRequest(rdr::U32 flags)
  112. {
  113. if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
  114. throw Exception("Client does not support extended clipboard");
  115. if (!(client->clipboardFlags() & clipboardRequest))
  116. throw Exception("Client does not support clipboard \"request\" action");
  117. startMsg(msgTypeServerCutText);
  118. os->pad(3);
  119. os->writeS32(-4);
  120. os->writeU32(flags | clipboardRequest);
  121. endMsg();
  122. }
  123. void SMsgWriter::writeClipboardPeek(rdr::U32 flags)
  124. {
  125. if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
  126. throw Exception("Client does not support extended clipboard");
  127. if (!(client->clipboardFlags() & clipboardPeek))
  128. throw Exception("Client does not support clipboard \"peek\" action");
  129. startMsg(msgTypeServerCutText);
  130. os->pad(3);
  131. os->writeS32(-4);
  132. os->writeU32(flags | clipboardPeek);
  133. endMsg();
  134. }
  135. void SMsgWriter::writeClipboardNotify(rdr::U32 flags)
  136. {
  137. if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
  138. throw Exception("Client does not support extended clipboard");
  139. if (!(client->clipboardFlags() & clipboardNotify))
  140. throw Exception("Client does not support clipboard \"notify\" action");
  141. startMsg(msgTypeServerCutText);
  142. os->pad(3);
  143. os->writeS32(-4);
  144. os->writeU32(flags | clipboardNotify);
  145. endMsg();
  146. }
  147. void SMsgWriter::writeClipboardProvide(rdr::U32 flags,
  148. const size_t* lengths,
  149. const rdr::U8* const* data)
  150. {
  151. rdr::MemOutStream mos;
  152. rdr::ZlibOutStream zos;
  153. int i, count;
  154. if (!client->supportsEncoding(pseudoEncodingExtendedClipboard))
  155. throw Exception("Client does not support extended clipboard");
  156. if (!(client->clipboardFlags() & clipboardProvide))
  157. throw Exception("Client does not support clipboard \"provide\" action");
  158. zos.setUnderlying(&mos);
  159. count = 0;
  160. for (i = 0;i < 16;i++) {
  161. if (!(flags & (1 << i)))
  162. continue;
  163. zos.writeU32(lengths[count]);
  164. zos.writeBytes(data[count], lengths[count]);
  165. count++;
  166. }
  167. zos.flush();
  168. startMsg(msgTypeServerCutText);
  169. os->pad(3);
  170. os->writeS32(-(4 + mos.length()));
  171. os->writeU32(flags | clipboardProvide);
  172. os->writeBytes(mos.data(), mos.length());
  173. endMsg();
  174. }
  175. void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
  176. {
  177. if (!client->supportsEncoding(pseudoEncodingFence))
  178. throw Exception("Client does not support fences");
  179. if (len > 64)
  180. throw Exception("Too large fence payload");
  181. if ((flags & ~fenceFlagsSupported) != 0)
  182. throw Exception("Unknown fence flags");
  183. startMsg(msgTypeServerFence);
  184. os->pad(3);
  185. os->writeU32(flags);
  186. os->writeU8(len);
  187. if (len > 0)
  188. os->writeBytes(data, len);
  189. endMsg();
  190. }
  191. void SMsgWriter::writeEndOfContinuousUpdates()
  192. {
  193. if (!client->supportsEncoding(pseudoEncodingContinuousUpdates))
  194. throw Exception("Client does not support continuous updates");
  195. startMsg(msgTypeEndOfContinuousUpdates);
  196. endMsg();
  197. }
  198. void SMsgWriter::writeDesktopSize(rdr::U16 reason, rdr::U16 result)
  199. {
  200. ExtendedDesktopSizeMsg msg;
  201. if (!client->supportsEncoding(pseudoEncodingDesktopSize) &&
  202. !client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
  203. throw Exception("Client does not support desktop size changes");
  204. msg.reason = reason;
  205. msg.result = result;
  206. extendedDesktopSizeMsgs.push_back(msg);
  207. }
  208. void SMsgWriter::writeSetDesktopName()
  209. {
  210. if (!client->supportsEncoding(pseudoEncodingDesktopName))
  211. throw Exception("Client does not support desktop name changes");
  212. needSetDesktopName = true;
  213. }
  214. void SMsgWriter::writeCursor()
  215. {
  216. if (!client->supportsEncoding(pseudoEncodingCursor) &&
  217. !client->supportsEncoding(pseudoEncodingXCursor) &&
  218. !client->supportsEncoding(pseudoEncodingCursorWithAlpha) &&
  219. !client->supportsEncoding(pseudoEncodingVMwareCursor))
  220. throw Exception("Client does not support local cursor");
  221. needCursor = true;
  222. }
  223. void SMsgWriter::writeLEDState()
  224. {
  225. if (!client->supportsEncoding(pseudoEncodingLEDState) &&
  226. !client->supportsEncoding(pseudoEncodingVMwareLEDState))
  227. throw Exception("Client does not support LED state");
  228. if (client->ledState() == ledUnknown)
  229. throw Exception("Server has not specified LED state");
  230. needLEDState = true;
  231. }
  232. void SMsgWriter::writeQEMUKeyEvent()
  233. {
  234. if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
  235. throw Exception("Client does not support QEMU key events");
  236. needQEMUKeyEvent = true;
  237. }
  238. bool SMsgWriter::needFakeUpdate()
  239. {
  240. if (needSetDesktopName)
  241. return true;
  242. if (needCursor)
  243. return true;
  244. if (needLEDState)
  245. return true;
  246. if (needQEMUKeyEvent)
  247. return true;
  248. if (needNoDataUpdate())
  249. return true;
  250. return false;
  251. }
  252. bool SMsgWriter::needNoDataUpdate()
  253. {
  254. if (!extendedDesktopSizeMsgs.empty())
  255. return true;
  256. return false;
  257. }
  258. void SMsgWriter::writeNoDataUpdate()
  259. {
  260. int nRects;
  261. nRects = 0;
  262. if (!extendedDesktopSizeMsgs.empty()) {
  263. if (client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
  264. nRects += extendedDesktopSizeMsgs.size();
  265. else
  266. nRects++;
  267. }
  268. writeFramebufferUpdateStart(nRects);
  269. writeNoDataRects();
  270. writeFramebufferUpdateEnd();
  271. }
  272. void SMsgWriter::writeFramebufferUpdateStart(int nRects)
  273. {
  274. startMsg(msgTypeFramebufferUpdate);
  275. os->pad(1);
  276. if (nRects != 0xFFFF) {
  277. if (needSetDesktopName)
  278. nRects++;
  279. if (needCursor)
  280. nRects++;
  281. if (needLEDState)
  282. nRects++;
  283. if (needQEMUKeyEvent)
  284. nRects++;
  285. }
  286. os->writeU16(nRects);
  287. nRectsInUpdate = 0;
  288. if (nRects == 0xFFFF)
  289. nRectsInHeader = 0;
  290. else
  291. nRectsInHeader = nRects;
  292. writePseudoRects();
  293. }
  294. void SMsgWriter::writeFramebufferUpdateEnd()
  295. {
  296. if (nRectsInUpdate != nRectsInHeader && nRectsInHeader)
  297. throw Exception("SMsgWriter::writeFramebufferUpdateEnd: "
  298. "nRects out of sync");
  299. if (nRectsInHeader == 0) {
  300. // Send last rect. marker
  301. os->writeS16(0);
  302. os->writeS16(0);
  303. os->writeU16(0);
  304. os->writeU16(0);
  305. os->writeU32(pseudoEncodingLastRect);
  306. }
  307. endMsg();
  308. }
  309. void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY)
  310. {
  311. startRect(r,encodingCopyRect);
  312. os->writeU16(srcX);
  313. os->writeU16(srcY);
  314. endRect();
  315. }
  316. void SMsgWriter::startRect(const Rect& r, int encoding)
  317. {
  318. if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
  319. throw Exception("SMsgWriter::startRect: nRects out of sync");
  320. os->writeS16(r.tl.x);
  321. os->writeS16(r.tl.y);
  322. os->writeU16(r.width());
  323. os->writeU16(r.height());
  324. os->writeU32(encoding);
  325. }
  326. void SMsgWriter::endRect()
  327. {
  328. os->flush();
  329. }
  330. void SMsgWriter::startMsg(int type)
  331. {
  332. os->writeU8(type);
  333. }
  334. void SMsgWriter::endMsg()
  335. {
  336. os->flush();
  337. }
  338. void SMsgWriter::writePseudoRects()
  339. {
  340. if (needCursor) {
  341. const Cursor& cursor = client->cursor();
  342. if (client->supportsEncoding(pseudoEncodingCursorWithAlpha)) {
  343. writeSetCursorWithAlphaRect(cursor.width(), cursor.height(),
  344. cursor.hotspot().x, cursor.hotspot().y,
  345. cursor.getBuffer());
  346. } else if (client->supportsEncoding(pseudoEncodingVMwareCursor)) {
  347. writeSetVMwareCursorRect(cursor.width(), cursor.height(),
  348. cursor.hotspot().x, cursor.hotspot().y,
  349. cursor.getBuffer());
  350. } else if (client->supportsEncoding(pseudoEncodingCursor)) {
  351. rdr::U8Array data(cursor.width()*cursor.height() * (client->pf().bpp/8));
  352. rdr::U8Array mask(cursor.getMask());
  353. const rdr::U8* in;
  354. rdr::U8* out;
  355. in = cursor.getBuffer();
  356. out = data.buf;
  357. for (int i = 0;i < cursor.width()*cursor.height();i++) {
  358. client->pf().bufferFromRGB(out, in, 1);
  359. in += 4;
  360. out += client->pf().bpp/8;
  361. }
  362. writeSetCursorRect(cursor.width(), cursor.height(),
  363. cursor.hotspot().x, cursor.hotspot().y,
  364. data.buf, mask.buf);
  365. } else if (client->supportsEncoding(pseudoEncodingXCursor)) {
  366. rdr::U8Array bitmap(cursor.getBitmap());
  367. rdr::U8Array mask(cursor.getMask());
  368. writeSetXCursorRect(cursor.width(), cursor.height(),
  369. cursor.hotspot().x, cursor.hotspot().y,
  370. bitmap.buf, mask.buf);
  371. } else {
  372. throw Exception("Client does not support local cursor");
  373. }
  374. needCursor = false;
  375. }
  376. if (needSetDesktopName) {
  377. writeSetDesktopNameRect(client->name());
  378. needSetDesktopName = false;
  379. }
  380. if (needLEDState) {
  381. writeLEDStateRect(client->ledState());
  382. needLEDState = false;
  383. }
  384. if (needQEMUKeyEvent) {
  385. writeQEMUKeyEventRect();
  386. needQEMUKeyEvent = false;
  387. }
  388. }
  389. void SMsgWriter::writeNoDataRects()
  390. {
  391. if (!extendedDesktopSizeMsgs.empty()) {
  392. if (client->supportsEncoding(pseudoEncodingExtendedDesktopSize)) {
  393. std::list<ExtendedDesktopSizeMsg>::const_iterator ri;
  394. for (ri = extendedDesktopSizeMsgs.begin();ri != extendedDesktopSizeMsgs.end();++ri) {
  395. // FIXME: We can probably skip multiple reasonServer entries
  396. writeExtendedDesktopSizeRect(ri->reason, ri->result,
  397. client->width(), client->height(),
  398. client->screenLayout());
  399. }
  400. } else if (client->supportsEncoding(pseudoEncodingDesktopSize)) {
  401. // Some clients assume this is the last rectangle so don't send anything
  402. // more after this
  403. writeSetDesktopSizeRect(client->width(), client->height());
  404. } else {
  405. throw Exception("Client does not support desktop size changes");
  406. }
  407. extendedDesktopSizeMsgs.clear();
  408. }
  409. }
  410. void SMsgWriter::writeSetDesktopSizeRect(int width, int height)
  411. {
  412. if (!client->supportsEncoding(pseudoEncodingDesktopSize))
  413. throw Exception("Client does not support desktop resize");
  414. if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
  415. throw Exception("SMsgWriter::writeSetDesktopSizeRect: nRects out of sync");
  416. os->writeS16(0);
  417. os->writeS16(0);
  418. os->writeU16(width);
  419. os->writeU16(height);
  420. os->writeU32(pseudoEncodingDesktopSize);
  421. }
  422. void SMsgWriter::writeExtendedDesktopSizeRect(rdr::U16 reason,
  423. rdr::U16 result,
  424. int fb_width,
  425. int fb_height,
  426. const ScreenSet& layout)
  427. {
  428. ScreenSet::const_iterator si;
  429. if (!client->supportsEncoding(pseudoEncodingExtendedDesktopSize))
  430. throw Exception("Client does not support extended desktop resize");
  431. if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
  432. throw Exception("SMsgWriter::writeExtendedDesktopSizeRect: nRects out of sync");
  433. os->writeU16(reason);
  434. os->writeU16(result);
  435. os->writeU16(fb_width);
  436. os->writeU16(fb_height);
  437. os->writeU32(pseudoEncodingExtendedDesktopSize);
  438. os->writeU8(layout.num_screens());
  439. os->pad(3);
  440. for (si = layout.begin();si != layout.end();++si) {
  441. os->writeU32(si->id);
  442. os->writeU16(si->dimensions.tl.x);
  443. os->writeU16(si->dimensions.tl.y);
  444. os->writeU16(si->dimensions.width());
  445. os->writeU16(si->dimensions.height());
  446. os->writeU32(si->flags);
  447. }
  448. }
  449. void SMsgWriter::writeSetDesktopNameRect(const char *name)
  450. {
  451. if (!client->supportsEncoding(pseudoEncodingDesktopName))
  452. throw Exception("Client does not support desktop rename");
  453. if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
  454. throw Exception("SMsgWriter::writeSetDesktopNameRect: nRects out of sync");
  455. os->writeS16(0);
  456. os->writeS16(0);
  457. os->writeU16(0);
  458. os->writeU16(0);
  459. os->writeU32(pseudoEncodingDesktopName);
  460. os->writeU32(strlen(name));
  461. os->writeBytes(name, strlen(name));
  462. }
  463. void SMsgWriter::writeSetCursorRect(int width, int height,
  464. int hotspotX, int hotspotY,
  465. const void* data, const void* mask)
  466. {
  467. if (!client->supportsEncoding(pseudoEncodingCursor))
  468. throw Exception("Client does not support local cursors");
  469. if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
  470. throw Exception("SMsgWriter::writeSetCursorRect: nRects out of sync");
  471. os->writeS16(hotspotX);
  472. os->writeS16(hotspotY);
  473. os->writeU16(width);
  474. os->writeU16(height);
  475. os->writeU32(pseudoEncodingCursor);
  476. os->writeBytes(data, width * height * (client->pf().bpp/8));
  477. os->writeBytes(mask, (width+7)/8 * height);
  478. }
  479. void SMsgWriter::writeSetXCursorRect(int width, int height,
  480. int hotspotX, int hotspotY,
  481. const void* data, const void* mask)
  482. {
  483. if (!client->supportsEncoding(pseudoEncodingXCursor))
  484. throw Exception("Client does not support local cursors");
  485. if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
  486. throw Exception("SMsgWriter::writeSetXCursorRect: nRects out of sync");
  487. os->writeS16(hotspotX);
  488. os->writeS16(hotspotY);
  489. os->writeU16(width);
  490. os->writeU16(height);
  491. os->writeU32(pseudoEncodingXCursor);
  492. if (width * height > 0) {
  493. os->writeU8(255);
  494. os->writeU8(255);
  495. os->writeU8(255);
  496. os->writeU8(0);
  497. os->writeU8(0);
  498. os->writeU8(0);
  499. os->writeBytes(data, (width+7)/8 * height);
  500. os->writeBytes(mask, (width+7)/8 * height);
  501. }
  502. }
  503. void SMsgWriter::writeSetCursorWithAlphaRect(int width, int height,
  504. int hotspotX, int hotspotY,
  505. const rdr::U8* data)
  506. {
  507. if (!client->supportsEncoding(pseudoEncodingCursorWithAlpha))
  508. throw Exception("Client does not support local cursors");
  509. if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
  510. throw Exception("SMsgWriter::writeSetCursorWithAlphaRect: nRects out of sync");
  511. os->writeS16(hotspotX);
  512. os->writeS16(hotspotY);
  513. os->writeU16(width);
  514. os->writeU16(height);
  515. os->writeU32(pseudoEncodingCursorWithAlpha);
  516. // FIXME: Use an encoder with compression?
  517. os->writeU32(encodingRaw);
  518. // Alpha needs to be pre-multiplied
  519. for (int i = 0;i < width*height;i++) {
  520. os->writeU8((unsigned)data[0] * data[3] / 255);
  521. os->writeU8((unsigned)data[1] * data[3] / 255);
  522. os->writeU8((unsigned)data[2] * data[3] / 255);
  523. os->writeU8(data[3]);
  524. data += 4;
  525. }
  526. }
  527. void SMsgWriter::writeSetVMwareCursorRect(int width, int height,
  528. int hotspotX, int hotspotY,
  529. const rdr::U8* data)
  530. {
  531. if (!client->supportsEncoding(pseudoEncodingVMwareCursor))
  532. throw Exception("Client does not support local cursors");
  533. if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
  534. throw Exception("SMsgWriter::writeSetVMwareCursorRect: nRects out of sync");
  535. os->writeS16(hotspotX);
  536. os->writeS16(hotspotY);
  537. os->writeU16(width);
  538. os->writeU16(height);
  539. os->writeU32(pseudoEncodingVMwareCursor);
  540. os->writeU8(1); // Alpha cursor
  541. os->pad(1);
  542. // FIXME: Should alpha be premultiplied?
  543. os->writeBytes(data, width*height*4);
  544. }
  545. void SMsgWriter::writeLEDStateRect(rdr::U8 state)
  546. {
  547. if (!client->supportsEncoding(pseudoEncodingLEDState) &&
  548. !client->supportsEncoding(pseudoEncodingVMwareLEDState))
  549. throw Exception("Client does not support LED state updates");
  550. if (client->ledState() == ledUnknown)
  551. throw Exception("Server does not support LED state updates");
  552. if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
  553. throw Exception("SMsgWriter::writeLEDStateRect: nRects out of sync");
  554. os->writeS16(0);
  555. os->writeS16(0);
  556. os->writeU16(0);
  557. os->writeU16(0);
  558. if (client->supportsEncoding(pseudoEncodingLEDState)) {
  559. os->writeU32(pseudoEncodingLEDState);
  560. os->writeU8(state);
  561. } else {
  562. os->writeU32(pseudoEncodingVMwareLEDState);
  563. os->writeU32(state);
  564. }
  565. }
  566. void SMsgWriter::writeQEMUKeyEventRect()
  567. {
  568. if (!client->supportsEncoding(pseudoEncodingQEMUKeyEvent))
  569. throw Exception("Client does not support QEMU extended key events");
  570. if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader)
  571. throw Exception("SMsgWriter::writeQEMUKeyEventRect: nRects out of sync");
  572. os->writeS16(0);
  573. os->writeS16(0);
  574. os->writeU16(0);
  575. os->writeU16(0);
  576. os->writeU32(pseudoEncodingQEMUKeyEvent);
  577. }