// // Copyright (C) 2001-2004 HorizonLive.com, Inc. All Rights Reserved. // Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved. // Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. // // This is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This software is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this software; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, // USA. // // // VncViewer.java - the VNC viewer applet. This class mainly just sets up the // user interface, leaving it to the VncCanvas to do the actual rendering of // a VNC desktop. // import java.applet.Applet; import java.awt.*; import java.awt.event.*; import java.awt.image.BufferedImage; import java.awt.image.MemoryImageSource; import java.io.*; import java.net.*; import java.util.Date; public class ConsoleViewer implements java.lang.Runnable { int id = getNextId(); boolean compressServerMessage = false; long createTime = System.currentTimeMillis(); long lastUsedTime = System.currentTimeMillis(); boolean dropMe = false; OutputStream clientStream; String clientStreamInfo; int status; public final static int STATUS_ERROR = -1; public final static int STATUS_UNINITIALIZED = 0; public final static int STATUS_CONNECTING = 1; public final static int STATUS_INITIALIZING = 2; public final static int STATUS_NORMAL_OPERATION = 3; public final static int STATUS_AUTHENTICATION_FAILURE = 100; boolean inAnApplet = true; boolean inSeparateFrame = false; boolean inProxyMode = false; String[] mainArgs; RfbProto rfb; Thread rfbThread; ConsoleApplet applet; Frame vncFrame; Container vncContainer; ScrollPane desktopScrollPane; GridBagLayout gridbag; ButtonPanel buttonPanel; Label connStatusLabel; ConsoleCanvas vc; OptionsFrame options; ClipboardFrame clipboard; private 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 proxyHost; int proxyPort; String passwordParam; boolean showControls; boolean offerRelogin; boolean showOfflineDesktop; int deferScreenUpdates; int deferCursorUpdates; int deferUpdateRequests; static int id_count = 1; synchronized static int getNextId() { return id_count++; } // // init() // public void init() { if (inProxyMode) { initProxy(); return; } readParameters(); if (inSeparateFrame) { vncFrame = new Frame("VMOps ConsoleViewer"); if (!inAnApplet) { // vncFrame.add("Center", this); } vncContainer = vncFrame; } else { vncContainer = applet; } recordingSync = new Object(); options = new OptionsFrame(this); // clipboard = new ClipboardFrame(this); // if (RecordingFrame.checkSecurity()) // rec = new RecordingFrame(this); sessionFileName = null; recordingActive = false; recordingStatusChanged = false; cursorUpdatesDef = null; eightBitColorsDef = null; if (inSeparateFrame) vncFrame.addWindowListener(applet); rfbThread = new Thread(this); rfbThread.start(); } private void initProxy() { recordingSync = new Object(); options = new OptionsFrame(this); options.viewOnly = true; sessionFileName = null; recordingActive = false; recordingStatusChanged = false; cursorUpdatesDef = null; eightBitColorsDef = null; rfbThread = new Thread(this); rfbThread.setName("RFB Thread " + rfbThread.getId() + " >" + host + ":" + port); rfbThread.start(); } public void update(Graphics g) { } // // run() - executed by the rfbThread to deal with the RFB socket. // public void run() { if (inProxyMode) { runProxy(); return; } int[] pixels = new int[16 * 16]; Image image = Toolkit.getDefaultToolkit().createImage( new MemoryImageSource(16, 16, pixels, 0, 16)); Cursor transparentCursor = Toolkit.getDefaultToolkit().createCustomCursor (image, new Point(0, 0), "invisibleCursor"); vncContainer.setCursor(transparentCursor); gridbag = new GridBagLayout(); vncContainer.setLayout(gridbag); GridBagConstraints gbc = new GridBagConstraints(); gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.anchor = GridBagConstraints.NORTHWEST; //gbc.anchor = GridBagConstraints.CENTER; if (showControls) { buttonPanel = new ButtonPanel(this); gridbag.setConstraints(buttonPanel, gbc); vncContainer.add(buttonPanel); } // FIXME: Use auto-scaling not only in a separate frame. if (options.autoScale && inSeparateFrame) { Dimension screenSize; try { screenSize = vncContainer.getToolkit().getScreenSize(); } catch (Exception e) { screenSize = new Dimension(0, 0); } createCanvas(screenSize.width - 32, screenSize.height - 32); } else { createCanvas(0, 0); } gbc.weightx = 1.0; gbc.weighty = 1.0; if (inSeparateFrame) { // Create a panel which itself is resizeable and can hold // non-resizeable VncCanvas component at the top left corner. Panel canvasPanel = new Panel(); canvasPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); canvasPanel.add(vc); // Create a ScrollPane which will hold a panel with VncCanvas // inside. desktopScrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); gbc.fill = GridBagConstraints.BOTH; gridbag.setConstraints(desktopScrollPane, gbc); desktopScrollPane.add(canvasPanel); // Finally, add our ScrollPane to the Frame window. vncFrame.add(desktopScrollPane); // vncFrame.setTitle(rfb.desktopName); vncFrame.pack(); vc.resizeDesktopFrame(); } else { gridbag.setConstraints(vc, gbc); applet.add(vc); applet.validate(); } if (showControls) buttonPanel.enableButtons(); moveFocusToDesktop(); try { connectAndAuthenticate(); doProtocolInitialisation(); vc.rfb = rfb; vc.setPixelFormat(); vc.rfb.writeFramebufferUpdateRequest(0, 0, vc.rfb.framebufferWidth, vc.rfb.framebufferHeight, false); vc.processNormalProtocol(); // We should never get here, but just in case showMessage("Disconnected from server"); } 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) { fatalError("Network error: remote side closed connection", e); } catch (IOException e) { fatalError(e.getMessage(), e); } catch (Exception e) { fatalError(e.getMessage(), e); } finally { encodingsSaved = null; nEncodingsSaved = 0; synchronized (this) { if (rfb != null) { rfb.close(); } } } } public void runProxy() { createCanvas(0, 0); int delay = 0; while (!dropMe) { try { status = STATUS_CONNECTING; connectAndAuthenticate(); delay = 0; // reset the delay interval status = STATUS_INITIALIZING; doProtocolInitialisation(); vc.rfb = rfb; vc.setPixelFormat(); vc.rfb.writeFramebufferUpdateRequest(0, 0, vc.rfb.framebufferWidth, vc.rfb.framebufferHeight, false); status = STATUS_NORMAL_OPERATION; vc.processNormalProtocol(); } catch (AuthenticationException e) { status = STATUS_AUTHENTICATION_FAILURE; String msg = e.getMessage(); Logger.log(Logger.INFO, msg); } catch (Exception e) { status = STATUS_ERROR; Logger.log(Logger.INFO, e.toString()); } finally { String oldName = Thread.currentThread().getName(); encodingsSaved = null; nEncodingsSaved = 0; synchronized (this) { if (rfb != null) { rfb.close(); } } } if (dropMe) { break; } if (status == STATUS_AUTHENTICATION_FAILURE) { break; } else { Logger.log(Logger.INFO, "Exception caught, retrying in " + delay + "ms"); try { Thread.sleep(delay); } catch (InterruptedException e) { // ignored } delay = (int) ((float) (delay + 700) * 1.5); if (delay > 3000) { Logger.log(Logger.INFO, "Delay value gets too large " + delay + ", stop retrying"); break; } } } Logger.log(Logger.INFO, "RFB thread terminating"); } // // Create a VncCanvas instance. // void createCanvas(int maxWidth, int maxHeight) { /* * // Determine if Java 2D API is available and use a special // version * of VncCanvas if it is present. vc = null; try { // This throws * ClassNotFoundException if there is no Java 2D API. Class cl = * Class.forName("java.awt.Graphics2D"); // If we could load Graphics2D * class, then we can use VncCanvas2D. cl = Class.forName("VncCanvas2"); * Class[] argClasses = { this.getClass(), Integer.TYPE, Integer.TYPE }; * java.lang.reflect.Constructor cstr = cl.getConstructor(argClasses); * Object[] argObjects = { this, new Integer(maxWidth), new * Integer(maxHeight) }; vc = (VncCanvas)cstr.newInstance(argObjects); } * catch (Exception e) { Logger.log(Logger.INFO, * "Warning: Java 2D API is not available"); } * * // If we failed to create VncCanvas2D, use old VncCanvas. if (vc == * null) */ vc = new ConsoleCanvas2(this, maxWidth, maxHeight); } /* * // // 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(); } catch (Exception e) { if (rfbThread == * null) { Logger.log(Logger.INFO, "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..."); if (!inProxyMode) { if (inSeparateFrame) { vncFrame.pack(); vncFrame.show(); } else { applet.validate(); } } if (proxyHost != null) { showConnectionStatus("Connecting to " + proxyHost + ", port " + proxyPort + "..."); rfb = new RfbProto(proxyHost, proxyPort, this); rfb.readProxyVersion(); rfb.writeProxyString(host, port, (passwordParam != null) ? passwordParam : ""); } else { showConnectionStatus("Connecting to " + host + ", port " + port + "..."); rfb = new RfbProto(host, port, this); } 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.initCapabilities(); 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 { throw new AuthenticationException("Bad password"); /* * 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) { if (inProxyMode) { if (msg != null) { Logger.log(Logger.INFO, msg); } return; } /* * if (msg == null) { if (vncContainer.isAncestorOf(connStatusLabel)) { * vncContainer.remove(connStatusLabel); } return; } * * Logger.log(Logger.INFO, msg); * * if (connStatusLabel == null) { connStatusLabel = new Label("Status: " * + msg); connStatusLabel.setFont(new Font("Helvetica", Font.PLAIN, * 12)); } else { connStatusLabel.setText("Status: " + msg); } * * if (!vncContainer.isAncestorOf(connStatusLabel)) { GridBagConstraints * gbc = new GridBagConstraints(); gbc.gridwidth = * GridBagConstraints.REMAINDER; gbc.fill = * GridBagConstraints.HORIZONTAL; gbc.anchor = * GridBagConstraints.NORTHWEST; gbc.weightx = 1.0; gbc.weighty = 1.0; * gbc.insets = new Insets(20, 30, 20, 30); * gridbag.setConstraints(connStatusLabel, gbc); * vncContainer.add(connStatusLabel); } * * if (inSeparateFrame) { vncFrame.pack(); } else { validate(); } */ } // // 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); if (inSeparateFrame) { vncFrame.pack(); } else { applet.validate(); } authPanel.moveFocusToDefaultField(); String pw = authPanel.getPassword(); vncContainer.remove(authPanel); return pw; } */ // // Do the rest of the protocol initialisation. // void doProtocolInitialisation() throws IOException { rfb.writeClientInit(); rfb.readServerInit(); /* * Logger.log(Logger.INFO, "Desktop name is " + rfb.desktopName); * Logger.log(Logger.INFO, "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. // Logger.log(Logger.INFO, "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. Logger.log(Logger.INFO, "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. Logger.log(Logger.INFO, "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) { Logger.log(Logger.ERROR, e.toString(), e); } 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) { Logger.log(Logger.ERROR, e.toString(), e); } } // // 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(); } * * Logger.log(Logger.INFO, "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(); Logger.log(Logger.INFO, * "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() { String str; host = readParameter("HOST", true); str = readParameter("PORT", true); port = Integer.parseInt(str); proxyHost = readParameter("PROXYHOST", false); if (proxyHost != null) { str = readParameter("PROXYPORT", true); proxyPort = Integer.parseInt(str); } // Read "ENCPASSWORD" or "PASSWORD" parameter if specified. readPasswordParameters(); 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 = false; 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 = false; 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", 50); // SocketFactory. socketFactory = readParameter("SocketFactory", false); } // // Read password parameters. // private void readPasswordParameters() { String str = readParameter("SID", false); if (str != null) passwordParam = str; } public String readParameter(String name, boolean required) { if (inAnApplet) { String s = applet.getParameter(name); // Logger.log(Logger.INFO, "getParameter " + name + " = " + s); if ((s == null) && required) { fatalError(name + " parameter not specified"); } 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; } } } 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; } // // moveFocusToDesktop() - move keyboard focus either to VncCanvas. // void moveFocusToDesktop() { if (vncContainer != null) { if (vc != null && vncContainer.isAncestorOf(vc)) vc.requestFocus(); } } // // disconnect() - close connection to server. // synchronized public void disconnect() { Logger.log(Logger.INFO, "Disconnect"); synchronized (this) { if (rfb != null) rfb.close(); } // options.dispose(); // clipboard.dispose(); if (rec != null) rec.dispose(); if (inAnApplet) { showMessage("Disconnected"); } else { 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) { Logger.log(Logger.INFO, str); if (inAnApplet) { // vncContainer null, applet not inited, // can not present the error to the user. Thread.currentThread().stop(); } else { 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. Logger.log(Logger.INFO, * "RFB thread finished"); return; } */ if (str == null) { str = ""; } Logger.log(Logger.INFO, str, e); showMessage(str); } // // Show message text and optionally "Relogin" and "Close" buttons. // void showMessage(String msg) { if (vc != null) { vc.paintErrorString(msg); return; } Logger.log(Logger.INFO, msg); /* * vncContainer.removeAll(); * * Logger.log(Logger.INFO, "showMessage " + msg); * * 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); * * } * * if (inSeparateFrame) { vncFrame.pack(); } else { validate(); } */ } // // Stop the applet. // Main applet thread will terminate on first exception // after seeing that rfb has been closed. // public void stop() { Logger.log(Logger.INFO, "Stopping applet"); synchronized (this) { if (rfb != null) { rfb.close(); } } } // // This method is called before the applet is destroyed. // public void destroy() { Logger.log(Logger.INFO, "Destroying applet"); vncContainer.removeAll(); // options.dispose(); // clipboard.dispose(); if (rec != null) rec.dispose(); synchronized (this) { if (rfb != null) rfb.close(); } if (inSeparateFrame) vncFrame.dispose(); } // // 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) { Logger.log(Logger.INFO, "Closing window"); if (rfb != null) disconnect(); vncContainer.hide(); if (!inAnApplet) { System.exit(0); } } public String toString() { return ("{ConsoleViewer-" + host + ":" + port + (" created=\"" + new Date(createTime)) + "\"" + (" lastused=\"" + new Date(lastUsedTime)) + "\"" + (clientStream != null ? (" client=" + clientStreamInfo) : "") + "}"); } }