📄 vnccanvas.java
字号:
//// Copyright (C) 2004 Horizon Wimba. All Rights Reserved.// Copyright (C) 2001-2003 HorizonLive.com, Inc. All Rights Reserved.// Copyright (C) 2001,2002 Constantin Kaplinsky. All Rights Reserved.// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.//// This is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation; either version 2 of the License, or// (at your option) any later version.//// This software is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this software; if not, write to the Free Software// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,// USA.//import java.awt.*;import java.awt.event.*;import java.awt.image.*;import java.io.*;import java.lang.*;import java.util.zip.*;//// VncCanvas is a subclass of Canvas which draws a VNC desktop on it.//class VncCanvas extends Canvas implements KeyListener, MouseListener, MouseMotionListener { VncViewer viewer; RfbProto rfb; ColorModel cm8, cm24; Color[] colors; int bytesPixel; int maxWidth = 0, maxHeight = 0; int scalingFactor; int scaledWidth, scaledHeight; Image memImage; Graphics memGraphics; Image rawPixelsImage; MemoryImageSource pixelsSource; byte[] pixels8; int[] pixels24; // ZRLE encoder's data. byte[] zrleBuf; int zrleBufLen = 0; byte[] zrleTilePixels8; int[] zrleTilePixels24; ZlibInStream zrleInStream; boolean zrleRecWarningShown = false; // Zlib encoder's data. byte[] zlibBuf; int zlibBufLen = 0; Inflater zlibInflater; // Tight encoder's data. final static int tightZlibBufferSize = 512; Inflater[] tightInflaters; // Since JPEG images are loaded asynchronously, we have to remember // their position in the framebuffer. Also, this jpegRect object is // used for synchronization between the rfbThread and a JVM's thread // which decodes and loads JPEG images. Rectangle jpegRect; // True if we process keyboard and mouse events. boolean inputEnabled; // // The constructors. // public VncCanvas(VncViewer v, int maxWidth_, int maxHeight_) throws IOException { viewer = v; maxWidth = maxWidth_; maxHeight = maxHeight_; rfb = viewer.rfb; scalingFactor = viewer.options.scalingFactor; tightInflaters = new Inflater[4]; cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); colors = new Color[256]; for (int i = 0; i < 256; i++) colors[i] = new Color(cm8.getRGB(i)); setPixelFormat(); inputEnabled = false; if (!viewer.options.viewOnly) enableInput(true); // Keyboard listener is enabled even in view-only mode, to catch // 'r' or 'R' key presses used to request screen update. addKeyListener(this); } public VncCanvas(VncViewer v) throws IOException { this(v, 0, 0); } // // Callback methods to determine geometry of our Component. // public Dimension getPreferredSize() { return new Dimension(scaledWidth, scaledHeight); } public Dimension getMinimumSize() { return new Dimension(scaledWidth, scaledHeight); } public Dimension getMaximumSize() { return new Dimension(scaledWidth, scaledHeight); } // // All painting is performed here. // public void update(Graphics g) { paint(g); } public void paint(Graphics g) { synchronized(memImage) { if (rfb.framebufferWidth == scaledWidth) { g.drawImage(memImage, 0, 0, null); } else { paintScaledFrameBuffer(g); } } if (showSoftCursor) { int x0 = cursorX - hotX, y0 = cursorY - hotY; Rectangle r = new Rectangle(x0, y0, cursorWidth, cursorHeight); if (r.intersects(g.getClipBounds())) { g.drawImage(softCursor, x0, y0, null); } } } public void paintScaledFrameBuffer(Graphics g) { g.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null); } // // Override the ImageObserver interface method to handle drawing of // JPEG-encoded data. // public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { if ((infoflags & (ALLBITS | ABORT)) == 0) { return true; // We need more image data. } else { // If the whole image is available, draw it now. if ((infoflags & ALLBITS) != 0) { if (jpegRect != null) { synchronized(jpegRect) { memGraphics.drawImage(img, jpegRect.x, jpegRect.y, null); scheduleRepaint(jpegRect.x, jpegRect.y, jpegRect.width, jpegRect.height); jpegRect.notify(); } } } return false; // All image data was processed. } } // // Start/stop receiving mouse events. Keyboard events are received // even in view-only mode, because we want to map the 'r' key to the // screen refreshing function. // public synchronized void enableInput(boolean enable) { if (enable && !inputEnabled) { inputEnabled = true; addMouseListener(this); addMouseMotionListener(this); if (viewer.showControls) { viewer.buttonPanel.enableRemoteAccessControls(true); } createSoftCursor(); // scaled cursor } else if (!enable && inputEnabled) { inputEnabled = false; removeMouseListener(this); removeMouseMotionListener(this); if (viewer.showControls) { viewer.buttonPanel.enableRemoteAccessControls(false); } createSoftCursor(); // non-scaled cursor } } public void setPixelFormat() throws IOException { if (viewer.options.eightBitColors) { rfb.writeSetPixelFormat(8, 8, false, true, 7, 7, 3, 0, 3, 6); bytesPixel = 1; } else { rfb.writeSetPixelFormat(32, 24, false, true, 255, 255, 255, 16, 8, 0); bytesPixel = 4; } updateFramebufferSize(); } void updateFramebufferSize() { // Useful shortcuts. int fbWidth = rfb.framebufferWidth; int fbHeight = rfb.framebufferHeight; // Calculate scaling factor for auto scaling. if (maxWidth > 0 && maxHeight > 0) { int f1 = maxWidth * 100 / fbWidth; int f2 = maxHeight * 100 / fbHeight; scalingFactor = Math.min(f1, f2); if (scalingFactor > 100) scalingFactor = 100; System.out.println("Scaling desktop at " + scalingFactor + "%"); } // Update scaled framebuffer geometry. scaledWidth = (fbWidth * scalingFactor + 50) / 100; scaledHeight = (fbHeight * scalingFactor + 50) / 100; // Create new off-screen image either if it does not exist, or if // its geometry should be changed. It's not necessary to replace // existing image if only pixel format should be changed. if (memImage == null) { memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); memGraphics = memImage.getGraphics(); } else if (memImage.getWidth(null) != fbWidth || memImage.getHeight(null) != fbHeight) { synchronized(memImage) { memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); memGraphics = memImage.getGraphics(); } } // Images with raw pixels should be re-allocated on every change // of geometry or pixel format. if (bytesPixel == 1) { pixels24 = null; pixels8 = new byte[fbWidth * fbHeight]; pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm8, pixels8, 0, fbWidth); zrleTilePixels24 = null; zrleTilePixels8 = new byte[64 * 64]; } else { pixels8 = null; pixels24 = new int[fbWidth * fbHeight]; pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm24, pixels24, 0, fbWidth); zrleTilePixels8 = null; zrleTilePixels24 = new int[64 * 64]; } pixelsSource.setAnimated(true); rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource); // Update the size of desktop containers. if (viewer.inSeparateFrame) { if (viewer.desktopScrollPane != null) resizeDesktopFrame(); } else { setSize(scaledWidth, scaledHeight); } viewer.moveFocusToDesktop(); } void resizeDesktopFrame() { setSize(scaledWidth, scaledHeight); // FIXME: Find a better way to determine correct size of a // ScrollPane. -- const Insets insets = viewer.desktopScrollPane.getInsets(); viewer.desktopScrollPane.setSize(scaledWidth + 2 * Math.min(insets.left, insets.right), scaledHeight + 2 * Math.min(insets.top, insets.bottom)); viewer.vncFrame.pack(); // Try to limit the frame size to the screen size. Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize(); Dimension frameSize = viewer.vncFrame.getSize(); Dimension newSize = frameSize; // Reduce Screen Size by 30 pixels in each direction; // This is a (poor) attempt to account for // 1) Menu bar on Macintosh (should really also account for // Dock on OSX). Usually 22px on top of screen. // 2) Taxkbar on Windows (usually about 28 px on bottom) // 3) Other obstructions. screenSize.height -= 30; screenSize.width -= 30; boolean needToResizeFrame = false; if (frameSize.height > screenSize.height) { newSize.height = screenSize.height; needToResizeFrame = true; } if (frameSize.width > screenSize.width) { newSize.width = screenSize.width; needToResizeFrame = true; } if (needToResizeFrame) { viewer.vncFrame.setSize(newSize); } viewer.desktopScrollPane.doLayout(); } // // processNormalProtocol() - executed by the rfbThread to deal with the // RFB socket. // public void processNormalProtocol() throws Exception { // Start/stop session recording if necessary. viewer.checkRecordingStatus(); rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, rfb.framebufferHeight, false); // // main dispatch loop // while (true) { // Read message type from the server. int msgType = rfb.readServerMessageType(); // Process the message depending on its type. switch (msgType) { case RfbProto.FramebufferUpdate: rfb.readFramebufferUpdate(); boolean cursorPosReceived = false; for (int i = 0; i < rfb.updateNRects; i++) { rfb.readFramebufferUpdateRectHdr(); int rx = rfb.updateRectX, ry = rfb.updateRectY; int rw = rfb.updateRectW, rh = rfb.updateRectH; if (rfb.updateRectEncoding == rfb.EncodingLastRect) break; if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) { rfb.setFramebufferSize(rw, rh); updateFramebufferSize(); break; } if (rfb.updateRectEncoding == rfb.EncodingXCursor || rfb.updateRectEncoding == rfb.EncodingRichCursor) { handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry, rw, rh); continue; } if (rfb.updateRectEncoding == rfb.EncodingPointerPos) { softCursorMove(rx, ry); cursorPosReceived = true; continue; } rfb.startTiming(); switch (rfb.updateRectEncoding) { case RfbProto.EncodingRaw: handleRawRect(rx, ry, rw, rh); break; case RfbProto.EncodingCopyRect: handleCopyRect(rx, ry, rw, rh); break; case RfbProto.EncodingRRE: handleRRERect(rx, ry, rw, rh); break; case RfbProto.EncodingCoRRE: handleCoRRERect(rx, ry, rw, rh); break; case RfbProto.EncodingHextile: handleHextileRect(rx, ry, rw, rh); break; case RfbProto.EncodingZRLE: handleZRLERect(rx, ry, rw, rh); break; case RfbProto.EncodingZlib: handleZlibRect(rx, ry, rw, rh); break; case RfbProto.EncodingTight: handleTightRect(rx, ry, rw, rh); break; default: throw new Exception("Unknown RFB rectangle encoding " + rfb.updateRectEncoding); } rfb.stopTiming(); } boolean fullUpdateNeeded = false; // Start/stop session recording if necessary. Request full // update if a new session file was opened. if (viewer.checkRecordingStatus()) fullUpdateNeeded = true; // Defer framebuffer update request if necessary. But wake up // immediately on keyboard or mouse event. Also, don't sleep // if there is some data to receive, or if the last update // included a PointerPos message. if (viewer.deferUpdateRequests > 0 && rfb.is.available() == 0 && !cursorPosReceived) { synchronized(rfb) { try { rfb.wait(viewer.deferUpdateRequests); } catch (InterruptedException e) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -