blob: 953e61d2e13ede013337597e79bbb7c9a856032b [file] [log] [blame]
Constantin Kaplinsky1215b992008-04-18 09:51:44 +00001//
2// Copyright (C) 2001,2002 HorizonLive.com, Inc. All Rights Reserved.
3// Copyright (C) 2001 Constantin Kaplinsky. All Rights Reserved.
4// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
5// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
6//
7// This is free software; you can redistribute it and/or modify
8// it under the terms of the GNU General Public License as published by
9// the Free Software Foundation; either version 2 of the License, or
10// (at your option) any later version.
11//
12// This software is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15// GNU General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this software; if not, write to the Free Software
19// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20// USA.
21//
22
23import java.awt.*;
24import java.awt.event.*;
25import java.awt.image.*;
26import java.io.*;
27import java.lang.*;
28import java.util.zip.*;
29
30
31//
32// VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
33//
34
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000035class VncCanvas extends Canvas {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000036
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000037 RfbPlayer player;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000038 RfbProto rfb;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000039 ColorModel cm24;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000040
41 Image memImage;
42 Graphics memGraphics;
43
44 Image rawPixelsImage;
45 MemoryImageSource pixelsSource;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000046 int[] pixels24;
47
48 // Zlib encoder's data.
49 byte[] zlibBuf;
50 int zlibBufLen = 0;
51 Inflater zlibInflater;
52
53 // Tight encoder's data.
54 final static int tightZlibBufferSize = 512;
55 Inflater[] tightInflaters;
56
57 // Since JPEG images are loaded asynchronously, we have to remember
58 // their position in the framebuffer. Also, this jpegRect object is
59 // used for synchronization between the rfbThread and a JVM's thread
60 // which decodes and loads JPEG images.
61 Rectangle jpegRect;
62
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000063 // When we're in the seeking mode, we should not update the desktop.
64 // This variable helps us to remember that repainting the desktop at
65 // once is necessary when the seek operation is finished.
66 boolean seekMode;
67
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000068 //
69 // The constructor.
70 //
71
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000072 VncCanvas(RfbPlayer player) throws IOException {
73 this.player = player;
74 rfb = player.rfb;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000075 seekMode = false;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000076
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000077 cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
78
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000079 updateFramebufferSize();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000080 }
81
82 //
83 // Callback methods to determine geometry of our Component.
84 //
85
86 public Dimension getPreferredSize() {
87 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
88 }
89
90 public Dimension getMinimumSize() {
91 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
92 }
93
94 public Dimension getMaximumSize() {
95 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
96 }
97
98 //
99 // All painting is performed here.
100 //
101
102 public void update(Graphics g) {
103 paint(g);
104 }
105
106 public void paint(Graphics g) {
107 synchronized(memImage) {
108 g.drawImage(memImage, 0, 0, null);
109 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000110 }
111
112 //
113 // Override the ImageObserver interface method to handle drawing of
114 // JPEG-encoded data.
115 //
116
117 public boolean imageUpdate(Image img, int infoflags,
118 int x, int y, int width, int height) {
119 if ((infoflags & (ALLBITS | ABORT)) == 0) {
120 return true; // We need more image data.
121 } else {
122 // If the whole image is available, draw it now.
123 if ((infoflags & ALLBITS) != 0) {
124 if (jpegRect != null) {
125 synchronized(jpegRect) {
126 memGraphics.drawImage(img, jpegRect.x, jpegRect.y, null);
127 scheduleRepaint(jpegRect.x, jpegRect.y,
128 jpegRect.width, jpegRect.height);
129 jpegRect.notify();
130 }
131 }
132 }
133 return false; // All image data was processed.
134 }
135 }
136
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000137 void updateFramebufferSize() {
138
139 // Useful shortcuts.
140 int fbWidth = rfb.framebufferWidth;
141 int fbHeight = rfb.framebufferHeight;
142
143 // Create new off-screen image either if it does not exist, or if
144 // its geometry should be changed. It's not necessary to replace
145 // existing image if only pixel format should be changed.
146 if (memImage == null) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000147 memImage = player.createImage(fbWidth, fbHeight);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000148 memGraphics = memImage.getGraphics();
149 } else if (memImage.getWidth(null) != fbWidth ||
150 memImage.getHeight(null) != fbHeight) {
151 synchronized(memImage) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000152 memImage = player.createImage(fbWidth, fbHeight);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000153 memGraphics = memImage.getGraphics();
154 }
155 }
156
157 // Images with raw pixels should be re-allocated on every change
158 // of geometry or pixel format.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000159 pixels24 = new int[fbWidth * fbHeight];
160 pixelsSource =
161 new MemoryImageSource(fbWidth, fbHeight, cm24, pixels24, 0, fbWidth);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000162 pixelsSource.setAnimated(true);
163 rawPixelsImage = createImage(pixelsSource);
164
165 // Update the size of desktop containers.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000166 if (player.inSeparateFrame) {
167 if (player.desktopScrollPane != null)
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000168 resizeDesktopFrame();
169 } else {
170 setSize(fbWidth, fbHeight);
171 }
172 }
173
174 void resizeDesktopFrame() {
175 setSize(rfb.framebufferWidth, rfb.framebufferHeight);
176
177 // FIXME: Find a better way to determine correct size of a
178 // ScrollPane. -- const
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000179 Insets insets = player.desktopScrollPane.getInsets();
180 player.desktopScrollPane.setSize(rfb.framebufferWidth +
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000181 2 * Math.min(insets.left, insets.right),
182 rfb.framebufferHeight +
183 2 * Math.min(insets.top, insets.bottom));
184
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000185 player.vncFrame.pack();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000186
187 // Try to limit the frame size to the screen size.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000188 Dimension screenSize = player.vncFrame.getToolkit().getScreenSize();
189 Dimension frameSize = player.vncFrame.getSize();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000190 Dimension newSize = frameSize;
191 boolean needToResizeFrame = false;
192 if (frameSize.height > screenSize.height) {
193 newSize.height = screenSize.height;
194 needToResizeFrame = true;
195 }
196 if (frameSize.width > screenSize.width) {
197 newSize.width = screenSize.width;
198 needToResizeFrame = true;
199 }
200 if (needToResizeFrame) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000201 player.vncFrame.setSize(newSize);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000202 }
203
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000204 player.desktopScrollPane.doLayout();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000205 }
206
207 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000208 // processNormalProtocol() - executed by the rfbThread to deal with
209 // the RFB data.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000210 //
211
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000212 public void processNormalProtocol() throws Exception {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000213
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000214 zlibInflater = new Inflater();
215 tightInflaters = new Inflater[4];
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000216
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000217 player.updatePos();
218
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000219 // Main dispatch loop.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000220
221 while (true) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000222
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000223 int msgType = rfb.readServerMessageType();
224
225 switch (msgType) {
226 case RfbProto.FramebufferUpdate:
227 rfb.readFramebufferUpdate();
228
229 for (int i = 0; i < rfb.updateNRects; i++) {
230 rfb.readFramebufferUpdateRectHdr();
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000231 int rx = rfb.updateRectX, ry = rfb.updateRectY;
232 int rw = rfb.updateRectW, rh = rfb.updateRectH;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000233
234 if (rfb.updateRectEncoding == rfb.EncodingLastRect)
235 break;
236
237 if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) {
238 rfb.setFramebufferSize(rfb.updateRectW, rfb.updateRectH);
239 updateFramebufferSize();
240 break;
241 }
242
243 if (rfb.updateRectEncoding == rfb.EncodingXCursor ||
244 rfb.updateRectEncoding == rfb.EncodingRichCursor) {
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000245 throw new Exception("Sorry, no support for" +
246 " cursor shape updates yet");
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000247 }
248
249 switch (rfb.updateRectEncoding) {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000250 case RfbProto.EncodingRaw:
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000251 handleRawRect(rx, ry, rw, rh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000252 break;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000253 case RfbProto.EncodingCopyRect:
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000254 handleCopyRect(rx, ry, rw, rh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000255 break;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000256 case RfbProto.EncodingRRE:
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000257 handleRRERect(rx, ry, rw, rh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000258 break;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000259 case RfbProto.EncodingCoRRE:
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000260 handleCoRRERect(rx, ry, rw, rh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000261 break;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000262 case RfbProto.EncodingHextile:
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000263 handleHextileRect(rx, ry, rw, rh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000264 break;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000265 case RfbProto.EncodingZlib:
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000266 handleZlibRect(rx, ry, rw, rh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000267 break;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000268 case RfbProto.EncodingTight:
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000269 handleTightRect(rx, ry, rw, rh);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000270 break;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000271 default:
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000272 throw new Exception("Unknown RFB rectangle encoding " +
273 rfb.updateRectEncoding);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000274 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000275 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000276 break;
277
278 case RfbProto.SetColourMapEntries:
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000279 throw new Exception("Can't handle SetColourMapEntries message");
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000280
281 case RfbProto.Bell:
282 Toolkit.getDefaultToolkit().beep();
283 break;
284
285 case RfbProto.ServerCutText:
286 String s = rfb.readServerCutText();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000287 break;
288
289 default:
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000290 throw new Exception("Unknown RFB message type " + msgType);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000291 }
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000292
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000293 player.updatePos();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000294 }
295 }
296
297
298 //
299 // Handle a raw rectangle.
300 //
301
302 void handleRawRect(int x, int y, int w, int h) throws IOException {
303
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000304 byte[] buf = new byte[w * 4];
305 int i, offset;
306 for (int dy = y; dy < y + h; dy++) {
307 rfb.is.readFully(buf);
308 offset = dy * rfb.framebufferWidth + x;
309 for (i = 0; i < w; i++) {
310 pixels24[offset + i] =
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000311 (buf[i * 4 + 2] & 0xFF) << 16 |
312 (buf[i * 4 + 1] & 0xFF) << 8 |
313 (buf[i * 4] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000314 }
315 }
316
317 handleUpdatedPixels(x, y, w, h);
318 scheduleRepaint(x, y, w, h);
319 }
320
321
322 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000323 // Handle a CopyRect rectangle.
324 //
325
326 void handleCopyRect(int x, int y, int w, int h) throws IOException {
327
328 rfb.readCopyRect();
329 memGraphics.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY, w, h,
330 x - rfb.copyRectSrcX, y - rfb.copyRectSrcY);
331
332 scheduleRepaint(x, y, w, h);
333 }
334
335 //
336 // Handle an RRE-encoded rectangle.
337 //
338
339 void handleRRERect(int x, int y, int w, int h) throws IOException {
340
341 int nSubrects = rfb.is.readInt();
342 int sx, sy, sw, sh;
343
344 byte[] buf = new byte[4];
345 rfb.is.readFully(buf);
346 Color pixel = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
347 memGraphics.setColor(pixel);
348 memGraphics.fillRect(x, y, w, h);
349
350 for (int j = 0; j < nSubrects; j++) {
351 rfb.is.readFully(buf);
352 pixel = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
353 sx = x + rfb.is.readUnsignedShort();
354 sy = y + rfb.is.readUnsignedShort();
355 sw = rfb.is.readUnsignedShort();
356 sh = rfb.is.readUnsignedShort();
357
358 memGraphics.setColor(pixel);
359 memGraphics.fillRect(sx, sy, sw, sh);
360 }
361
362 scheduleRepaint(x, y, w, h);
363 }
364
365 //
366 // Handle a CoRRE-encoded rectangle.
367 //
368
369 void handleCoRRERect(int x, int y, int w, int h) throws IOException {
370
371 int nSubrects = rfb.is.readInt();
372 int sx, sy, sw, sh;
373
374 byte[] buf = new byte[4];
375 rfb.is.readFully(buf);
376 Color pixel = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
377 memGraphics.setColor(pixel);
378 memGraphics.fillRect(x, y, w, h);
379
380 for (int j = 0; j < nSubrects; j++) {
381 rfb.is.readFully(buf);
382 pixel = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
383 sx = x + rfb.is.readUnsignedByte();
384 sy = y + rfb.is.readUnsignedByte();
385 sw = rfb.is.readUnsignedByte();
386 sh = rfb.is.readUnsignedByte();
387
388 memGraphics.setColor(pixel);
389 memGraphics.fillRect(sx, sy, sw, sh);
390 }
391
392 scheduleRepaint(x, y, w, h);
393 }
394
395 //
396 // Handle a Hextile-encoded rectangle.
397 //
398
399 // These colors should be kept between handleHextileSubrect() calls.
400 private Color hextile_bg, hextile_fg;
401
402 void handleHextileRect(int x, int y, int w, int h) throws IOException {
403
404 hextile_bg = new Color(0, 0, 0);
405 hextile_fg = new Color(0, 0, 0);
406
407 for (int ty = y; ty < y + h; ty += 16) {
408 int th = 16;
409 if (y + h - ty < 16)
410 th = y + h - ty;
411
412 for (int tx = x; tx < x + w; tx += 16) {
413 int tw = 16;
414 if (x + w - tx < 16)
415 tw = x + w - tx;
416
417 handleHextileSubrect(tx, ty, tw, th);
418 }
419
420 // Finished with a row of tiles, now let's show it.
421 scheduleRepaint(x, y, w, h);
422 }
423 }
424
425 //
426 // Handle one tile in the Hextile-encoded data.
427 //
428
429 void handleHextileSubrect(int tx, int ty, int tw, int th)
430 throws IOException {
431
432 byte[] buf = new byte[256 * 4];
433
434 int subencoding = rfb.is.readUnsignedByte();
435
436 // Is it a raw-encoded sub-rectangle?
437 if ((subencoding & rfb.HextileRaw) != 0) {
438 int count, offset;
439 for (int j = ty; j < ty + th; j++) {
440 rfb.is.readFully(buf, 0, tw * 4);
441 offset = j * rfb.framebufferWidth + tx;
442 for (count = 0; count < tw; count++) {
443 pixels24[offset + count] =
444 (buf[count * 4 + 2] & 0xFF) << 16 |
445 (buf[count * 4 + 1] & 0xFF) << 8 |
446 (buf[count * 4] & 0xFF);
447 }
448 }
449 handleUpdatedPixels(tx, ty, tw, th);
450 return;
451 }
452
453 // Read and draw the background if specified.
454 if ((subencoding & rfb.HextileBackgroundSpecified) != 0) {
455 rfb.is.readFully(buf, 0, 4);
456 hextile_bg = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
457 }
458 memGraphics.setColor(hextile_bg);
459 memGraphics.fillRect(tx, ty, tw, th);
460
461 // Read the foreground color if specified.
462 if ((subencoding & rfb.HextileForegroundSpecified) != 0) {
463 rfb.is.readFully(buf, 0, 4);
464 hextile_fg = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
465 }
466
467 // Done with this tile if there is no sub-rectangles.
468 if ((subencoding & rfb.HextileAnySubrects) == 0)
469 return;
470
471 int nSubrects = rfb.is.readUnsignedByte();
472
473 int b1, b2, sx, sy, sw, sh;
474 if ((subencoding & rfb.HextileSubrectsColoured) != 0) {
475 for (int j = 0; j < nSubrects; j++) {
476 rfb.is.readFully(buf, 0, 4);
477 hextile_fg = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
478 b1 = rfb.is.readUnsignedByte();
479 b2 = rfb.is.readUnsignedByte();
480 sx = tx + (b1 >> 4);
481 sy = ty + (b1 & 0xf);
482 sw = (b2 >> 4) + 1;
483 sh = (b2 & 0xf) + 1;
484 memGraphics.setColor(hextile_fg);
485 memGraphics.fillRect(sx, sy, sw, sh);
486 }
487 } else {
488 memGraphics.setColor(hextile_fg);
489 for (int j = 0; j < nSubrects; j++) {
490 b1 = rfb.is.readUnsignedByte();
491 b2 = rfb.is.readUnsignedByte();
492 sx = tx + (b1 >> 4);
493 sy = ty + (b1 & 0xf);
494 sw = (b2 >> 4) + 1;
495 sh = (b2 & 0xf) + 1;
496 memGraphics.fillRect(sx, sy, sw, sh);
497 }
498 }
499 }
500
501 //
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000502 // Handle a Zlib-encoded rectangle.
503 //
504
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000505 void handleZlibRect(int x, int y, int w, int h) throws Exception {
506
507 int nBytes = rfb.is.readInt();
508
509 if (zlibBuf == null || zlibBufLen < nBytes) {
510 zlibBufLen = nBytes * 2;
511 zlibBuf = new byte[zlibBufLen];
512 }
513
514 rfb.is.readFully(zlibBuf, 0, nBytes);
515 zlibInflater.setInput(zlibBuf, 0, nBytes);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000516
517 try {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000518 byte[] buf = new byte[w * 4];
519 int i, offset;
520 for (int dy = y; dy < y + h; dy++) {
521 zlibInflater.inflate(buf);
522 offset = dy * rfb.framebufferWidth + x;
523 for (i = 0; i < w; i++) {
524 pixels24[offset + i] =
Constantin Kaplinskya5fcd982002-05-20 13:06:33 +0000525 (buf[i * 4 + 2] & 0xFF) << 16 |
526 (buf[i * 4 + 1] & 0xFF) << 8 |
527 (buf[i * 4] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000528 }
529 }
530 }
531 catch (DataFormatException dfe) {
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000532 throw new Exception(dfe.toString());
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000533 }
534
535 handleUpdatedPixels(x, y, w, h);
536 scheduleRepaint(x, y, w, h);
537 }
538
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000539 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000540 // Handle a Tight-encoded rectangle.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000541 //
542
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000543 void handleTightRect(int x, int y, int w, int h) throws Exception {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000544
545 int comp_ctl = rfb.is.readUnsignedByte();
546
547 // Flush zlib streams if we are told by the server to do so.
548 for (int stream_id = 0; stream_id < 4; stream_id++) {
549 if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) {
550 tightInflaters[stream_id] = null;
551 }
552 comp_ctl >>= 1;
553 }
554
555 // Check correctness of subencoding value.
556 if (comp_ctl > rfb.TightMaxSubencoding) {
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000557 throw new Exception("Incorrect tight subencoding: " + comp_ctl);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000558 }
559
560 // Handle solid-color rectangles.
561 if (comp_ctl == rfb.TightFill) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000562 byte[] buf = new byte[3];
563 rfb.is.readFully(buf);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000564 Color bg = new Color(buf[0] & 0xFF, buf[1] & 0xFF, buf[2] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000565 memGraphics.setColor(bg);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000566 memGraphics.fillRect(x, y, w, h);
567 scheduleRepaint(x, y, w, h);
568 return;
569 }
570
571 if (comp_ctl == rfb.TightJpeg) {
572
573 // Read JPEG data.
574 byte[] jpegData = new byte[rfb.readCompactLen()];
575 rfb.is.readFully(jpegData);
576
577 // Create an Image object from the JPEG data.
578 Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData);
579
580 // Remember the rectangle where the image should be drawn.
581 jpegRect = new Rectangle(x, y, w, h);
582
583 // Let the imageUpdate() method do the actual drawing, here just
584 // wait until the image is fully loaded and drawn.
585 synchronized(jpegRect) {
586 Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1, this);
587 try {
588 // Wait no longer than three seconds.
589 jpegRect.wait(3000);
590 } catch (InterruptedException e) {
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000591 throw new Exception("Interrupted while decoding JPEG image");
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000592 }
593 }
594
595 // Done, jpegRect is not needed any more.
596 jpegRect = null;
597 return;
598
599 }
600
601 // Read filter id and parameters.
602 int numColors = 0, rowSize = w;
603 byte[] palette8 = new byte[2];
604 int[] palette24 = new int[256];
605 boolean useGradient = false;
606 if ((comp_ctl & rfb.TightExplicitFilter) != 0) {
607 int filter_id = rfb.is.readUnsignedByte();
608 if (filter_id == rfb.TightFilterPalette) {
609 numColors = rfb.is.readUnsignedByte() + 1;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000610 byte[] buf = new byte[numColors * 3];
611 rfb.is.readFully(buf);
612 for (int i = 0; i < numColors; i++) {
613 palette24[i] = ((buf[i * 3] & 0xFF) << 16 |
614 (buf[i * 3 + 1] & 0xFF) << 8 |
615 (buf[i * 3 + 2] & 0xFF));
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000616 }
617 if (numColors == 2)
618 rowSize = (w + 7) / 8;
619 } else if (filter_id == rfb.TightFilterGradient) {
620 useGradient = true;
621 } else if (filter_id != rfb.TightFilterCopy) {
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000622 throw new Exception("Incorrect tight filter id: " + filter_id);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000623 }
624 }
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000625 if (numColors == 0)
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000626 rowSize *= 3;
627
628 // Read, optionally uncompress and decode data.
629 int dataSize = h * rowSize;
630 if (dataSize < rfb.TightMinToCompress) {
631 // Data size is small - not compressed with zlib.
632 if (numColors != 0) {
633 // Indexed colors.
634 byte[] indexedData = new byte[dataSize];
635 rfb.is.readFully(indexedData);
636 if (numColors == 2) {
637 // Two colors.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000638 decodeMonoData(x, y, w, h, indexedData, palette24);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000639 } else {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000640 // 3..255 colors.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000641 int i = 0;
642 for (int dy = y; dy < y + h; dy++) {
643 for (int dx = x; dx < x + w; dx++) {
644 pixels24[dy * rfb.framebufferWidth + dx] =
645 palette24[indexedData[i++] & 0xFF];
646 }
647 }
648 }
649 } else if (useGradient) {
650 // "Gradient"-processed data
651 byte[] buf = new byte[w * h * 3];
652 rfb.is.readFully(buf);
653 decodeGradientData(x, y, w, h, buf);
654 } else {
655 // Raw truecolor data.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000656 byte[] buf = new byte[w * 3];
657 int i, offset;
658 for (int dy = y; dy < y + h; dy++) {
659 rfb.is.readFully(buf);
660 offset = dy * rfb.framebufferWidth + x;
661 for (i = 0; i < w; i++) {
662 pixels24[offset + i] =
663 (buf[i * 3] & 0xFF) << 16 |
664 (buf[i * 3 + 1] & 0xFF) << 8 |
665 (buf[i * 3 + 2] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000666 }
667 }
668 }
669 } else {
670 // Data was compressed with zlib.
671 int zlibDataLen = rfb.readCompactLen();
672 byte[] zlibData = new byte[zlibDataLen];
673 rfb.is.readFully(zlibData);
674 int stream_id = comp_ctl & 0x03;
675 if (tightInflaters[stream_id] == null) {
676 tightInflaters[stream_id] = new Inflater();
677 }
678 Inflater myInflater = tightInflaters[stream_id];
679 myInflater.setInput(zlibData);
680 try {
681 if (numColors != 0) {
682 // Indexed colors.
683 byte[] indexedData = new byte[dataSize];
684 myInflater.inflate(indexedData);
685 if (numColors == 2) {
686 // Two colors.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000687 decodeMonoData(x, y, w, h, indexedData, palette24);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000688 } else {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000689 // More than two colors.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000690 int i = 0;
691 for (int dy = y; dy < y + h; dy++) {
692 for (int dx = x; dx < x + w; dx++) {
693 pixels24[dy * rfb.framebufferWidth + dx] =
694 palette24[indexedData[i++] & 0xFF];
695 }
696 }
697 }
698 } else if (useGradient) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000699 // Compressed "Gradient"-filtered data.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000700 byte[] buf = new byte[w * h * 3];
701 myInflater.inflate(buf);
702 decodeGradientData(x, y, w, h, buf);
703 } else {
704 // Compressed truecolor data.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000705 byte[] buf = new byte[w * 3];
706 int i, offset;
707 for (int dy = y; dy < y + h; dy++) {
708 myInflater.inflate(buf);
709 offset = dy * rfb.framebufferWidth + x;
710 for (i = 0; i < w; i++) {
711 pixels24[offset + i] =
712 (buf[i * 3] & 0xFF) << 16 |
713 (buf[i * 3 + 1] & 0xFF) << 8 |
714 (buf[i * 3 + 2] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000715 }
716 }
717 }
718 }
719 catch(DataFormatException dfe) {
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000720 throw new Exception(dfe.toString());
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000721 }
722 }
723
724 handleUpdatedPixels(x, y, w, h);
725 scheduleRepaint(x, y, w, h);
726 }
727
728 //
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000729 // Decode 1bpp-encoded bi-color rectangle.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000730 //
731
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000732 void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000733
734 int dx, dy, n;
735 int i = y * rfb.framebufferWidth + x;
736 int rowBytes = (w + 7) / 8;
737 byte b;
738
739 for (dy = 0; dy < h; dy++) {
740 for (dx = 0; dx < w / 8; dx++) {
741 b = src[dy*rowBytes+dx];
742 for (n = 7; n >= 0; n--)
743 pixels24[i++] = palette[b >> n & 1];
744 }
745 for (n = 7; n >= 8 - w % 8; n--) {
746 pixels24[i++] = palette[src[dy*rowBytes+dx] >> n & 1];
747 }
748 i += (rfb.framebufferWidth - w);
749 }
750 }
751
752 //
753 // Decode data processed with the "Gradient" filter.
754 //
755
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000756 void decodeGradientData (int x, int y, int w, int h, byte[] buf) {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000757
758 int dx, dy, c;
759 byte[] prevRow = new byte[w * 3];
760 byte[] thisRow = new byte[w * 3];
761 byte[] pix = new byte[3];
762 int[] est = new int[3];
763
764 int offset = y * rfb.framebufferWidth + x;
765
766 for (dy = 0; dy < h; dy++) {
767
768 /* First pixel in a row */
769 for (c = 0; c < 3; c++) {
770 pix[c] = (byte)(prevRow[c] + buf[dy * w * 3 + c]);
771 thisRow[c] = pix[c];
772 }
773 pixels24[offset++] =
774 (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
775
776 /* Remaining pixels of a row */
777 for (dx = 1; dx < w; dx++) {
778 for (c = 0; c < 3; c++) {
779 est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) -
780 (prevRow[(dx-1) * 3 + c] & 0xFF));
781 if (est[c] > 0xFF) {
782 est[c] = 0xFF;
783 } else if (est[c] < 0x00) {
784 est[c] = 0x00;
785 }
786 pix[c] = (byte)(est[c] + buf[(dy * w + dx) * 3 + c]);
787 thisRow[dx * 3 + c] = pix[c];
788 }
789 pixels24[offset++] =
790 (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
791 }
792
793 System.arraycopy(thisRow, 0, prevRow, 0, w * 3);
794 offset += (rfb.framebufferWidth - w);
795 }
796 }
797
798
799 //
800 // Display newly updated area of pixels.
801 //
802
803 void handleUpdatedPixels(int x, int y, int w, int h) {
804
805 // Draw updated pixels of the off-screen image.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000806
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000807 pixelsSource.newPixels(x, y, w, h);
808 memGraphics.setClip(x, y, w, h);
809 memGraphics.drawImage(rawPixelsImage, 0, 0, null);
810 memGraphics.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight);
811 }
812
813 //
814 // Tell JVM to repaint specified desktop area.
815 //
816
817 void scheduleRepaint(int x, int y, int w, int h) {
Constantin Kaplinsky37cc43e2002-05-30 17:30:11 +0000818 if (rfb.fbs.isSeeking()) {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +0000819 // Do nothing, and remember we are seeking.
820 seekMode = true;
821 } else {
822 if (seekMode) {
823 // Full-screen immediate repaint after seeking.
824 repaint();
825 } else {
826 // Usual incremental repaint in playback mode.
827 repaint(player.deferScreenUpdates, x, y, w, h);
828 }
829 seekMode = false;
830 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000831 }
832
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000833}