| 1 | // | 
|---|
| 2 | //  Copyright (C) 2004 Horizon Wimba.  All Rights Reserved. | 
|---|
| 3 | //  Copyright (C) 2001-2003 HorizonLive.com, Inc.  All Rights Reserved. | 
|---|
| 4 | //  Copyright (C) 2001,2002 Constantin Kaplinsky.  All Rights Reserved. | 
|---|
| 5 | //  Copyright (C) 2000 Tridia Corporation.  All Rights Reserved. | 
|---|
| 6 | //  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved. | 
|---|
| 7 | // | 
|---|
| 8 | //  This is free software; you can redistribute it and/or modify | 
|---|
| 9 | //  it under the terms of the GNU General Public License as published by | 
|---|
| 10 | //  the Free Software Foundation; either version 2 of the License, or | 
|---|
| 11 | //  (at your option) any later version. | 
|---|
| 12 | // | 
|---|
| 13 | //  This software is distributed in the hope that it will be useful, | 
|---|
| 14 | //  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 15 | //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
| 16 | //  GNU General Public License for more details. | 
|---|
| 17 | // | 
|---|
| 18 | //  You should have received a copy of the GNU General Public License | 
|---|
| 19 | //  along with this software; if not, write to the Free Software | 
|---|
| 20 | //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, | 
|---|
| 21 | //  USA. | 
|---|
| 22 | // | 
|---|
| 23 |  | 
|---|
| 24 | import java.awt.*; | 
|---|
| 25 | import java.awt.event.*; | 
|---|
| 26 | import java.awt.image.*; | 
|---|
| 27 | import java.io.*; | 
|---|
| 28 | import java.lang.*; | 
|---|
| 29 | import java.util.zip.*; | 
|---|
| 30 |  | 
|---|
| 31 |  | 
|---|
| 32 | // | 
|---|
| 33 | // VncCanvas is a subclass of Canvas which draws a VNC desktop on it. | 
|---|
| 34 | // | 
|---|
| 35 |  | 
|---|
| 36 | class VncCanvas extends Canvas | 
|---|
| 37 |   implements KeyListener, MouseListener, MouseMotionListener { | 
|---|
| 38 |  | 
|---|
| 39 |   VncViewer viewer; | 
|---|
| 40 |   RfbProto rfb; | 
|---|
| 41 |   ColorModel cm8, cm24; | 
|---|
| 42 |   Color[] colors; | 
|---|
| 43 |   int bytesPixel; | 
|---|
| 44 |  | 
|---|
| 45 |   int maxWidth = 0, maxHeight = 0; | 
|---|
| 46 |   int scalingFactor; | 
|---|
| 47 |   int scaledWidth, scaledHeight; | 
|---|
| 48 |  | 
|---|
| 49 |   Image memImage; | 
|---|
| 50 |   Graphics memGraphics; | 
|---|
| 51 |  | 
|---|
| 52 |   Image rawPixelsImage; | 
|---|
| 53 |   MemoryImageSource pixelsSource; | 
|---|
| 54 |   byte[] pixels8; | 
|---|
| 55 |   int[] pixels24; | 
|---|
| 56 |  | 
|---|
| 57 |   // ZRLE encoder's data. | 
|---|
| 58 |   byte[] zrleBuf; | 
|---|
| 59 |   int zrleBufLen = 0; | 
|---|
| 60 |   byte[] zrleTilePixels8; | 
|---|
| 61 |   int[] zrleTilePixels24; | 
|---|
| 62 |   ZlibInStream zrleInStream; | 
|---|
| 63 |   boolean zrleRecWarningShown = false; | 
|---|
| 64 |  | 
|---|
| 65 |   // Zlib encoder's data. | 
|---|
| 66 |   byte[] zlibBuf; | 
|---|
| 67 |   int zlibBufLen = 0; | 
|---|
| 68 |   Inflater zlibInflater; | 
|---|
| 69 |  | 
|---|
| 70 |   // Tight encoder's data. | 
|---|
| 71 |   final static int tightZlibBufferSize = 512; | 
|---|
| 72 |   Inflater[] tightInflaters; | 
|---|
| 73 |  | 
|---|
| 74 |   // Since JPEG images are loaded asynchronously, we have to remember | 
|---|
| 75 |   // their position in the framebuffer. Also, this jpegRect object is | 
|---|
| 76 |   // used for synchronization between the rfbThread and a JVM's thread | 
|---|
| 77 |   // which decodes and loads JPEG images. | 
|---|
| 78 |   Rectangle jpegRect; | 
|---|
| 79 |  | 
|---|
| 80 |   // True if we process keyboard and mouse events. | 
|---|
| 81 |   boolean inputEnabled; | 
|---|
| 82 |   int extraModifiers = 0; | 
|---|
| 83 |  | 
|---|
| 84 |   // | 
|---|
| 85 |   // The constructors. | 
|---|
| 86 |   // | 
|---|
| 87 |  | 
|---|
| 88 |   public VncCanvas(VncViewer v, int maxWidth_, int maxHeight_) | 
|---|
| 89 |     throws IOException { | 
|---|
| 90 |  | 
|---|
| 91 |     viewer = v; | 
|---|
| 92 |     maxWidth = maxWidth_; | 
|---|
| 93 |     maxHeight = maxHeight_; | 
|---|
| 94 |  | 
|---|
| 95 |     rfb = viewer.rfb; | 
|---|
| 96 |     scalingFactor = viewer.options.scalingFactor; | 
|---|
| 97 |  | 
|---|
| 98 |     tightInflaters = new Inflater[4]; | 
|---|
| 99 |  | 
|---|
| 100 |     cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); | 
|---|
| 101 |     cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); | 
|---|
| 102 |  | 
|---|
| 103 |     colors = new Color[256]; | 
|---|
| 104 |     for (int i = 0; i < 256; i++) | 
|---|
| 105 |       colors[i] = new Color(cm8.getRGB(i)); | 
|---|
| 106 |  | 
|---|
| 107 |     setPixelFormat(); | 
|---|
| 108 |  | 
|---|
| 109 |     inputEnabled = false; | 
|---|
| 110 |     if (!viewer.options.viewOnly) | 
|---|
| 111 |       enableInput(true); | 
|---|
| 112 |  | 
|---|
| 113 |     // Keyboard listener is enabled even in view-only mode, to catch | 
|---|
| 114 |     // 'r' or 'R' key presses used to request screen update. | 
|---|
| 115 |     addKeyListener(this); | 
|---|
| 116 |   } | 
|---|
| 117 |  | 
|---|
| 118 |   public VncCanvas(VncViewer v) throws IOException { | 
|---|
| 119 |     this(v, 0, 0); | 
|---|
| 120 |   } | 
|---|
| 121 |  | 
|---|
| 122 |   // | 
|---|
| 123 |   // Callback methods to determine geometry of our Component. | 
|---|
| 124 |   // | 
|---|
| 125 |  | 
|---|
| 126 |   public Dimension getPreferredSize() { | 
|---|
| 127 |     return new Dimension(scaledWidth, scaledHeight); | 
|---|
| 128 |   } | 
|---|
| 129 |  | 
|---|
| 130 |   public Dimension getMinimumSize() { | 
|---|
| 131 |     return new Dimension(scaledWidth, scaledHeight); | 
|---|
| 132 |   } | 
|---|
| 133 |  | 
|---|
| 134 |   public Dimension getMaximumSize() { | 
|---|
| 135 |     return new Dimension(scaledWidth, scaledHeight); | 
|---|
| 136 |   } | 
|---|
| 137 |  | 
|---|
| 138 |   // | 
|---|
| 139 |   // All painting is performed here. | 
|---|
| 140 |   // | 
|---|
| 141 |  | 
|---|
| 142 |   public void update(Graphics g) { | 
|---|
| 143 |     paint(g); | 
|---|
| 144 |   } | 
|---|
| 145 |  | 
|---|
| 146 |   public void paint(Graphics g) { | 
|---|
| 147 |     synchronized(memImage) { | 
|---|
| 148 |       if (rfb.framebufferWidth == scaledWidth) { | 
|---|
| 149 |         g.drawImage(memImage, 0, 0, null); | 
|---|
| 150 |       } else { | 
|---|
| 151 |         paintScaledFrameBuffer(g); | 
|---|
| 152 |       } | 
|---|
| 153 |     } | 
|---|
| 154 |     if (showSoftCursor) { | 
|---|
| 155 |       int x0 = cursorX - hotX, y0 = cursorY - hotY; | 
|---|
| 156 |       Rectangle r = new Rectangle(x0, y0, cursorWidth, cursorHeight); | 
|---|
| 157 |       if (r.intersects(g.getClipBounds())) { | 
|---|
| 158 |         g.drawImage(softCursor, x0, y0, null); | 
|---|
| 159 |       } | 
|---|
| 160 |     } | 
|---|
| 161 |   } | 
|---|
| 162 |  | 
|---|
| 163 |   public void paintScaledFrameBuffer(Graphics g) { | 
|---|
| 164 |     g.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null); | 
|---|
| 165 |   } | 
|---|
| 166 |  | 
|---|
| 167 |   // | 
|---|
| 168 |   // Override the ImageObserver interface method to handle drawing of | 
|---|
| 169 |   // JPEG-encoded data. | 
|---|
| 170 |   // | 
|---|
| 171 |  | 
|---|
| 172 |   public boolean imageUpdate(Image img, int infoflags, | 
|---|
| 173 |                              int x, int y, int width, int height) { | 
|---|
| 174 |     if ((infoflags & (ALLBITS | ABORT)) == 0) { | 
|---|
| 175 |       return true;              // We need more image data. | 
|---|
| 176 |     } else { | 
|---|
| 177 |       // If the whole image is available, draw it now. | 
|---|
| 178 |       if ((infoflags & ALLBITS) != 0) { | 
|---|
| 179 |         if (jpegRect != null) { | 
|---|
| 180 |           synchronized(jpegRect) { | 
|---|
| 181 |             memGraphics.drawImage(img, jpegRect.x, jpegRect.y, null); | 
|---|
| 182 |             scheduleRepaint(jpegRect.x, jpegRect.y, | 
|---|
| 183 |                             jpegRect.width, jpegRect.height); | 
|---|
| 184 |             jpegRect.notify(); | 
|---|
| 185 |           } | 
|---|
| 186 |         } | 
|---|
| 187 |       } | 
|---|
| 188 |       return false;             // All image data was processed. | 
|---|
| 189 |     } | 
|---|
| 190 |   } | 
|---|
| 191 |  | 
|---|
| 192 |   // | 
|---|
| 193 |   // Start/stop receiving mouse events. Keyboard events are received | 
|---|
| 194 |   // even in view-only mode, because we want to map the 'r' key to the | 
|---|
| 195 |   // screen refreshing function. | 
|---|
| 196 |   // | 
|---|
| 197 |  | 
|---|
| 198 |   public synchronized void enableInput(boolean enable) { | 
|---|
| 199 |     if (enable && !inputEnabled) { | 
|---|
| 200 |       inputEnabled = true; | 
|---|
| 201 |       addMouseListener(this); | 
|---|
| 202 |       addMouseMotionListener(this); | 
|---|
| 203 |       if (viewer.showControls) { | 
|---|
| 204 |         viewer.buttonPanel.enableRemoteAccessControls(true); | 
|---|
| 205 |       } | 
|---|
| 206 |       createSoftCursor();       // scaled cursor | 
|---|
| 207 |     } else if (!enable && inputEnabled) { | 
|---|
| 208 |       inputEnabled = false; | 
|---|
| 209 |       removeMouseListener(this); | 
|---|
| 210 |       removeMouseMotionListener(this); | 
|---|
| 211 |       if (viewer.showControls) { | 
|---|
| 212 |         viewer.buttonPanel.enableRemoteAccessControls(false); | 
|---|
| 213 |       } | 
|---|
| 214 |       createSoftCursor();       // non-scaled cursor | 
|---|
| 215 |     } | 
|---|
| 216 |   } | 
|---|
| 217 |  | 
|---|
| 218 |   public void setPixelFormat() throws IOException { | 
|---|
| 219 |     if (viewer.options.eightBitColors) { | 
|---|
| 220 |       rfb.writeSetPixelFormat(8, 8, false, true, 7, 7, 3, 0, 3, 6); | 
|---|
| 221 |       bytesPixel = 1; | 
|---|
| 222 |     } else { | 
|---|
| 223 |       rfb.writeSetPixelFormat(32, 24, false, true, 255, 255, 255, 16, 8, 0); | 
|---|
| 224 |       bytesPixel = 4; | 
|---|
| 225 |     } | 
|---|
| 226 |     updateFramebufferSize(); | 
|---|
| 227 |   } | 
|---|
| 228 |  | 
|---|
| 229 |   void updateFramebufferSize() { | 
|---|
| 230 |  | 
|---|
| 231 |     // Useful shortcuts. | 
|---|
| 232 |     int fbWidth = rfb.framebufferWidth; | 
|---|
| 233 |     int fbHeight = rfb.framebufferHeight; | 
|---|
| 234 |  | 
|---|
| 235 |     // Calculate scaling factor for auto scaling. | 
|---|
| 236 |     if (maxWidth > 0 && maxHeight > 0) { | 
|---|
| 237 |       int f1 = maxWidth * 100 / fbWidth; | 
|---|
| 238 |       int f2 = maxHeight * 100 / fbHeight; | 
|---|
| 239 |       scalingFactor = Math.min(f1, f2); | 
|---|
| 240 |       if (scalingFactor > 100) | 
|---|
| 241 |         scalingFactor = 100; | 
|---|
| 242 |       System.out.println("Scaling desktop at " + scalingFactor + "%"); | 
|---|
| 243 |     } | 
|---|
| 244 |  | 
|---|
| 245 |     // Update scaled framebuffer geometry. | 
|---|
| 246 |     scaledWidth = (fbWidth * scalingFactor + 50) / 100; | 
|---|
| 247 |     scaledHeight = (fbHeight * scalingFactor + 50) / 100; | 
|---|
| 248 |  | 
|---|
| 249 |     // Create new off-screen image either if it does not exist, or if | 
|---|
| 250 |     // its geometry should be changed. It's not necessary to replace | 
|---|
| 251 |     // existing image if only pixel format should be changed. | 
|---|
| 252 |     if (memImage == null) { | 
|---|
| 253 |       memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); | 
|---|
| 254 |       memGraphics = memImage.getGraphics(); | 
|---|
| 255 |     } else if (memImage.getWidth(null) != fbWidth || | 
|---|
| 256 |                memImage.getHeight(null) != fbHeight) { | 
|---|
| 257 |       synchronized(memImage) { | 
|---|
| 258 |         memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); | 
|---|
| 259 |         memGraphics = memImage.getGraphics(); | 
|---|
| 260 |       } | 
|---|
| 261 |     } | 
|---|
| 262 |  | 
|---|
| 263 |     // Images with raw pixels should be re-allocated on every change | 
|---|
| 264 |     // of geometry or pixel format. | 
|---|
| 265 |     if (bytesPixel == 1) { | 
|---|
| 266 |  | 
|---|
| 267 |       pixels24 = null; | 
|---|
| 268 |       pixels8 = new byte[fbWidth * fbHeight]; | 
|---|
| 269 |  | 
|---|
| 270 |       pixelsSource = | 
|---|
| 271 |         new MemoryImageSource(fbWidth, fbHeight, cm8, pixels8, 0, fbWidth); | 
|---|
| 272 |  | 
|---|
| 273 |       zrleTilePixels24 = null; | 
|---|
| 274 |       zrleTilePixels8 = new byte[64 * 64]; | 
|---|
| 275 |  | 
|---|
| 276 |     } else { | 
|---|
| 277 |  | 
|---|
| 278 |       pixels8 = null; | 
|---|
| 279 |       pixels24 = new int[fbWidth * fbHeight]; | 
|---|
| 280 |  | 
|---|
| 281 |       pixelsSource = | 
|---|
| 282 |         new MemoryImageSource(fbWidth, fbHeight, cm24, pixels24, 0, fbWidth); | 
|---|
| 283 |  | 
|---|
| 284 |       zrleTilePixels8 = null; | 
|---|
| 285 |       zrleTilePixels24 = new int[64 * 64]; | 
|---|
| 286 |  | 
|---|
| 287 |     } | 
|---|
| 288 |     pixelsSource.setAnimated(true); | 
|---|
| 289 |     rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource); | 
|---|
| 290 |  | 
|---|
| 291 |     // Update the size of desktop containers. | 
|---|
| 292 |     if (viewer.inSeparateFrame) { | 
|---|
| 293 |       if (viewer.desktopScrollPane != null) | 
|---|
| 294 |         resizeDesktopFrame(); | 
|---|
| 295 |     } else { | 
|---|
| 296 |       setSize(scaledWidth, scaledHeight); | 
|---|
| 297 |     } | 
|---|
| 298 |     viewer.moveFocusToDesktop(); | 
|---|
| 299 |   } | 
|---|
| 300 |  | 
|---|
| 301 |   void resizeDesktopFrame() { | 
|---|
| 302 |     setSize(scaledWidth, scaledHeight); | 
|---|
| 303 |  | 
|---|
| 304 |     // FIXME: Find a better way to determine correct size of a | 
|---|
| 305 |     // ScrollPane.  -- const | 
|---|
| 306 |     Insets insets = viewer.desktopScrollPane.getInsets(); | 
|---|
| 307 |     viewer.desktopScrollPane.setSize(scaledWidth + | 
|---|
| 308 |                                      2 * Math.min(insets.left, insets.right), | 
|---|
| 309 |                                      scaledHeight + | 
|---|
| 310 |                                      2 * Math.min(insets.top, insets.bottom)); | 
|---|
| 311 |  | 
|---|
| 312 |     viewer.vncFrame.pack(); | 
|---|
| 313 |  | 
|---|
| 314 |     // Try to limit the frame size to the screen size. | 
|---|
| 315 |  | 
|---|
| 316 |     Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize(); | 
|---|
| 317 |     Dimension frameSize = viewer.vncFrame.getSize(); | 
|---|
| 318 |     Dimension newSize = frameSize; | 
|---|
| 319 |  | 
|---|
| 320 |     // Reduce Screen Size by 30 pixels in each direction; | 
|---|
| 321 |     // This is a (poor) attempt to account for | 
|---|
| 322 |     //     1) Menu bar on Macintosh (should really also account for | 
|---|
| 323 |     //        Dock on OSX).  Usually 22px on top of screen. | 
|---|
| 324 |     //     2) Taxkbar on Windows (usually about 28 px on bottom) | 
|---|
| 325 |     //     3) Other obstructions. | 
|---|
| 326 |  | 
|---|
| 327 |     screenSize.height -= 30; | 
|---|
| 328 |     screenSize.width  -= 30; | 
|---|
| 329 |  | 
|---|
| 330 |     boolean needToResizeFrame = false; | 
|---|
| 331 |     if (frameSize.height > screenSize.height) { | 
|---|
| 332 |       newSize.height = screenSize.height; | 
|---|
| 333 |       needToResizeFrame = true; | 
|---|
| 334 |     } | 
|---|
| 335 |     if (frameSize.width > screenSize.width) { | 
|---|
| 336 |       newSize.width = screenSize.width; | 
|---|
| 337 |       needToResizeFrame = true; | 
|---|
| 338 |     } | 
|---|
| 339 |     if (needToResizeFrame) { | 
|---|
| 340 |       viewer.vncFrame.setSize(newSize); | 
|---|
| 341 |     } | 
|---|
| 342 |  | 
|---|
| 343 |     viewer.desktopScrollPane.doLayout(); | 
|---|
| 344 |   } | 
|---|
| 345 |  | 
|---|
| 346 |   // | 
|---|
| 347 |   // processNormalProtocol() - executed by the rfbThread to deal with the | 
|---|
| 348 |   // RFB socket. | 
|---|
| 349 |   // | 
|---|
| 350 |  | 
|---|
| 351 |   public void processNormalProtocol() throws Exception { | 
|---|
| 352 |  | 
|---|
| 353 |     // Start/stop session recording if necessary. | 
|---|
| 354 |     viewer.checkRecordingStatus(); | 
|---|
| 355 |  | 
|---|
| 356 |     rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, | 
|---|
| 357 |                                       rfb.framebufferHeight, false); | 
|---|
| 358 |  | 
|---|
| 359 |     // | 
|---|
| 360 |     // main dispatch loop | 
|---|
| 361 |     // | 
|---|
| 362 |  | 
|---|
| 363 |     while (true) { | 
|---|
| 364 |  | 
|---|
| 365 |       // Read message type from the server. | 
|---|
| 366 |       int msgType = rfb.readServerMessageType(); | 
|---|
| 367 |  | 
|---|
| 368 |       // Process the message depending on its type. | 
|---|
| 369 |       switch (msgType) { | 
|---|
| 370 |       case RfbProto.FramebufferUpdate: | 
|---|
| 371 |         rfb.readFramebufferUpdate(); | 
|---|
| 372 |  | 
|---|
| 373 |         boolean cursorPosReceived = false; | 
|---|
| 374 |  | 
|---|
| 375 |         for (int i = 0; i < rfb.updateNRects; i++) { | 
|---|
| 376 |           rfb.readFramebufferUpdateRectHdr(); | 
|---|
| 377 |           int rx = rfb.updateRectX, ry = rfb.updateRectY; | 
|---|
| 378 |           int rw = rfb.updateRectW, rh = rfb.updateRectH; | 
|---|
| 379 |  | 
|---|
| 380 |           if (rfb.updateRectEncoding == rfb.EncodingLastRect) | 
|---|
| 381 |             break; | 
|---|
| 382 |  | 
|---|
| 383 |           if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) { | 
|---|
| 384 |             rfb.setFramebufferSize(rw, rh); | 
|---|
| 385 |             updateFramebufferSize(); | 
|---|
| 386 |             break; | 
|---|
| 387 |           } | 
|---|
| 388 |  | 
|---|
| 389 |           if (rfb.updateRectEncoding == rfb.EncodingXCursor || | 
|---|
| 390 |               rfb.updateRectEncoding == rfb.EncodingRichCursor) { | 
|---|
| 391 |             handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry, rw, rh); | 
|---|
| 392 |             continue; | 
|---|
| 393 |           } | 
|---|
| 394 |  | 
|---|
| 395 |           if (rfb.updateRectEncoding == rfb.EncodingPointerPos) { | 
|---|
| 396 |             softCursorMove(rx, ry); | 
|---|
| 397 |             cursorPosReceived = true; | 
|---|
| 398 |             continue; | 
|---|
| 399 |           } | 
|---|
| 400 |  | 
|---|
| 401 |           rfb.startTiming(); | 
|---|
| 402 |  | 
|---|
| 403 |           switch (rfb.updateRectEncoding) { | 
|---|
| 404 |           case RfbProto.EncodingRaw: | 
|---|
| 405 |             handleRawRect(rx, ry, rw, rh); | 
|---|
| 406 |             break; | 
|---|
| 407 |           case RfbProto.EncodingCopyRect: | 
|---|
| 408 |             handleCopyRect(rx, ry, rw, rh); | 
|---|
| 409 |             break; | 
|---|
| 410 |           case RfbProto.EncodingRRE: | 
|---|
| 411 |             handleRRERect(rx, ry, rw, rh); | 
|---|
| 412 |             break; | 
|---|
| 413 |           case RfbProto.EncodingCoRRE: | 
|---|
| 414 |             handleCoRRERect(rx, ry, rw, rh); | 
|---|
| 415 |             break; | 
|---|
| 416 |           case RfbProto.EncodingHextile: | 
|---|
| 417 |             handleHextileRect(rx, ry, rw, rh); | 
|---|
| 418 |             break; | 
|---|
| 419 |           case RfbProto.EncodingZRLE: | 
|---|
| 420 |             handleZRLERect(rx, ry, rw, rh); | 
|---|
| 421 |             break; | 
|---|
| 422 |           case RfbProto.EncodingZlib: | 
|---|
| 423 |             handleZlibRect(rx, ry, rw, rh); | 
|---|
| 424 |             break; | 
|---|
| 425 |           case RfbProto.EncodingTight: | 
|---|
| 426 |             handleTightRect(rx, ry, rw, rh); | 
|---|
| 427 |             break; | 
|---|
| 428 |           default: | 
|---|
| 429 |             throw new Exception("Unknown RFB rectangle encoding " + | 
|---|
| 430 |                                 rfb.updateRectEncoding); | 
|---|
| 431 |           } | 
|---|
| 432 |  | 
|---|
| 433 |           rfb.stopTiming(); | 
|---|
| 434 |         } | 
|---|
| 435 |  | 
|---|
| 436 |         boolean fullUpdateNeeded = false; | 
|---|
| 437 |  | 
|---|
| 438 |         // Start/stop session recording if necessary. Request full | 
|---|
| 439 |         // update if a new session file was opened. | 
|---|
| 440 |         if (viewer.checkRecordingStatus()) | 
|---|
| 441 |           fullUpdateNeeded = true; | 
|---|
| 442 |  | 
|---|
| 443 |         // Defer framebuffer update request if necessary. But wake up | 
|---|
| 444 |         // immediately on keyboard or mouse event. Also, don't sleep | 
|---|
| 445 |         // if there is some data to receive, or if the last update | 
|---|
| 446 |         // included a PointerPos message. | 
|---|
| 447 |         if (viewer.deferUpdateRequests > 0 && | 
|---|
| 448 |             rfb.is.available() == 0 && !cursorPosReceived) { | 
|---|
| 449 |           synchronized(rfb) { | 
|---|
| 450 |             try { | 
|---|
| 451 |               rfb.wait(viewer.deferUpdateRequests); | 
|---|
| 452 |             } catch (InterruptedException e) { | 
|---|
| 453 |             } | 
|---|
| 454 |           } | 
|---|
| 455 |         } | 
|---|
| 456 |  | 
|---|
| 457 |         // Before requesting framebuffer update, check if the pixel | 
|---|
| 458 |         // format should be changed. If it should, request full update | 
|---|
| 459 |         // instead of an incremental one. | 
|---|
| 460 |         if (viewer.options.eightBitColors != (bytesPixel == 1)) { | 
|---|
| 461 |           setPixelFormat(); | 
|---|
| 462 |           fullUpdateNeeded = true; | 
|---|
| 463 |         } | 
|---|
| 464 |  | 
|---|
| 465 |         viewer.autoSelectEncodings(); | 
|---|
| 466 |  | 
|---|
| 467 |         rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, | 
|---|
| 468 |                                           rfb.framebufferHeight, | 
|---|
| 469 |                                           !fullUpdateNeeded); | 
|---|
| 470 |  | 
|---|
| 471 |         break; | 
|---|
| 472 |  | 
|---|
| 473 |       case RfbProto.SetColourMapEntries: | 
|---|
| 474 |         throw new Exception("Can't handle SetColourMapEntries message"); | 
|---|
| 475 |  | 
|---|
| 476 |       case RfbProto.Bell: | 
|---|
| 477 |         Toolkit.getDefaultToolkit().beep(); | 
|---|
| 478 |         break; | 
|---|
| 479 |  | 
|---|
| 480 |       case RfbProto.ServerCutText: | 
|---|
| 481 |         String s = rfb.readServerCutText(); | 
|---|
| 482 |         viewer.clipboard.setCutText(s); | 
|---|
| 483 |         break; | 
|---|
| 484 |  | 
|---|
| 485 |       default: | 
|---|
| 486 |         throw new Exception("Unknown RFB message type " + msgType); | 
|---|
| 487 |       } | 
|---|
| 488 |     } | 
|---|
| 489 |   } | 
|---|
| 490 |  | 
|---|
| 491 |  | 
|---|
| 492 |   // | 
|---|
| 493 |   // Handle a raw rectangle. The second form with paint==false is used | 
|---|
| 494 |   // by the Hextile decoder for raw-encoded tiles. | 
|---|
| 495 |   // | 
|---|
| 496 |  | 
|---|
| 497 |   void handleRawRect(int x, int y, int w, int h) throws IOException { | 
|---|
| 498 |     handleRawRect(x, y, w, h, true); | 
|---|
| 499 |   } | 
|---|
| 500 |  | 
|---|
| 501 |   void handleRawRect(int x, int y, int w, int h, boolean paint) | 
|---|
| 502 |     throws IOException { | 
|---|
| 503 |  | 
|---|
| 504 |     if (bytesPixel == 1) { | 
|---|
| 505 |       for (int dy = y; dy < y + h; dy++) { | 
|---|
| 506 |         rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w); | 
|---|
| 507 |         if (rfb.rec != null) { | 
|---|
| 508 |           rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w); | 
|---|
| 509 |         } | 
|---|
| 510 |       } | 
|---|
| 511 |     } else { | 
|---|
| 512 |       byte[] buf = new byte[w * 4]; | 
|---|
| 513 |       int i, offset; | 
|---|
| 514 |       for (int dy = y; dy < y + h; dy++) { | 
|---|
| 515 |         rfb.readFully(buf); | 
|---|
| 516 |         if (rfb.rec != null) { | 
|---|
| 517 |           rfb.rec.write(buf); | 
|---|
| 518 |         } | 
|---|
| 519 |         offset = dy * rfb.framebufferWidth + x; | 
|---|
| 520 |         for (i = 0; i < w; i++) { | 
|---|
| 521 |           pixels24[offset + i] = | 
|---|
| 522 |             (buf[i * 4 + 2] & 0xFF) << 16 | | 
|---|
| 523 |             (buf[i * 4 + 1] & 0xFF) << 8 | | 
|---|
| 524 |             (buf[i * 4] & 0xFF); | 
|---|
| 525 |         } | 
|---|
| 526 |       } | 
|---|
| 527 |     } | 
|---|
| 528 |  | 
|---|
| 529 |     handleUpdatedPixels(x, y, w, h); | 
|---|
| 530 |     if (paint) | 
|---|
| 531 |       scheduleRepaint(x, y, w, h); | 
|---|
| 532 |   } | 
|---|
| 533 |  | 
|---|
| 534 |   // | 
|---|
| 535 |   // Handle a CopyRect rectangle. | 
|---|
| 536 |   // | 
|---|
| 537 |  | 
|---|
| 538 |   void handleCopyRect(int x, int y, int w, int h) throws IOException { | 
|---|
| 539 |  | 
|---|
| 540 |     rfb.readCopyRect(); | 
|---|
| 541 |     memGraphics.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY, w, h, | 
|---|
| 542 |                          x - rfb.copyRectSrcX, y - rfb.copyRectSrcY); | 
|---|
| 543 |  | 
|---|
| 544 |     scheduleRepaint(x, y, w, h); | 
|---|
| 545 |   } | 
|---|
| 546 |  | 
|---|
| 547 |   // | 
|---|
| 548 |   // Handle an RRE-encoded rectangle. | 
|---|
| 549 |   // | 
|---|
| 550 |  | 
|---|
| 551 |   void handleRRERect(int x, int y, int w, int h) throws IOException { | 
|---|
| 552 |  | 
|---|
| 553 |     int nSubrects = rfb.is.readInt(); | 
|---|
| 554 |  | 
|---|
| 555 |     byte[] bg_buf = new byte[bytesPixel]; | 
|---|
| 556 |     rfb.readFully(bg_buf); | 
|---|
| 557 |     Color pixel; | 
|---|
| 558 |     if (bytesPixel == 1) { | 
|---|
| 559 |       pixel = colors[bg_buf[0] & 0xFF]; | 
|---|
| 560 |     } else { | 
|---|
| 561 |       pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, bg_buf[0] & 0xFF); | 
|---|
| 562 |     } | 
|---|
| 563 |     memGraphics.setColor(pixel); | 
|---|
| 564 |     memGraphics.fillRect(x, y, w, h); | 
|---|
| 565 |  | 
|---|
| 566 |     byte[] buf = new byte[nSubrects * (bytesPixel + 8)]; | 
|---|
| 567 |     rfb.readFully(buf); | 
|---|
| 568 |     DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf)); | 
|---|
| 569 |  | 
|---|
| 570 |     if (rfb.rec != null) { | 
|---|
| 571 |       rfb.rec.writeIntBE(nSubrects); | 
|---|
| 572 |       rfb.rec.write(bg_buf); | 
|---|
| 573 |       rfb.rec.write(buf); | 
|---|
| 574 |     } | 
|---|
| 575 |  | 
|---|
| 576 |     int sx, sy, sw, sh; | 
|---|
| 577 |  | 
|---|
| 578 |     for (int j = 0; j < nSubrects; j++) { | 
|---|
| 579 |       if (bytesPixel == 1) { | 
|---|
| 580 |         pixel = colors[ds.readUnsignedByte()]; | 
|---|
| 581 |       } else { | 
|---|
| 582 |         ds.skip(4); | 
|---|
| 583 |         pixel = new Color(buf[j*12+2] & 0xFF, | 
|---|
| 584 |                           buf[j*12+1] & 0xFF, | 
|---|
| 585 |                           buf[j*12]   & 0xFF); | 
|---|
| 586 |       } | 
|---|
| 587 |       sx = x + ds.readUnsignedShort(); | 
|---|
| 588 |       sy = y + ds.readUnsignedShort(); | 
|---|
| 589 |       sw = ds.readUnsignedShort(); | 
|---|
| 590 |       sh = ds.readUnsignedShort(); | 
|---|
| 591 |  | 
|---|
| 592 |       memGraphics.setColor(pixel); | 
|---|
| 593 |       memGraphics.fillRect(sx, sy, sw, sh); | 
|---|
| 594 |     } | 
|---|
| 595 |  | 
|---|
| 596 |     scheduleRepaint(x, y, w, h); | 
|---|
| 597 |   } | 
|---|
| 598 |  | 
|---|
| 599 |   // | 
|---|
| 600 |   // Handle a CoRRE-encoded rectangle. | 
|---|
| 601 |   // | 
|---|
| 602 |  | 
|---|
| 603 |   void handleCoRRERect(int x, int y, int w, int h) throws IOException { | 
|---|
| 604 |     int nSubrects = rfb.is.readInt(); | 
|---|
| 605 |  | 
|---|
| 606 |     byte[] bg_buf = new byte[bytesPixel]; | 
|---|
| 607 |     rfb.readFully(bg_buf); | 
|---|
| 608 |     Color pixel; | 
|---|
| 609 |     if (bytesPixel == 1) { | 
|---|
| 610 |       pixel = colors[bg_buf[0] & 0xFF]; | 
|---|
| 611 |     } else { | 
|---|
| 612 |       pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, bg_buf[0] & 0xFF); | 
|---|
| 613 |     } | 
|---|
| 614 |     memGraphics.setColor(pixel); | 
|---|
| 615 |     memGraphics.fillRect(x, y, w, h); | 
|---|
| 616 |  | 
|---|
| 617 |     byte[] buf = new byte[nSubrects * (bytesPixel + 4)]; | 
|---|
| 618 |     rfb.readFully(buf); | 
|---|
| 619 |  | 
|---|
| 620 |     if (rfb.rec != null) { | 
|---|
| 621 |       rfb.rec.writeIntBE(nSubrects); | 
|---|
| 622 |       rfb.rec.write(bg_buf); | 
|---|
| 623 |       rfb.rec.write(buf); | 
|---|
| 624 |     } | 
|---|
| 625 |  | 
|---|
| 626 |     int sx, sy, sw, sh; | 
|---|
| 627 |     int i = 0; | 
|---|
| 628 |  | 
|---|
| 629 |     for (int j = 0; j < nSubrects; j++) { | 
|---|
| 630 |       if (bytesPixel == 1) { | 
|---|
| 631 |         pixel = colors[buf[i++] & 0xFF]; | 
|---|
| 632 |       } else { | 
|---|
| 633 |         pixel = new Color(buf[i+2] & 0xFF, buf[i+1] & 0xFF, buf[i] & 0xFF); | 
|---|
| 634 |         i += 4; | 
|---|
| 635 |       } | 
|---|
| 636 |       sx = x + (buf[i++] & 0xFF); | 
|---|
| 637 |       sy = y + (buf[i++] & 0xFF); | 
|---|
| 638 |       sw = buf[i++] & 0xFF; | 
|---|
| 639 |       sh = buf[i++] & 0xFF; | 
|---|
| 640 |  | 
|---|
| 641 |       memGraphics.setColor(pixel); | 
|---|
| 642 |       memGraphics.fillRect(sx, sy, sw, sh); | 
|---|
| 643 |     } | 
|---|
| 644 |  | 
|---|
| 645 |     scheduleRepaint(x, y, w, h); | 
|---|
| 646 |   } | 
|---|
| 647 |  | 
|---|
| 648 |   // | 
|---|
| 649 |   // Handle a Hextile-encoded rectangle. | 
|---|
| 650 |   // | 
|---|
| 651 |  | 
|---|
| 652 |   // These colors should be kept between handleHextileSubrect() calls. | 
|---|
| 653 |   private Color hextile_bg, hextile_fg; | 
|---|
| 654 |  | 
|---|
| 655 |   void handleHextileRect(int x, int y, int w, int h) throws IOException { | 
|---|
| 656 |  | 
|---|
| 657 |     hextile_bg = new Color(0); | 
|---|
| 658 |     hextile_fg = new Color(0); | 
|---|
| 659 |  | 
|---|
| 660 |     for (int ty = y; ty < y + h; ty += 16) { | 
|---|
| 661 |       int th = 16; | 
|---|
| 662 |       if (y + h - ty < 16) | 
|---|
| 663 |         th = y + h - ty; | 
|---|
| 664 |  | 
|---|
| 665 |       for (int tx = x; tx < x + w; tx += 16) { | 
|---|
| 666 |         int tw = 16; | 
|---|
| 667 |         if (x + w - tx < 16) | 
|---|
| 668 |           tw = x + w - tx; | 
|---|
| 669 |  | 
|---|
| 670 |         handleHextileSubrect(tx, ty, tw, th); | 
|---|
| 671 |       } | 
|---|
| 672 |  | 
|---|
| 673 |       // Finished with a row of tiles, now let's show it. | 
|---|
| 674 |       scheduleRepaint(x, y, w, h); | 
|---|
| 675 |     } | 
|---|
| 676 |   } | 
|---|
| 677 |  | 
|---|
| 678 |   // | 
|---|
| 679 |   // Handle one tile in the Hextile-encoded data. | 
|---|
| 680 |   // | 
|---|
| 681 |  | 
|---|
| 682 |   void handleHextileSubrect(int tx, int ty, int tw, int th) | 
|---|
| 683 |     throws IOException { | 
|---|
| 684 |  | 
|---|
| 685 |     int subencoding = rfb.is.readUnsignedByte(); | 
|---|
| 686 |     if (rfb.rec != null) { | 
|---|
| 687 |       rfb.rec.writeByte(subencoding); | 
|---|
| 688 |     } | 
|---|
| 689 |  | 
|---|
| 690 |     // Is it a raw-encoded sub-rectangle? | 
|---|
| 691 |     if ((subencoding & rfb.HextileRaw) != 0) { | 
|---|
| 692 |       handleRawRect(tx, ty, tw, th, false); | 
|---|
| 693 |       return; | 
|---|
| 694 |     } | 
|---|
| 695 |  | 
|---|
| 696 |     // Read and draw the background if specified. | 
|---|
| 697 |     byte[] cbuf = new byte[bytesPixel]; | 
|---|
| 698 |     if ((subencoding & rfb.HextileBackgroundSpecified) != 0) { | 
|---|
| 699 |       rfb.readFully(cbuf); | 
|---|
| 700 |       if (bytesPixel == 1) { | 
|---|
| 701 |         hextile_bg = colors[cbuf[0] & 0xFF]; | 
|---|
| 702 |       } else { | 
|---|
| 703 |         hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, cbuf[0] & 0xFF); | 
|---|
| 704 |       } | 
|---|
| 705 |       if (rfb.rec != null) { | 
|---|
| 706 |         rfb.rec.write(cbuf); | 
|---|
| 707 |       } | 
|---|
| 708 |     } | 
|---|
| 709 |     memGraphics.setColor(hextile_bg); | 
|---|
| 710 |     memGraphics.fillRect(tx, ty, tw, th); | 
|---|
| 711 |  | 
|---|
| 712 |     // Read the foreground color if specified. | 
|---|
| 713 |     if ((subencoding & rfb.HextileForegroundSpecified) != 0) { | 
|---|
| 714 |       rfb.readFully(cbuf); | 
|---|
| 715 |       if (bytesPixel == 1) { | 
|---|
| 716 |         hextile_fg = colors[cbuf[0] & 0xFF]; | 
|---|
| 717 |       } else { | 
|---|
| 718 |         hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, cbuf[0] & 0xFF); | 
|---|
| 719 |       } | 
|---|
| 720 |       if (rfb.rec != null) { | 
|---|
| 721 |         rfb.rec.write(cbuf); | 
|---|
| 722 |       } | 
|---|
| 723 |     } | 
|---|
| 724 |  | 
|---|
| 725 |     // Done with this tile if there is no sub-rectangles. | 
|---|
| 726 |     if ((subencoding & rfb.HextileAnySubrects) == 0) | 
|---|
| 727 |       return; | 
|---|
| 728 |  | 
|---|
| 729 |     int nSubrects = rfb.is.readUnsignedByte(); | 
|---|
| 730 |     int bufsize = nSubrects * 2; | 
|---|
| 731 |     if ((subencoding & rfb.HextileSubrectsColoured) != 0) { | 
|---|
| 732 |       bufsize += nSubrects * bytesPixel; | 
|---|
| 733 |     } | 
|---|
| 734 |     byte[] buf = new byte[bufsize]; | 
|---|
| 735 |     rfb.readFully(buf); | 
|---|
| 736 |     if (rfb.rec != null) { | 
|---|
| 737 |       rfb.rec.writeByte(nSubrects); | 
|---|
| 738 |       rfb.rec.write(buf); | 
|---|
| 739 |     } | 
|---|
| 740 |  | 
|---|
| 741 |     int b1, b2, sx, sy, sw, sh; | 
|---|
| 742 |     int i = 0; | 
|---|
| 743 |  | 
|---|
| 744 |     if ((subencoding & rfb.HextileSubrectsColoured) == 0) { | 
|---|
| 745 |  | 
|---|
| 746 |       // Sub-rectangles are all of the same color. | 
|---|
| 747 |       memGraphics.setColor(hextile_fg); | 
|---|
| 748 |       for (int j = 0; j < nSubrects; j++) { | 
|---|
| 749 |         b1 = buf[i++] & 0xFF; | 
|---|
| 750 |         b2 = buf[i++] & 0xFF; | 
|---|
| 751 |         sx = tx + (b1 >> 4); | 
|---|
| 752 |         sy = ty + (b1 & 0xf); | 
|---|
| 753 |         sw = (b2 >> 4) + 1; | 
|---|
| 754 |         sh = (b2 & 0xf) + 1; | 
|---|
| 755 |         memGraphics.fillRect(sx, sy, sw, sh); | 
|---|
| 756 |       } | 
|---|
| 757 |     } else if (bytesPixel == 1) { | 
|---|
| 758 |  | 
|---|
| 759 |       // BGR233 (8-bit color) version for colored sub-rectangles. | 
|---|
| 760 |       for (int j = 0; j < nSubrects; j++) { | 
|---|
| 761 |         hextile_fg = colors[buf[i++] & 0xFF]; | 
|---|
| 762 |         b1 = buf[i++] & 0xFF; | 
|---|
| 763 |         b2 = buf[i++] & 0xFF; | 
|---|
| 764 |         sx = tx + (b1 >> 4); | 
|---|
| 765 |         sy = ty + (b1 & 0xf); | 
|---|
| 766 |         sw = (b2 >> 4) + 1; | 
|---|
| 767 |         sh = (b2 & 0xf) + 1; | 
|---|
| 768 |         memGraphics.setColor(hextile_fg); | 
|---|
| 769 |         memGraphics.fillRect(sx, sy, sw, sh); | 
|---|
| 770 |       } | 
|---|
| 771 |  | 
|---|
| 772 |     } else { | 
|---|
| 773 |  | 
|---|
| 774 |       // Full-color (24-bit) version for colored sub-rectangles. | 
|---|
| 775 |       for (int j = 0; j < nSubrects; j++) { | 
|---|
| 776 |         hextile_fg = new Color(buf[i+2] & 0xFF, | 
|---|
| 777 |                                buf[i+1] & 0xFF, | 
|---|
| 778 |                                buf[i] & 0xFF); | 
|---|
| 779 |         i += 4; | 
|---|
| 780 |         b1 = buf[i++] & 0xFF; | 
|---|
| 781 |         b2 = buf[i++] & 0xFF; | 
|---|
| 782 |         sx = tx + (b1 >> 4); | 
|---|
| 783 |         sy = ty + (b1 & 0xf); | 
|---|
| 784 |         sw = (b2 >> 4) + 1; | 
|---|
| 785 |         sh = (b2 & 0xf) + 1; | 
|---|
| 786 |         memGraphics.setColor(hextile_fg); | 
|---|
| 787 |         memGraphics.fillRect(sx, sy, sw, sh); | 
|---|
| 788 |       } | 
|---|
| 789 |  | 
|---|
| 790 |     } | 
|---|
| 791 |   } | 
|---|
| 792 |  | 
|---|
| 793 |   // | 
|---|
| 794 |   // Handle a ZRLE-encoded rectangle. | 
|---|
| 795 |   // | 
|---|
| 796 |   // FIXME: Currently, session recording is not fully supported for ZRLE. | 
|---|
| 797 |   // | 
|---|
| 798 |  | 
|---|
| 799 |   void handleZRLERect(int x, int y, int w, int h) throws Exception { | 
|---|
| 800 |  | 
|---|
| 801 |     if (zrleInStream == null) | 
|---|
| 802 |       zrleInStream = new ZlibInStream(); | 
|---|
| 803 |  | 
|---|
| 804 |     int nBytes = rfb.is.readInt(); | 
|---|
| 805 |     if (nBytes > 64 * 1024 * 1024) | 
|---|
| 806 |       throw new Exception("ZRLE decoder: illegal compressed data size"); | 
|---|
| 807 |  | 
|---|
| 808 |     if (zrleBuf == null || zrleBufLen < nBytes) { | 
|---|
| 809 |       zrleBufLen = nBytes + 4096; | 
|---|
| 810 |       zrleBuf = new byte[zrleBufLen]; | 
|---|
| 811 |     } | 
|---|
| 812 |  | 
|---|
| 813 |     // FIXME: Do not wait for all the data before decompression. | 
|---|
| 814 |     rfb.readFully(zrleBuf, 0, nBytes); | 
|---|
| 815 |  | 
|---|
| 816 |     if (rfb.rec != null) { | 
|---|
| 817 |       if (rfb.recordFromBeginning) { | 
|---|
| 818 |         rfb.rec.writeIntBE(nBytes); | 
|---|
| 819 |         rfb.rec.write(zrleBuf, 0, nBytes); | 
|---|
| 820 |       } else if (!zrleRecWarningShown) { | 
|---|
| 821 |         System.out.println("Warning: ZRLE session can be recorded" + | 
|---|
| 822 |                            " only from the beginning"); | 
|---|
| 823 |         System.out.println("Warning: Recorded file may be corrupted"); | 
|---|
| 824 |         zrleRecWarningShown = true; | 
|---|
| 825 |       } | 
|---|
| 826 |     } | 
|---|
| 827 |  | 
|---|
| 828 |     zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes); | 
|---|
| 829 |  | 
|---|
| 830 |     for (int ty = y; ty < y+h; ty += 64) { | 
|---|
| 831 |  | 
|---|
| 832 |       int th = Math.min(y+h-ty, 64); | 
|---|
| 833 |  | 
|---|
| 834 |       for (int tx = x; tx < x+w; tx += 64) { | 
|---|
| 835 |  | 
|---|
| 836 |         int tw = Math.min(x+w-tx, 64); | 
|---|
| 837 |  | 
|---|
| 838 |         int mode = zrleInStream.readU8(); | 
|---|
| 839 |         boolean rle = (mode & 128) != 0; | 
|---|
| 840 |         int palSize = mode & 127; | 
|---|
| 841 |         int[] palette = new int[128]; | 
|---|
| 842 |  | 
|---|
| 843 |         readZrlePalette(palette, palSize); | 
|---|
| 844 |  | 
|---|
| 845 |         if (palSize == 1) { | 
|---|
| 846 |           int pix = palette[0]; | 
|---|
| 847 |           Color c = (bytesPixel == 1) ? | 
|---|
| 848 |             colors[pix] : new Color(0xFF000000 | pix); | 
|---|
| 849 |           memGraphics.setColor(c); | 
|---|
| 850 |           memGraphics.fillRect(tx, ty, tw, th); | 
|---|
| 851 |           continue; | 
|---|
| 852 |         } | 
|---|
| 853 |  | 
|---|
| 854 |         if (!rle) { | 
|---|
| 855 |           if (palSize == 0) { | 
|---|
| 856 |             readZrleRawPixels(tw, th); | 
|---|
| 857 |           } else { | 
|---|
| 858 |             readZrlePackedPixels(tw, th, palette, palSize); | 
|---|
| 859 |           } | 
|---|
| 860 |         } else { | 
|---|
| 861 |           if (palSize == 0) { | 
|---|
| 862 |             readZrlePlainRLEPixels(tw, th); | 
|---|
| 863 |           } else { | 
|---|
| 864 |             readZrlePackedRLEPixels(tw, th, palette); | 
|---|
| 865 |           } | 
|---|
| 866 |         } | 
|---|
| 867 |         handleUpdatedZrleTile(tx, ty, tw, th); | 
|---|
| 868 |       } | 
|---|
| 869 |     } | 
|---|
| 870 |  | 
|---|
| 871 |     zrleInStream.reset(); | 
|---|
| 872 |  | 
|---|
| 873 |     scheduleRepaint(x, y, w, h); | 
|---|
| 874 |   } | 
|---|
| 875 |  | 
|---|
| 876 |   int readPixel(InStream is) throws Exception { | 
|---|
| 877 |     int pix; | 
|---|
| 878 |     if (bytesPixel == 1) { | 
|---|
| 879 |       pix = is.readU8(); | 
|---|
| 880 |     } else { | 
|---|
| 881 |       int p1 = is.readU8(); | 
|---|
| 882 |       int p2 = is.readU8(); | 
|---|
| 883 |       int p3 = is.readU8(); | 
|---|
| 884 |       pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF); | 
|---|
| 885 |     } | 
|---|
| 886 |     return pix; | 
|---|
| 887 |   } | 
|---|
| 888 |  | 
|---|
| 889 |   void readPixels(InStream is, int[] dst, int count) throws Exception { | 
|---|
| 890 |     int pix; | 
|---|
| 891 |     if (bytesPixel == 1) { | 
|---|
| 892 |       byte[] buf = new byte[count]; | 
|---|
| 893 |       is.readBytes(buf, 0, count); | 
|---|
| 894 |       for (int i = 0; i < count; i++) { | 
|---|
| 895 |         dst[i] = (int)buf[i] & 0xFF; | 
|---|
| 896 |       } | 
|---|
| 897 |     } else { | 
|---|
| 898 |       byte[] buf = new byte[count * 3]; | 
|---|
| 899 |       is.readBytes(buf, 0, count * 3); | 
|---|
| 900 |       for (int i = 0; i < count; i++) { | 
|---|
| 901 |         dst[i] = ((buf[i*3+2] & 0xFF) << 16 | | 
|---|
| 902 |                   (buf[i*3+1] & 0xFF) << 8 | | 
|---|
| 903 |                   (buf[i*3] & 0xFF)); | 
|---|
| 904 |       } | 
|---|
| 905 |     } | 
|---|
| 906 |   } | 
|---|
| 907 |  | 
|---|
| 908 |   void readZrlePalette(int[] palette, int palSize) throws Exception { | 
|---|
| 909 |     readPixels(zrleInStream, palette, palSize); | 
|---|
| 910 |   } | 
|---|
| 911 |  | 
|---|
| 912 |   void readZrleRawPixels(int tw, int th) throws Exception { | 
|---|
| 913 |     if (bytesPixel == 1) { | 
|---|
| 914 |       zrleInStream.readBytes(zrleTilePixels8, 0, tw * th); | 
|---|
| 915 |     } else { | 
|---|
| 916 |       readPixels(zrleInStream, zrleTilePixels24, tw * th); /// | 
|---|
| 917 |     } | 
|---|
| 918 |   } | 
|---|
| 919 |  | 
|---|
| 920 |   void readZrlePackedPixels(int tw, int th, int[] palette, int palSize) | 
|---|
| 921 |     throws Exception { | 
|---|
| 922 |  | 
|---|
| 923 |     int bppp = ((palSize > 16) ? 8 : | 
|---|
| 924 |                 ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1))); | 
|---|
| 925 |     int ptr = 0; | 
|---|
| 926 |  | 
|---|
| 927 |     for (int i = 0; i < th; i++) { | 
|---|
| 928 |       int eol = ptr + tw; | 
|---|
| 929 |       int b = 0; | 
|---|
| 930 |       int nbits = 0; | 
|---|
| 931 |  | 
|---|
| 932 |       while (ptr < eol) { | 
|---|
| 933 |         if (nbits == 0) { | 
|---|
| 934 |           b = zrleInStream.readU8(); | 
|---|
| 935 |           nbits = 8; | 
|---|
| 936 |         } | 
|---|
| 937 |         nbits -= bppp; | 
|---|
| 938 |         int index = (b >> nbits) & ((1 << bppp) - 1) & 127; | 
|---|
| 939 |         if (bytesPixel == 1) { | 
|---|
| 940 |           zrleTilePixels8[ptr++] = (byte)palette[index]; | 
|---|
| 941 |         } else { | 
|---|
| 942 |           zrleTilePixels24[ptr++] = palette[index]; | 
|---|
| 943 |         } | 
|---|
| 944 |       } | 
|---|
| 945 |     } | 
|---|
| 946 |   } | 
|---|
| 947 |  | 
|---|
| 948 |   void readZrlePlainRLEPixels(int tw, int th) throws Exception { | 
|---|
| 949 |     int ptr = 0; | 
|---|
| 950 |     int end = ptr + tw * th; | 
|---|
| 951 |     while (ptr < end) { | 
|---|
| 952 |       int pix = readPixel(zrleInStream); | 
|---|
| 953 |       int len = 1; | 
|---|
| 954 |       int b; | 
|---|
| 955 |       do { | 
|---|
| 956 |         b = zrleInStream.readU8(); | 
|---|
| 957 |         len += b; | 
|---|
| 958 |       } while (b == 255); | 
|---|
| 959 |  | 
|---|
| 960 |       if (!(len <= end - ptr)) | 
|---|
| 961 |         throw new Exception("ZRLE decoder: assertion failed" + | 
|---|
| 962 |                             " (len <= end-ptr)"); | 
|---|
| 963 |  | 
|---|
| 964 |       if (bytesPixel == 1) { | 
|---|
| 965 |         while (len-- > 0) zrleTilePixels8[ptr++] = (byte)pix; | 
|---|
| 966 |       } else { | 
|---|
| 967 |         while (len-- > 0) zrleTilePixels24[ptr++] = pix; | 
|---|
| 968 |       } | 
|---|
| 969 |     } | 
|---|
| 970 |   } | 
|---|
| 971 |  | 
|---|
| 972 |   void readZrlePackedRLEPixels(int tw, int th, int[] palette) | 
|---|
| 973 |     throws Exception { | 
|---|
| 974 |  | 
|---|
| 975 |     int ptr = 0; | 
|---|
| 976 |     int end = ptr + tw * th; | 
|---|
| 977 |     while (ptr < end) { | 
|---|
| 978 |       int index = zrleInStream.readU8(); | 
|---|
| 979 |       int len = 1; | 
|---|
| 980 |       if ((index & 128) != 0) { | 
|---|
| 981 |         int b; | 
|---|
| 982 |         do { | 
|---|
| 983 |           b = zrleInStream.readU8(); | 
|---|
| 984 |           len += b; | 
|---|
| 985 |         } while (b == 255); | 
|---|
| 986 |          | 
|---|
| 987 |         if (!(len <= end - ptr)) | 
|---|
| 988 |           throw new Exception("ZRLE decoder: assertion failed" + | 
|---|
| 989 |                               " (len <= end - ptr)"); | 
|---|
| 990 |       } | 
|---|
| 991 |  | 
|---|
| 992 |       index &= 127; | 
|---|
| 993 |       int pix = palette[index]; | 
|---|
| 994 |  | 
|---|
| 995 |       if (bytesPixel == 1) { | 
|---|
| 996 |         while (len-- > 0) zrleTilePixels8[ptr++] = (byte)pix; | 
|---|
| 997 |       } else { | 
|---|
| 998 |         while (len-- > 0) zrleTilePixels24[ptr++] = pix; | 
|---|
| 999 |       } | 
|---|
| 1000 |     } | 
|---|
| 1001 |   } | 
|---|
| 1002 |  | 
|---|
| 1003 |   // | 
|---|
| 1004 |   // Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update. | 
|---|
| 1005 |   // | 
|---|
| 1006 |  | 
|---|
| 1007 |   void handleUpdatedZrleTile(int x, int y, int w, int h) { | 
|---|
| 1008 |     Object src, dst; | 
|---|
| 1009 |     if (bytesPixel == 1) { | 
|---|
| 1010 |       src = zrleTilePixels8; dst = pixels8; | 
|---|
| 1011 |     } else { | 
|---|
| 1012 |       src = zrleTilePixels24; dst = pixels24; | 
|---|
| 1013 |     } | 
|---|
| 1014 |     int offsetSrc = 0; | 
|---|
| 1015 |     int offsetDst = (y * rfb.framebufferWidth + x); | 
|---|
| 1016 |     for (int j = 0; j < h; j++) { | 
|---|
| 1017 |       System.arraycopy(src, offsetSrc, dst, offsetDst, w); | 
|---|
| 1018 |       offsetSrc += w; | 
|---|
| 1019 |       offsetDst += rfb.framebufferWidth; | 
|---|
| 1020 |     } | 
|---|
| 1021 |     handleUpdatedPixels(x, y, w, h); | 
|---|
| 1022 |   } | 
|---|
| 1023 |  | 
|---|
| 1024 |   // | 
|---|
| 1025 |   // Handle a Zlib-encoded rectangle. | 
|---|
| 1026 |   // | 
|---|
| 1027 |  | 
|---|
| 1028 |   void handleZlibRect(int x, int y, int w, int h) throws Exception { | 
|---|
| 1029 |  | 
|---|
| 1030 |     int nBytes = rfb.is.readInt(); | 
|---|
| 1031 |  | 
|---|
| 1032 |     if (zlibBuf == null || zlibBufLen < nBytes) { | 
|---|
| 1033 |       zlibBufLen = nBytes * 2; | 
|---|
| 1034 |       zlibBuf = new byte[zlibBufLen]; | 
|---|
| 1035 |     } | 
|---|
| 1036 |  | 
|---|
| 1037 |     rfb.readFully(zlibBuf, 0, nBytes); | 
|---|
| 1038 |  | 
|---|
| 1039 |     if (rfb.rec != null && rfb.recordFromBeginning) { | 
|---|
| 1040 |       rfb.rec.writeIntBE(nBytes); | 
|---|
| 1041 |       rfb.rec.write(zlibBuf, 0, nBytes); | 
|---|
| 1042 |     } | 
|---|
| 1043 |  | 
|---|
| 1044 |     if (zlibInflater == null) { | 
|---|
| 1045 |       zlibInflater = new Inflater(); | 
|---|
| 1046 |     } | 
|---|
| 1047 |     zlibInflater.setInput(zlibBuf, 0, nBytes); | 
|---|
| 1048 |  | 
|---|
| 1049 |     if (bytesPixel == 1) { | 
|---|
| 1050 |       for (int dy = y; dy < y + h; dy++) { | 
|---|
| 1051 |         zlibInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w); | 
|---|
| 1052 |         if (rfb.rec != null && !rfb.recordFromBeginning) | 
|---|
| 1053 |           rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w); | 
|---|
| 1054 |       } | 
|---|
| 1055 |     } else { | 
|---|
| 1056 |       byte[] buf = new byte[w * 4]; | 
|---|
| 1057 |       int i, offset; | 
|---|
| 1058 |       for (int dy = y; dy < y + h; dy++) { | 
|---|
| 1059 |         zlibInflater.inflate(buf); | 
|---|
| 1060 |         offset = dy * rfb.framebufferWidth + x; | 
|---|
| 1061 |         for (i = 0; i < w; i++) { | 
|---|
| 1062 |           pixels24[offset + i] = | 
|---|
| 1063 |             (buf[i * 4 + 2] & 0xFF) << 16 | | 
|---|
| 1064 |             (buf[i * 4 + 1] & 0xFF) << 8 | | 
|---|
| 1065 |             (buf[i * 4] & 0xFF); | 
|---|
| 1066 |         } | 
|---|
| 1067 |         if (rfb.rec != null && !rfb.recordFromBeginning) | 
|---|
| 1068 |           rfb.rec.write(buf); | 
|---|
| 1069 |       } | 
|---|
| 1070 |     } | 
|---|
| 1071 |  | 
|---|
| 1072 |     handleUpdatedPixels(x, y, w, h); | 
|---|
| 1073 |     scheduleRepaint(x, y, w, h); | 
|---|
| 1074 |   } | 
|---|
| 1075 |  | 
|---|
| 1076 |   // | 
|---|
| 1077 |   // Handle a Tight-encoded rectangle. | 
|---|
| 1078 |   // | 
|---|
| 1079 |  | 
|---|
| 1080 |   void handleTightRect(int x, int y, int w, int h) throws Exception { | 
|---|
| 1081 |  | 
|---|
| 1082 |     int comp_ctl = rfb.is.readUnsignedByte(); | 
|---|
| 1083 |     if (rfb.rec != null) { | 
|---|
| 1084 |       if (rfb.recordFromBeginning || | 
|---|
| 1085 |           comp_ctl == (rfb.TightFill << 4) || | 
|---|
| 1086 |           comp_ctl == (rfb.TightJpeg << 4)) { | 
|---|
| 1087 |         // Send data exactly as received. | 
|---|
| 1088 |         rfb.rec.writeByte(comp_ctl); | 
|---|
| 1089 |       } else { | 
|---|
| 1090 |         // Tell the decoder to flush each of the four zlib streams. | 
|---|
| 1091 |         rfb.rec.writeByte(comp_ctl | 0x0F); | 
|---|
| 1092 |       } | 
|---|
| 1093 |     } | 
|---|
| 1094 |  | 
|---|
| 1095 |     // Flush zlib streams if we are told by the server to do so. | 
|---|
| 1096 |     for (int stream_id = 0; stream_id < 4; stream_id++) { | 
|---|
| 1097 |       if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) { | 
|---|
| 1098 |         tightInflaters[stream_id] = null; | 
|---|
| 1099 |       } | 
|---|
| 1100 |       comp_ctl >>= 1; | 
|---|
| 1101 |     } | 
|---|
| 1102 |  | 
|---|
| 1103 |     // Check correctness of subencoding value. | 
|---|
| 1104 |     if (comp_ctl > rfb.TightMaxSubencoding) { | 
|---|
| 1105 |       throw new Exception("Incorrect tight subencoding: " + comp_ctl); | 
|---|
| 1106 |     } | 
|---|
| 1107 |  | 
|---|
| 1108 |     // Handle solid-color rectangles. | 
|---|
| 1109 |     if (comp_ctl == rfb.TightFill) { | 
|---|
| 1110 |  | 
|---|
| 1111 |       if (bytesPixel == 1) { | 
|---|
| 1112 |         int idx = rfb.is.readUnsignedByte(); | 
|---|
| 1113 |         memGraphics.setColor(colors[idx]); | 
|---|
| 1114 |         if (rfb.rec != null) { | 
|---|
| 1115 |           rfb.rec.writeByte(idx); | 
|---|
| 1116 |         } | 
|---|
| 1117 |       } else { | 
|---|
| 1118 |         byte[] buf = new byte[3]; | 
|---|
| 1119 |         rfb.readFully(buf); | 
|---|
| 1120 |         if (rfb.rec != null) { | 
|---|
| 1121 |           rfb.rec.write(buf); | 
|---|
| 1122 |         } | 
|---|
| 1123 |         Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16 | | 
|---|
| 1124 |                              (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF)); | 
|---|
| 1125 |         memGraphics.setColor(bg); | 
|---|
| 1126 |       } | 
|---|
| 1127 |       memGraphics.fillRect(x, y, w, h); | 
|---|
| 1128 |       scheduleRepaint(x, y, w, h); | 
|---|
| 1129 |       return; | 
|---|
| 1130 |  | 
|---|
| 1131 |     } | 
|---|
| 1132 |  | 
|---|
| 1133 |     if (comp_ctl == rfb.TightJpeg) { | 
|---|
| 1134 |  | 
|---|
| 1135 |       // Read JPEG data. | 
|---|
| 1136 |       byte[] jpegData = new byte[rfb.readCompactLen()]; | 
|---|
| 1137 |       rfb.readFully(jpegData); | 
|---|
| 1138 |       if (rfb.rec != null) { | 
|---|
| 1139 |         if (!rfb.recordFromBeginning) { | 
|---|
| 1140 |           rfb.recordCompactLen(jpegData.length); | 
|---|
| 1141 |         } | 
|---|
| 1142 |         rfb.rec.write(jpegData); | 
|---|
| 1143 |       } | 
|---|
| 1144 |  | 
|---|
| 1145 |       // Create an Image object from the JPEG data. | 
|---|
| 1146 |       Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData); | 
|---|
| 1147 |  | 
|---|
| 1148 |       // Remember the rectangle where the image should be drawn. | 
|---|
| 1149 |       jpegRect = new Rectangle(x, y, w, h); | 
|---|
| 1150 |  | 
|---|
| 1151 |       // Let the imageUpdate() method do the actual drawing, here just | 
|---|
| 1152 |       // wait until the image is fully loaded and drawn. | 
|---|
| 1153 |       synchronized(jpegRect) { | 
|---|
| 1154 |         Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1, this); | 
|---|
| 1155 |         try { | 
|---|
| 1156 |           // Wait no longer than three seconds. | 
|---|
| 1157 |           jpegRect.wait(3000); | 
|---|
| 1158 |         } catch (InterruptedException e) { | 
|---|
| 1159 |           throw new Exception("Interrupted while decoding JPEG image"); | 
|---|
| 1160 |         } | 
|---|
| 1161 |       } | 
|---|
| 1162 |  | 
|---|
| 1163 |       // Done, jpegRect is not needed any more. | 
|---|
| 1164 |       jpegRect = null; | 
|---|
| 1165 |       return; | 
|---|
| 1166 |  | 
|---|
| 1167 |     } | 
|---|
| 1168 |  | 
|---|
| 1169 |     // Read filter id and parameters. | 
|---|
| 1170 |     int numColors = 0, rowSize = w; | 
|---|
| 1171 |     byte[] palette8 = new byte[2]; | 
|---|
| 1172 |     int[] palette24 = new int[256]; | 
|---|
| 1173 |     boolean useGradient = false; | 
|---|
| 1174 |     if ((comp_ctl & rfb.TightExplicitFilter) != 0) { | 
|---|
| 1175 |       int filter_id = rfb.is.readUnsignedByte(); | 
|---|
| 1176 |       if (rfb.rec != null) { | 
|---|
| 1177 |         rfb.rec.writeByte(filter_id); | 
|---|
| 1178 |       } | 
|---|
| 1179 |       if (filter_id == rfb.TightFilterPalette) { | 
|---|
| 1180 |         numColors = rfb.is.readUnsignedByte() + 1; | 
|---|
| 1181 |         if (rfb.rec != null) { | 
|---|
| 1182 |           rfb.rec.writeByte(numColors - 1); | 
|---|
| 1183 |         } | 
|---|
| 1184 |         if (bytesPixel == 1) { | 
|---|
| 1185 |           if (numColors != 2) { | 
|---|
| 1186 |             throw new Exception("Incorrect tight palette size: " + numColors); | 
|---|
| 1187 |           } | 
|---|
| 1188 |           rfb.readFully(palette8); | 
|---|
| 1189 |           if (rfb.rec != null) { | 
|---|
| 1190 |             rfb.rec.write(palette8); | 
|---|
| 1191 |           } | 
|---|
| 1192 |         } else { | 
|---|
| 1193 |           byte[] buf = new byte[numColors * 3]; | 
|---|
| 1194 |           rfb.readFully(buf); | 
|---|
| 1195 |           if (rfb.rec != null) { | 
|---|
| 1196 |             rfb.rec.write(buf); | 
|---|
| 1197 |           } | 
|---|
| 1198 |           for (int i = 0; i < numColors; i++) { | 
|---|
| 1199 |             palette24[i] = ((buf[i * 3] & 0xFF) << 16 | | 
|---|
| 1200 |                             (buf[i * 3 + 1] & 0xFF) << 8 | | 
|---|
| 1201 |                             (buf[i * 3 + 2] & 0xFF)); | 
|---|
| 1202 |           } | 
|---|
| 1203 |         } | 
|---|
| 1204 |         if (numColors == 2) | 
|---|
| 1205 |           rowSize = (w + 7) / 8; | 
|---|
| 1206 |       } else if (filter_id == rfb.TightFilterGradient) { | 
|---|
| 1207 |         useGradient = true; | 
|---|
| 1208 |       } else if (filter_id != rfb.TightFilterCopy) { | 
|---|
| 1209 |         throw new Exception("Incorrect tight filter id: " + filter_id); | 
|---|
| 1210 |       } | 
|---|
| 1211 |     } | 
|---|
| 1212 |     if (numColors == 0 && bytesPixel == 4) | 
|---|
| 1213 |       rowSize *= 3; | 
|---|
| 1214 |  | 
|---|
| 1215 |     // Read, optionally uncompress and decode data. | 
|---|
| 1216 |     int dataSize = h * rowSize; | 
|---|
| 1217 |     if (dataSize < rfb.TightMinToCompress) { | 
|---|
| 1218 |       // Data size is small - not compressed with zlib. | 
|---|
| 1219 |       if (numColors != 0) { | 
|---|
| 1220 |         // Indexed colors. | 
|---|
| 1221 |         byte[] indexedData = new byte[dataSize]; | 
|---|
| 1222 |         rfb.readFully(indexedData); | 
|---|
| 1223 |         if (rfb.rec != null) { | 
|---|
| 1224 |           rfb.rec.write(indexedData); | 
|---|
| 1225 |         } | 
|---|
| 1226 |         if (numColors == 2) { | 
|---|
| 1227 |           // Two colors. | 
|---|
| 1228 |           if (bytesPixel == 1) { | 
|---|
| 1229 |             decodeMonoData(x, y, w, h, indexedData, palette8); | 
|---|
| 1230 |           } else { | 
|---|
| 1231 |             decodeMonoData(x, y, w, h, indexedData, palette24); | 
|---|
| 1232 |           } | 
|---|
| 1233 |         } else { | 
|---|
| 1234 |           // 3..255 colors (assuming bytesPixel == 4). | 
|---|
| 1235 |           int i = 0; | 
|---|
| 1236 |           for (int dy = y; dy < y + h; dy++) { | 
|---|
| 1237 |             for (int dx = x; dx < x + w; dx++) { | 
|---|
| 1238 |               pixels24[dy * rfb.framebufferWidth + dx] = | 
|---|
| 1239 |                 palette24[indexedData[i++] & 0xFF]; | 
|---|
| 1240 |             } | 
|---|
| 1241 |           } | 
|---|
| 1242 |         } | 
|---|
| 1243 |       } else if (useGradient) { | 
|---|
| 1244 |         // "Gradient"-processed data | 
|---|
| 1245 |         byte[] buf = new byte[w * h * 3]; | 
|---|
| 1246 |         rfb.readFully(buf); | 
|---|
| 1247 |         if (rfb.rec != null) { | 
|---|
| 1248 |           rfb.rec.write(buf); | 
|---|
| 1249 |         } | 
|---|
| 1250 |         decodeGradientData(x, y, w, h, buf); | 
|---|
| 1251 |       } else { | 
|---|
| 1252 |         // Raw truecolor data. | 
|---|
| 1253 |         if (bytesPixel == 1) { | 
|---|
| 1254 |           for (int dy = y; dy < y + h; dy++) { | 
|---|
| 1255 |             rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w); | 
|---|
| 1256 |             if (rfb.rec != null) { | 
|---|
| 1257 |               rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w); | 
|---|
| 1258 |             } | 
|---|
| 1259 |           } | 
|---|
| 1260 |         } else { | 
|---|
| 1261 |           byte[] buf = new byte[w * 3]; | 
|---|
| 1262 |           int i, offset; | 
|---|
| 1263 |           for (int dy = y; dy < y + h; dy++) { | 
|---|
| 1264 |             rfb.readFully(buf); | 
|---|
| 1265 |             if (rfb.rec != null) { | 
|---|
| 1266 |               rfb.rec.write(buf); | 
|---|
| 1267 |             } | 
|---|
| 1268 |             offset = dy * rfb.framebufferWidth + x; | 
|---|
| 1269 |             for (i = 0; i < w; i++) { | 
|---|
| 1270 |               pixels24[offset + i] = | 
|---|
| 1271 |                 (buf[i * 3] & 0xFF) << 16 | | 
|---|
| 1272 |                 (buf[i * 3 + 1] & 0xFF) << 8 | | 
|---|
| 1273 |                 (buf[i * 3 + 2] & 0xFF); | 
|---|
| 1274 |             } | 
|---|
| 1275 |           } | 
|---|
| 1276 |         } | 
|---|
| 1277 |       } | 
|---|
| 1278 |     } else { | 
|---|
| 1279 |       // Data was compressed with zlib. | 
|---|
| 1280 |       int zlibDataLen = rfb.readCompactLen(); | 
|---|
| 1281 |       byte[] zlibData = new byte[zlibDataLen]; | 
|---|
| 1282 |       rfb.readFully(zlibData); | 
|---|
| 1283 |       if (rfb.rec != null && rfb.recordFromBeginning) { | 
|---|
| 1284 |         rfb.rec.write(zlibData); | 
|---|
| 1285 |       } | 
|---|
| 1286 |       int stream_id = comp_ctl & 0x03; | 
|---|
| 1287 |       if (tightInflaters[stream_id] == null) { | 
|---|
| 1288 |         tightInflaters[stream_id] = new Inflater(); | 
|---|
| 1289 |       } | 
|---|
| 1290 |       Inflater myInflater = tightInflaters[stream_id]; | 
|---|
| 1291 |       myInflater.setInput(zlibData); | 
|---|
| 1292 |       byte[] buf = new byte[dataSize]; | 
|---|
| 1293 |       myInflater.inflate(buf); | 
|---|
| 1294 |       if (rfb.rec != null && !rfb.recordFromBeginning) { | 
|---|
| 1295 |         rfb.recordCompressedData(buf); | 
|---|
| 1296 |       } | 
|---|
| 1297 |  | 
|---|
| 1298 |       if (numColors != 0) { | 
|---|
| 1299 |         // Indexed colors. | 
|---|
| 1300 |         if (numColors == 2) { | 
|---|
| 1301 |           // Two colors. | 
|---|
| 1302 |           if (bytesPixel == 1) { | 
|---|
| 1303 |             decodeMonoData(x, y, w, h, buf, palette8); | 
|---|
| 1304 |           } else { | 
|---|
| 1305 |             decodeMonoData(x, y, w, h, buf, palette24); | 
|---|
| 1306 |           } | 
|---|
| 1307 |         } else { | 
|---|
| 1308 |           // More than two colors (assuming bytesPixel == 4). | 
|---|
| 1309 |           int i = 0; | 
|---|
| 1310 |           for (int dy = y; dy < y + h; dy++) { | 
|---|
| 1311 |             for (int dx = x; dx < x + w; dx++) { | 
|---|
| 1312 |               pixels24[dy * rfb.framebufferWidth + dx] = | 
|---|
| 1313 |                 palette24[buf[i++] & 0xFF]; | 
|---|
| 1314 |             } | 
|---|
| 1315 |           } | 
|---|
| 1316 |         } | 
|---|
| 1317 |       } else if (useGradient) { | 
|---|
| 1318 |         // Compressed "Gradient"-filtered data (assuming bytesPixel == 4). | 
|---|
| 1319 |         decodeGradientData(x, y, w, h, buf); | 
|---|
| 1320 |       } else { | 
|---|
| 1321 |         // Compressed truecolor data. | 
|---|
| 1322 |         if (bytesPixel == 1) { | 
|---|
| 1323 |           int destOffset = y * rfb.framebufferWidth + x; | 
|---|
| 1324 |           for (int dy = 0; dy < h; dy++) { | 
|---|
| 1325 |             System.arraycopy(buf, dy * w, pixels8, destOffset, w); | 
|---|
| 1326 |             destOffset += rfb.framebufferWidth; | 
|---|
| 1327 |           } | 
|---|
| 1328 |         } else { | 
|---|
| 1329 |           int srcOffset = 0; | 
|---|
| 1330 |           int destOffset, i; | 
|---|
| 1331 |           for (int dy = 0; dy < h; dy++) { | 
|---|
| 1332 |             myInflater.inflate(buf); | 
|---|
| 1333 |             destOffset = (y + dy) * rfb.framebufferWidth + x; | 
|---|
| 1334 |             for (i = 0; i < w; i++) { | 
|---|
| 1335 |               pixels24[destOffset + i] = | 
|---|
| 1336 |                 (buf[srcOffset] & 0xFF) << 16 | | 
|---|
| 1337 |                 (buf[srcOffset + 1] & 0xFF) << 8 | | 
|---|
| 1338 |                 (buf[srcOffset + 2] & 0xFF); | 
|---|
| 1339 |               srcOffset += 3; | 
|---|
| 1340 |             } | 
|---|
| 1341 |           } | 
|---|
| 1342 |         } | 
|---|
| 1343 |       } | 
|---|
| 1344 |     } | 
|---|
| 1345 |  | 
|---|
| 1346 |     handleUpdatedPixels(x, y, w, h); | 
|---|
| 1347 |     scheduleRepaint(x, y, w, h); | 
|---|
| 1348 |   } | 
|---|
| 1349 |  | 
|---|
| 1350 |   // | 
|---|
| 1351 |   // Decode 1bpp-encoded bi-color rectangle (8-bit and 24-bit versions). | 
|---|
| 1352 |   // | 
|---|
| 1353 |  | 
|---|
| 1354 |   void decodeMonoData(int x, int y, int w, int h, byte[] src, byte[] palette) { | 
|---|
| 1355 |  | 
|---|
| 1356 |     int dx, dy, n; | 
|---|
| 1357 |     int i = y * rfb.framebufferWidth + x; | 
|---|
| 1358 |     int rowBytes = (w + 7) / 8; | 
|---|
| 1359 |     byte b; | 
|---|
| 1360 |  | 
|---|
| 1361 |     for (dy = 0; dy < h; dy++) { | 
|---|
| 1362 |       for (dx = 0; dx < w / 8; dx++) { | 
|---|
| 1363 |         b = src[dy*rowBytes+dx]; | 
|---|
| 1364 |         for (n = 7; n >= 0; n--) | 
|---|
| 1365 |           pixels8[i++] = palette[b >> n & 1]; | 
|---|
| 1366 |       } | 
|---|
| 1367 |       for (n = 7; n >= 8 - w % 8; n--) { | 
|---|
| 1368 |         pixels8[i++] = palette[src[dy*rowBytes+dx] >> n & 1]; | 
|---|
| 1369 |       } | 
|---|
| 1370 |       i += (rfb.framebufferWidth - w); | 
|---|
| 1371 |     } | 
|---|
| 1372 |   } | 
|---|
| 1373 |  | 
|---|
| 1374 |   void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) { | 
|---|
| 1375 |  | 
|---|
| 1376 |     int dx, dy, n; | 
|---|
| 1377 |     int i = y * rfb.framebufferWidth + x; | 
|---|
| 1378 |     int rowBytes = (w + 7) / 8; | 
|---|
| 1379 |     byte b; | 
|---|
| 1380 |  | 
|---|
| 1381 |     for (dy = 0; dy < h; dy++) { | 
|---|
| 1382 |       for (dx = 0; dx < w / 8; dx++) { | 
|---|
| 1383 |         b = src[dy*rowBytes+dx]; | 
|---|
| 1384 |         for (n = 7; n >= 0; n--) | 
|---|
| 1385 |           pixels24[i++] = palette[b >> n & 1]; | 
|---|
| 1386 |       } | 
|---|
| 1387 |       for (n = 7; n >= 8 - w % 8; n--) { | 
|---|
| 1388 |         pixels24[i++] = palette[src[dy*rowBytes+dx] >> n & 1]; | 
|---|
| 1389 |       } | 
|---|
| 1390 |       i += (rfb.framebufferWidth - w); | 
|---|
| 1391 |     } | 
|---|
| 1392 |   } | 
|---|
| 1393 |  | 
|---|
| 1394 |   // | 
|---|
| 1395 |   // Decode data processed with the "Gradient" filter. | 
|---|
| 1396 |   // | 
|---|
| 1397 |  | 
|---|
| 1398 |   void decodeGradientData (int x, int y, int w, int h, byte[] buf) { | 
|---|
| 1399 |  | 
|---|
| 1400 |     int dx, dy, c; | 
|---|
| 1401 |     byte[] prevRow = new byte[w * 3]; | 
|---|
| 1402 |     byte[] thisRow = new byte[w * 3]; | 
|---|
| 1403 |     byte[] pix = new byte[3]; | 
|---|
| 1404 |     int[] est = new int[3]; | 
|---|
| 1405 |  | 
|---|
| 1406 |     int offset = y * rfb.framebufferWidth + x; | 
|---|
| 1407 |  | 
|---|
| 1408 |     for (dy = 0; dy < h; dy++) { | 
|---|
| 1409 |  | 
|---|
| 1410 |       /* First pixel in a row */ | 
|---|
| 1411 |       for (c = 0; c < 3; c++) { | 
|---|
| 1412 |         pix[c] = (byte)(prevRow[c] + buf[dy * w * 3 + c]); | 
|---|
| 1413 |         thisRow[c] = pix[c]; | 
|---|
| 1414 |       } | 
|---|
| 1415 |       pixels24[offset++] = | 
|---|
| 1416 |         (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF); | 
|---|
| 1417 |  | 
|---|
| 1418 |       /* Remaining pixels of a row */ | 
|---|
| 1419 |       for (dx = 1; dx < w; dx++) { | 
|---|
| 1420 |         for (c = 0; c < 3; c++) { | 
|---|
| 1421 |           est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) - | 
|---|
| 1422 |                     (prevRow[(dx-1) * 3 + c] & 0xFF)); | 
|---|
| 1423 |           if (est[c] > 0xFF) { | 
|---|
| 1424 |             est[c] = 0xFF; | 
|---|
| 1425 |           } else if (est[c] < 0x00) { | 
|---|
| 1426 |             est[c] = 0x00; | 
|---|
| 1427 |           } | 
|---|
| 1428 |           pix[c] = (byte)(est[c] + buf[(dy * w + dx) * 3 + c]); | 
|---|
| 1429 |           thisRow[dx * 3 + c] = pix[c]; | 
|---|
| 1430 |         } | 
|---|
| 1431 |         pixels24[offset++] = | 
|---|
| 1432 |           (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF); | 
|---|
| 1433 |       } | 
|---|
| 1434 |  | 
|---|
| 1435 |       System.arraycopy(thisRow, 0, prevRow, 0, w * 3); | 
|---|
| 1436 |       offset += (rfb.framebufferWidth - w); | 
|---|
| 1437 |     } | 
|---|
| 1438 |   } | 
|---|
| 1439 |  | 
|---|
| 1440 |   // | 
|---|
| 1441 |   // Display newly updated area of pixels. | 
|---|
| 1442 |   // | 
|---|
| 1443 |  | 
|---|
| 1444 |   void handleUpdatedPixels(int x, int y, int w, int h) { | 
|---|
| 1445 |  | 
|---|
| 1446 |     // Draw updated pixels of the off-screen image. | 
|---|
| 1447 |     pixelsSource.newPixels(x, y, w, h); | 
|---|
| 1448 |     memGraphics.setClip(x, y, w, h); | 
|---|
| 1449 |     memGraphics.drawImage(rawPixelsImage, 0, 0, null); | 
|---|
| 1450 |     memGraphics.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight); | 
|---|
| 1451 |   } | 
|---|
| 1452 |  | 
|---|
| 1453 |   // | 
|---|
| 1454 |   // Tell JVM to repaint specified desktop area. | 
|---|
| 1455 |   // | 
|---|
| 1456 |  | 
|---|
| 1457 |   void scheduleRepaint(int x, int y, int w, int h) { | 
|---|
| 1458 |     // Request repaint, deferred if necessary. | 
|---|
| 1459 |     if (rfb.framebufferWidth == scaledWidth) { | 
|---|
| 1460 |       repaint(viewer.deferScreenUpdates, x, y, w, h); | 
|---|
| 1461 |     } else { | 
|---|
| 1462 |       int sx = x * scalingFactor / 100; | 
|---|
| 1463 |       int sy = y * scalingFactor / 100; | 
|---|
| 1464 |       int sw = ((x + w) * scalingFactor + 49) / 100 - sx + 1; | 
|---|
| 1465 |       int sh = ((y + h) * scalingFactor + 49) / 100 - sy + 1; | 
|---|
| 1466 |       repaint(viewer.deferScreenUpdates, sx, sy, sw, sh); | 
|---|
| 1467 |     } | 
|---|
| 1468 |   } | 
|---|
| 1469 |  | 
|---|
| 1470 |   // | 
|---|
| 1471 |   // Handle events. | 
|---|
| 1472 |   // | 
|---|
| 1473 |  | 
|---|
| 1474 |   public void keyPressed(KeyEvent evt) { | 
|---|
| 1475 |     processLocalKeyEvent(evt); | 
|---|
| 1476 |   } | 
|---|
| 1477 |   public void keyReleased(KeyEvent evt) { | 
|---|
| 1478 |     processLocalKeyEvent(evt); | 
|---|
| 1479 |   } | 
|---|
| 1480 |   public void keyTyped(KeyEvent evt) { | 
|---|
| 1481 |     evt.consume(); | 
|---|
| 1482 |   } | 
|---|
| 1483 |  | 
|---|
| 1484 |   public void mousePressed(MouseEvent evt) { | 
|---|
| 1485 |     processLocalMouseEvent(evt, false); | 
|---|
| 1486 |   } | 
|---|
| 1487 |   public void mouseReleased(MouseEvent evt) { | 
|---|
| 1488 |     processLocalMouseEvent(evt, false); | 
|---|
| 1489 |   } | 
|---|
| 1490 |   public void mouseMoved(MouseEvent evt) { | 
|---|
| 1491 |     processLocalMouseEvent(evt, true); | 
|---|
| 1492 |   } | 
|---|
| 1493 |   public void mouseDragged(MouseEvent evt) { | 
|---|
| 1494 |     processLocalMouseEvent(evt, true); | 
|---|
| 1495 |   } | 
|---|
| 1496 |  | 
|---|
| 1497 |   public void processLocalKeyEvent(KeyEvent evt) { | 
|---|
| 1498 |     if (viewer.rfb != null && rfb.inNormalProtocol) { | 
|---|
| 1499 |       if (!inputEnabled) { | 
|---|
| 1500 |         if ((evt.getKeyChar() == 'r' || evt.getKeyChar() == 'R') && | 
|---|
| 1501 |             evt.getID() == KeyEvent.KEY_PRESSED ) { | 
|---|
| 1502 |           // Request screen update. | 
|---|
| 1503 |           try { | 
|---|
| 1504 |             rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, | 
|---|
| 1505 |                                               rfb.framebufferHeight, false); | 
|---|
| 1506 |           } catch (IOException e) { | 
|---|
| 1507 |             e.printStackTrace(); | 
|---|
| 1508 |           } | 
|---|
| 1509 |         } | 
|---|
| 1510 |       } else { | 
|---|
| 1511 |         // Input enabled. | 
|---|
| 1512 |         synchronized(rfb) { | 
|---|
| 1513 |           try { | 
|---|
| 1514 |             if (extraModifiers != 0) { | 
|---|
| 1515 |               evt.setModifiers(evt.getModifiers() | extraModifiers); | 
|---|
| 1516 |             } | 
|---|
| 1517 |             rfb.writeKeyEvent(evt); | 
|---|
| 1518 |           } catch (Exception e) { | 
|---|
| 1519 |             e.printStackTrace(); | 
|---|
| 1520 |           } | 
|---|
| 1521 |           rfb.notify(); | 
|---|
| 1522 |         } | 
|---|
| 1523 |       } | 
|---|
| 1524 |     } | 
|---|
| 1525 |     // Don't ever pass keyboard events to AWT for default processing.  | 
|---|
| 1526 |     // Otherwise, pressing Tab would switch focus to ButtonPanel etc. | 
|---|
| 1527 |     evt.consume(); | 
|---|
| 1528 |   } | 
|---|
| 1529 |  | 
|---|
| 1530 |   public void processLocalMouseEvent(MouseEvent evt, boolean moved) { | 
|---|
| 1531 |     if (viewer.rfb != null && rfb.inNormalProtocol) { | 
|---|
| 1532 |       if (moved) { | 
|---|
| 1533 |         softCursorMove(evt.getX(), evt.getY()); | 
|---|
| 1534 |       } | 
|---|
| 1535 |       if (rfb.framebufferWidth != scaledWidth) { | 
|---|
| 1536 |         int sx = (evt.getX() * 100 + scalingFactor/2) / scalingFactor; | 
|---|
| 1537 |         int sy = (evt.getY() * 100 + scalingFactor/2) / scalingFactor; | 
|---|
| 1538 |         evt.translatePoint(sx - evt.getX(), sy - evt.getY()); | 
|---|
| 1539 |       } | 
|---|
| 1540 |       synchronized(rfb) { | 
|---|
| 1541 |         try { | 
|---|
| 1542 |           rfb.writePointerEvent(evt); | 
|---|
| 1543 |         } catch (Exception e) { | 
|---|
| 1544 |           e.printStackTrace(); | 
|---|
| 1545 |         } | 
|---|
| 1546 |         rfb.notify(); | 
|---|
| 1547 |       } | 
|---|
| 1548 |     } | 
|---|
| 1549 |   } | 
|---|
| 1550 |  | 
|---|
| 1551 |   // | 
|---|
| 1552 |   // Ignored events. | 
|---|
| 1553 |   // | 
|---|
| 1554 |  | 
|---|
| 1555 |   public void mouseClicked(MouseEvent evt) {} | 
|---|
| 1556 |   public void mouseEntered(MouseEvent evt) {} | 
|---|
| 1557 |   public void mouseExited(MouseEvent evt) {} | 
|---|
| 1558 |  | 
|---|
| 1559 |  | 
|---|
| 1560 |   ////////////////////////////////////////////////////////////////// | 
|---|
| 1561 |   // | 
|---|
| 1562 |   // Handle cursor shape updates (XCursor and RichCursor encodings). | 
|---|
| 1563 |   // | 
|---|
| 1564 |  | 
|---|
| 1565 |   boolean showSoftCursor = false; | 
|---|
| 1566 |  | 
|---|
| 1567 |   MemoryImageSource softCursorSource; | 
|---|
| 1568 |   Image softCursor; | 
|---|
| 1569 |  | 
|---|
| 1570 |   int cursorX = 0, cursorY = 0; | 
|---|
| 1571 |   int cursorWidth, cursorHeight; | 
|---|
| 1572 |   int origCursorWidth, origCursorHeight; | 
|---|
| 1573 |   int hotX, hotY; | 
|---|
| 1574 |   int origHotX, origHotY; | 
|---|
| 1575 |  | 
|---|
| 1576 |   // | 
|---|
| 1577 |   // Handle cursor shape update (XCursor and RichCursor encodings). | 
|---|
| 1578 |   // | 
|---|
| 1579 |  | 
|---|
| 1580 |   synchronized void | 
|---|
| 1581 |     handleCursorShapeUpdate(int encodingType, | 
|---|
| 1582 |                             int xhot, int yhot, int width, int height) | 
|---|
| 1583 |     throws IOException { | 
|---|
| 1584 |  | 
|---|
| 1585 |     softCursorFree(); | 
|---|
| 1586 |  | 
|---|
| 1587 |     if (width * height == 0) | 
|---|
| 1588 |       return; | 
|---|
| 1589 |  | 
|---|
| 1590 |     // Ignore cursor shape data if requested by user. | 
|---|
| 1591 |     if (viewer.options.ignoreCursorUpdates) { | 
|---|
| 1592 |       int bytesPerRow = (width + 7) / 8; | 
|---|
| 1593 |       int bytesMaskData = bytesPerRow * height; | 
|---|
| 1594 |  | 
|---|
| 1595 |       if (encodingType == rfb.EncodingXCursor) { | 
|---|
| 1596 |         rfb.is.skipBytes(6 + bytesMaskData * 2); | 
|---|
| 1597 |       } else { | 
|---|
| 1598 |         // rfb.EncodingRichCursor | 
|---|
| 1599 |         rfb.is.skipBytes(width * height + bytesMaskData); | 
|---|
| 1600 |       } | 
|---|
| 1601 |       return; | 
|---|
| 1602 |     } | 
|---|
| 1603 |  | 
|---|
| 1604 |     // Decode cursor pixel data. | 
|---|
| 1605 |     softCursorSource = decodeCursorShape(encodingType, width, height); | 
|---|
| 1606 |  | 
|---|
| 1607 |     // Set original (non-scaled) cursor dimensions. | 
|---|
| 1608 |     origCursorWidth = width; | 
|---|
| 1609 |     origCursorHeight = height; | 
|---|
| 1610 |     origHotX = xhot; | 
|---|
| 1611 |     origHotY = yhot; | 
|---|
| 1612 |  | 
|---|
| 1613 |     // Create off-screen cursor image. | 
|---|
| 1614 |     createSoftCursor(); | 
|---|
| 1615 |  | 
|---|
| 1616 |     // Show the cursor. | 
|---|
| 1617 |     showSoftCursor = true; | 
|---|
| 1618 |     repaint(viewer.deferCursorUpdates, | 
|---|
| 1619 |             cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight); | 
|---|
| 1620 |   } | 
|---|
| 1621 |  | 
|---|
| 1622 |   // | 
|---|
| 1623 |   // decodeCursorShape(). Decode cursor pixel data and return | 
|---|
| 1624 |   // corresponding MemoryImageSource instance. | 
|---|
| 1625 |   // | 
|---|
| 1626 |  | 
|---|
| 1627 |   synchronized MemoryImageSource | 
|---|
| 1628 |     decodeCursorShape(int encodingType, int width, int height) | 
|---|
| 1629 |     throws IOException { | 
|---|
| 1630 |  | 
|---|
| 1631 |     int bytesPerRow = (width + 7) / 8; | 
|---|
| 1632 |     int bytesMaskData = bytesPerRow * height; | 
|---|
| 1633 |  | 
|---|
| 1634 |     int[] softCursorPixels = new int[width * height]; | 
|---|
| 1635 |  | 
|---|
| 1636 |     if (encodingType == rfb.EncodingXCursor) { | 
|---|
| 1637 |  | 
|---|
| 1638 |       // Read foreground and background colors of the cursor. | 
|---|
| 1639 |       byte[] rgb = new byte[6]; | 
|---|
| 1640 |       rfb.readFully(rgb); | 
|---|
| 1641 |       int[] colors = { (0xFF000000 | (rgb[3] & 0xFF) << 16 | | 
|---|
| 1642 |                         (rgb[4] & 0xFF) << 8 | (rgb[5] & 0xFF)), | 
|---|
| 1643 |                        (0xFF000000 | (rgb[0] & 0xFF) << 16 | | 
|---|
| 1644 |                         (rgb[1] & 0xFF) << 8 | (rgb[2] & 0xFF)) }; | 
|---|
| 1645 |  | 
|---|
| 1646 |       // Read pixel and mask data. | 
|---|
| 1647 |       byte[] pixBuf = new byte[bytesMaskData]; | 
|---|
| 1648 |       rfb.readFully(pixBuf); | 
|---|
| 1649 |       byte[] maskBuf = new byte[bytesMaskData]; | 
|---|
| 1650 |       rfb.readFully(maskBuf); | 
|---|
| 1651 |  | 
|---|
| 1652 |       // Decode pixel data into softCursorPixels[]. | 
|---|
| 1653 |       byte pixByte, maskByte; | 
|---|
| 1654 |       int x, y, n, result; | 
|---|
| 1655 |       int i = 0; | 
|---|
| 1656 |       for (y = 0; y < height; y++) { | 
|---|
| 1657 |         for (x = 0; x < width / 8; x++) { | 
|---|
| 1658 |           pixByte = pixBuf[y * bytesPerRow + x]; | 
|---|
| 1659 |           maskByte = maskBuf[y * bytesPerRow + x]; | 
|---|
| 1660 |           for (n = 7; n >= 0; n--) { | 
|---|
| 1661 |             if ((maskByte >> n & 1) != 0) { | 
|---|
| 1662 |               result = colors[pixByte >> n & 1]; | 
|---|
| 1663 |             } else { | 
|---|
| 1664 |               result = 0;       // Transparent pixel | 
|---|
| 1665 |             } | 
|---|
| 1666 |             softCursorPixels[i++] = result; | 
|---|
| 1667 |           } | 
|---|
| 1668 |         } | 
|---|
| 1669 |         for (n = 7; n >= 8 - width % 8; n--) { | 
|---|
| 1670 |           if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { | 
|---|
| 1671 |             result = colors[pixBuf[y * bytesPerRow + x] >> n & 1]; | 
|---|
| 1672 |           } else { | 
|---|
| 1673 |             result = 0;         // Transparent pixel | 
|---|
| 1674 |           } | 
|---|
| 1675 |           softCursorPixels[i++] = result; | 
|---|
| 1676 |         } | 
|---|
| 1677 |       } | 
|---|
| 1678 |  | 
|---|
| 1679 |     } else { | 
|---|
| 1680 |       // encodingType == rfb.EncodingRichCursor | 
|---|
| 1681 |  | 
|---|
| 1682 |       // Read pixel and mask data. | 
|---|
| 1683 |       byte[] pixBuf = new byte[width * height * bytesPixel]; | 
|---|
| 1684 |       rfb.readFully(pixBuf); | 
|---|
| 1685 |       byte[] maskBuf = new byte[bytesMaskData]; | 
|---|
| 1686 |       rfb.readFully(maskBuf); | 
|---|
| 1687 |  | 
|---|
| 1688 |       // Decode pixel data into softCursorPixels[]. | 
|---|
| 1689 |       byte pixByte, maskByte; | 
|---|
| 1690 |       int x, y, n, result; | 
|---|
| 1691 |       int i = 0; | 
|---|
| 1692 |       for (y = 0; y < height; y++) { | 
|---|
| 1693 |         for (x = 0; x < width / 8; x++) { | 
|---|
| 1694 |           maskByte = maskBuf[y * bytesPerRow + x]; | 
|---|
| 1695 |           for (n = 7; n >= 0; n--) { | 
|---|
| 1696 |             if ((maskByte >> n & 1) != 0) { | 
|---|
| 1697 |               if (bytesPixel == 1) { | 
|---|
| 1698 |                 result = cm8.getRGB(pixBuf[i]); | 
|---|
| 1699 |               } else { | 
|---|
| 1700 |                 result = 0xFF000000 | | 
|---|
| 1701 |                   (pixBuf[i * 4 + 2] & 0xFF) << 16 | | 
|---|
| 1702 |                   (pixBuf[i * 4 + 1] & 0xFF) << 8 | | 
|---|
| 1703 |                   (pixBuf[i * 4] & 0xFF); | 
|---|
| 1704 |               } | 
|---|
| 1705 |             } else { | 
|---|
| 1706 |               result = 0;       // Transparent pixel | 
|---|
| 1707 |             } | 
|---|
| 1708 |             softCursorPixels[i++] = result; | 
|---|
| 1709 |           } | 
|---|
| 1710 |         } | 
|---|
| 1711 |         for (n = 7; n >= 8 - width % 8; n--) { | 
|---|
| 1712 |           if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { | 
|---|
| 1713 |             if (bytesPixel == 1) { | 
|---|
| 1714 |               result = cm8.getRGB(pixBuf[i]); | 
|---|
| 1715 |             } else { | 
|---|
| 1716 |               result = 0xFF000000 | | 
|---|
| 1717 |                 (pixBuf[i * 4 + 2] & 0xFF) << 16 | | 
|---|
| 1718 |                 (pixBuf[i * 4 + 1] & 0xFF) << 8 | | 
|---|
| 1719 |                 (pixBuf[i * 4] & 0xFF); | 
|---|
| 1720 |             } | 
|---|
| 1721 |           } else { | 
|---|
| 1722 |             result = 0;         // Transparent pixel | 
|---|
| 1723 |           } | 
|---|
| 1724 |           softCursorPixels[i++] = result; | 
|---|
| 1725 |         } | 
|---|
| 1726 |       } | 
|---|
| 1727 |  | 
|---|
| 1728 |     } | 
|---|
| 1729 |  | 
|---|
| 1730 |     return new MemoryImageSource(width, height, softCursorPixels, 0, width); | 
|---|
| 1731 |   } | 
|---|
| 1732 |  | 
|---|
| 1733 |   // | 
|---|
| 1734 |   // createSoftCursor(). Assign softCursor new Image (scaled if necessary). | 
|---|
| 1735 |   // Uses softCursorSource as a source for new cursor image. | 
|---|
| 1736 |   // | 
|---|
| 1737 |  | 
|---|
| 1738 |   synchronized void | 
|---|
| 1739 |     createSoftCursor() { | 
|---|
| 1740 |  | 
|---|
| 1741 |     if (softCursorSource == null) | 
|---|
| 1742 |       return; | 
|---|
| 1743 |  | 
|---|
| 1744 |     int scaleCursor = viewer.options.scaleCursor; | 
|---|
| 1745 |     if (scaleCursor == 0 || !inputEnabled) | 
|---|
| 1746 |       scaleCursor = 100; | 
|---|
| 1747 |  | 
|---|
| 1748 |     // Save original cursor coordinates. | 
|---|
| 1749 |     int x = cursorX - hotX; | 
|---|
| 1750 |     int y = cursorY - hotY; | 
|---|
| 1751 |     int w = cursorWidth; | 
|---|
| 1752 |     int h = cursorHeight; | 
|---|
| 1753 |  | 
|---|
| 1754 |     cursorWidth = (origCursorWidth * scaleCursor + 50) / 100; | 
|---|
| 1755 |     cursorHeight = (origCursorHeight * scaleCursor + 50) / 100; | 
|---|
| 1756 |     hotX = (origHotX * scaleCursor + 50) / 100; | 
|---|
| 1757 |     hotY = (origHotY * scaleCursor + 50) / 100; | 
|---|
| 1758 |     softCursor = Toolkit.getDefaultToolkit().createImage(softCursorSource); | 
|---|
| 1759 |  | 
|---|
| 1760 |     if (scaleCursor != 100) { | 
|---|
| 1761 |       softCursor = softCursor.getScaledInstance(cursorWidth, cursorHeight, | 
|---|
| 1762 |                                                 Image.SCALE_SMOOTH); | 
|---|
| 1763 |     } | 
|---|
| 1764 |  | 
|---|
| 1765 |     if (showSoftCursor) { | 
|---|
| 1766 |       // Compute screen area to update. | 
|---|
| 1767 |       x = Math.min(x, cursorX - hotX); | 
|---|
| 1768 |       y = Math.min(y, cursorY - hotY); | 
|---|
| 1769 |       w = Math.max(w, cursorWidth); | 
|---|
| 1770 |       h = Math.max(h, cursorHeight); | 
|---|
| 1771 |  | 
|---|
| 1772 |       repaint(viewer.deferCursorUpdates, x, y, w, h); | 
|---|
| 1773 |     } | 
|---|
| 1774 |   } | 
|---|
| 1775 |  | 
|---|
| 1776 |   // | 
|---|
| 1777 |   // softCursorMove(). Moves soft cursor into a particular location. | 
|---|
| 1778 |   // | 
|---|
| 1779 |  | 
|---|
| 1780 |   synchronized void softCursorMove(int x, int y) { | 
|---|
| 1781 |     int oldX = cursorX; | 
|---|
| 1782 |     int oldY = cursorY; | 
|---|
| 1783 |     cursorX = x; | 
|---|
| 1784 |     cursorY = y; | 
|---|
| 1785 |     if (showSoftCursor) { | 
|---|
| 1786 |       repaint(viewer.deferCursorUpdates, | 
|---|
| 1787 |               oldX - hotX, oldY - hotY, cursorWidth, cursorHeight); | 
|---|
| 1788 |       repaint(viewer.deferCursorUpdates, | 
|---|
| 1789 |               cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight); | 
|---|
| 1790 |     } | 
|---|
| 1791 |   } | 
|---|
| 1792 |  | 
|---|
| 1793 |   // | 
|---|
| 1794 |   // softCursorFree(). Remove soft cursor, dispose resources. | 
|---|
| 1795 |   // | 
|---|
| 1796 |  | 
|---|
| 1797 |   synchronized void softCursorFree() { | 
|---|
| 1798 |     if (showSoftCursor) { | 
|---|
| 1799 |       showSoftCursor = false; | 
|---|
| 1800 |       softCursor = null; | 
|---|
| 1801 |       softCursorSource = null; | 
|---|
| 1802 |  | 
|---|
| 1803 |       repaint(viewer.deferCursorUpdates, | 
|---|
| 1804 |               cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight); | 
|---|
| 1805 |     } | 
|---|
| 1806 |   } | 
|---|
| 1807 | } | 
|---|