blob: b369c631131f6f9e67ec50f000cc9d91829256a4 [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
wimba.comc23aeb02004-09-16 00:00:00 +000023package com.HorizonLive.RfbPlayer;
24
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000025import java.awt.*;
26import java.awt.event.*;
27import java.awt.image.*;
28import java.io.*;
29import java.lang.*;
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +000030import java.util.*;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000031import java.util.zip.*;
32
33
34//
35// VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
36//
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +000037class VncCanvas extends Canvas implements Observer {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000038
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000039 RfbPlayer player;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000040 RfbProto rfb;
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000041 ColorModel cm24;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000042
43 Image memImage;
44 Graphics memGraphics;
45
46 Image rawPixelsImage;
47 MemoryImageSource pixelsSource;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000048 int[] pixels24;
49
50 // Zlib encoder's data.
51 byte[] zlibBuf;
52 int zlibBufLen = 0;
53 Inflater zlibInflater;
54
55 // Tight encoder's data.
56 final static int tightZlibBufferSize = 512;
57 Inflater[] tightInflaters;
58
59 // Since JPEG images are loaded asynchronously, we have to remember
60 // their position in the framebuffer. Also, this jpegRect object is
61 // used for synchronization between the rfbThread and a JVM's thread
62 // which decodes and loads JPEG images.
63 Rectangle jpegRect;
64
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000065 // When we're in the seeking mode, we should not update the desktop.
66 // This variable helps us to remember that repainting the desktop at
67 // once is necessary when the seek operation is finished.
68 boolean seekMode;
69
wimba.com16092722004-09-21 20:54:37 +000070 // Distance of mouse from viewer border to trigger automatic scrolling
71 final static int SCROLL_MARGIN = 50;
72
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000073 //
74 // The constructor.
75 //
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000076 VncCanvas(RfbPlayer player) throws IOException {
77 this.player = player;
78 rfb = player.rfb;
Constantin Kaplinsky52c48242002-05-30 13:26:34 +000079 seekMode = false;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000080
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000081 cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
82
Constantin Kaplinsky903009e2002-05-20 10:55:47 +000083 updateFramebufferSize();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000084 }
85
86 //
87 // Callback methods to determine geometry of our Component.
88 //
Constantin Kaplinsky1215b992008-04-18 09:51:44 +000089 public Dimension getPreferredSize() {
90 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
91 }
92
93 public Dimension getMinimumSize() {
94 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
95 }
96
97 public Dimension getMaximumSize() {
98 return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
99 }
100
101 //
102 // All painting is performed here.
103 //
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000104 public void update(Graphics g) {
105 paint(g);
106 }
107
108 public void paint(Graphics g) {
109 synchronized(memImage) {
110 g.drawImage(memImage, 0, 0, null);
111 }
wimba.coma5a4f4f2004-09-21 15:25:05 +0000112 if (showSoftCursor) {
113 int x0 = cursorX - hotX, y0 = cursorY - hotY;
114 Rectangle r = new Rectangle(x0, y0, cursorWidth, cursorHeight);
115 if (r.intersects(g.getClipBounds())) {
116 g.drawImage(softCursor, x0, y0, null);
117 }
118 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000119 }
120
121 //
122 // Override the ImageObserver interface method to handle drawing of
123 // JPEG-encoded data.
124 //
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000125 public boolean imageUpdate(Image img, int infoflags,
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000126 int x, int y, int width, int height) {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000127 if ((infoflags & (ALLBITS | ABORT)) == 0) {
128 return true; // We need more image data.
129 } else {
130 // If the whole image is available, draw it now.
131 if ((infoflags & ALLBITS) != 0) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000132 if (jpegRect != null) {
133 synchronized(jpegRect) {
134 memGraphics.drawImage(img, jpegRect.x, jpegRect.y, null);
135 scheduleRepaint(jpegRect.x, jpegRect.y,
136 jpegRect.width, jpegRect.height);
137 jpegRect.notify();
138 }
139 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000140 }
141 return false; // All image data was processed.
142 }
143 }
144
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000145 void updateFramebufferSize() {
146
147 // Useful shortcuts.
148 int fbWidth = rfb.framebufferWidth;
149 int fbHeight = rfb.framebufferHeight;
150
151 // Create new off-screen image either if it does not exist, or if
152 // its geometry should be changed. It's not necessary to replace
153 // existing image if only pixel format should be changed.
154 if (memImage == null) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000155 memImage = player.createImage(fbWidth, fbHeight);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000156 memGraphics = memImage.getGraphics();
157 } else if (memImage.getWidth(null) != fbWidth ||
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000158 memImage.getHeight(null) != fbHeight) {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000159 synchronized(memImage) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000160 memImage = player.createImage(fbWidth, fbHeight);
161 memGraphics = memImage.getGraphics();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000162 }
163 }
164
165 // Images with raw pixels should be re-allocated on every change
166 // of geometry or pixel format.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000167 pixels24 = new int[fbWidth * fbHeight];
168 pixelsSource =
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000169 new MemoryImageSource(fbWidth, fbHeight, cm24, pixels24, 0, fbWidth);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000170 pixelsSource.setAnimated(true);
171 rawPixelsImage = createImage(pixelsSource);
172
173 // Update the size of desktop containers.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000174 if (player.inSeparateFrame) {
175 if (player.desktopScrollPane != null)
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000176 resizeDesktopFrame();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000177 } else {
178 setSize(fbWidth, fbHeight);
179 }
180 }
181
182 void resizeDesktopFrame() {
wimba.com252eb3b2004-09-21 21:27:54 +0000183 // size the canvas
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000184 setSize(rfb.framebufferWidth, rfb.framebufferHeight);
185
wimba.com252eb3b2004-09-21 21:27:54 +0000186 // determine screen size
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000187 Dimension screenSize = player.vncFrame.getToolkit().getScreenSize();
wimba.com252eb3b2004-09-21 21:27:54 +0000188 Dimension scrollSize = player.desktopScrollPane.getSize();
wimba.comc23aeb02004-09-16 00:00:00 +0000189
190 // Reduce Screen Size by 30 pixels in each direction;
191 // This is a (poor) attempt to account for
192 // 1) Menu bar on Macintosh (should really also account for
193 // Dock on OSX). Usually 22px on top of screen.
wimba.com252eb3b2004-09-21 21:27:54 +0000194 // 2) Taxkbar on Windows (usually about 28 px on bottom)
195 // 3) Other obstructions.
wimba.comc23aeb02004-09-16 00:00:00 +0000196 screenSize.height -= 30;
197 screenSize.width -= 30;
198
wimba.com252eb3b2004-09-21 21:27:54 +0000199 // Further reduce the screen size to account for the
200 // scroll pane's insets.
201 Insets insets = player.desktopScrollPane.getInsets();
202 screenSize.height -= insets.top + insets.bottom;
203 screenSize.width -= insets.left + insets.right;
wimba.comc23aeb02004-09-16 00:00:00 +0000204
wimba.com252eb3b2004-09-21 21:27:54 +0000205 // Limit pane size to fit on screen.
206 boolean needResize = false;
207 if (scrollSize.width != rfb.framebufferWidth ||
208 scrollSize.height != rfb.framebufferHeight)
209 needResize = true;
210 int w = rfb.framebufferWidth, h = rfb.framebufferHeight;
211 if (w > screenSize.width) {
212 w = screenSize.width;
213 needResize = true;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000214 }
wimba.com252eb3b2004-09-21 21:27:54 +0000215 if (h > screenSize.height) {
216 h = screenSize.height;
217 needResize = true;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000218 }
wimba.com252eb3b2004-09-21 21:27:54 +0000219 if (needResize)
220 player.desktopScrollPane.setSize(w, h);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000221
wimba.com252eb3b2004-09-21 21:27:54 +0000222 player.vncFrame.pack();
223 }
224
225 void resizeEmbeddedApplet() {
226 // size the canvas
227 setSize(rfb.framebufferWidth, rfb.framebufferHeight);
228
229 // resize scroll pane if necessary
230 Dimension scrollSize = player.desktopScrollPane.getSize();
231 if (scrollSize.width != player.dispW ||
232 scrollSize.height != player.dispH)
233 player.desktopScrollPane.setSize(player.dispW, player.dispH);
234
235 player.validate();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000236 }
237
238 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000239 // processNormalProtocol() - executed by the rfbThread to deal with
240 // the RFB data.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000241 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000242 public void processNormalProtocol() throws Exception {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000243
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000244 zlibInflater = new Inflater();
245 tightInflaters = new Inflater[4];
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000246
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000247 // Show current time position in the control panel.
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000248 player.updatePos();
249
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +0000250 // Tell our FbsInputStream object to notify us when it goes to the
251 // `paused' mode.
252 rfb.fbs.addObserver(this);
253
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000254 // Main dispatch loop.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000255
256 while (true) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000257
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000258 int msgType = rfb.readServerMessageType();
259
260 switch (msgType) {
261 case RfbProto.FramebufferUpdate:
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000262 rfb.readFramebufferUpdate();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000263
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000264 for (int i = 0; i < rfb.updateNRects; i++) {
265 rfb.readFramebufferUpdateRectHdr();
wimba.coma5a4f4f2004-09-21 15:25:05 +0000266
267 boolean cursorPosReceived = false;
268
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000269 int rx = rfb.updateRectX, ry = rfb.updateRectY;
270 int rw = rfb.updateRectW, rh = rfb.updateRectH;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000271
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000272 if (rfb.updateRectEncoding == rfb.EncodingLastRect)
273 break;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000274
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000275 if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) {
wimba.comb7017b72004-09-16 16:11:55 +0000276 if (rfb.updateRectW != 0 && rfb.updateRectH != 0) {
277 rfb.setFramebufferSize(rfb.updateRectW, rfb.updateRectH);
278 updateFramebufferSize();
279 }
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000280 break;
281 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000282
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000283 if (rfb.updateRectEncoding == rfb.EncodingXCursor ||
284 rfb.updateRectEncoding == rfb.EncodingRichCursor) {
wimba.coma5a4f4f2004-09-21 15:25:05 +0000285 handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry, rw, rh);
286 continue;
287 }
288// if (rfb.updateRectEncoding == rfb.EncodingXCursor ||
289// rfb.updateRectEncoding == rfb.EncodingRichCursor) {
290// throw new Exception("Sorry, no support for" +
291// " cursor shape updates yet");
292// }
293
294 if (rfb.updateRectEncoding == rfb.EncodingPointerPos) {
295 softCursorMove(rx, ry);
296 cursorPosReceived = true;
297 continue;
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000298 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000299
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000300 switch (rfb.updateRectEncoding) {
301 case RfbProto.EncodingRaw:
302 handleRawRect(rx, ry, rw, rh);
303 break;
304 case RfbProto.EncodingCopyRect:
305 handleCopyRect(rx, ry, rw, rh);
306 break;
307 case RfbProto.EncodingRRE:
308 handleRRERect(rx, ry, rw, rh);
309 break;
310 case RfbProto.EncodingCoRRE:
311 handleCoRRERect(rx, ry, rw, rh);
312 break;
313 case RfbProto.EncodingHextile:
314 handleHextileRect(rx, ry, rw, rh);
315 break;
316 case RfbProto.EncodingZlib:
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000317 handleZlibRect(rx, ry, rw, rh);
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000318 break;
319 case RfbProto.EncodingTight:
320 handleTightRect(rx, ry, rw, rh);
321 break;
322 default:
323 throw new Exception("Unknown RFB rectangle encoding " +
wimba.coma5a4f4f2004-09-21 15:25:05 +0000324 Integer.toString(rfb.updateRectEncoding, 16));
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000325 }
326 }
327 break;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000328
329 case RfbProto.SetColourMapEntries:
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000330 throw new Exception("Can't handle SetColourMapEntries message");
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000331
332 case RfbProto.Bell:
333 Toolkit.getDefaultToolkit().beep();
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000334 break;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000335
336 case RfbProto.ServerCutText:
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000337 String s = rfb.readServerCutText();
338 break;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000339
340 default:
wimba.coma5a4f4f2004-09-21 15:25:05 +0000341 throw new Exception("Unknown RFB message type " +
342 Integer.toString(msgType, 16));
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000343 }
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000344
Constantin Kaplinskyfe079832002-05-29 00:52:32 +0000345 player.updatePos();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000346 }
347 }
348
349
350 //
351 // Handle a raw rectangle.
352 //
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000353 void handleRawRect(int x, int y, int w, int h) throws IOException {
354
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000355 byte[] buf = new byte[w * 4];
356 int i, offset;
357 for (int dy = y; dy < y + h; dy++) {
358 rfb.is.readFully(buf);
359 offset = dy * rfb.framebufferWidth + x;
360 for (i = 0; i < w; i++) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000361 pixels24[offset + i] =
362 (buf[i * 4 + 2] & 0xFF) << 16 |
363 (buf[i * 4 + 1] & 0xFF) << 8 |
364 (buf[i * 4] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000365 }
366 }
367
368 handleUpdatedPixels(x, y, w, h);
369 scheduleRepaint(x, y, w, h);
370 }
371
372
373 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000374 // Handle a CopyRect rectangle.
375 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000376 void handleCopyRect(int x, int y, int w, int h) throws IOException {
377
378 rfb.readCopyRect();
379 memGraphics.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY, w, h,
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000380 x - rfb.copyRectSrcX, y - rfb.copyRectSrcY);
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000381
382 scheduleRepaint(x, y, w, h);
383 }
384
385 //
386 // Handle an RRE-encoded rectangle.
387 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000388 void handleRRERect(int x, int y, int w, int h) throws IOException {
389
390 int nSubrects = rfb.is.readInt();
391 int sx, sy, sw, sh;
392
393 byte[] buf = new byte[4];
394 rfb.is.readFully(buf);
395 Color pixel = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
396 memGraphics.setColor(pixel);
397 memGraphics.fillRect(x, y, w, h);
398
399 for (int j = 0; j < nSubrects; j++) {
400 rfb.is.readFully(buf);
401 pixel = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
402 sx = x + rfb.is.readUnsignedShort();
403 sy = y + rfb.is.readUnsignedShort();
404 sw = rfb.is.readUnsignedShort();
405 sh = rfb.is.readUnsignedShort();
406
407 memGraphics.setColor(pixel);
408 memGraphics.fillRect(sx, sy, sw, sh);
409 }
410
411 scheduleRepaint(x, y, w, h);
412 }
413
414 //
415 // Handle a CoRRE-encoded rectangle.
416 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000417 void handleCoRRERect(int x, int y, int w, int h) throws IOException {
418
419 int nSubrects = rfb.is.readInt();
420 int sx, sy, sw, sh;
421
422 byte[] buf = new byte[4];
423 rfb.is.readFully(buf);
424 Color pixel = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
425 memGraphics.setColor(pixel);
426 memGraphics.fillRect(x, y, w, h);
427
428 for (int j = 0; j < nSubrects; j++) {
429 rfb.is.readFully(buf);
430 pixel = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
431 sx = x + rfb.is.readUnsignedByte();
432 sy = y + rfb.is.readUnsignedByte();
433 sw = rfb.is.readUnsignedByte();
434 sh = rfb.is.readUnsignedByte();
435
436 memGraphics.setColor(pixel);
437 memGraphics.fillRect(sx, sy, sw, sh);
438 }
439
440 scheduleRepaint(x, y, w, h);
441 }
442
443 //
444 // Handle a Hextile-encoded rectangle.
445 //
446
447 // These colors should be kept between handleHextileSubrect() calls.
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000448 private Color hextile_bg, hextile_fg;
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000449
450 void handleHextileRect(int x, int y, int w, int h) throws IOException {
451
452 hextile_bg = new Color(0, 0, 0);
453 hextile_fg = new Color(0, 0, 0);
454
455 for (int ty = y; ty < y + h; ty += 16) {
456 int th = 16;
457 if (y + h - ty < 16)
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000458 th = y + h - ty;
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000459
460 for (int tx = x; tx < x + w; tx += 16) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000461 int tw = 16;
462 if (x + w - tx < 16)
463 tw = x + w - tx;
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000464
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000465 handleHextileSubrect(tx, ty, tw, th);
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000466 }
467
468 // Finished with a row of tiles, now let's show it.
469 scheduleRepaint(x, y, w, h);
470 }
471 }
472
473 //
474 // Handle one tile in the Hextile-encoded data.
475 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000476 void handleHextileSubrect(int tx, int ty, int tw, int th)
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000477 throws IOException {
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000478
479 byte[] buf = new byte[256 * 4];
480
481 int subencoding = rfb.is.readUnsignedByte();
482
483 // Is it a raw-encoded sub-rectangle?
484 if ((subencoding & rfb.HextileRaw) != 0) {
485 int count, offset;
486 for (int j = ty; j < ty + th; j++) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000487 rfb.is.readFully(buf, 0, tw * 4);
488 offset = j * rfb.framebufferWidth + tx;
489 for (count = 0; count < tw; count++) {
490 pixels24[offset + count] =
491 (buf[count * 4 + 2] & 0xFF) << 16 |
492 (buf[count * 4 + 1] & 0xFF) << 8 |
493 (buf[count * 4] & 0xFF);
494 }
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000495 }
496 handleUpdatedPixels(tx, ty, tw, th);
497 return;
498 }
499
500 // Read and draw the background if specified.
501 if ((subencoding & rfb.HextileBackgroundSpecified) != 0) {
502 rfb.is.readFully(buf, 0, 4);
503 hextile_bg = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
504 }
505 memGraphics.setColor(hextile_bg);
506 memGraphics.fillRect(tx, ty, tw, th);
507
508 // Read the foreground color if specified.
509 if ((subencoding & rfb.HextileForegroundSpecified) != 0) {
510 rfb.is.readFully(buf, 0, 4);
511 hextile_fg = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
512 }
513
514 // Done with this tile if there is no sub-rectangles.
515 if ((subencoding & rfb.HextileAnySubrects) == 0)
516 return;
517
518 int nSubrects = rfb.is.readUnsignedByte();
519
520 int b1, b2, sx, sy, sw, sh;
521 if ((subencoding & rfb.HextileSubrectsColoured) != 0) {
522 for (int j = 0; j < nSubrects; j++) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000523 rfb.is.readFully(buf, 0, 4);
524 hextile_fg = new Color(buf[2] & 0xFF, buf[1] & 0xFF, buf[0] & 0xFF);
525 b1 = rfb.is.readUnsignedByte();
526 b2 = rfb.is.readUnsignedByte();
527 sx = tx + (b1 >> 4);
528 sy = ty + (b1 & 0xf);
529 sw = (b2 >> 4) + 1;
530 sh = (b2 & 0xf) + 1;
531 memGraphics.setColor(hextile_fg);
532 memGraphics.fillRect(sx, sy, sw, sh);
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000533 }
534 } else {
535 memGraphics.setColor(hextile_fg);
536 for (int j = 0; j < nSubrects; j++) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000537 b1 = rfb.is.readUnsignedByte();
538 b2 = rfb.is.readUnsignedByte();
539 sx = tx + (b1 >> 4);
540 sy = ty + (b1 & 0xf);
541 sw = (b2 >> 4) + 1;
542 sh = (b2 & 0xf) + 1;
543 memGraphics.fillRect(sx, sy, sw, sh);
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000544 }
545 }
546 }
547
548 //
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000549 // Handle a Zlib-encoded rectangle.
550 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000551 void handleZlibRect(int x, int y, int w, int h) throws Exception {
552
553 int nBytes = rfb.is.readInt();
554
555 if (zlibBuf == null || zlibBufLen < nBytes) {
556 zlibBufLen = nBytes * 2;
557 zlibBuf = new byte[zlibBufLen];
558 }
559
560 rfb.is.readFully(zlibBuf, 0, nBytes);
561 zlibInflater.setInput(zlibBuf, 0, nBytes);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000562
563 try {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000564 byte[] buf = new byte[w * 4];
565 int i, offset;
566 for (int dy = y; dy < y + h; dy++) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000567 zlibInflater.inflate(buf);
568 offset = dy * rfb.framebufferWidth + x;
569 for (i = 0; i < w; i++) {
570 pixels24[offset + i] =
571 (buf[i * 4 + 2] & 0xFF) << 16 |
572 (buf[i * 4 + 1] & 0xFF) << 8 |
573 (buf[i * 4] & 0xFF);
574 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000575 }
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000576 } catch (DataFormatException dfe) {
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000577 throw new Exception(dfe.toString());
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000578 }
579
580 handleUpdatedPixels(x, y, w, h);
581 scheduleRepaint(x, y, w, h);
582 }
583
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000584 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000585 // Handle a Tight-encoded rectangle.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000586 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000587 void handleTightRect(int x, int y, int w, int h) throws Exception {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000588
589 int comp_ctl = rfb.is.readUnsignedByte();
590
591 // Flush zlib streams if we are told by the server to do so.
592 for (int stream_id = 0; stream_id < 4; stream_id++) {
593 if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000594 tightInflaters[stream_id] = null;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000595 }
596 comp_ctl >>= 1;
597 }
598
599 // Check correctness of subencoding value.
600 if (comp_ctl > rfb.TightMaxSubencoding) {
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000601 throw new Exception("Incorrect tight subencoding: " + comp_ctl);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000602 }
603
604 // Handle solid-color rectangles.
605 if (comp_ctl == rfb.TightFill) {
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000606 byte[] buf = new byte[3];
607 rfb.is.readFully(buf);
Constantin Kaplinskya628bf02002-05-20 13:33:46 +0000608 Color bg = new Color(buf[0] & 0xFF, buf[1] & 0xFF, buf[2] & 0xFF);
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000609 memGraphics.setColor(bg);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000610 memGraphics.fillRect(x, y, w, h);
611 scheduleRepaint(x, y, w, h);
612 return;
613 }
614
615 if (comp_ctl == rfb.TightJpeg) {
616
617 // Read JPEG data.
618 byte[] jpegData = new byte[rfb.readCompactLen()];
619 rfb.is.readFully(jpegData);
620
621 // Create an Image object from the JPEG data.
622 Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData);
623
624 // Remember the rectangle where the image should be drawn.
625 jpegRect = new Rectangle(x, y, w, h);
626
627 // Let the imageUpdate() method do the actual drawing, here just
628 // wait until the image is fully loaded and drawn.
629 synchronized(jpegRect) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000630 Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1, this);
631 try {
632 // Wait no longer than three seconds.
633 jpegRect.wait(3000);
634 } catch (InterruptedException e) {
635 throw new Exception("Interrupted while decoding JPEG image");
636 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000637 }
638
639 // Done, jpegRect is not needed any more.
640 jpegRect = null;
641 return;
642
643 }
644
645 // Read filter id and parameters.
646 int numColors = 0, rowSize = w;
647 byte[] palette8 = new byte[2];
648 int[] palette24 = new int[256];
649 boolean useGradient = false;
650 if ((comp_ctl & rfb.TightExplicitFilter) != 0) {
651 int filter_id = rfb.is.readUnsignedByte();
652 if (filter_id == rfb.TightFilterPalette) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000653 numColors = rfb.is.readUnsignedByte() + 1;
654 byte[] buf = new byte[numColors * 3];
655 rfb.is.readFully(buf);
656 for (int i = 0; i < numColors; i++) {
657 palette24[i] = ((buf[i * 3] & 0xFF) << 16 |
658 (buf[i * 3 + 1] & 0xFF) << 8 |
659 (buf[i * 3 + 2] & 0xFF));
660 }
661 if (numColors == 2)
662 rowSize = (w + 7) / 8;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000663 } else if (filter_id == rfb.TightFilterGradient) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000664 useGradient = true;
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000665 } else if (filter_id != rfb.TightFilterCopy) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000666 throw new Exception("Incorrect tight filter id: " + filter_id);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000667 }
668 }
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000669 if (numColors == 0)
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000670 rowSize *= 3;
671
672 // Read, optionally uncompress and decode data.
673 int dataSize = h * rowSize;
674 if (dataSize < rfb.TightMinToCompress) {
675 // Data size is small - not compressed with zlib.
676 if (numColors != 0) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000677 // Indexed colors.
678 byte[] indexedData = new byte[dataSize];
679 rfb.is.readFully(indexedData);
680 if (numColors == 2) {
681 // Two colors.
682 decodeMonoData(x, y, w, h, indexedData, palette24);
683 } else {
684 // 3..255 colors.
685 int i = 0;
686 for (int dy = y; dy < y + h; dy++) {
687 for (int dx = x; dx < x + w; dx++) {
688 pixels24[dy * rfb.framebufferWidth + dx] =
689 palette24[indexedData[i++] & 0xFF];
690 }
691 }
692 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000693 } else if (useGradient) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000694 // "Gradient"-processed data
695 byte[] buf = new byte[w * h * 3];
696 rfb.is.readFully(buf);
697 decodeGradientData(x, y, w, h, buf);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000698 } else {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000699 // Raw truecolor data.
700 byte[] buf = new byte[w * 3];
701 int i, offset;
702 for (int dy = y; dy < y + h; dy++) {
703 rfb.is.readFully(buf);
704 offset = dy * rfb.framebufferWidth + x;
705 for (i = 0; i < w; i++) {
706 pixels24[offset + i] =
707 (buf[i * 3] & 0xFF) << 16 |
708 (buf[i * 3 + 1] & 0xFF) << 8 |
709 (buf[i * 3 + 2] & 0xFF);
710 }
711 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000712 }
713 } else {
714 // Data was compressed with zlib.
715 int zlibDataLen = rfb.readCompactLen();
716 byte[] zlibData = new byte[zlibDataLen];
717 rfb.is.readFully(zlibData);
718 int stream_id = comp_ctl & 0x03;
719 if (tightInflaters[stream_id] == null) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000720 tightInflaters[stream_id] = new Inflater();
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000721 }
722 Inflater myInflater = tightInflaters[stream_id];
723 myInflater.setInput(zlibData);
724 try {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000725 if (numColors != 0) {
726 // Indexed colors.
727 byte[] indexedData = new byte[dataSize];
728 myInflater.inflate(indexedData);
729 if (numColors == 2) {
730 // Two colors.
731 decodeMonoData(x, y, w, h, indexedData, palette24);
732 } else {
733 // More than two colors.
734 int i = 0;
735 for (int dy = y; dy < y + h; dy++) {
736 for (int dx = x; dx < x + w; dx++) {
737 pixels24[dy * rfb.framebufferWidth + dx] =
738 palette24[indexedData[i++] & 0xFF];
739 }
740 }
741 }
742 } else if (useGradient) {
743 // Compressed "Gradient"-filtered data.
744 byte[] buf = new byte[w * h * 3];
745 myInflater.inflate(buf);
746 decodeGradientData(x, y, w, h, buf);
747 } else {
748 // Compressed truecolor data.
749 byte[] buf = new byte[w * 3];
750 int i, offset;
751 for (int dy = y; dy < y + h; dy++) {
752 myInflater.inflate(buf);
753 offset = dy * rfb.framebufferWidth + x;
754 for (i = 0; i < w; i++) {
755 pixels24[offset + i] =
756 (buf[i * 3] & 0xFF) << 16 |
757 (buf[i * 3 + 1] & 0xFF) << 8 |
758 (buf[i * 3 + 2] & 0xFF);
759 }
760 }
761 }
762 } catch (DataFormatException dfe) {
763 throw new Exception(dfe.toString());
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000764 }
765 }
766
767 handleUpdatedPixels(x, y, w, h);
768 scheduleRepaint(x, y, w, h);
769 }
770
771 //
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000772 // Decode 1bpp-encoded bi-color rectangle.
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000773 //
Constantin Kaplinsky99df0a72002-06-04 06:13:20 +0000774 void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000775
776 int dx, dy, n;
777 int i = y * rfb.framebufferWidth + x;
778 int rowBytes = (w + 7) / 8;
779 byte b;
780
781 for (dy = 0; dy < h; dy++) {
782 for (dx = 0; dx < w / 8; dx++) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000783 b = src[dy * rowBytes + dx];
784 for (n = 7; n >= 0; n--)
785 pixels24[i++] = palette[b >> n & 1];
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000786 }
787 for (n = 7; n >= 8 - w % 8; n--) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000788 pixels24[i++] = palette[src[dy * rowBytes + dx] >> n & 1];
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000789 }
790 i += (rfb.framebufferWidth - w);
791 }
792 }
793
794 //
795 // Decode data processed with the "Gradient" filter.
796 //
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000797 void decodeGradientData(int x, int y, int w, int h, byte[] buf) {
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000798
799 int dx, dy, c;
800 byte[] prevRow = new byte[w * 3];
801 byte[] thisRow = new byte[w * 3];
802 byte[] pix = new byte[3];
803 int[] est = new int[3];
804
805 int offset = y * rfb.framebufferWidth + x;
806
807 for (dy = 0; dy < h; dy++) {
808
809 /* First pixel in a row */
810 for (c = 0; c < 3; c++) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000811 pix[c] = (byte)(prevRow[c] + buf[dy * w * 3 + c]);
812 thisRow[c] = pix[c];
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000813 }
814 pixels24[offset++] =
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000815 (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000816
817 /* Remaining pixels of a row */
818 for (dx = 1; dx < w; dx++) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +0000819 for (c = 0; c < 3; c++) {
820 est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) -
821 (prevRow[(dx - 1) * 3 + c] & 0xFF));
822 if (est[c] > 0xFF) {
823 est[c] = 0xFF;
824 } else if (est[c] < 0x00) {
825 est[c] = 0x00;
826 }
827 pix[c] = (byte)(est[c] + buf[(dy * w + dx) * 3 + c]);
828 thisRow[dx * 3 + c] = pix[c];
829 }
830 pixels24[offset++] =
831 (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000832 }
833
834 System.arraycopy(thisRow, 0, prevRow, 0, w * 3);
835 offset += (rfb.framebufferWidth - w);
836 }
837 }
838
839
840 //
841 // Display newly updated area of pixels.
842 //
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000843 void handleUpdatedPixels(int x, int y, int w, int h) {
844
845 // Draw updated pixels of the off-screen image.
Constantin Kaplinsky903009e2002-05-20 10:55:47 +0000846
Constantin Kaplinsky1215b992008-04-18 09:51:44 +0000847 pixelsSource.newPixels(x, y, w, h);
848 memGraphics.setClip(x, y, w, h);
849 memGraphics.drawImage(rawPixelsImage, 0, 0, null);
850 memGraphics.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight);
851 }
852
wimba.coma5a4f4f2004-09-21 15:25:05 +0000853 //////////////////////////////////////////////////////////////////
854 //
855 // Handle cursor shape updates (XCursor and RichCursor encodings).
856 //
857 boolean showSoftCursor = false;
858
859 int[] softCursorPixels;
860 MemoryImageSource softCursorSource;
861 Image softCursor;
862
863 int cursorX = 0, cursorY = 0;
864 int cursorWidth, cursorHeight;
865 int origCursorWidth, origCursorHeight;
866 int hotX, hotY;
867 int origHotX, origHotY;
868 int deferCursorUpdates = 10;
869
870 //
871 // Handle cursor shape update (XCursor and RichCursor encodings).
872 //
873 synchronized void handleCursorShapeUpdate(int encodingType,
874 int xhot, int yhot, int width,
875 int height)
876 throws IOException {
877
878 int bytesPerRow = (width + 7) / 8;
879 int bytesMaskData = bytesPerRow * height;
880
881 softCursorFree();
882
883 if (width * height == 0)
884 return;
885
886// // Ignore cursor shape data if requested by user.
887//
888// if (viewer.options.ignoreCursorUpdates) {
889// if (encodingType == rfb.EncodingXCursor) {
890// rfb.is.skipBytes(6 + bytesMaskData * 2);
891// } else {
892// // rfb.EncodingRichCursor
893// rfb.is.skipBytes(width * height + bytesMaskData);
894// }
895// return;
896// }
897
898 // Decode cursor pixel data.
899
900 softCursorPixels = new int[width * height];
901
902 if (encodingType == rfb.EncodingXCursor) {
wimba.coma5a4f4f2004-09-21 15:25:05 +0000903
904 // Read foreground and background colors of the cursor.
905 byte[] rgb = new byte[6];
906 rfb.is.readFully(rgb);
907 int[] colors = {(0xFF000000 | (rgb[3] & 0xFF) << 16 |
908 (rgb[4] & 0xFF) << 8 | (rgb[5] & 0xFF)),
909 (0xFF000000 | (rgb[0] & 0xFF) << 16 |
910 (rgb[1] & 0xFF) << 8 | (rgb[2] & 0xFF))
911 };
wimba.coma5a4f4f2004-09-21 15:25:05 +0000912
913 // Read pixel and mask data.
914 byte[] pixBuf = new byte[bytesMaskData];
915 rfb.is.readFully(pixBuf);
916 byte[] maskBuf = new byte[bytesMaskData];
917 rfb.is.readFully(maskBuf);
918
919 // Decode pixel data into softCursorPixels[].
920 byte pixByte, maskByte;
921 int x, y, n, result;
922 int i = 0;
923 for (y = 0; y < height; y++) {
924 for (x = 0; x < width / 8; x++) {
925 pixByte = pixBuf[y * bytesPerRow + x];
926 maskByte = maskBuf[y * bytesPerRow + x];
927 for (n = 7; n >= 0; n--) {
928 if ((maskByte >> n & 1) != 0) {
929 result = colors[pixByte >> n & 1];
930 } else {
931 result = 0; // Transparent pixel
932 }
933 softCursorPixels[i++] = result;
934 }
935 }
936 for (n = 7; n >= 8 - width % 8; n--) {
937 if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) {
938 result = colors[pixBuf[y * bytesPerRow + x] >> n & 1];
939 } else {
940 result = 0; // Transparent pixel
941 }
942 softCursorPixels[i++] = result;
943 }
944 }
945
946 } else {
947 // encodingType == rfb.EncodingRichCursor
wimba.coma5a4f4f2004-09-21 15:25:05 +0000948
949 // Read pixel and mask data.
950 byte[] pixBuf = new byte[width * height * 4];
951 rfb.is.readFully(pixBuf);
952 byte[] maskBuf = new byte[bytesMaskData];
953 rfb.is.readFully(maskBuf);
954
955 // Decode pixel data into softCursorPixels[].
956 byte pixByte, maskByte;
957 int x, y, n, result;
958 int i = 0;
959 for (y = 0; y < height; y++) {
960 for (x = 0; x < width / 8; x++) {
961 maskByte = maskBuf[y * bytesPerRow + x];
962 for (n = 7; n >= 0; n--) {
963 if ((maskByte >> n & 1) != 0) {
964// if (bytesPerPixel == 1) {
965// result = cm8.getRGB(pixBuf[i]);
966// } else {
967 result = (pixBuf[i * 4] & 0xFF) << 24 |
968 (pixBuf[i * 4 + 1] & 0xFF) << 16 |
969 (pixBuf[i * 4 + 2] & 0xFF) << 8 |
970 (pixBuf[i * 4 + 3] & 0xFF);
971 //result = 0xFF000000 |
972 // (pixBuf[i * 4 + 1] & 0xFF) << 16 |
973 // (pixBuf[i * 4 + 2] & 0xFF) << 8 |
974 // (pixBuf[i * 4 + 3] & 0xFF);
975// }
976 } else {
977 result = 0; // Transparent pixel
978 }
979 softCursorPixels[i++] = result;
980 }
981 }
982
983 for (n = 7; n >= 8 - width % 8; n--) {
984 if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) {
985// if (bytesPerPixel == 1) {
986// result = cm8.getRGB(pixBuf[i]);
987// } else {
988 result = 0xFF000000 |
989 (pixBuf[i * 4 + 1] & 0xFF) << 16 |
990 (pixBuf[i * 4 + 2] & 0xFF) << 8 |
991 (pixBuf[i * 4 + 3] & 0xFF);
992// }
993 } else {
994 result = 0; // Transparent pixel
995 }
996 softCursorPixels[i++] = result;
997 }
998 }
999
1000 }
1001
1002 // Draw the cursor on an off-screen image.
1003
1004 softCursorSource =
1005 new MemoryImageSource(width, height, softCursorPixels, 0, width);
1006 softCursor = Toolkit.getDefaultToolkit().createImage(softCursorSource);
1007// if (inputEnabled && viewer.options.scaleCursor != 0) {
1008// int w = (width * viewer.options.scaleCursor) / 100;
1009// int h = (height * viewer.options.scaleCursor) / 100;
1010// Image newCursor = softCursor.getScaledInstance(w, h, Image.SCALE_SMOOTH);
1011// softCursor = newCursor;
1012// cursorWidth = w;
1013// cursorHeight = h;
1014// hotX = (xhot * viewer.options.scaleCursor) / 100;
1015// hotY = (yhot * viewer.options.scaleCursor) / 100;
1016// } else {
1017 cursorWidth = width;
1018 cursorHeight = height;
1019 hotX = xhot;
1020 hotY = yhot;
1021// }
1022
1023 // Set data associated with cursor.
1024 origCursorWidth = width;
1025 origCursorHeight = height;
1026 origHotX = xhot;
1027 origHotY = yhot;
1028
1029 showSoftCursor = true;
1030
1031 // Show the cursor.
1032
1033 repaint(deferCursorUpdates,
1034 cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight);
1035 }
1036
1037 //
1038 // softCursorMove(). Moves soft cursor into a particular location.
1039 //
1040 synchronized void softCursorMove(int x, int y) {
1041 if (showSoftCursor) {
1042 repaint(deferCursorUpdates,
1043 cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight);
1044 repaint(deferCursorUpdates,
1045 x - hotX, y - hotY, cursorWidth, cursorHeight);
1046
wimba.com16092722004-09-21 20:54:37 +00001047 // Automatic viewport scrolling
1048 if (player.desktopScrollPane != null) {
1049 boolean needScroll = false;
1050 Dimension d = player.desktopScrollPane.getSize();
1051 Point topLeft = player.desktopScrollPane.getScrollPosition();
1052 Point botRight = new Point(topLeft.x + d.width, topLeft.y + d.height);
1053
1054 if (x < topLeft.x + SCROLL_MARGIN) {
1055 // shift left
1056 topLeft.x = x - SCROLL_MARGIN;
1057 needScroll = true;
1058 } else if (x > botRight.x - SCROLL_MARGIN) {
1059 // shift right
1060 topLeft.x = x - d.width + SCROLL_MARGIN;
1061 needScroll = true;
1062 }
1063 if (y < topLeft.y + SCROLL_MARGIN) {
1064 // shift up
1065 topLeft.y = y - SCROLL_MARGIN;
1066 needScroll = true;
1067 } else if (y > botRight.y - SCROLL_MARGIN) {
1068 // shift down
1069 topLeft.y = y - d.height + SCROLL_MARGIN;
1070 needScroll = true;
1071 }
1072 player.desktopScrollPane.setScrollPosition(topLeft.x, topLeft.y);
1073 }
wimba.coma5a4f4f2004-09-21 15:25:05 +00001074 }
1075
1076 cursorX = x;
1077 cursorY = y;
1078 }
1079 //
1080 // softCursorFree(). Remove soft cursor, dispose resources.
1081 //
1082 synchronized void softCursorFree() {
1083 if (showSoftCursor) {
1084 showSoftCursor = false;
1085 softCursor = null;
1086 softCursorSource = null;
1087 softCursorPixels = null;
1088
1089 repaint(deferCursorUpdates,
1090 cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight);
1091 }
1092 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +00001093 //
1094 // Tell JVM to repaint specified desktop area.
1095 //
Constantin Kaplinsky1215b992008-04-18 09:51:44 +00001096 void scheduleRepaint(int x, int y, int w, int h) {
Constantin Kaplinsky37cc43e2002-05-30 17:30:11 +00001097 if (rfb.fbs.isSeeking()) {
Constantin Kaplinsky52c48242002-05-30 13:26:34 +00001098 // Do nothing, and remember we are seeking.
1099 seekMode = true;
1100 } else {
1101 if (seekMode) {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +00001102 // Immediate repaint of the whole desktop after seeking.
1103 repaint();
Constantin Kaplinsky52c48242002-05-30 13:26:34 +00001104 } else {
Constantin Kaplinsky72e47ef2008-04-18 17:48:16 +00001105 // Usual incremental repaint.
1106 repaint(player.deferScreenUpdates, x, y, w, h);
Constantin Kaplinsky52c48242002-05-30 13:26:34 +00001107 }
1108 seekMode = false;
1109 }
Constantin Kaplinsky1215b992008-04-18 09:51:44 +00001110 }
1111
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +00001112 //
1113 // We are observing our FbsInputStream object to get notified on
1114 // switching to the `paused' mode. In such cases we want to repaint
1115 // our desktop if we were seeking.
1116 //
Constantin Kaplinsky5a7133a2002-07-24 17:02:04 +00001117 public void update(Observable o, Object arg) {
1118 // Immediate repaint of the whole desktop after seeking.
1119 repaint();
1120 // Let next scheduleRepaint() call invoke incremental drawing.
1121 seekMode = false;
1122 }
1123
Constantin Kaplinsky1215b992008-04-18 09:51:44 +00001124}