Mercurial > hg > Members > nobuyasu > tightVNCClient
changeset 60:18a19d8a09f4
add some files. for to use Client with CUI.
author | e085711 |
---|---|
date | Fri, 05 Aug 2011 01:05:38 +0900 |
parents | d086ec1b4a7a |
children | 646e6fc14d62 |
files | src/myVncClient/AcceptThread.java src/myVncClient/CuiMyVncClient.java src/myVncClient/CuiVncCanvas.java src/myVncClient/EchoClient.java src/myVncClient/InterfaceForViewer.java src/myVncClient/MyRfbProto.java src/myVncClient/MyVncClient.java src/myVncClient/OptionNoFrame.java src/myVncClient/VncCanvas.java src/myVncClient/VncViewer.java src/myVncClient/WaitReply.java |
diffstat | 11 files changed, 3654 insertions(+), 195 deletions(-) [+] |
line wrap: on
line diff
--- a/src/myVncClient/AcceptThread.java Fri Jul 29 17:26:17 2011 +0900 +++ b/src/myVncClient/AcceptThread.java Fri Aug 05 01:05:38 2011 +0900 @@ -4,28 +4,33 @@ import java.io.InputStream; import java.io.OutputStream; -import myVncClient.MyRfbProto; +public class AcceptThread implements Runnable { + MyRfbProto rfb; + byte[] imageBytes; + int port; + + AcceptThread(MyRfbProto _rfb) { + rfb = _rfb; + } -public class AcceptThread implements Runnable { - MyRfbProto rfb; - byte[] imageBytes; + AcceptThread(MyRfbProto _rfb, int p) { + rfb = _rfb; + port = p; + } - AcceptThread(MyRfbProto _rfb ) { - rfb = _rfb; - } - public void run() { - rfb.selectPort(); - while (true) { - try { - Socket newCli = rfb.accept(); - - OutputStream os = newCli.getOutputStream(); - InputStream is = newCli.getInputStream(); - rfb.newClient(this, newCli, os, is); - } catch (IOException e) { - e.printStackTrace(); - System.out.println(e); - } - } - } -} \ No newline at end of file + public void run() { + rfb.selectPort(port); + while (true) { + try { + Socket newCli = rfb.accept(); + + OutputStream os = newCli.getOutputStream(); + InputStream is = newCli.getInputStream(); + rfb.newClient(this, newCli, os, is); + } catch (IOException e) { + e.printStackTrace(); + System.out.println(e); + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/myVncClient/CuiMyVncClient.java Fri Aug 05 01:05:38 2011 +0900 @@ -0,0 +1,881 @@ +package myVncClient; +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.net.*; + +import myVncClient.AcceptThread; +import myVncClient.OptionsNoFrame; + +public class CuiMyVncClient implements InterfaceForViewer, java.lang.Runnable { + + public static void main(String[] argv) { + CuiMyVncClient v = new CuiMyVncClient(); + v.runClient(argv, v); + + } + + private void runClient(String[] argv, CuiMyVncClient v) { + mainArgs = argv; + + v.init(null); + v.start_threads(); + + } + + + + String[] mainArgs; + String username; + + // RfbProto rfb; + MyRfbProto rfb; + Thread rfbThread; + Thread accThread; + Thread clientThread; + + Frame vncFrame; + Container vncContainer; + ScrollPane desktopScrollPane; + GridBagLayout gridbag; + ButtonPanel buttonPanel; + Label connStatusLabel; + CuiVncCanvas vc; +// OptionsFrame options; + OptionsNoFrame options; + ClipboardFrame clipboard; + RecordingFrame rec; + + // Control session recording. + Object recordingSync; + String sessionFileName; + boolean recordingActive; + boolean recordingStatusChanged; + String cursorUpdatesDef; + String eightBitColorsDef; + + // Variables read from parameter values. + String socketFactory; + String host; + int port; + String passwordParam; + boolean showControls; + boolean offerRelogin; + boolean showOfflineDesktop; + int deferScreenUpdates; + int deferCursorUpdates; + int deferUpdateRequests; + int debugStatsExcludeUpdates; + int debugStatsMeasureUpdates; + + private static final long serialVersionUID = 1L; + boolean inAnApplet = true; + boolean inSeparateFrame = false; + Socket clientSocket = null; + String parent, treenum; + private String leaderflag; + boolean runflag = false; + + EchoClient echo; + + + void checkArgs(String[] argv){ + if(argv.length > 3){ + username = argv[3]; + }else if(argv.length < 2){ + System.out.println("Please enter argv"); + System.out.println("hostname(IPaddress) port password"); + System.exit(0); + }else{ + username = argv[0]; + } + } + + // + // init() + // + + public void init(EchoClient value) { + + // readParameters(); + + readParameters(value); + + options = new OptionsNoFrame(this); + recordingSync = new Object(); + + sessionFileName = null; + recordingActive = false; + recordingStatusChanged = false; + cursorUpdatesDef = null; + eightBitColorsDef = null; + + try{ + connectAndAuthenticate(); + }catch (NoRouteToHostException e) { + fatalError("Network error: no route to server: " + host, e); + } catch (UnknownHostException e) { + fatalError("Network error: server name unknown: " + host, e); + } catch (ConnectException e) { + fatalError("Network error: could not connect to server: " + host + + ":" + port, e); + }catch(Exception e){} + + rfbThread = new Thread(this); + accThread = new Thread(new AcceptThread(rfb, 5999)); + + } + + // + // run() - executed by the rfbThread to deal with the RFB socket. + // + + public void start_threads(){ + rfbThread.start(); + } + + + public void run() { + + try { +// connectAndAuthenticate(); + doProtocolInitialisation(); +/* + htmlFile = new CreateHtmlFile(rfb, host, username); + htmlFile.createHtml(); +*/ + vc = new CuiVncCanvas(this, 0, 0); + vc.updateFramebufferSize(); + + processNormalProtocol();// main loop + + } catch (NoRouteToHostException e) { + fatalError("Network error: no route to server: " + host, e); + } catch (UnknownHostException e) { + fatalError("Network error: server name unknown: " + host, e); + } catch (ConnectException e) { + fatalError("Network error: could not connect to server: " + host + + ":" + port, e); + } catch (EOFException e) { + if (showOfflineDesktop) { + e.printStackTrace(); + System.out + .println("Network error: remote side closed connection"); + if (vc != null) { + vc.enableInput(false); + } + if (rfb != null && !rfb.closed()) + rfb.close(); + if (showControls && buttonPanel != null) { + buttonPanel.disableButtonsOnDisconnect(); + } + } else { + fatalError("Network error: remote side closed connection", e); + } + } catch (IOException e) { + String str = e.getMessage(); + if (str != null && str.length() != 0) { + fatalError("Network Error: " + str, e); + } else { + fatalError(e.toString(), e); + } + } catch (Exception e) { + String str = e.getMessage(); + if (str != null && str.length() != 0) { + fatalError("Error: " + str, e); + } else { + fatalError(e.toString(), e); + } + } + + } + + // + // Process RFB socket messages. + // If the rfbThread is being stopped, ignore any exceptions, + // otherwise rethrow the exception so it can be handled. + // + + void processNormalProtocol() throws Exception { + try { + vc.processNormalProtocol();// main loop + } catch (Exception e) { + if (rfbThread == null) { + System.out.println("Ignoring RFB socket exceptions" + + " because applet is stopping"); + } else { + throw e; + } + } + } + + // + // Connect to the RFB server and authenticate the user. + // + + void connectAndAuthenticate() throws Exception { + showConnectionStatus("Initializing..."); + + showConnectionStatus("Connecting to " + host + ", port " + port + "..."); + + // rfb = new RfbProto(host, port, this); + rfb = new MyRfbProto(host, port); + showConnectionStatus("Connected to server"); + + rfb.readVersionMsg(); + showConnectionStatus("RFB server supports protocol version " + + rfb.serverMajor + "." + rfb.serverMinor); + + rfb.writeVersionMsg(); + showConnectionStatus("Using RFB protocol version " + rfb.clientMajor + + "." + rfb.clientMinor); + + int secType = rfb.negotiateSecurity(); + int authType; + if (secType == RfbProto.SecTypeTight) { + showConnectionStatus("Enabling TightVNC protocol extensions"); + rfb.setupTunneling(); + authType = rfb.negotiateAuthenticationTight(); + } else { + authType = secType; + } + + switch (authType) { + case RfbProto.AuthNone: + showConnectionStatus("No authentication needed"); + rfb.authenticateNone(); + break; + case RfbProto.AuthVNC: + showConnectionStatus("Performing standard VNC authentication"); + if (passwordParam != null) { + rfb.authenticateVNC(passwordParam); + } else { + String pw = askPassword(); + rfb.authenticateVNC(pw); + } + break; + default: + throw new Exception("Unknown authentication scheme " + authType); + } + } + + // + // Show a message describing the connection status. + // To hide the connection status label, use (msg == null). + // + + void showConnectionStatus(String msg) { + System.out.println(msg); + } + + // + // Show an authentication panel. + // + + String askPassword() throws Exception { + /* + * showConnectionStatus(null); + * AuthPanel authPanel = new AuthPanel(this); + * + * GridBagConstraints gbc = new GridBagConstraints(); gbc.gridwidth = + * GridBagConstraints.REMAINDER; gbc.anchor = + * GridBagConstraints.NORTHWEST; gbc.weightx = 1.0; gbc.weighty = 1.0; + * gbc.ipadx = 100; gbc.ipady = 50; gridbag.setConstraints(authPanel, + * gbc); vncContainer.add(authPanel); + + + authPanel.moveFocusToDefaultField(); + vncContainer.remove(authPanel); + */ + showConnectionStatus("ask password..."); + String pw = mainArgs[2]; + return pw; + } + + // + // Do the rest of the protocol initialisation. + // + + void doProtocolInitialisation() throws IOException { + rfb.writeClientInit(); + rfb.readServerInit(); + + + System.out.println("Desktop name is " + rfb.desktopName); + System.out.println("Desktop size is " + rfb.framebufferWidth + " x " + + rfb.framebufferHeight); + + setEncodings(); + + //showConnectionStatus(null); + } + + // + // Send current encoding list to the RFB server. + // + + int[] encodingsSaved; + int nEncodingsSaved; + + void setEncodings() { + setEncodings(false); + } + + void autoSelectEncodings() { + setEncodings(true); + } + + void setEncodings(boolean autoSelectOnly) { + if (options == null || rfb == null || !rfb.inNormalProtocol) + return; + + int preferredEncoding = options.preferredEncoding; + if (preferredEncoding == -1) { + long kbitsPerSecond = rfb.kbitsPerSecond(); + if (nEncodingsSaved < 1) { + // Choose Tight or ZRLE encoding for the very first update. + System.out.println("Using Tight/ZRLE encodings"); + preferredEncoding = RfbProto.EncodingTight; + } else if (kbitsPerSecond > 2000 + && encodingsSaved[0] != RfbProto.EncodingHextile) { + // Switch to Hextile if the connection speed is above 2Mbps. + System.out.println("Throughput " + kbitsPerSecond + + " kbit/s - changing to Hextile encoding"); + preferredEncoding = RfbProto.EncodingHextile; + } else if (kbitsPerSecond < 1000 + && encodingsSaved[0] != RfbProto.EncodingTight) { + // Switch to Tight/ZRLE if the connection speed is below 1Mbps. + System.out.println("Throughput " + kbitsPerSecond + + " kbit/s - changing to Tight/ZRLE encodings"); + preferredEncoding = RfbProto.EncodingTight; + } else { + // Don't change the encoder. + if (autoSelectOnly) + return; + preferredEncoding = encodingsSaved[0]; + } + } else { + // Auto encoder selection is not enabled. + if (autoSelectOnly) + return; + } + + int[] encodings = new int[20]; + int nEncodings = 0; + + + encodings[nEncodings++] = preferredEncoding; + if (options.useCopyRect) { + encodings[nEncodings++] = RfbProto.EncodingCopyRect; + } + if (preferredEncoding != RfbProto.EncodingTight) { + encodings[nEncodings++] = RfbProto.EncodingTight; + } + if (preferredEncoding != RfbProto.EncodingZRLE) { + encodings[nEncodings++] = RfbProto.EncodingZRLE; + } + if (preferredEncoding != RfbProto.EncodingHextile) { + encodings[nEncodings++] = RfbProto.EncodingHextile; + } + if (preferredEncoding != RfbProto.EncodingZlib) { + encodings[nEncodings++] = RfbProto.EncodingZlib; + } + /* + if (preferredEncoding != RfbProto.EncodingCoRRE) { + encodings[nEncodings++] = RfbProto.EncodingCoRRE; + } + if (preferredEncoding != RfbProto.EncodingRRE) { + encodings[nEncodings++] = RfbProto.EncodingRRE; + } + + if (options.compressLevel >= 0 && options.compressLevel <= 9) { + encodings[nEncodings++] = RfbProto.EncodingCompressLevel0 + + options.compressLevel; + } + if (options.jpegQuality >= 0 && options.jpegQuality <= 9) { + encodings[nEncodings++] = RfbProto.EncodingQualityLevel0 + + options.jpegQuality; + } + if (options.requestCursorUpdates) { + encodings[nEncodings++] = RfbProto.EncodingXCursor; + encodings[nEncodings++] = RfbProto.EncodingRichCursor; + if (!options.ignoreCursorUpdates) + encodings[nEncodings++] = RfbProto.EncodingPointerPos; + } + */ + + encodings[nEncodings++] = RfbProto.EncodingLastRect; + encodings[nEncodings++] = RfbProto.EncodingNewFBSize; + + boolean encodingsWereChanged = false; + if (nEncodings != nEncodingsSaved) { + encodingsWereChanged = true; + } else { + for (int i = 0; i < nEncodings; i++) { + if (encodings[i] != encodingsSaved[i]) { + encodingsWereChanged = true; + break; + } + } + } + + if (encodingsWereChanged) { + try { + //rfb.writeSetEncodings(encodings, nEncodings); + if (vc != null) { + vc.softCursorFree(); + } + } catch (Exception e) { + e.printStackTrace(); + } + encodingsSaved = encodings; + nEncodingsSaved = nEncodings; + } + } + + // + // setCutText() - send the given cut text to the RFB server. + // + + void setCutText(String text) { + try { + if (rfb != null && rfb.inNormalProtocol) { + rfb.writeClientCutText(text); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // + // Order change in session recording status. To stop recording, pass + // null in place of the fname argument. + // + + void setRecordingStatus(String fname) { + synchronized (recordingSync) { + sessionFileName = fname; + recordingStatusChanged = true; + } + } + + // + // Start or stop session recording. Returns true if this method call + // causes recording of a new session. + // + + boolean checkRecordingStatus() throws IOException { + synchronized (recordingSync) { + if (recordingStatusChanged) { + recordingStatusChanged = false; + if (sessionFileName != null) { + startRecording(); + return true; + } else { + stopRecording(); + } + } + } + return false; + } + + // + // Start session recording. + // + + protected void startRecording() throws IOException { + synchronized (recordingSync) { + if (!recordingActive) { + // Save settings to restore them after recording the session. + cursorUpdatesDef = options.choices[options.cursorUpdatesIndex] + .getSelectedItem(); + eightBitColorsDef = options.choices[options.eightBitColorsIndex] + .getSelectedItem(); + // Set options to values suitable for recording. + options.choices[options.cursorUpdatesIndex].select("Disable"); + options.choices[options.cursorUpdatesIndex].setEnabled(false); + options.setEncodings(); + options.choices[options.eightBitColorsIndex].select("No"); + options.choices[options.eightBitColorsIndex].setEnabled(false); + options.setColorFormat(); + } else { + rfb.closeSession(); + } + + System.out.println("Recording the session in " + sessionFileName); + rfb.startSession(sessionFileName); + recordingActive = true; + } + } + + // + // Stop session recording. + // + + protected void stopRecording() throws IOException { + synchronized (recordingSync) { + if (recordingActive) { + // Restore options. + options.choices[options.cursorUpdatesIndex] + .select(cursorUpdatesDef); + options.choices[options.cursorUpdatesIndex].setEnabled(true); + options.setEncodings(); + options.choices[options.eightBitColorsIndex] + .select(eightBitColorsDef); + options.choices[options.eightBitColorsIndex].setEnabled(true); + options.setColorFormat(); + + rfb.closeSession(); + System.out.println("Session recording stopped."); + } + sessionFileName = null; + recordingActive = false; + } + } + + // + // readParameters() - read parameters from the html source or from the + // command line. On the command line, the arguments are just a sequence of + // param_name/param_value pairs where the names and values correspond to + // those expected in the html applet tag source. + // + + void readParameters(EchoClient value) { + /* + * host = readParameter("HOST", !inAnApplet); + * + * if (host == null) { host = getCodeBase().getHost(); if + * (host.equals("")) { fatalError("HOST parameter not specified"); } } + * + * port = readIntParameter("PORT", 5550); + */ + if (value == null) { + + if (clientSocket == null) { + String pHost; + if (mainArgs.length > 0) pHost = mainArgs[0]; + else pHost = "cls080.ie.u-ryukyu.ac.jp"; + echo = new EchoClient(pHost,this); + echo.openport(); + + value = echo.hostn("1"); + } else { + echo = new EchoClient(); + value = echo.Interruption(clientSocket); + } + } + // proxyからの返信で接続先を決定する + host = value.responseLine; + parent = value.parent; + if(value.treenum != null) { + treenum = value.treenum; + } else { + treenum = echo.treenum; + } + if(value.leaderflag != null) { + leaderflag = value.leaderflag; + } else { + leaderflag = echo.leaderflag; + } + System.out.println("Parent =" + parent); + System.out.println("mynumber =" + treenum); + System.out.println("connect host =" + host); + System.out.println("leaderflag(boolean) = " + leaderflag); + + echo = value; + +/* + if (host == null) { + host = getCodeBase().getHost(); + if (host.equals("")) { + fatalError("HOST parameter not specified"); + } + } +*/ + port = 5999; + // Read "ENCPASSWORD" or "PASSWORD" parameter if specified. + //readPasswordParameters(); + + String str; + if (inAnApplet) { + str = readParameter("Open New Window", false); + if (str != null && str.equalsIgnoreCase("Yes")) + inSeparateFrame = true; + } + + // "Show Controls" set to "No" disables button panel. + showControls = true; + str = readParameter("Show Controls", false); + if (str != null && str.equalsIgnoreCase("No")) + showControls = false; + + // "Offer Relogin" set to "No" disables "Login again" and "Close + // window" buttons under error messages in applet mode. + offerRelogin = true; + str = readParameter("Offer Relogin", false); + if (str != null && str.equalsIgnoreCase("No")) + offerRelogin = false; + + // Do we continue showing desktop on remote disconnect? + showOfflineDesktop = false; + str = readParameter("Show Offline Desktop", false); + if (str != null && str.equalsIgnoreCase("Yes")) + showOfflineDesktop = true; + + // Fine tuning options. + deferScreenUpdates = readIntParameter("Defer screen updates", 20); + deferCursorUpdates = readIntParameter("Defer cursor updates", 10); + deferUpdateRequests = readIntParameter("Defer update requests", 0); + + // Debugging options. + debugStatsExcludeUpdates = readIntParameter("DEBUG_XU", 0); + debugStatsMeasureUpdates = readIntParameter("DEBUG_CU", 0); + + // SocketFactory. + socketFactory = readParameter("SocketFactory", false); + } + + // + // Read password parameters. If an "ENCPASSWORD" parameter is set, + // then decrypt the password into the passwordParam string. Otherwise, + // try to read the "PASSWORD" parameter directly to passwordParam. + // + + private void readPasswordParameters() { + String encPasswordParam = readParameter("ENCPASSWORD", false); + + + if (encPasswordParam == null) { + passwordParam = readParameter("PASSWORD", false); + } else { + // ENCPASSWORD is hexascii-encoded. Decode. + byte[] pw = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int len = encPasswordParam.length() / 2; + if (len > 8) + len = 8; + for (int i = 0; i < len; i++) { + String hex = encPasswordParam.substring(i * 2, i * 2 + 2); + Integer x = new Integer(Integer.parseInt(hex, 16)); + pw[i] = x.byteValue(); + } + // Decrypt the password. + byte[] key = { 23, 82, 107, 6, 35, 78, 88, 7 }; + DesCipher des = new DesCipher(key); + des.decrypt(pw, 0, pw, 0); + passwordParam = new String(pw); + + } + } + + public String readParameter(String name, boolean required) { + for (int i = 0; i < mainArgs.length; i += 2) { + if (mainArgs[i].equalsIgnoreCase(name)) { + try { + return mainArgs[i + 1]; + } catch (Exception e) { + if (required) { + fatalError(name + " parameter not specified"); + } + return null; + } + } + } + if (required) { + fatalError(name + " parameter not specified"); + } + return null; + } + + int readIntParameter(String name, int defaultValue) { + String str = readParameter(name, false); + int result = defaultValue; + if (str != null) { + try { + result = Integer.parseInt(str); + } catch (NumberFormatException e) { + } + } + return result; + } + + // + // disconnect() - close connection to server. + // + + synchronized public void disconnect() { + System.out.println("Disconnecting"); + + if (vc != null) { + double sec = (System.currentTimeMillis() - vc.statStartTime) / 1000.0; + double rate = Math.round(vc.statNumUpdates / sec * 100) / 100.0; + int nRealRects = vc.statNumPixelRects; + int nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects; + System.out.println("Updates received: " + vc.statNumUpdates + " (" + + nRealRects + " rectangles + " + nPseudoRects + + " pseudo), " + rate + " updates/sec"); + int numRectsOther = nRealRects - vc.statNumRectsTight + - vc.statNumRectsZRLE - vc.statNumRectsHextile + - vc.statNumRectsRaw - vc.statNumRectsCopy; + System.out.println("Rectangles:" + " Tight=" + vc.statNumRectsTight + + "(JPEG=" + vc.statNumRectsTightJPEG + ") ZRLE=" + + vc.statNumRectsZRLE + " Hextile=" + + vc.statNumRectsHextile + " Raw=" + vc.statNumRectsRaw + + " CopyRect=" + vc.statNumRectsCopy + " other=" + + numRectsOther); + + int raw = vc.statNumBytesDecoded; + int compressed = vc.statNumBytesEncoded; + if (compressed > 0) { + double ratio = Math.round((double) raw / compressed * 1000) / 1000.0; + System.out.println("Pixel data: " + vc.statNumBytesDecoded + + " bytes, " + vc.statNumBytesEncoded + + " compressed, ratio " + ratio); + } + } + + if (rfb != null && !rfb.closed()) + rfb.close(); +// options.dispose(); + clipboard.dispose(); + if (rec != null) + rec.dispose(); + + System.exit(0); + + } + + // + // fatalError() - print out a fatal error message. + // FIXME: Do we really need two versions of the fatalError() method? + // + + synchronized public void fatalError(String str) { + System.out.println(str); + System.exit(1); + } + + synchronized public void fatalError(String str, Exception e) { + + if (rfb != null && rfb.closed()) { + // Not necessary to show error message if the error was caused + // by I/O problems after the rfb.close() method call. + System.out.println("RFB thread finished"); + return; + } + + System.out.println(str); + e.printStackTrace(); + + if (rfb != null) + rfb.close(); + + System.exit(1); + + } + + // + // Show message text and optionally "Relogin" and "Close" buttons. + // + + void showMessage(String msg) { + vncContainer.removeAll(); + + Label errLabel = new Label(msg, Label.CENTER); + errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); + + if (offerRelogin) { + /* + * Panel gridPanel = new Panel(new GridLayout(0, 1)); Panel + * outerPanel = new Panel(new FlowLayout(FlowLayout.LEFT)); + * outerPanel.add(gridPanel); vncContainer.setLayout(new + * FlowLayout(FlowLayout.LEFT, 30, 16)); + * vncContainer.add(outerPanel); Panel textPanel = new Panel(new + * FlowLayout(FlowLayout.CENTER)); textPanel.add(errLabel); + * gridPanel.add(textPanel); gridPanel.add(new ReloginPanel(this)); + */ + } else { + /* + * vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30)); + * vncContainer.add(errLabel); + */ + } + + } + + // + // Stop the applet. + // Main applet thread will terminate on first exception + // after seeing that rfbThread has been set to null. + // + + public void stop() { + System.out.println("Stopping applet"); + rfbThread = null; + } + + // + // This method is called before the applet is destroyed. + // + + public void destroy() { + System.out.println("Destroying applet"); + + vncContainer.removeAll(); +// options.dispose(); + clipboard.dispose(); + if (rec != null) + rec.dispose(); + if (rfb != null && !rfb.closed()) + rfb.close(); + } + + // + // Start/stop receiving mouse events. + // + + public void enableInput(boolean enable) { + vc.enableInput(enable); + } + + // + // Close application properly on window close event. + // + + public void windowClosing(WindowEvent evt) { + System.out.println("Closing window"); + if (rfb != null) + disconnect(); + + vncContainer.hide(); + + } + + // + // Ignore window events we're not interested in. + // + + public void windowActivated(WindowEvent evt) { + } + + public void windowDeactivated(WindowEvent evt) { + } + + public void windowOpened(WindowEvent evt) { + } + + public void windowClosed(WindowEvent evt) { + } + + public void windowIconified(WindowEvent evt) { + } + + public void windowDeiconified(WindowEvent evt) { + } + + public void setClientSocket(Socket sock) { + clientSocket = sock; + } + + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/myVncClient/CuiVncCanvas.java Fri Aug 05 01:05:38 2011 +0900 @@ -0,0 +1,1935 @@ +package myVncClient; +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import java.io.*; +import java.lang.*; +import java.nio.ByteBuffer; +import java.util.zip.*; + +import java.net.Socket; + +import javax.imageio.ImageIO; + +// +//VncCanvas is a subclass of Canvas which draws a VNC desktop on it. +// + +class CuiVncCanvas extends Canvas implements KeyListener, MouseListener, + MouseMotionListener { + + CuiMyVncClient viewer; + MyRfbProto rfb; + ColorModel cm8, cm24; + Color[] colors; + int bytesPixel; + + int maxWidth = 0, maxHeight = 0; + int scalingFactor; + int scaledWidth, scaledHeight; + +// Image memImage; + BufferedImage memImage; + Graphics memGraphics; + + Image rawPixelsImage; +// BufferedImage rawPixelsImage; + BufferedImage bimg; + + MemoryImageSource pixelsSource; + byte[] pixels8; + int[] pixels24; + + // Update statistics. + long statStartTime; // time on first framebufferUpdateRequest + int statNumUpdates; // counter for FramebufferUpdate messages + int statNumTotalRects; // rectangles in FramebufferUpdate messages + int statNumPixelRects; // the same, but excluding pseudo-rectangles + int statNumRectsTight; // Tight-encoded rectangles (including JPEG) + int statNumRectsTightJPEG; // JPEG-compressed Tight-encoded rectangles + int statNumRectsZRLE; // ZRLE-encoded rectangles + int statNumRectsHextile; // Hextile-encoded rectangles + int statNumRectsRaw; // Raw-encoded rectangles + int statNumRectsCopy; // CopyRect rectangles + int statNumBytesEncoded; // number of bytes in updates, as received + int statNumBytesDecoded; // number of bytes, as if Raw encoding was used + + // 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; + private int b = 0; + + + + // + // The constructors. + // + + public CuiVncCanvas(CuiMyVncClient v, int maxWidth_, int maxHeight_) + throws IOException { + + viewer = v; + maxWidth = maxWidth_; + maxHeight = maxHeight_; + + rfb = viewer.rfb; + + 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; + // Keyboard listener is enabled even in view-only mode, to catch + // 'r' or 'R' key presses used to request screen update. + addKeyListener(this); + } + + public CuiVncCanvas(CuiMyVncClient 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(); + } + } +*/ + memImage = new BufferedImage(rfb.framebufferWidth, rfb.framebufferHeight, BufferedImage.TYPE_INT_RGB ); + 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); +// rawPixelsImage = (BufferedImage) Toolkit.getDefaultToolkit().createImage(pixelsSource); + + } + + 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); + + resetStats(); + boolean statsRestarted = false; + + // + // main dispatch loop + // + + long count = 0; + while (true) { + System.out.println("\ncount=" + count); + + count++; + + /** + * read Data from parents and send Data to Client. + */ + rfb.sendDataToClient(); + + long numBytesRead = rfb.getNumBytesRead(); + + // Read message type from the server. + int msgType = rfb.readServerMessageType(); + + // Process the message depending on its type. + switch (msgType) { + case MyRfbProto.SpeedCheckMillis: + rfb.readSpeedCheck(); + + break; + case RfbProto.FramebufferUpdate: + + if (statNumUpdates == viewer.debugStatsExcludeUpdates + && !statsRestarted) { + resetStats(); + statsRestarted = true; + } else if (statNumUpdates == viewer.debugStatsMeasureUpdates + && statsRestarted) { + viewer.disconnect(); + } + + rfb.readFramebufferUpdate(); + statNumUpdates++; + + boolean cursorPosReceived = false; + + for (int i = 0; i < rfb.updateNRects; i++) { + + rfb.readFramebufferUpdateRectHdr(); + statNumTotalRects++; + 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; + } + + long numBytesReadBefore = rfb.getNumBytesRead(); + + rfb.startTiming(); + + switch (rfb.updateRectEncoding) { + case RfbProto.EncodingRaw: + statNumRectsRaw++; + handleRawRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingCopyRect: + statNumRectsCopy++; + 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: + statNumRectsHextile++; + handleHextileRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingZRLE: + statNumRectsZRLE++; + handleZRLERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingZlib: + handleZlibRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingTight: + statNumRectsTight++; + handleTightRect(rx, ry, rw, rh); + break; + default: + throw new Exception("Unknown RFB rectangle encoding " + + rfb.updateRectEncoding); + } + + rfb.stopTiming(); + + statNumPixelRects++; + statNumBytesDecoded += rw * rh * bytesPixel; + statNumBytesEncoded += (int) (rfb.getNumBytesRead() - numBytesReadBefore); + } + + 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.available() == 0 + && !cursorPosReceived) { + synchronized (rfb) { + try { + rfb.wait(viewer.deferUpdateRequests); + } catch (InterruptedException e) { + } + } + } + + viewer.autoSelectEncodings(); + + // Before requesting framebuffer update, check if the pixel + // format should be changed. +/* + if (viewer.options.eightBitColors != (bytesPixel == 1)) { + // Pixel format should be changed. + setPixelFormat(); + fullUpdateNeeded = true; + } +*/ + // Request framebuffer update if needed. + int w = rfb.framebufferWidth; + int h = rfb.framebufferHeight; + rfb.writeFramebufferUpdateRequest(0, 0, w, h, !fullUpdateNeeded); + + break; + + case RfbProto.SetColourMapEntries: + throw new Exception("Can't handle SetColourMapEntries message"); + + case RfbProto.Bell: + Toolkit.getDefaultToolkit().beep(); + break; + + case RfbProto.ServerCutText: + String s = rfb.readServerCutText(); + viewer.clipboard.setCutText(s); + break; + + default: + throw new Exception("Unknown RFB message type " + msgType); + } + + int bufSize = (int)(rfb.getNumBytesRead() - numBytesRead); +// System.out.println("bufSize="+bufSize); +// rfb.bufResetSend(bufSize); + + + + if(rfb.createBimgFlag){ +// bimg = createBufferedImage(rawPixelsImage); + bimg = createBufferedImage(memImage); + //bimg(BufferedImage) -> rfb.pngBytes(byte[]) + rfb.createPngBytes(bimg); + rfb.sendPngImage(); + rfb.createBimgFlag = false; + } + + +/* + boolean result = false; + try{ + result = ImageIO.write(bimg, "png", new File("sample.png")); + }catch(Exception e){ + e.printStackTrace(); + result = false; + } +*/ + } + } + + // + // Handle a raw rectangle. The second form with paint==false is used + // by the Hextile decoder for raw-encoded tiles. + // + + void handleRawRect(int x, int y, int w, int h) throws IOException { + handleRawRect(x, y, w, h, true); + } + + void handleRawRect(int x, int y, int w, int h, boolean paint) + throws IOException { + + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w); + if (rfb.rec != null) { + rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w); + } + } + } else { + byte[] buf = new byte[w * 4]; + int i, offset; + for (int dy = y; dy < y + h; dy++) { + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(buf); + } + + offset = dy * rfb.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 | + (buf[i * 4 + 1] & 0xFF) << 8 | + (buf[i * 4] & 0xFF); + } + + } + } + + handleUpdatedPixels(x, y, w, h); +// if (paint) scheduleRepaint(x, y, w, h); + + } + + // + // Handle a CopyRect rectangle. + // + + void handleCopyRect(int x, int y, int w, int h) throws IOException { + + rfb.readCopyRect(); + memGraphics.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY, w, h, x + - rfb.copyRectSrcX, y - rfb.copyRectSrcY); + + scheduleRepaint(x, y, w, h); + } + + // + // Handle an RRE-encoded rectangle. + // + + void handleRRERect(int x, int y, int w, int h) throws IOException { + + int nSubrects = rfb.readU32(); + + byte[] bg_buf = new byte[bytesPixel]; + rfb.readFully(bg_buf); + Color pixel; + if (bytesPixel == 1) { + pixel = colors[bg_buf[0] & 0xFF]; + } else { + pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, + bg_buf[0] & 0xFF); + } + memGraphics.setColor(pixel); + memGraphics.fillRect(x, y, w, h); + + byte[] buf = new byte[nSubrects * (bytesPixel + 8)]; + rfb.readFully(buf); + DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf)); + + if (rfb.rec != null) { + rfb.rec.writeIntBE(nSubrects); + rfb.rec.write(bg_buf); + rfb.rec.write(buf); + } + + int sx, sy, sw, sh; + + for (int j = 0; j < nSubrects; j++) { + if (bytesPixel == 1) { + pixel = colors[ds.readUnsignedByte()]; + } else { + ds.skip(4); + pixel = new Color(buf[j * 12 + 2] & 0xFF, + buf[j * 12 + 1] & 0xFF, buf[j * 12] & 0xFF); + } + sx = x + ds.readUnsignedShort(); + sy = y + ds.readUnsignedShort(); + sw = ds.readUnsignedShort(); + sh = ds.readUnsignedShort(); + + memGraphics.setColor(pixel); + memGraphics.fillRect(sx, sy, sw, sh); + } + + scheduleRepaint(x, y, w, h); + } + + // + // Handle a CoRRE-encoded rectangle. + // + + void handleCoRRERect(int x, int y, int w, int h) throws IOException { + int nSubrects = rfb.readU32(); + + byte[] bg_buf = new byte[bytesPixel]; + rfb.readFully(bg_buf); + Color pixel; + if (bytesPixel == 1) { + pixel = colors[bg_buf[0] & 0xFF]; + } else { + pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, + bg_buf[0] & 0xFF); + } + memGraphics.setColor(pixel); + memGraphics.fillRect(x, y, w, h); + + byte[] buf = new byte[nSubrects * (bytesPixel + 4)]; + rfb.readFully(buf); + + if (rfb.rec != null) { + rfb.rec.writeIntBE(nSubrects); + rfb.rec.write(bg_buf); + rfb.rec.write(buf); + } + + int sx, sy, sw, sh; + int i = 0; + + for (int j = 0; j < nSubrects; j++) { + if (bytesPixel == 1) { + pixel = colors[buf[i++] & 0xFF]; + } else { + pixel = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF, + buf[i] & 0xFF); + i += 4; + } + sx = x + (buf[i++] & 0xFF); + sy = y + (buf[i++] & 0xFF); + sw = buf[i++] & 0xFF; + sh = buf[i++] & 0xFF; + + memGraphics.setColor(pixel); + memGraphics.fillRect(sx, sy, sw, sh); + } + + scheduleRepaint(x, y, w, h); + } + + // + // Handle a Hextile-encoded rectangle. + // + + // These colors should be kept between handleHextileSubrect() calls. + private Color hextile_bg, hextile_fg; + + void handleHextileRect(int x, int y, int w, int h) throws IOException { + + hextile_bg = new Color(0); + hextile_fg = new Color(0); + + for (int ty = y; ty < y + h; ty += 16) { + int th = 16; + if (y + h - ty < 16) + th = y + h - ty; + + for (int tx = x; tx < x + w; tx += 16) { + int tw = 16; + if (x + w - tx < 16) + tw = x + w - tx; + + handleHextileSubrect(tx, ty, tw, th); + } + + // Finished with a row of tiles, now let's show it. + scheduleRepaint(x, y, w, h); + } + } + + // + // Handle one tile in the Hextile-encoded data. + // + + void handleHextileSubrect(int tx, int ty, int tw, int th) + throws IOException { + + int subencoding = rfb.readU8(); + if (rfb.rec != null) { + rfb.rec.writeByte(subencoding); + } + + // Is it a raw-encoded sub-rectangle? + if ((subencoding & rfb.HextileRaw) != 0) { + handleRawRect(tx, ty, tw, th, false); + return; + } + + // Read and draw the background if specified. + byte[] cbuf = new byte[bytesPixel]; + if ((subencoding & rfb.HextileBackgroundSpecified) != 0) { + rfb.readFully(cbuf); + if (bytesPixel == 1) { + hextile_bg = colors[cbuf[0] & 0xFF]; + } else { + hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, + cbuf[0] & 0xFF); + } + if (rfb.rec != null) { + rfb.rec.write(cbuf); + } + } + memGraphics.setColor(hextile_bg); + memGraphics.fillRect(tx, ty, tw, th); + + // Read the foreground color if specified. + if ((subencoding & rfb.HextileForegroundSpecified) != 0) { + rfb.readFully(cbuf); + if (bytesPixel == 1) { + hextile_fg = colors[cbuf[0] & 0xFF]; + } else { + hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, + cbuf[0] & 0xFF); + } + if (rfb.rec != null) { + rfb.rec.write(cbuf); + } + } + + // Done with this tile if there is no sub-rectangles. + if ((subencoding & rfb.HextileAnySubrects) == 0) + return; + + int nSubrects = rfb.readU8(); + int bufsize = nSubrects * 2; + if ((subencoding & rfb.HextileSubrectsColoured) != 0) { + bufsize += nSubrects * bytesPixel; + } + byte[] buf = new byte[bufsize]; + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.writeByte(nSubrects); + rfb.rec.write(buf); + } + + int b1, b2, sx, sy, sw, sh; + int i = 0; + + if ((subencoding & rfb.HextileSubrectsColoured) == 0) { + + // Sub-rectangles are all of the same color. + memGraphics.setColor(hextile_fg); + for (int j = 0; j < nSubrects; j++) { + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.fillRect(sx, sy, sw, sh); + } + } else if (bytesPixel == 1) { + + // BGR233 (8-bit color) version for colored sub-rectangles. + for (int j = 0; j < nSubrects; j++) { + hextile_fg = colors[buf[i++] & 0xFF]; + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.setColor(hextile_fg); + memGraphics.fillRect(sx, sy, sw, sh); + } + + } else { + + // Full-color (24-bit) version for colored sub-rectangles. + for (int j = 0; j < nSubrects; j++) { + hextile_fg = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF, + buf[i] & 0xFF); + i += 4; + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.setColor(hextile_fg); + memGraphics.fillRect(sx, sy, sw, sh); + } + + } + } + + // + // Handle a ZRLE-encoded rectangle. + // + // FIXME: Currently, session recording is not fully supported for ZRLE. + // + + void handleZRLERect(int x, int y, int w, int h) throws Exception { + + if (zrleInStream == null) + zrleInStream = new ZlibInStream(); + + int nBytes = rfb.readU32(); + if (nBytes > 64 * 1024 * 1024) + throw new Exception("ZRLE decoder: illegal compressed data size"); + + if (zrleBuf == null || zrleBufLen < nBytes) { + zrleBufLen = nBytes + 4096; + zrleBuf = new byte[zrleBufLen]; + } + + // FIXME: Do not wait for all the data before decompression. + rfb.readFully(zrleBuf, 0, nBytes); + + if (rfb.rec != null) { + if (rfb.recordFromBeginning) { + rfb.rec.writeIntBE(nBytes); + rfb.rec.write(zrleBuf, 0, nBytes); + } else if (!zrleRecWarningShown) { + System.out.println("Warning: ZRLE session can be recorded" + + " only from the beginning"); + System.out.println("Warning: Recorded file may be corrupted"); + zrleRecWarningShown = true; + } + + } + + zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes); + + for (int ty = y; ty < y + h; ty += 64) { + + int th = Math.min(y + h - ty, 64); + + for (int tx = x; tx < x + w; tx += 64) { + + int tw = Math.min(x + w - tx, 64); + + int mode = zrleInStream.readU8(); + boolean rle = (mode & 128) != 0; + int palSize = mode & 127; + int[] palette = new int[128]; + + readZrlePalette(palette, palSize); + + if (palSize == 1) { + int pix = palette[0]; + Color c = (bytesPixel == 1) ? colors[pix] : new Color( + 0xFF000000 | pix); + memGraphics.setColor(c); + memGraphics.fillRect(tx, ty, tw, th); + continue; + } + + if (!rle) { + if (palSize == 0) { + readZrleRawPixels(tw, th); + } else { + readZrlePackedPixels(tw, th, palette, palSize); + } + } else { + if (palSize == 0) { + readZrlePlainRLEPixels(tw, th); + } else { + readZrlePackedRLEPixels(tw, th, palette); + } + } + handleUpdatedZrleTile(tx, ty, tw, th); + } + } + + zrleInStream.reset(); + + scheduleRepaint(x, y, w, h); + } + + int readPixel(InStream is) throws Exception { + int pix; + + if (bytesPixel == 1) { + + pix = is.readU8(); + } else { + int p1 = is.readU8(); + int p2 = is.readU8(); + int p3 = is.readU8(); + pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF); + } + return pix; + } + + void readPixels(InStream is, int[] dst, int count) throws Exception { + int pix; + if (bytesPixel == 1) { + byte[] buf = new byte[count]; + is.readBytes(buf, 0, count); + for (int i = 0; i < count; i++) { + dst[i] = (int) buf[i] & 0xFF; + } + } else { + byte[] buf = new byte[count * 3]; + is.readBytes(buf, 0, count * 3); + for (int i = 0; i < count; i++) { + dst[i] = ((buf[i * 3 + 2] & 0xFF) << 16 + | (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3] & 0xFF)); + } + } + } + + void readZrlePalette(int[] palette, int palSize) throws Exception { + readPixels(zrleInStream, palette, palSize); + } + + void readZrleRawPixels(int tw, int th) throws Exception { + if (bytesPixel == 1) { + zrleInStream.readBytes(zrleTilePixels8, 0, tw * th); + } else { + readPixels(zrleInStream, zrleTilePixels24, tw * th); // / + } + } + + void readZrlePackedPixels(int tw, int th, int[] palette, int palSize) + throws Exception { + + int bppp = ((palSize > 16) ? 8 : ((palSize > 4) ? 4 + : ((palSize > 2) ? 2 : 1))); + int ptr = 0; + + for (int i = 0; i < th; i++) { + int eol = ptr + tw; + int b = 0; + int nbits = 0; + + while (ptr < eol) { + if (nbits == 0) { + b = zrleInStream.readU8(); + nbits = 8; + } + nbits -= bppp; + int index = (b >> nbits) & ((1 << bppp) - 1) & 127; + if (bytesPixel == 1) { + zrleTilePixels8[ptr++] = (byte) palette[index]; + } else { + zrleTilePixels24[ptr++] = palette[index]; + } + } + } + } + + void readZrlePlainRLEPixels(int tw, int th) throws Exception { + int ptr = 0; + int end = ptr + tw * th; + while (ptr < end) { + int pix = readPixel(zrleInStream); + int len = 1; + int b; + do { + b = zrleInStream.readU8(); + len += b; + } while (b == 255); + + if (!(len <= end - ptr)) + throw new Exception("ZRLE decoder: assertion failed" + + " (len <= end-ptr)"); + + if (bytesPixel == 1) { + while (len-- > 0) + zrleTilePixels8[ptr++] = (byte) pix; + } else { + while (len-- > 0) + zrleTilePixels24[ptr++] = pix; + } + } + } + + void readZrlePackedRLEPixels(int tw, int th, int[] palette) + throws Exception { + + int ptr = 0; + int end = ptr + tw * th; + while (ptr < end) { + int index = zrleInStream.readU8(); + int len = 1; + if ((index & 128) != 0) { + int b; + do { + b = zrleInStream.readU8(); + len += b; + } while (b == 255); + + if (!(len <= end - ptr)) + throw new Exception("ZRLE decoder: assertion failed" + + " (len <= end - ptr)"); + } + + index &= 127; + int pix = palette[index]; + + if (bytesPixel == 1) { + while (len-- > 0) + zrleTilePixels8[ptr++] = (byte) pix; + } else { + while (len-- > 0) + zrleTilePixels24[ptr++] = pix; + } + } + } + + // + // Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update. + // + + void handleUpdatedZrleTile(int x, int y, int w, int h) { + Object src, dst; + if (bytesPixel == 1) { + src = zrleTilePixels8; + dst = pixels8; + } else { + src = zrleTilePixels24; + dst = pixels24; + } + int offsetSrc = 0; + int offsetDst = (y * rfb.framebufferWidth + x); + for (int j = 0; j < h; j++) { + System.arraycopy(src, offsetSrc, dst, offsetDst, w); + offsetSrc += w; + offsetDst += rfb.framebufferWidth; + } + handleUpdatedPixels(x, y, w, h); + } + + // + // Handle a Zlib-encoded rectangle. + // + + void handleZlibRect(int x, int y, int w, int h) throws Exception { + + int nBytes = rfb.readU32(); + + if (zlibBuf == null || zlibBufLen < nBytes) { + zlibBufLen = nBytes * 2; + zlibBuf = new byte[zlibBufLen]; + } + + rfb.readFully(zlibBuf, 0, nBytes); + + if (rfb.rec != null && rfb.recordFromBeginning) { + rfb.rec.writeIntBE(nBytes); + rfb.rec.write(zlibBuf, 0, nBytes); + } + + if (zlibInflater == null) { + zlibInflater = new Inflater(); + } + zlibInflater.setInput(zlibBuf, 0, nBytes); + + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + zlibInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w); + if (rfb.rec != null && !rfb.recordFromBeginning) + rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w); + } + } else { + byte[] buf = new byte[w * 4]; + int i, offset; + for (int dy = y; dy < y + h; dy++) { + zlibInflater.inflate(buf); + offset = dy * rfb.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 + | (buf[i * 4 + 1] & 0xFF) << 8 + | (buf[i * 4] & 0xFF); + } + if (rfb.rec != null && !rfb.recordFromBeginning) + rfb.rec.write(buf); + } + } + + handleUpdatedPixels(x, y, w, h); + scheduleRepaint(x, y, w, h); + } + + // + // Handle a Tight-encoded rectangle. + // + + void handleTightRect(int x, int y, int w, int h) throws Exception { + + int comp_ctl = rfb.readU8(); + if (rfb.rec != null) { + if (rfb.recordFromBeginning || comp_ctl == (rfb.TightFill << 4) + || comp_ctl == (rfb.TightJpeg << 4)) { + // Send data exactly as received. + rfb.rec.writeByte(comp_ctl); + } else { + // Tell the decoder to flush each of the four zlib streams. + rfb.rec.writeByte(comp_ctl | 0x0F); + } + } + + // Flush zlib streams if we are told by the server to do so. + for (int stream_id = 0; stream_id < 4; stream_id++) { + if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) { + tightInflaters[stream_id] = null; + } + comp_ctl >>= 1; + } + + // Check correctness of subencoding value. + if (comp_ctl > rfb.TightMaxSubencoding) { + throw new Exception("Incorrect tight subencoding: " + comp_ctl); + } + + // Handle solid-color rectangles. + if (comp_ctl == rfb.TightFill) { + + if (bytesPixel == 1) { + int idx = rfb.readU8(); + memGraphics.setColor(colors[idx]); + if (rfb.rec != null) { + rfb.rec.writeByte(idx); + } + } else { + byte[] buf = new byte[3]; + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(buf); + } + Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16 + | (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF)); + memGraphics.setColor(bg); + } + memGraphics.fillRect(x, y, w, h); + scheduleRepaint(x, y, w, h); + return; + + } + + if (comp_ctl == rfb.TightJpeg) { + + statNumRectsTightJPEG++; + + // Read JPEG data. + byte[] jpegData = new byte[rfb.readCompactLen()]; + rfb.readFully(jpegData); + if (rfb.rec != null) { + if (!rfb.recordFromBeginning) { + rfb.recordCompactLen(jpegData.length); + } + rfb.rec.write(jpegData); + } + + // Create an Image object from the JPEG data. + Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData); + + // Remember the rectangle where the image should be drawn. + jpegRect = new Rectangle(x, y, w, h); + + // Let the imageUpdate() method do the actual drawing, here just + // wait until the image is fully loaded and drawn. + synchronized (jpegRect) { + Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1, + this); + try { + // Wait no longer than three seconds. + jpegRect.wait(3000); + } catch (InterruptedException e) { + throw new Exception("Interrupted while decoding JPEG image"); + } + } + + // Done, jpegRect is not needed any more. + jpegRect = null; + return; + + } + + // Read filter id and parameters. + int numColors = 0, rowSize = w; + byte[] palette8 = new byte[2]; + int[] palette24 = new int[256]; + boolean useGradient = false; + if ((comp_ctl & rfb.TightExplicitFilter) != 0) { + int filter_id = rfb.readU8(); + if (rfb.rec != null) { + rfb.rec.writeByte(filter_id); + } + if (filter_id == rfb.TightFilterPalette) { + numColors = rfb.readU8() + 1; + if (rfb.rec != null) { + rfb.rec.writeByte(numColors - 1); + } + if (bytesPixel == 1) { + if (numColors != 2) { + throw new Exception("Incorrect tight palette size: " + + numColors); + } + rfb.readFully(palette8); + if (rfb.rec != null) { + rfb.rec.write(palette8); + } + } else { + byte[] buf = new byte[numColors * 3]; + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(buf); + } + for (int i = 0; i < numColors; i++) { + palette24[i] = ((buf[i * 3] & 0xFF) << 16 + | (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3 + 2] & 0xFF)); + } + } + if (numColors == 2) + rowSize = (w + 7) / 8; + } else if (filter_id == rfb.TightFilterGradient) { + useGradient = true; + } else if (filter_id != rfb.TightFilterCopy) { + throw new Exception("Incorrect tight filter id: " + filter_id); + } + } + if (numColors == 0 && bytesPixel == 4) + rowSize *= 3; + + // Read, optionally uncompress and decode data. + int dataSize = h * rowSize; + if (dataSize < rfb.TightMinToCompress) { + // Data size is small - not compressed with zlib. + if (numColors != 0) { + // Indexed colors. + byte[] indexedData = new byte[dataSize]; + rfb.readFully(indexedData); + if (rfb.rec != null) { + rfb.rec.write(indexedData); + } + if (numColors == 2) { + // Two colors. + if (bytesPixel == 1) { + decodeMonoData(x, y, w, h, indexedData, palette8); + } else { + decodeMonoData(x, y, w, h, indexedData, palette24); + } + } else { + // 3..255 colors (assuming bytesPixel == 4). + int i = 0; + for (int dy = y; dy < y + h; dy++) { + for (int dx = x; dx < x + w; dx++) { + pixels24[dy * rfb.framebufferWidth + dx] = palette24[indexedData[i++] & 0xFF]; + } + } + } + } else if (useGradient) { + // "Gradient"-processed data + byte[] buf = new byte[w * h * 3]; + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(buf); + } + decodeGradientData(x, y, w, h, buf); + } else { + // Raw truecolor data. + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w); + if (rfb.rec != null) { + rfb.rec.write(pixels8, dy * rfb.framebufferWidth + + x, w); + } + } + } else { + byte[] buf = new byte[w * 3]; + int i, offset; + for (int dy = y; dy < y + h; dy++) { + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(buf); + } + offset = dy * rfb.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[offset + i] = (buf[i * 3] & 0xFF) << 16 + | (buf[i * 3 + 1] & 0xFF) << 8 + | (buf[i * 3 + 2] & 0xFF); + } + } + } + } + } else { + // Data was compressed with zlib. + int zlibDataLen = rfb.readCompactLen(); + byte[] zlibData = new byte[zlibDataLen]; + rfb.readFully(zlibData); + if (rfb.rec != null && rfb.recordFromBeginning) { + rfb.rec.write(zlibData); + } + int stream_id = comp_ctl & 0x03; + if (tightInflaters[stream_id] == null) { + tightInflaters[stream_id] = new Inflater(); + } + Inflater myInflater = tightInflaters[stream_id]; + myInflater.setInput(zlibData); + byte[] buf = new byte[dataSize]; + myInflater.inflate(buf); + if (rfb.rec != null && !rfb.recordFromBeginning) { + rfb.recordCompressedData(buf); + } + + if (numColors != 0) { + // Indexed colors. + if (numColors == 2) { + // Two colors. + if (bytesPixel == 1) { + decodeMonoData(x, y, w, h, buf, palette8); + } else { + decodeMonoData(x, y, w, h, buf, palette24); + } + } else { + // More than two colors (assuming bytesPixel == 4). + int i = 0; + for (int dy = y; dy < y + h; dy++) { + for (int dx = x; dx < x + w; dx++) { + pixels24[dy * rfb.framebufferWidth + dx] = palette24[buf[i++] & 0xFF]; + } + } + } + } else if (useGradient) { + // Compressed "Gradient"-filtered data (assuming bytesPixel == + // 4). + decodeGradientData(x, y, w, h, buf); + } else { + // Compressed truecolor data. + if (bytesPixel == 1) { + int destOffset = y * rfb.framebufferWidth + x; + for (int dy = 0; dy < h; dy++) { + System.arraycopy(buf, dy * w, pixels8, destOffset, w); + destOffset += rfb.framebufferWidth; + } + } else { + int srcOffset = 0; + int destOffset, i; + for (int dy = 0; dy < h; dy++) { + myInflater.inflate(buf); + destOffset = (y + dy) * rfb.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16 + | (buf[srcOffset + 1] & 0xFF) << 8 + | (buf[srcOffset + 2] & 0xFF); + srcOffset += 3; + } + } + } + } + } + + handleUpdatedPixels(x, y, w, h); + scheduleRepaint(x, y, w, h); + } + + // + // Decode 1bpp-encoded bi-color rectangle (8-bit and 24-bit versions). + // + + void decodeMonoData(int x, int y, int w, int h, byte[] src, byte[] palette) { + + int dx, dy, n; + int i = y * rfb.framebufferWidth + x; + int rowBytes = (w + 7) / 8; + byte b; + + for (dy = 0; dy < h; dy++) { + for (dx = 0; dx < w / 8; dx++) { + b = src[dy * rowBytes + dx]; + for (n = 7; n >= 0; n--) + pixels8[i++] = palette[b >> n & 1]; + } + for (n = 7; n >= 8 - w % 8; n--) { + pixels8[i++] = palette[src[dy * rowBytes + dx] >> n & 1]; + } + i += (rfb.framebufferWidth - w); + } + } + + void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) { + + int dx, dy, n; + int i = y * rfb.framebufferWidth + x; + int rowBytes = (w + 7) / 8; + byte b; + + for (dy = 0; dy < h; dy++) { + for (dx = 0; dx < w / 8; dx++) { + b = src[dy * rowBytes + dx]; + for (n = 7; n >= 0; n--) + pixels24[i++] = palette[b >> n & 1]; + } + for (n = 7; n >= 8 - w % 8; n--) { + pixels24[i++] = palette[src[dy * rowBytes + dx] >> n & 1]; + } + i += (rfb.framebufferWidth - w); + } + } + + // + // Decode data processed with the "Gradient" filter. + // + + void decodeGradientData(int x, int y, int w, int h, byte[] buf) { + + int dx, dy, c; + byte[] prevRow = new byte[w * 3]; + byte[] thisRow = new byte[w * 3]; + byte[] pix = new byte[3]; + int[] est = new int[3]; + + int offset = y * rfb.framebufferWidth + x; + + for (dy = 0; dy < h; dy++) { + + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = (byte) (prevRow[c] + buf[dy * w * 3 + c]); + thisRow[c] = pix[c]; + } + pixels24[offset++] = (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 + | (pix[2] & 0xFF); + + /* Remaining pixels of a row */ + for (dx = 1; dx < w; dx++) { + for (c = 0; c < 3; c++) { + est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) - (prevRow[(dx - 1) + * 3 + c] & 0xFF)); + if (est[c] > 0xFF) { + est[c] = 0xFF; + } else if (est[c] < 0x00) { + est[c] = 0x00; + } + pix[c] = (byte) (est[c] + buf[(dy * w + dx) * 3 + c]); + thisRow[dx * 3 + c] = pix[c]; + } + pixels24[offset++] = (pix[0] & 0xFF) << 16 + | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF); + } + + System.arraycopy(thisRow, 0, prevRow, 0, w * 3); + offset += (rfb.framebufferWidth - w); + } + } + + // + // Display newly updated area of pixels. + // + + void handleUpdatedPixels(int x, int y, int w, int h) { + + // Draw updated pixels of the off-screen image. + + pixelsSource.newPixels(x, y, w, h); + memGraphics.setClip(x, y, w, h); + memGraphics.drawImage(rawPixelsImage, 0, 0, null); + memGraphics.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight); + + } + + // + // Tell JVM to repaint specified desktop area. + // + + void scheduleRepaint(int x, int y, int w, int h) { + // Request repaint, deferred if necessary. + if (rfb.framebufferWidth == scaledWidth) { + repaint(viewer.deferScreenUpdates, x, y, w, h); + } else { + int sx = x * scalingFactor / 100; + int sy = y * scalingFactor / 100; + int sw = ((x + w) * scalingFactor + 49) / 100 - sx + 1; + int sh = ((y + h) * scalingFactor + 49) / 100 - sy + 1; + repaint(viewer.deferScreenUpdates, sx, sy, sw, sh); + } + } + + // + // Handle events. + // + + public void keyPressed(KeyEvent evt) { + processLocalKeyEvent(evt); + } + + public void keyReleased(KeyEvent evt) { + processLocalKeyEvent(evt); + } + + public void keyTyped(KeyEvent evt) { + evt.consume(); + } + + public void mousePressed(MouseEvent evt) { + processLocalMouseEvent(evt, false); + } + + public void mouseReleased(MouseEvent evt) { + processLocalMouseEvent(evt, false); + } + + public void mouseMoved(MouseEvent evt) { + processLocalMouseEvent(evt, true); + } + + public void mouseDragged(MouseEvent evt) { + processLocalMouseEvent(evt, true); + } + + public void processLocalKeyEvent(KeyEvent evt) { + if (viewer.rfb != null && rfb.inNormalProtocol) { + if (!inputEnabled) { + if ((evt.getKeyChar() == 'r' || evt.getKeyChar() == 'R') + && evt.getID() == KeyEvent.KEY_PRESSED) { + // Request screen update. + try { + rfb.writeFramebufferUpdateRequest(0, 0, + rfb.framebufferWidth, rfb.framebufferHeight, + false); + } catch (IOException e) { + e.printStackTrace(); + } + } + } else { + // Input enabled. + synchronized (rfb) { + try { + rfb.writeKeyEvent(evt); + } catch (Exception e) { + e.printStackTrace(); + } + rfb.notify(); + } + } + } + // Don't ever pass keyboard events to AWT for default processing. + // Otherwise, pressing Tab would switch focus to ButtonPanel etc. + evt.consume(); + } + + public void processLocalMouseEvent(MouseEvent evt, boolean moved) { + if (viewer.rfb != null && rfb.inNormalProtocol) { + if (moved) { + softCursorMove(evt.getX(), evt.getY()); + } + if (rfb.framebufferWidth != scaledWidth) { + int sx = (evt.getX() * 100 + scalingFactor / 2) / scalingFactor; + int sy = (evt.getY() * 100 + scalingFactor / 2) / scalingFactor; + evt.translatePoint(sx - evt.getX(), sy - evt.getY()); + } + synchronized (rfb) { + try { + rfb.writePointerEvent(evt); + } catch (Exception e) { + e.printStackTrace(); + } + rfb.notify(); + } + } + } + + // + // Ignored events. + // + + public void mouseClicked(MouseEvent evt) { + } + + public void mouseEntered(MouseEvent evt) { + } + + public void mouseExited(MouseEvent evt) { + } + + // + // Reset update statistics. + // + + void resetStats() { + statStartTime = System.currentTimeMillis(); + statNumUpdates = 0; + statNumTotalRects = 0; + statNumPixelRects = 0; + statNumRectsTight = 0; + statNumRectsTightJPEG = 0; + statNumRectsZRLE = 0; + statNumRectsHextile = 0; + statNumRectsRaw = 0; + statNumRectsCopy = 0; + statNumBytesEncoded = 0; + statNumBytesDecoded = 0; + } + + // //////////////////////////////////////////////////////////////// + // + // Handle cursor shape updates (XCursor and RichCursor encodings). + // + + boolean showSoftCursor = false; + + MemoryImageSource softCursorSource; + Image softCursor; + + int cursorX = 0, cursorY = 0; + int cursorWidth, cursorHeight; + int origCursorWidth, origCursorHeight; + int hotX, hotY; + int origHotX, origHotY; + + // + // Handle cursor shape update (XCursor and RichCursor encodings). + // + + synchronized void handleCursorShapeUpdate(int encodingType, int xhot, + int yhot, int width, int height) throws IOException { + + softCursorFree(); + + if (width * height == 0) + return; + + // Ignore cursor shape data if requested by user. + if (viewer.options.ignoreCursorUpdates) { + int bytesPerRow = (width + 7) / 8; + int bytesMaskData = bytesPerRow * height; + + if (encodingType == rfb.EncodingXCursor) { + rfb.skipBytes(6 + bytesMaskData * 2); + } else { + // rfb.EncodingRichCursor + rfb.skipBytes(width * height * bytesPixel + bytesMaskData); + } + return; + } + + // Decode cursor pixel data. + softCursorSource = decodeCursorShape(encodingType, width, height); + + // Set original (non-scaled) cursor dimensions. + origCursorWidth = width; + origCursorHeight = height; + origHotX = xhot; + origHotY = yhot; + + // Create off-screen cursor image. + createSoftCursor(); + + // Show the cursor. + showSoftCursor = true; + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); + } + + // + // decodeCursorShape(). Decode cursor pixel data and return + // corresponding MemoryImageSource instance. + // + + synchronized MemoryImageSource decodeCursorShape(int encodingType, + int width, int height) throws IOException { + + int bytesPerRow = (width + 7) / 8; + int bytesMaskData = bytesPerRow * height; + + int[] softCursorPixels = new int[width * height]; + + if (encodingType == rfb.EncodingXCursor) { + + // Read foreground and background colors of the cursor. + byte[] rgb = new byte[6]; + rfb.readFully(rgb); + int[] colors = { + (0xFF000000 | (rgb[3] & 0xFF) << 16 | (rgb[4] & 0xFF) << 8 | (rgb[5] & 0xFF)), + (0xFF000000 | (rgb[0] & 0xFF) << 16 | (rgb[1] & 0xFF) << 8 | (rgb[2] & 0xFF)) }; + + // Read pixel and mask data. + byte[] pixBuf = new byte[bytesMaskData]; + rfb.readFully(pixBuf); + byte[] maskBuf = new byte[bytesMaskData]; + rfb.readFully(maskBuf); + + // Decode pixel data into softCursorPixels[]. + byte pixByte, maskByte; + int x, y, n, result; + int i = 0; + for (y = 0; y < height; y++) { + for (x = 0; x < width / 8; x++) { + pixByte = pixBuf[y * bytesPerRow + x]; + maskByte = maskBuf[y * bytesPerRow + x]; + for (n = 7; n >= 0; n--) { + if ((maskByte >> n & 1) != 0) { + result = colors[pixByte >> n & 1]; + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + for (n = 7; n >= 8 - width % 8; n--) { + if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { + result = colors[pixBuf[y * bytesPerRow + x] >> n & 1]; + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + + } else { + // encodingType == rfb.EncodingRichCursor + + // Read pixel and mask data. + byte[] pixBuf = new byte[width * height * bytesPixel]; + rfb.readFully(pixBuf); + byte[] maskBuf = new byte[bytesMaskData]; + rfb.readFully(maskBuf); + + // Decode pixel data into softCursorPixels[]. + byte pixByte, maskByte; + int x, y, n, result; + int i = 0; + for (y = 0; y < height; y++) { + for (x = 0; x < width / 8; x++) { + maskByte = maskBuf[y * bytesPerRow + x]; + for (n = 7; n >= 0; n--) { + if ((maskByte >> n & 1) != 0) { + if (bytesPixel == 1) { + result = cm8.getRGB(pixBuf[i]); + } else { + result = 0xFF000000 + | (pixBuf[i * 4 + 2] & 0xFF) << 16 + | (pixBuf[i * 4 + 1] & 0xFF) << 8 + | (pixBuf[i * 4] & 0xFF); + } + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + for (n = 7; n >= 8 - width % 8; n--) { + if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { + if (bytesPixel == 1) { + result = cm8.getRGB(pixBuf[i]); + } else { + result = 0xFF000000 + | (pixBuf[i * 4 + 2] & 0xFF) << 16 + | (pixBuf[i * 4 + 1] & 0xFF) << 8 + | (pixBuf[i * 4] & 0xFF); + } + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + + } + + return new MemoryImageSource(width, height, softCursorPixels, 0, width); + } + + // + // createSoftCursor(). Assign softCursor new Image (scaled if necessary). + // Uses softCursorSource as a source for new cursor image. + // + + synchronized void createSoftCursor() { + + if (softCursorSource == null) + return; + + int scaleCursor = viewer.options.scaleCursor; + if (scaleCursor == 0 || !inputEnabled) + scaleCursor = 100; + + // Save original cursor coordinates. + int x = cursorX - hotX; + int y = cursorY - hotY; + int w = cursorWidth; + int h = cursorHeight; + + cursorWidth = (origCursorWidth * scaleCursor + 50) / 100; + cursorHeight = (origCursorHeight * scaleCursor + 50) / 100; + hotX = (origHotX * scaleCursor + 50) / 100; + hotY = (origHotY * scaleCursor + 50) / 100; + softCursor = Toolkit.getDefaultToolkit().createImage(softCursorSource); + + if (scaleCursor != 100) { + softCursor = softCursor.getScaledInstance(cursorWidth, + cursorHeight, Image.SCALE_SMOOTH); + } + + if (showSoftCursor) { + // Compute screen area to update. + x = Math.min(x, cursorX - hotX); + y = Math.min(y, cursorY - hotY); + w = Math.max(w, cursorWidth); + h = Math.max(h, cursorHeight); + + repaint(viewer.deferCursorUpdates, x, y, w, h); + } + } + + // + // softCursorMove(). Moves soft cursor into a particular location. + // + + synchronized void softCursorMove(int x, int y) { + int oldX = cursorX; + int oldY = cursorY; + cursorX = x; + cursorY = y; + if (showSoftCursor) { + repaint(viewer.deferCursorUpdates, oldX - hotX, oldY - hotY, + cursorWidth, cursorHeight); + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); + } + } + + // + // softCursorFree(). Remove soft cursor, dispose resources. + // + + synchronized void softCursorFree() { + if (showSoftCursor) { + showSoftCursor = false; + softCursor = null; + softCursorSource = null; + + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); + } + } + + BufferedImage createBufferedImage(Image img){ + BufferedImage bimg = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB ); + + Graphics g = bimg.getGraphics(); + g.drawImage(img, 0, 0, null); + g.dispose(); + return bimg; + } + + byte[] getBytes(BufferedImage img)throws IOException { + byte[] b = getImageBytes(img, "raw"); + return b; + } + + byte[] getImageBytes(BufferedImage image, String imageFormat) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + BufferedOutputStream os = new BufferedOutputStream(bos); + image.flush(); + ImageIO.write(image, imageFormat, os); + os.flush(); + os.close(); + return bos.toByteArray(); + } + + +}
--- a/src/myVncClient/EchoClient.java Fri Jul 29 17:26:17 2011 +0900 +++ b/src/myVncClient/EchoClient.java Fri Aug 05 01:05:38 2011 +0900 @@ -18,9 +18,8 @@ String treenum;// 自分の番号 String leaderflag;// リーダフラグ Socket clientSocket = null; - MyVncClient client; - - +// MyVncClient client; + InterfaceForViewer client; // WaitReplyに自分自身を渡している public EchoClient() { @@ -40,6 +39,20 @@ treenum = echo.treenum; } + public EchoClient(String _name,CuiMyVncClient client) { + this.client = client; + name = _name; + } + + public EchoClient(EchoClient echo,CuiMyVncClient client) { + this.client = client; + name = echo.name; + leaderflag = echo.leaderflag; + parent = echo.parent; + treenum = echo.treenum; + } + + // void hostn(String args){ void openport() { // ソケットや入出力用のストリームの宣言
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/myVncClient/InterfaceForViewer.java Fri Aug 05 01:05:38 2011 +0900 @@ -0,0 +1,28 @@ +package myVncClient; + +import java.awt.Graphics; +import java.net.Socket; + +public interface InterfaceForViewer { + + public void init(EchoClient value); + public void start_threads(); + public void start(); + + + public String readParameter(String name, boolean required); + + // synchronized + public void disconnect(); + public void fatalError(String str); + public void fatalError(String str, Exception e); + + + public void destroy(); + + public void enableInput(boolean enable); + + + public void setClientSocket(Socket sock); + +}
--- a/src/myVncClient/MyRfbProto.java Fri Jul 29 17:26:17 2011 +0900 +++ b/src/myVncClient/MyRfbProto.java Fri Aug 05 01:05:38 2011 +0900 @@ -13,19 +13,31 @@ import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; +import java.nio.ByteBuffer; +import java.util.Iterator; import java.util.LinkedList; import javax.imageio.ImageIO; + import myVncClient.MulticastQueue.Client; import java.util.concurrent.ExecutorService; -//import java.util.concurrent.Executors; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; import java.io.OutputStream; -class MyRfbProto extends RfbProto { - +public +class MyRfbProto<ByteBuffersIterator> extends RfbProto { final static String versionMsg_3_998 = "RFB 003.998\n"; + /** + * CheckMillis is one of new msgType for RFB 3.998. + */ + final static byte SpeedCheckMillis = 4; + private static final int INFLATE_BUFSIZE = 1024*100; + boolean printStatusFlag = false; + long startCheckTime; private int messageType; private int rectangles; @@ -35,20 +47,23 @@ private int rectH; private int encoding; private int zLen; + private boolean clicomp; private ServerSocket servSock; private int acceptPort; private byte initData[]; private LinkedList<Socket> cliListTmp; private LinkedList<Socket> cliList; - //private LinkedList<Thread> sendThreads; boolean createBimgFlag; ExecutorService executor; byte[] pngBytes; - private MulticastQueue<byte[]> multicastqueue = new MulticastQueue<byte[]>(); + // private MulticastQueue<LinkedList<ByteBuffer>> multicastqueue = new MostRecentMultiCast<LinkedList<ByteBuffer>>(10); + private MulticastQueue<LinkedList<ByteBuffer>> multicastqueue = new MulticastQueue<LinkedList<ByteBuffer>>(); + private int clients = 0; + private Inflater inflater = new Inflater(); MyRfbProto(String h, int p, VncViewer v) throws IOException { super(h, p, v); @@ -98,21 +113,21 @@ acceptPort = port; } - // 5550を開けるが、開いてないなら+1のポートを開ける。 - void selectPort() { - int i = 5550; + + void selectPort(int p) { + int port = p; while (true) { try { - initServSock(i); + initServSock(port); break; } catch (BindException e) { - i++; + port++; continue; } catch (IOException e) { } } - System.out.println("accept port = " + i); + System.out.println("accept port = " + port); } int getAcceptPort() { @@ -136,30 +151,22 @@ cliListTmp.add(sock); } - void mark(int len) throws IOException { - is.mark(len); - } - - void reset() throws IOException { - is.reset(); - } - boolean markSupported() { return is.markSupported(); } void readServerInit() throws IOException { - mark(255); + is.mark(255); skipBytes(20); int nlen = readU32(); int blen = 20 + 4 + nlen; initData = new byte[blen]; - reset(); + is.reset(); - mark(blen); + is.mark(blen); readFully(initData); - reset(); + is.reset(); framebufferWidth = readU16(); framebufferHeight = readU16(); @@ -251,20 +258,6 @@ os.write(initData); } - void sendData(byte b[]) { - try { - multicastqueue.put(b); - - /* - * // for(Socket cli : cliList){ // try{ // - * cli.getOutputStream().write(b, 0, b.length); // - * }catch(IOException e){ // // if socket closed // - * cliList.remove(cli); // } // } - */ - // System.out.println("cliSize="+cliSize()); - } catch (Exception e) { - } - } void sendPngImage() { try { @@ -296,39 +289,41 @@ System.out.println("numBytesRead=" + numBytesRead); } - void bufResetSend(int size) throws IOException { - reset(); - int len = size; - if (available() < size) - len = available(); - byte buffer[] = new byte[len]; - readFully(buffer); - sendData(buffer); - } void regiFramebufferUpdate() throws IOException { - mark(20); - messageType = readU8(); - skipBytes(1); - rectangles = readU16(); - rectX = readU16(); - rectY = readU16(); - rectW = readU16(); - rectH = readU16(); - encoding = readU32(); - if (encoding == 16) + is.mark(20); + messageType = readU8(); // 0 + skipBytes(1); // 1 + rectangles = readU16(); // 2 + rectX = readU16(); // 4 + rectY = readU16(); // 6 + rectW = readU16(); // 8 + rectH = readU16(); // 10 + encoding = readU32(); // 12 + System.out.println("encoding = "+encoding); + if (encoding == EncodingZRLE) zLen = readU32(); - reset(); - } - - int checkAndMark() throws IOException { + else + zLen = 0; + is.reset(); +/* int dataLen; switch (encoding) { case RfbProto.EncodingRaw: dataLen = rectW * rectH * 4 + 16; mark(dataLen); break; + case RfbProto.EncodingCopyRect: + dataLen = 16 + 4; + mark(dataLen); + break; + case RfbProto.EncodingRRE: + case RfbProto.EncodingCoRRE: + case RfbProto.EncodingHextile: + + case RfbProto.EncodingZlib: + case RfbProto.EncodingTight: case RfbProto.EncodingZRLE: dataLen = zLen + 20; mark(dataLen); @@ -337,29 +332,51 @@ dataLen = 1000000; mark(dataLen); } + +*/ + + } + + int checkAndMark() throws IOException { + int dataLen; + switch (encoding) { + case RfbProto.EncodingRaw: + dataLen = rectW * rectH * 4 + 16; + is.mark(dataLen); + break; + case RfbProto.EncodingCopyRect: + dataLen = 16 + 4; + is.mark(dataLen); + break; + case RfbProto.EncodingRRE: + case RfbProto.EncodingCoRRE: + case RfbProto.EncodingHextile: + case RfbProto.EncodingTight: + dataLen = zLen + 20; + is.mark(dataLen); + break; + case RfbProto.EncodingZlib: + case RfbProto.EncodingZRLE: + dataLen = zLen + 20; + is.mark(dataLen); + break; + case RfbProto.EncodingXCursor: + case RfbProto.EncodingRichCursor: + int pixArray = rectW * rectH * 4; + int u8Array = (int)Math.floor((rectW + 7)/8) * rectH; + dataLen = pixArray + u8Array; + printFramebufferUpdate(); + is.mark(dataLen); + break; + default: + dataLen = 1000000; + is.mark(dataLen); + } return dataLen; } - void readSendData(int dataLen) throws IOException { - byte buffer[] = new byte[dataLen]; - readFully(buffer); - multicastqueue.put(buffer); - reset(); -/* - for (Socket cli : cliList) { - try { - OutputStream out = cli.getOutputStream(); - executor.execute(new SendThread(out, buffer)); - } catch (IOException e) { - // if client socket closed - cliListTmp.remove(cli); - } catch (Exception e) { + - } - - } -*/ - } - void sendDataToClient() throws IOException { + void sendDataToClient() throws Exception { regiFramebufferUpdate(); int dataLen = checkAndMark(); readSendData(dataLen); @@ -414,17 +431,19 @@ BufferedImage bimg = ImageIO.read(new ByteArrayInputStream(pngBytes)); return bimg; } - +/* void readPngData() throws IOException { pngBytes = new byte[is.available()]; readFully(pngBytes); } - +*/ void printFramebufferUpdate() { System.out.println("messageType=" + messageType); System.out.println("rectangles=" + rectangles); System.out.println("encoding=" + encoding); + System.out.println("rectX = "+rectX+": rectY = "+rectY); + System.out.println("rectW = "+rectW+": rectH = "+rectH); switch (encoding) { case RfbProto.EncodingRaw: System.out.println("rectW * rectH * 4 + 16 =" + rectW * rectH * 4 @@ -433,17 +452,223 @@ default: } } + + void readSpeedCheck() throws IOException { + byte[] b = new byte[1]; + readFully(b); + } + + void startSpeedCheck() { + ByteBuffer b = ByteBuffer.allocate(10); + b.put((byte)SpeedCheckMillis); + b.flip(); + startCheckTime = System.currentTimeMillis(); + System.out.println("startChckTime = "+ startCheckTime); + LinkedList<ByteBuffer>bufs = new LinkedList<ByteBuffer>(); + bufs.add(b); + multicastqueue.put(bufs); + } + + void endSpeedCheck() { + long accTime = System.currentTimeMillis(); + long time = accTime - startCheckTime; + System.out.println("checkMillis: " + time); + } + + void printStatus() { + System.out.println(); + } + + synchronized void changeStatusFlag() { + printStatusFlag = true; + } + + void printMills() { + if(printStatusFlag) { + + changeStatusFlag(); + } else { + changeStatusFlag(); + } + } + + void speedCheckMillis() { + Runnable stdin = new Runnable() { + public void run() { + int c; + try { + while( (c = System.in.read()) != -1 ) { + switch(c) { + case 's': + break; + default: + startSpeedCheck(); + break; + } + } + }catch(IOException e){ + System.out.println(e); + } + } + }; + + new Thread(stdin).start(); + } + + /** + * gzip byte arrays + * @param deflater + * @param inputs + * byte data[] + * @param inputIndex + * @param outputs + * byte data[] + * @return byte length in last byte array + * @throws IOException + */ + public int zip(Deflater deflater,LinkedList<ByteBuffer> inputs, int inputIndex, LinkedList<ByteBuffer> outputs) throws IOException { + int len = 0; + ByteBuffer c1= ByteBuffer.allocate(INFLATE_BUFSIZE); + while(inputIndex < inputs.size() ) { + ByteBuffer b1 = inputs.get(inputIndex++); + deflater.setInput(b1.array(),b1.position(),b1.remaining()); + if (inputIndex==inputs.size()) deflater.finish(); + do { + int len1 = deflater.deflate(c1.array(),c1.position(),c1.remaining()); + if (len1>0) { + len += len1; + c1.position(c1.position()+len1); + if (c1.remaining()==0) { + c1.flip(); outputs.addLast(c1); + c1 = ByteBuffer.allocate(INFLATE_BUFSIZE); + } + } + } while (!deflater.needsInput()&&!deflater.finished()); + } + if (c1.position()!=0) { + c1.flip(); outputs.addLast(c1); + } + deflater.reset(); + return len; + } + + /** + * gunzip byte arrays + * @param inflater + * @param inputs + * byte data[] + * @param outputs + * byte data[] + *@return number of total bytes + * @throws IOException + */ + public int unzip(Inflater inflater, LinkedList<ByteBuffer> inputs, int inputIndex, LinkedList<ByteBuffer> outputs) + throws DataFormatException { + int len=0; + ByteBuffer buf = ByteBuffer.allocate(INFLATE_BUFSIZE); + while (inputIndex < inputs.size()) { + ByteBuffer input = inputs.get(inputIndex++); + inflater.setInput(input.array(),input.position(),input.limit()); + do { + int len0 = inflater.inflate(buf.array(),buf.position(),buf.remaining()); + if (len0>0) { + buf.position(buf.position()+len0); + len += len0; + if (buf.remaining()==0) { + buf.flip(); + outputs.addLast(buf); + buf = ByteBuffer.allocate(INFLATE_BUFSIZE); + } + } + } while (!inflater.needsInput()); + } + if (buf.position()!=0) { + buf.flip(); + outputs.addLast(buf); + } + return len; + } + + void readSendData(int dataLen) throws IOException, DataFormatException { + LinkedList<ByteBuffer>bufs = new LinkedList<ByteBuffer>(); + ByteBuffer header = ByteBuffer.allocate(16); + readFully(header.array(),0,16); + header.limit(16); + if (header.get(0)==RfbProto.FramebufferUpdate) { + int encoding = header.getInt(12); + if (encoding==RfbProto.EncodingZlib||encoding==RfbProto.EncodingZRLE) { + ByteBuffer len = ByteBuffer.allocate(4); + readFully(len.array(),0,4); len.limit(4); + ByteBuffer inputData = ByteBuffer.allocate(dataLen-20); + readFully(inputData.array(),0,inputData.capacity()); inputData.limit(dataLen-20); + LinkedList<ByteBuffer>inputs = new LinkedList<ByteBuffer>(); + inputs.add(inputData); + if (clicomp) { + unzip(inflater, inputs, 0, bufs); + } else { + Deflater nDeflater = new Deflater(); + LinkedList<ByteBuffer> out = new LinkedList<ByteBuffer>(); + unzip(inflater, inputs, 0 , out); + int len2 = zip(nDeflater, out, 0, bufs); + ByteBuffer blen = ByteBuffer.allocate(4); blen.putInt(len2); blen.flip(); + bufs.addFirst(blen); + } + bufs.addFirst(header); + multicastqueue.put(bufs); + is.reset(); + return ; + } + } + bufs.add(header); + if (dataLen>16) { + ByteBuffer b = ByteBuffer.allocate(dataLen-16); + readFully(b.array(),0,dataLen-16); b.limit(dataLen-16); + bufs.add(b); + } + multicastqueue.put(bufs); + is.reset(); + + // It may be compressed. We can inflate here to avoid repeating clients decompressing here, + // but it may generate too many large data. It is better to do it in each client. + // But we have do inflation for all input data, so we have to do it here. + } void newClient(AcceptThread acceptThread, final Socket newCli, final OutputStream os, final InputStream is) throws IOException { // createBimgFlag = true; // rfb.addSockTmp(newCli); // addSock(newCli); - final Client<byte[]> c = multicastqueue.newClient(); - Runnable sender = new Runnable() { + final Client <LinkedList<ByteBuffer>> c = multicastqueue.newClient(); + + final Runnable reader = new Runnable() { public void run() { + byte b[] = new byte[4096]; + for(;;) { + try { + int c = is.read(b); + if (c<=0) throw new IOException(); + System.out.println("client read "+c); + } catch (IOException e) { + try { + os.close(); + is.close(); + } catch (IOException e1) { + } + return; + } + } + } + }; + Runnable sender = new Runnable() { + + + public void run() { + + Deflater deflater = new Deflater(); try { - // 初期接続確立の部分 + /** + * initial connection of RFB protocol + */ sendRfbVersion(os); readVersionMsg(is); sendSecurityType(os); @@ -451,23 +676,57 @@ sendSecResult(os); readClientInit(is); sendInitData(os); - + new Thread(reader).start(); for (;;) { - byte[] b = c.poll(); - os.write(b, 0, b.length); + LinkedList<ByteBuffer> bufs = c.poll(); + int inputIndex = 0; + ByteBuffer header = bufs.get(inputIndex++); + if (header==null) continue; + if (header.get(0)==RfbProto.FramebufferUpdate) { + System.out.println("client "+ clients); + int encoding = header.getInt(12); + if (encoding==RfbProto.EncodingZlib||encoding==RfbProto.EncodingZRLE) { + LinkedList<ByteBuffer> outs; + if (clicomp) { + outs = new LinkedList<ByteBuffer>(); + int len2 = zip(deflater, bufs, inputIndex, outs); + ByteBuffer blen = ByteBuffer.allocate(4); blen.putInt(len2); blen.flip(); + outs.addFirst(blen); + outs.addFirst(header); + inputIndex = 0; + } else { + outs = bufs; + inputIndex = 0; + } + while(inputIndex < outs.size()) { + ByteBuffer out= outs.get(inputIndex++); + os.write(out.array(),out.position(),out.limit()); + } + } + os.flush(); + continue; + } + os.write(header.array(),header.position(),header.limit()); + while(inputIndex < bufs.size()) { + ByteBuffer b = bufs.get(inputIndex++); + os.write(b.array(), b.position(), b.limit()); + } + os.flush(); } } catch (IOException e) { - /** - * if socket closed - */ - // cliList.remove(newCli); - + try { + os.close(); + } catch (IOException e1) { + } + /* if socket closed cliList.remove(newCli); */ } - } - }; + clients++; new Thread(sender).start(); } + } + +
--- a/src/myVncClient/MyVncClient.java Fri Jul 29 17:26:17 2011 +0900 +++ b/src/myVncClient/MyVncClient.java Fri Aug 05 01:05:38 2011 +0900 @@ -6,8 +6,7 @@ import java.net.*; import java.util.Random; - -public class MyVncClient extends VncViewer implements java.lang.Runnable, +public class MyVncClient extends VncViewer implements InterfaceForViewer, java.lang.Runnable, WindowListener { /** @@ -21,14 +20,12 @@ private String leaderflag; boolean runflag = false; - - // // main() is called when run as a java program from the command line. // It simply runs the applet inside a newly-created frame. // - public static void main(String[] argv){ + public static void main(String[] argv) { MyVncClient v = new MyVncClient(); v.runClient(argv); } @@ -48,7 +45,7 @@ // public void init(EchoClient value) { - + // readParameters(); readParameters(value); @@ -90,11 +87,11 @@ // run() - executed by the rfbThread to deal with the RFB socket. // - public void start_threads(){ + public void start_threads() { rfbThread.start(); } - - public void run() { + + public void run() { gridbag = new GridBagLayout(); vncContainer.setLayout(gridbag); @@ -107,11 +104,11 @@ gridbag.setConstraints(buttonPanel, gbc); vncContainer.add(buttonPanel); } - + try { connectAndAuthenticate(); - accThread = new Thread(new AcceptThread(rfb)); + accThread = new Thread(new AcceptThread(rfb, 5999)); accThread.start(); doProtocolInitialisation(); @@ -127,42 +124,41 @@ Random rnd = new Random(); long ran = rnd.nextInt(5000) + 5000; System.out.println(ran); - //親がいない場合の処理はここに書く!!!! + // 親がいない場合の処理はここに書く!!!! /** - * this while reconnection + * this while reconnection */ - + int counter = 0; - //window を消してnullを突っ込んでGCで削除させる。 + // window を消してnullを突っ込んでGCで削除させる。 /* - vncFrame.setVisible(false); - vncFrame = null; - */ + * vncFrame.setVisible(false); vncFrame = null; + */ while (true) { /** - * if my last node case reconnectoion stop + * if my last node case reconnectoion stop */ - - echo = new EchoClient(echo,this); + + echo = new EchoClient(echo, this); try { Thread.sleep(ran); - } catch (InterruptedException e1) { + } catch (InterruptedException e1) { e1.printStackTrace(); } - - if(counter >= 3) { + + if (counter >= 3) { echo.openport(); echo.notfoundParent(); } - + echo.openport(); - //runflag = echo.losthost(); - if(echo.losthost()) { + // runflag = echo.losthost(); + if (echo.losthost()) { break; } counter++; } - + // System.exit(0); } catch (Exception e) { System.out.println(e); @@ -219,17 +215,17 @@ + ":" + port, e); } catch (EOFException e) { - //window を消してnullを突っ込んでGCで削除させる。 + // window を消してnullを突っ込んでGCで削除させる。 vncFrame.setVisible(false); vncFrame = null; - //num4 + // num4 // リーダーの子ノードがproxyに対して親が落ちたことを報告をする - if(leaderflag != null) { - while(true) { - echo = new EchoClient(echo,this); + if (leaderflag != null) { + while (true) { + echo = new EchoClient(echo, this); echo.openport(); - //runflag = echo.losthost(); - if(echo.losthost()) { + // runflag = echo.losthost(); + if (echo.losthost()) { break; } } @@ -282,7 +278,6 @@ // Create a VncCanvas instance. // - void createCanvas(int maxWidth, int maxHeight) throws IOException { // Determine if Java 2D API is available and use a special // version of VncCanvas if it is present. @@ -694,11 +689,11 @@ } /** - readParameters() - read parameters from the html source or from the - command line. On the command line, the arguments are just a sequence of - param_name/param_value pairs where the names and values correspond to - those expected in the html applet tag source. - */ + * readParameters() - read parameters from the html source or from the + * command line. On the command line, the arguments are just a sequence of + * param_name/param_value pairs where the names and values correspond to + * those expected in the html applet tag source. + */ void readParameters(EchoClient value) { /* @@ -713,11 +708,13 @@ if (clientSocket == null) { String pHost; - if (mainArgs.length > 0) pHost = mainArgs[0]; - else pHost = "cls080.ie.u-ryukyu.ac.jp"; - echo = new EchoClient(pHost,this); + if (mainArgs.length > 0) + pHost = mainArgs[0]; + else + pHost = "cls080.ie.u-ryukyu.ac.jp"; + echo = new EchoClient(pHost, this); echo.openport(); - + value = echo.hostn("1"); } else { echo = new EchoClient(); @@ -727,12 +724,12 @@ // proxyからの返信で接続先を決定する host = value.responseLine; parent = value.parent; - if(value.treenum != null) { + if (value.treenum != null) { treenum = value.treenum; } else { treenum = echo.treenum; } - if(value.leaderflag != null) { + if (value.leaderflag != null) { leaderflag = value.leaderflag; } else { leaderflag = echo.leaderflag; @@ -741,7 +738,7 @@ System.out.println("mynumber =" + treenum); System.out.println("connect host =" + host); System.out.println("leaderflag(boolean) = " + leaderflag); - + echo = value; if (host == null) { @@ -750,7 +747,7 @@ fatalError("HOST parameter not specified"); } } - port = 5550; + port = 5999; // Read "ENCPASSWORD" or "PASSWORD" parameter if specified. readPasswordParameters(); @@ -832,20 +829,12 @@ } return s; } -/* - for (int i = 0; i < mainArgs.length; i += 2) { - if (mainArgs[i].equalsIgnoreCase(name)) { - try { - return mainArgs[i + 1]; - } catch (Exception e) { - if (required) { - fatalError(name + " parameter not specified"); - } - return null; - } - } - } -*/ + /* + * for (int i = 0; i < mainArgs.length; i += 2) { if + * (mainArgs[i].equalsIgnoreCase(name)) { try { return mainArgs[i + 1]; + * } catch (Exception e) { if (required) { fatalError(name + + * " parameter not specified"); } return null; } } } + */ if (required) { fatalError(name + " parameter not specified"); } @@ -962,7 +951,7 @@ System.exit(1); } } - + // // Show message text and optionally "Relogin" and "Close" buttons. // @@ -1076,7 +1065,7 @@ public static void main(Socket _clientSocket) { MyVncClient v = new MyVncClient(); - //v.clientSocket = _clientSocket; + // v.clientSocket = _clientSocket; // v.mainArgs = argv; v.inAnApplet = false; v.inSeparateFrame = true; @@ -1092,8 +1081,14 @@ v.inSeparateFrame = true; v.init(value); - v.start_threads(); + v.start_threads(); v.start(); } + + public void setClientSocket(Socket sock) { + clientSocket = sock; + } + + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/myVncClient/OptionNoFrame.java Fri Aug 05 01:05:38 2011 +0900 @@ -0,0 +1,330 @@ +package myVncClient; + +import java.awt.*; +import java.awt.event.*; + +class OptionsNoFrame{ + + static String[] names = { + "Encoding", + "Compression level", + "JPEG image quality", + "Cursor shape updates", + "Use CopyRect", + "Restricted colors", + "Mouse buttons 2 and 3", + "View only", + "Scale remote cursor", + "Share desktop", + }; + + static String[][] values = { + { "Auto", "Raw", "RRE", "CoRRE", "Hextile", "Zlib", "Tight", "ZRLE" }, + { "Default", "1", "2", "3", "4", "5", "6", "7", "8", "9" }, + { "JPEG off", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }, + { "Enable", "Ignore", "Disable" }, + { "Yes", "No" }, + { "Yes", "No" }, + { "Normal", "Reversed" }, + { "Yes", "No" }, + { "No", "50%", "75%", "125%", "150%" }, + { "Yes", "No" }, + }; + + final int + encodingIndex = 0, + compressLevelIndex = 1, + jpegQualityIndex = 2, + cursorUpdatesIndex = 3, + useCopyRectIndex = 4, + eightBitColorsIndex = 5, + mouseButtonIndex = 6, + viewOnlyIndex = 7, + scaleCursorIndex = 8, + shareDesktopIndex = 9; + + Label[] labels = new Label[names.length]; + Choice[] choices = new Choice[names.length]; + Button closeButton; + CuiMyVncClient viewer; + + + // + // The actual data which other classes look at: + // + + int preferredEncoding; + int compressLevel; + int jpegQuality; + boolean useCopyRect; + boolean requestCursorUpdates; + boolean ignoreCursorUpdates; + + boolean eightBitColors; + + boolean reverseMouseButtons2And3; + boolean shareDesktop; + boolean viewOnly; + int scaleCursor; + + boolean autoScale; + int scalingFactor; + + // + // Constructor. Set up the labels and choices from the names and values + // arrays. + // + + OptionsNoFrame(CuiMyVncClient v) { + viewer = v; + + for (int i = 0; i < names.length; i++) { + labels[i] = new Label(names[i]); + + choices[i] = new Choice(); + + + for (int j = 0; j < values[i]. length; j++) { + choices[i].addItem(values[i][j]); + } + } + + // Set up defaults + + choices[encodingIndex].select("Auto"); + choices[compressLevelIndex].select("Default"); + choices[jpegQualityIndex].select("6"); + choices[cursorUpdatesIndex].select("Enable"); + choices[useCopyRectIndex].select("Yes"); + choices[eightBitColorsIndex].select("No"); + choices[mouseButtonIndex].select("Normal"); + choices[viewOnlyIndex].select("No"); + choices[scaleCursorIndex].select("No"); + choices[shareDesktopIndex].select("Yes"); + + // But let them be overridden by parameters + + for (int i = 0; i < names.length; i++) { + String s = viewer.readParameter(names[i], false); + if (s != null) { + for (int j = 0; j < values[i].length; j++) { + if (s.equalsIgnoreCase(values[i][j])) { + choices[i].select(j); + } + } + } + } + + // FIXME: Provide some sort of GUI for "Scaling Factor". + + autoScale = false; + scalingFactor = 100; + String s = viewer.readParameter("Scaling Factor", false); + if (s != null) { + if (s.equalsIgnoreCase("Auto")) { + autoScale = true; + } else { + // Remove the '%' char at the end of string if present. + if (s.charAt(s.length() - 1) == '%') { + s = s.substring(0, s.length() - 1); + } + // Convert to an integer. + try { + scalingFactor = Integer.parseInt(s); + } + catch (NumberFormatException e) { + scalingFactor = 100; + } + // Make sure scalingFactor is in the range of [1..1000]. + if (scalingFactor < 1) { + scalingFactor = 1; + } else if (scalingFactor > 1000) { + scalingFactor = 1000; + } + } + } + + // Make the booleans and encodings array correspond to the state of the GUI + + setEncodings(); + setColorFormat(); + setOtherOptions(); + } + + + // + // Disable the shareDesktop option + // + + void disableShareDesktop() { + labels[shareDesktopIndex].setEnabled(false); + choices[shareDesktopIndex].setEnabled(false); + } + + // + // setEncodings looks at the encoding, compression level, JPEG + // quality level, cursor shape updates and copyRect choices and sets + // corresponding variables properly. Then it calls the VncViewer's + // setEncodings method to send a SetEncodings message to the RFB + // server. + // + + void setEncodings() { +// useCopyRect = choices[useCopyRectIndex].getSelectedItem().equals("Yes"); + + preferredEncoding = RfbProto.EncodingRaw; + boolean enableCompressLevel = false; + + if (choices[encodingIndex].getSelectedItem().equals("RRE")) { + preferredEncoding = RfbProto.EncodingRRE; + } else if (choices[encodingIndex].getSelectedItem().equals("CoRRE")) { + preferredEncoding = RfbProto.EncodingCoRRE; + } else if (choices[encodingIndex].getSelectedItem().equals("Hextile")) { + preferredEncoding = RfbProto.EncodingHextile; + } else if (choices[encodingIndex].getSelectedItem().equals("ZRLE")) { + preferredEncoding = RfbProto.EncodingZRLE; + } else if (choices[encodingIndex].getSelectedItem().equals("Zlib")) { + preferredEncoding = RfbProto.EncodingZlib; + enableCompressLevel = true; + } else if (choices[encodingIndex].getSelectedItem().equals("Tight")) { + preferredEncoding = RfbProto.EncodingTight; + enableCompressLevel = true; + } else if (choices[encodingIndex].getSelectedItem().equals("Auto")) { + preferredEncoding = -1; + } + + // Handle compression level setting. + + try { + compressLevel = + Integer.parseInt(choices[compressLevelIndex].getSelectedItem()); + } + catch (NumberFormatException e) { + compressLevel = -1; + } + if (compressLevel < 1 || compressLevel > 9) { + compressLevel = -1; + } + labels[compressLevelIndex].setEnabled(enableCompressLevel); + choices[compressLevelIndex].setEnabled(enableCompressLevel); + + // Handle JPEG quality setting. + + try { + jpegQuality = + Integer.parseInt(choices[jpegQualityIndex].getSelectedItem()); + } + catch (NumberFormatException e) { + jpegQuality = -1; + } + if (jpegQuality < 0 || jpegQuality > 9) { + jpegQuality = -1; + } + + // Request cursor shape updates if necessary. + + requestCursorUpdates = + !choices[cursorUpdatesIndex].getSelectedItem().equals("Disable"); + + if (requestCursorUpdates) { + ignoreCursorUpdates = + choices[cursorUpdatesIndex].getSelectedItem().equals("Ignore"); + } + + viewer.setEncodings(); + } + + // + // setColorFormat sets eightBitColors variable depending on the GUI + // setting, causing switches between 8-bit and 24-bit colors mode if + // necessary. + // + + void setColorFormat() { + + eightBitColors = + choices[eightBitColorsIndex].getSelectedItem().equals("Yes"); + + boolean enableJPEG = !eightBitColors; + + labels[jpegQualityIndex].setEnabled(enableJPEG); + choices[jpegQualityIndex].setEnabled(enableJPEG); + } + + // + // setOtherOptions looks at the "other" choices (ones that do not + // cause sending any protocol messages) and sets the boolean flags + // appropriately. + // + + void setOtherOptions() { + + reverseMouseButtons2And3 + = choices[mouseButtonIndex].getSelectedItem().equals("Reversed"); + + viewOnly + = choices[viewOnlyIndex].getSelectedItem().equals("Yes"); + if (viewer.vc != null) + viewer.vc.enableInput(!viewOnly); + + shareDesktop + = choices[shareDesktopIndex].getSelectedItem().equals("Yes"); + + String scaleString = choices[scaleCursorIndex].getSelectedItem(); + if (scaleString.endsWith("%")) + scaleString = scaleString.substring(0, scaleString.length() - 1); + try { + scaleCursor = Integer.parseInt(scaleString); + } + catch (NumberFormatException e) { + scaleCursor = 0; + } + if (scaleCursor < 10 || scaleCursor > 500) { + scaleCursor = 0; + } + if (requestCursorUpdates && !ignoreCursorUpdates && !viewOnly) { + labels[scaleCursorIndex].setEnabled(true); + choices[scaleCursorIndex].setEnabled(true); + } else { + labels[scaleCursorIndex].setEnabled(false); + choices[scaleCursorIndex].setEnabled(false); + } + if (viewer.vc != null) + viewer.vc.createSoftCursor(); // update cursor scaling + } + + + // + // Respond to actions on Choice controls + // + + public void itemStateChanged(ItemEvent evt) { + Object source = evt.getSource(); + + if (source == choices[encodingIndex] || + source == choices[compressLevelIndex] || + source == choices[jpegQualityIndex] || + source == choices[cursorUpdatesIndex] || + source == choices[useCopyRectIndex]) { + + setEncodings(); + + if (source == choices[cursorUpdatesIndex]) { + setOtherOptions(); // update scaleCursor state + } + + } else if (source == choices[eightBitColorsIndex]) { + + setColorFormat(); + + } else if (source == choices[mouseButtonIndex] || + source == choices[shareDesktopIndex] || + source == choices[viewOnlyIndex] || + source == choices[scaleCursorIndex]) { + + setOtherOptions(); + + } + } + +}
--- a/src/myVncClient/VncCanvas.java Fri Jul 29 17:26:17 2011 +0900 +++ b/src/myVncClient/VncCanvas.java Fri Aug 05 01:05:38 2011 +0900 @@ -395,7 +395,7 @@ * read Data from parents and send Data to Client. * */ - rfb.sendDataToClient(); + rfb.sendDataToClient(); int bufSize = (int)rfb.getNumBytesRead(); @@ -424,6 +424,9 @@ for (int i = 0; i < rfb.updateNRects; i++) { rfb.readFramebufferUpdateRectHdr(); + System.out.println("encoding = "+rfb.updateRectEncoding); + + statNumTotalRects++; int rx = rfb.updateRectX, ry = rfb.updateRectY; int rw = rfb.updateRectW, rh = rfb.updateRectH;
--- a/src/myVncClient/VncViewer.java Fri Jul 29 17:26:17 2011 +0900 +++ b/src/myVncClient/VncViewer.java Fri Aug 05 01:05:38 2011 +0900 @@ -5,7 +5,7 @@ import java.io.*; import java.net.*; -public class VncViewer extends java.applet.Applet implements +public class VncViewer extends java.applet.Applet implements java.lang.Runnable, WindowListener { boolean inAnApplet = true; @@ -147,7 +147,7 @@ rfb = new MyRfbProto(host, port, this); rfb.readServerInit(); - rfb.readPngData(); +// rfb.readPngData(); createCanvas(0, 0); vc.drawFirstImage(); @@ -676,8 +676,7 @@ if (host == null) { host = getCodeBase().getHost(); if (host.equals("")) { - host = "nw0811.st.ie.u-ryukyu.ac.jp"; -// fatalError("HOST parameter not specified"); + fatalError("HOST parameter not specified"); } }
--- a/src/myVncClient/WaitReply.java Fri Jul 29 17:26:17 2011 +0900 +++ b/src/myVncClient/WaitReply.java Fri Aug 05 01:05:38 2011 +0900 @@ -5,14 +5,24 @@ public class WaitReply extends Thread { - MyVncClient client; +// MyVncClient client; + InterfaceForViewer client; private String treenum; + +/* public WaitReply(String treenum,MyVncClient client) { this.client = client; this.treenum = treenum; } +*/ + public WaitReply(String treenum,InterfaceForViewer client) { + this.client = client; + this.treenum = treenum; + } + + public void run(){ Socket clientSocket = null; ServerSocket echoServer=null; @@ -24,11 +34,12 @@ } catch (IOException e) { System.out.println(e); - } + } try { clientSocket = echoServer.accept(); if(clientSocket != null){ - client.clientSocket = clientSocket; +// client.clientSocket = clientSocket; + client.setClientSocket(clientSocket); client.init(null); client.start_threads(); client.start();