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.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright (C) 2011-2019 Brian P. Hinz
  3. * Copyright (C) 2017 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
  18. * USA.
  19. */
  20. //
  21. // CMsgReader - class for reading RFB messages on the client side
  22. // (i.e. messages from server to client).
  23. //
  24. package com.tigervnc.rfb;
  25. import java.awt.image.*;
  26. import java.nio.ByteBuffer;
  27. import java.nio.CharBuffer;
  28. import java.nio.charset.Charset;
  29. import com.tigervnc.rdr.*;
  30. public class CMsgReader {
  31. static LogWriter vlog = new LogWriter("CMsgReader");
  32. protected CMsgReader(CMsgHandler handler_, InStream is_)
  33. {
  34. imageBufIdealSize = 0;
  35. handler = handler_;
  36. is = is_;
  37. nUpdateRectsLeft = 0;
  38. imageBuf = null;
  39. imageBufSize = 0;
  40. }
  41. public void readServerInit()
  42. {
  43. int width = is.readU16();
  44. int height = is.readU16();
  45. PixelFormat pf = new PixelFormat();
  46. pf.read(is);
  47. String name = is.readString();
  48. handler.serverInit(width, height, pf, name);
  49. }
  50. public void readMsg()
  51. {
  52. if (nUpdateRectsLeft == 0) {
  53. int type = is.readU8();
  54. switch (type) {
  55. case MsgTypes.msgTypeSetColourMapEntries:
  56. readSetColourMapEntries();
  57. break;
  58. case MsgTypes.msgTypeBell:
  59. readBell();
  60. break;
  61. case MsgTypes.msgTypeServerCutText:
  62. readServerCutText();
  63. break;
  64. case MsgTypes.msgTypeFramebufferUpdate:
  65. readFramebufferUpdate();
  66. break;
  67. case MsgTypes.msgTypeServerFence:
  68. readFence();
  69. break;
  70. case MsgTypes.msgTypeEndOfContinuousUpdates:
  71. readEndOfContinuousUpdates();
  72. break;
  73. default:
  74. vlog.error("unknown message type "+type);
  75. throw new Exception("unknown message type");
  76. }
  77. } else {
  78. int x = is.readU16();
  79. int y = is.readU16();
  80. int w = is.readU16();
  81. int h = is.readU16();
  82. int encoding = is.readS32();
  83. switch (encoding) {
  84. case Encodings.pseudoEncodingLastRect:
  85. nUpdateRectsLeft = 1; // this rectangle is the last one
  86. break;
  87. case Encodings.pseudoEncodingXCursor:
  88. readSetXCursor(w, h, new Point(x,y));
  89. break;
  90. case Encodings.pseudoEncodingCursor:
  91. readSetCursor(w, h, new Point(x,y));
  92. break;
  93. case Encodings.pseudoEncodingCursorWithAlpha:
  94. readSetCursorWithAlpha(w, h, new Point(x,y));
  95. break;
  96. case Encodings.pseudoEncodingVMwareCursor:
  97. readSetVMwareCursor(w, h, new Point(x,y));
  98. break;
  99. case Encodings.pseudoEncodingDesktopName:
  100. readSetDesktopName(x, y, w, h);
  101. break;
  102. case Encodings.pseudoEncodingDesktopSize:
  103. handler.setDesktopSize(w, h);
  104. break;
  105. case Encodings.pseudoEncodingExtendedDesktopSize:
  106. readExtendedDesktopSize(x, y, w, h);
  107. break;
  108. case Encodings.pseudoEncodingClientRedirect:
  109. nUpdateRectsLeft = 0;
  110. readClientRedirect(x, y, w, h);
  111. return;
  112. default:
  113. readRect(new Rect(x, y, x+w, y+h), encoding);
  114. break;
  115. };
  116. nUpdateRectsLeft--;
  117. if (nUpdateRectsLeft == 0)
  118. handler.framebufferUpdateEnd();
  119. }
  120. }
  121. protected void readSetColourMapEntries()
  122. {
  123. is.skip(1);
  124. int firstColour = is.readU16();
  125. int nColours = is.readU16();
  126. int[] rgbs = new int[nColours * 3];
  127. for (int i = 0; i < nColours * 3; i++)
  128. rgbs[i] = is.readU16();
  129. handler.setColourMapEntries(firstColour, nColours, rgbs);
  130. }
  131. protected void readBell()
  132. {
  133. handler.bell();
  134. }
  135. protected void readServerCutText()
  136. {
  137. is.skip(3);
  138. int len = is.readU32();
  139. if (len > 256*1024) {
  140. is.skip(len);
  141. vlog.error("cut text too long ("+len+" bytes) - ignoring");
  142. return;
  143. }
  144. ByteBuffer buf = ByteBuffer.allocate(len);
  145. is.readBytes(buf, len);
  146. Charset latin1 = Charset.forName("ISO-8859-1");
  147. CharBuffer chars = latin1.decode(buf.compact());
  148. handler.serverCutText(chars.toString(), len);
  149. }
  150. protected void readFence()
  151. {
  152. int flags;
  153. int len;
  154. ByteBuffer data = ByteBuffer.allocate(64);
  155. is.skip(3);
  156. flags = is.readU32();
  157. len = is.readU8();
  158. if (len > data.capacity()) {
  159. System.out.println("Ignoring fence with too large payload\n");
  160. is.skip(len);
  161. return;
  162. }
  163. is.readBytes(data, len);
  164. handler.fence(flags, len, data.array());
  165. }
  166. protected void readEndOfContinuousUpdates()
  167. {
  168. handler.endOfContinuousUpdates();
  169. }
  170. protected void readFramebufferUpdate()
  171. {
  172. is.skip(1);
  173. nUpdateRectsLeft = is.readU16();
  174. handler.framebufferUpdateStart();
  175. }
  176. protected void readRect(Rect r, int encoding)
  177. {
  178. if ((r.br.x > handler.server.width()) || (r.br.y > handler.server.height())) {
  179. vlog.error("Rect too big: "+r.width()+"x"+r.height()+" at "+
  180. r.tl.x+","+r.tl.y+" exceeds "+handler.server.width()+"x"+
  181. handler.server.height());
  182. throw new Exception("Rect too big");
  183. }
  184. if (r.is_empty())
  185. vlog.error("Ignoring zero size rect");
  186. handler.dataRect(r, encoding);
  187. }
  188. protected void readSetXCursor(int width, int height, Point hotspot)
  189. {
  190. byte pr, pg, pb;
  191. byte sr, sg, sb;
  192. int data_len = ((width+7)/8) * height;
  193. int mask_len = ((width+7)/8) * height;
  194. ByteBuffer data = ByteBuffer.allocate(data_len);
  195. ByteBuffer mask = ByteBuffer.allocate(mask_len);
  196. int x, y;
  197. byte[] buf = new byte[width*height*4];
  198. ByteBuffer out;
  199. if (width * height == 0)
  200. return;
  201. pr = (byte)is.readU8();
  202. pg = (byte)is.readU8();
  203. pb = (byte)is.readU8();
  204. sr = (byte)is.readU8();
  205. sg = (byte)is.readU8();
  206. sb = (byte)is.readU8();
  207. is.readBytes(data, data_len);
  208. is.readBytes(mask, mask_len);
  209. int maskBytesPerRow = (width+7)/8;
  210. out = ByteBuffer.wrap(buf);
  211. for (y = 0;y < height;y++) {
  212. for (x = 0;x < width;x++) {
  213. int byte_ = y * maskBytesPerRow + x / 8;
  214. int bit = 7 - x % 8;
  215. // NOTE: BufferedImage needs ARGB, rather than RGBA
  216. if ((mask.get(byte_) & (1 << bit)) > 0)
  217. out.put(out.position(), (byte)255);
  218. else
  219. out.put(out.position(), (byte)0);
  220. if ((data.get(byte_) & (1 << bit)) > 0) {
  221. out.put(out.position() + 1, pr);
  222. out.put(out.position() + 2, pg);
  223. out.put(out.position() + 3, pb);
  224. } else {
  225. out.put(out.position() + 1, sr);
  226. out.put(out.position() + 2, sg);
  227. out.put(out.position() + 3, sb);
  228. }
  229. out.position(out.position() + 4);
  230. }
  231. }
  232. handler.setCursor(width, height, hotspot, buf);
  233. }
  234. protected void readSetCursor(int width, int height, Point hotspot)
  235. {
  236. int data_len = width * height * (handler.server.pf().bpp/8);
  237. int mask_len = ((width+7)/8) * height;
  238. ByteBuffer data = ByteBuffer.allocate(data_len);
  239. ByteBuffer mask = ByteBuffer.allocate(mask_len);
  240. int x, y;
  241. byte[] buf = new byte[width*height*4];
  242. ByteBuffer in;
  243. ByteBuffer out;
  244. is.readBytes(data, data_len);
  245. is.readBytes(mask, mask_len);
  246. int maskBytesPerRow = (width+7)/8;
  247. in = ByteBuffer.wrap(data.array());
  248. out = ByteBuffer.wrap(buf);
  249. for (y = 0;y < height;y++) {
  250. for (x = 0;x < width;x++) {
  251. int byte_ = y * maskBytesPerRow + x / 8;
  252. int bit = 7 - x % 8;
  253. // NOTE: BufferedImage needs ARGB, rather than RGBA
  254. if ((mask.get(byte_) & (1 << bit)) != 0)
  255. out.put((byte)255);
  256. else
  257. out.put((byte)0);
  258. handler.server.pf().rgbFromBuffer(out.duplicate(), in.duplicate(), 1);
  259. in.position(in.position() + handler.server.pf().bpp/8);
  260. out.position(out.position() + 3);
  261. }
  262. }
  263. handler.setCursor(width, height, hotspot, buf);
  264. }
  265. protected void readSetCursorWithAlpha(int width, int height, Point hotspot)
  266. {
  267. int encoding;
  268. PixelFormat rgbaPF =
  269. new PixelFormat(32, 32, false, true, 255, 255, 255, 16, 8, 0);
  270. ManagedPixelBuffer pb =
  271. new ManagedPixelBuffer(rgbaPF, width, height);
  272. PixelFormat origPF;
  273. ByteBuffer buf =
  274. ByteBuffer.allocate(pb.area()*4).order(rgbaPF.getByteOrder());;
  275. encoding = is.readS32();
  276. origPF = handler.server.pf();
  277. handler.server.setPF(rgbaPF);
  278. handler.readAndDecodeRect(pb.getRect(), encoding, pb);
  279. handler.server.setPF(origPF);
  280. // ARGB with pre-multiplied alpha works best for BufferedImage
  281. if (pb.area() > 0) {
  282. // Sometimes a zero width or height cursor is sent.
  283. DataBuffer db = pb.getBuffer(pb.getRect()).getDataBuffer();
  284. for (int i = 0;i < pb.area();i++)
  285. buf.asIntBuffer().put(i, db.getElem(i));
  286. }
  287. for (int i = 0;i < pb.area();i++) {
  288. byte alpha = buf.get(buf.position()+3);
  289. buf.put(i*4+3, buf.get(i*4+2));
  290. buf.put(i*4+2, buf.get(i*4+1));
  291. buf.put(i*4+1, buf.get(i*4+0));
  292. buf.put(i*4+0, alpha);
  293. buf.position(buf.position() + 4);
  294. }
  295. handler.setCursor(width, height, hotspot, buf.array());
  296. }
  297. protected void readSetVMwareCursor(int width, int height, Point hotspot)
  298. {
  299. // VMware cursor sends RGBA, java BufferedImage needs ARGB
  300. if (width > maxCursorSize || height > maxCursorSize)
  301. throw new Exception("Too big cursor");
  302. byte type;
  303. type = (byte)is.readU8();
  304. is.skip(1);
  305. if (type == 0) {
  306. int len = width * height * (handler.server.pf().bpp/8);
  307. ByteBuffer andMask = ByteBuffer.allocate(len);
  308. ByteBuffer xorMask = ByteBuffer.allocate(len);
  309. ByteBuffer data = ByteBuffer.allocate(width*height*4);
  310. ByteBuffer andIn;
  311. ByteBuffer xorIn;
  312. ByteBuffer out;
  313. int Bpp;
  314. is.readBytes(andMask, len);
  315. is.readBytes(xorMask, len);
  316. andIn = ByteBuffer.wrap(andMask.array());
  317. xorIn = ByteBuffer.wrap(xorMask.array());
  318. out = ByteBuffer.wrap(data.array());
  319. Bpp = handler.server.pf().bpp/8;
  320. for (int y = 0;y < height;y++) {
  321. for (int x = 0;x < width;x++) {
  322. int andPixel, xorPixel;
  323. andPixel = handler.server.pf().pixelFromBuffer(andIn.duplicate());
  324. xorPixel = handler.server.pf().pixelFromBuffer(xorIn.duplicate());
  325. andIn.position(andIn.position() + Bpp);
  326. xorIn.position(xorIn.position() + Bpp);
  327. if (andPixel == 0) {
  328. byte r, g, b;
  329. // Opaque pixel
  330. r = (byte)handler.server.pf().getColorModel().getRed(xorPixel);
  331. g = (byte)handler.server.pf().getColorModel().getGreen(xorPixel);
  332. b = (byte)handler.server.pf().getColorModel().getBlue(xorPixel);
  333. out.put((byte)0xff);
  334. out.put(r);
  335. out.put(g);
  336. out.put(b);
  337. } else if (xorPixel == 0) {
  338. // Fully transparent pixel
  339. out.put((byte)0);
  340. out.put((byte)0);
  341. out.put((byte)0);
  342. out.put((byte)0);
  343. } else if (andPixel == xorPixel) {
  344. // Inverted pixel
  345. // We don't really support this, so just turn the pixel black
  346. // FIXME: Do an outline like WinVNC does?
  347. out.put((byte)0xff);
  348. out.put((byte)0);
  349. out.put((byte)0);
  350. out.put((byte)0);
  351. } else {
  352. // Partially transparent/inverted pixel
  353. // We _really_ can't handle this, just make it black
  354. out.put((byte)0xff);
  355. out.put((byte)0);
  356. out.put((byte)0);
  357. out.put((byte)0);
  358. }
  359. }
  360. }
  361. handler.setCursor(width, height, hotspot, data.array());
  362. } else if (type == 1) {
  363. ByteBuffer data = ByteBuffer.allocate(width*height*4);
  364. // FIXME: Is alpha premultiplied?
  365. ByteBuffer buf = ByteBuffer.allocate(4);
  366. for (int i=0;i < width*height*4;i+=4) {
  367. is.readBytes(buf,4);
  368. data.put(buf.array(),3,1);
  369. data.put(buf.array(),0,3);
  370. buf.clear();
  371. }
  372. handler.setCursor(width, height, hotspot, data.array());
  373. } else {
  374. throw new Exception("Unknown cursor type");
  375. }
  376. }
  377. protected void readSetDesktopName(int x, int y, int w, int h)
  378. {
  379. String name = is.readString();
  380. if (x != 0 || y != 0 || w != 0 || h != 0) {
  381. vlog.error("Ignoring DesktopName rect with non-zero position/size");
  382. } else {
  383. handler.setName(name);
  384. }
  385. }
  386. protected void readExtendedDesktopSize(int x, int y, int w, int h)
  387. {
  388. int screens, i;
  389. int id, flags;
  390. int sx, sy, sw, sh;
  391. ScreenSet layout = new ScreenSet();
  392. screens = is.readU8();
  393. is.skip(3);
  394. for (i = 0;i < screens;i++) {
  395. id = is.readU32();
  396. sx = is.readU16();
  397. sy = is.readU16();
  398. sw = is.readU16();
  399. sh = is.readU16();
  400. flags = is.readU32();
  401. layout.add_screen(new Screen(id, sx, sy, sw, sh, flags));
  402. }
  403. handler.setExtendedDesktopSize(x, y, w, h, layout);
  404. }
  405. protected void readClientRedirect(int x, int y, int w, int h)
  406. {
  407. int port = is.readU16();
  408. String host = is.readString();
  409. String x509subject = is.readString();
  410. if (x != 0 || y != 0 || w != 0 || h != 0)
  411. vlog.error("Ignoring ClientRedirect rect with non-zero position/size");
  412. else
  413. handler.clientRedirect(port, host, x509subject);
  414. }
  415. public int[] getImageBuf(int required) { return getImageBuf(required, 0, 0); }
  416. public int[] getImageBuf(int required, int requested, int nPixels)
  417. {
  418. int requiredBytes = required;
  419. int requestedBytes = requested;
  420. int size = requestedBytes;
  421. if (size > imageBufIdealSize) size = imageBufIdealSize;
  422. if (size < requiredBytes)
  423. size = requiredBytes;
  424. if (imageBufSize < size) {
  425. imageBufSize = size;
  426. imageBuf = new int[imageBufSize];
  427. }
  428. if (nPixels != 0)
  429. nPixels = imageBufSize / (handler.server.pf().bpp / 8);
  430. return imageBuf;
  431. }
  432. public InStream getInStream() { return is; }
  433. public int imageBufIdealSize;
  434. protected CMsgHandler handler;
  435. protected InStream is;
  436. protected int nUpdateRectsLeft;
  437. protected final int maxCursorSize = 256;
  438. protected int[] imageBuf;
  439. protected int imageBufSize;
  440. }