From c456387fe1ed922c5bfb5daf0e780f0000cab2fb Mon Sep 17 00:00:00 2001 From: "Brian P. Hinz" Date: Sat, 27 Aug 2016 17:33:04 -0400 Subject: [PATCH] Make all viewer parameters static. Viewer instances are isolated from each other by spawning a completely new process for each viewer, rather than simply starting a new thread. All dialogs were redesigned to better match the look of the native viewer, and also to be more tolerant of sizing differences between platforms. --- java/com/tigervnc/rfb/AliasParameter.java | 8 + java/com/tigervnc/rfb/CConnection.java | 12 +- java/com/tigervnc/rfb/CSecurityTLS.java | 16 +- java/com/tigervnc/rfb/CSecurityVeNCrypt.java | 2 +- java/com/tigervnc/rfb/Configuration.java | 76 +- java/com/tigervnc/rfb/Security.java | 38 +- java/com/tigervnc/rfb/SecurityClient.java | 6 +- .../com/tigervnc/vncviewer/BIPixelBuffer.java | 4 +- java/com/tigervnc/vncviewer/CConn.java | 899 ++------- .../tigervnc/vncviewer/ClipboardDialog.java | 204 +- .../com/tigervnc/vncviewer/DesktopWindow.java | 44 +- java/com/tigervnc/vncviewer/Dialog.java | 25 +- java/com/tigervnc/vncviewer/F8Menu.java | 97 +- .../com/tigervnc/vncviewer/OptionsDialog.java | 1636 +++++++++-------- .../vncviewer/OptionsDialogCallback.java | 24 - java/com/tigervnc/vncviewer/PasswdDialog.java | 1 - .../vncviewer/PlatformPixelBuffer.java | 8 +- java/com/tigervnc/vncviewer/ServerDialog.java | 234 ++- java/com/tigervnc/vncviewer/Tunnel.java | 184 +- java/com/tigervnc/vncviewer/Viewport.java | 26 +- java/com/tigervnc/vncviewer/VncViewer.java | 434 ++--- 21 files changed, 1748 insertions(+), 2230 deletions(-) delete mode 100644 java/com/tigervnc/vncviewer/OptionsDialogCallback.java diff --git a/java/com/tigervnc/rfb/AliasParameter.java b/java/com/tigervnc/rfb/AliasParameter.java index a1ae838c..3f20ae46 100644 --- a/java/com/tigervnc/rfb/AliasParameter.java +++ b/java/com/tigervnc/rfb/AliasParameter.java @@ -44,5 +44,13 @@ public class AliasParameter extends VoidParameter { param.setImmutable(); } + public void setHasBeenSet() { + param.setHasBeenSet(); + } + + public boolean hasBeenSet() { + return param.hasBeenSet(); + } + protected VoidParameter param; } diff --git a/java/com/tigervnc/rfb/CConnection.java b/java/com/tigervnc/rfb/CConnection.java index c354868b..483d1f84 100644 --- a/java/com/tigervnc/rfb/CConnection.java +++ b/java/com/tigervnc/rfb/CConnection.java @@ -111,7 +111,7 @@ abstract public class CConnection extends CMsgHandler { int secType = Security.secTypeInvalid; List secTypes = new ArrayList(); - secTypes = Security.GetEnabledSecTypes(); + secTypes = security.GetEnabledSecTypes(); if (cp.isVersion(3,3)) { @@ -292,6 +292,16 @@ abstract public class CConnection extends CMsgHandler { // Ownership of the IdentityVerifier is retained by the CConnection instance. //public IdentityVerifier getIdentityVerifier() { return 0; } + public void framebufferUpdateStart() + { + super.framebufferUpdateStart(); + } + + public void framebufferUpdateEnd() + { + super.framebufferUpdateEnd(); + } + // authSuccess() is called when authentication has succeeded. public void authSuccess() {} diff --git a/java/com/tigervnc/rfb/CSecurityTLS.java b/java/com/tigervnc/rfb/CSecurityTLS.java index a8f6df35..4b20e0bf 100644 --- a/java/com/tigervnc/rfb/CSecurityTLS.java +++ b/java/com/tigervnc/rfb/CSecurityTLS.java @@ -58,11 +58,11 @@ import com.tigervnc.vncviewer.*; public class CSecurityTLS extends CSecurity { - public static StringParameter x509ca - = new StringParameter("x509ca", + public static StringParameter X509CA + = new StringParameter("X509CA", "X509 CA certificate", "", Configuration.ConfigurationObject.ConfViewer); - public static StringParameter x509crl - = new StringParameter("x509crl", + public static StringParameter X509CRL + = new StringParameter("X509CRL", "X509 CRL file", "", Configuration.ConfigurationObject.ConfViewer); private void initGlobal() @@ -80,8 +80,8 @@ public class CSecurityTLS extends CSecurity { manager = null; setDefaults(); - cafile = x509ca.getData(); - crlfile = x509crl.getData(); + cafile = X509CA.getData(); + crlfile = X509CRL.getData(); } public static String getDefaultCA() { @@ -99,9 +99,9 @@ public class CSecurityTLS extends CSecurity { public static void setDefaults() { if (new File(getDefaultCA()).exists()) - x509ca.setDefaultStr(getDefaultCA()); + X509CA.setDefaultStr(getDefaultCA()); if (new File(getDefaultCRL()).exists()) - x509crl.setDefaultStr(getDefaultCRL()); + X509CRL.setDefaultStr(getDefaultCRL()); } // FIXME: diff --git a/java/com/tigervnc/rfb/CSecurityVeNCrypt.java b/java/com/tigervnc/rfb/CSecurityVeNCrypt.java index f353874c..179900a4 100644 --- a/java/com/tigervnc/rfb/CSecurityVeNCrypt.java +++ b/java/com/tigervnc/rfb/CSecurityVeNCrypt.java @@ -134,7 +134,7 @@ public class CSecurityVeNCrypt extends CSecurity { Iterator j; List secTypes = new ArrayList(); - secTypes = Security.GetEnabledExtSecTypes(); + secTypes = security.GetEnabledExtSecTypes(); /* Honor server's security type order */ for (i = 0; i < nAvailableTypes; i++) { diff --git a/java/com/tigervnc/rfb/Configuration.java b/java/com/tigervnc/rfb/Configuration.java index 5d140d95..11fc89a4 100644 --- a/java/com/tigervnc/rfb/Configuration.java +++ b/java/com/tigervnc/rfb/Configuration.java @@ -26,14 +26,19 @@ package com.tigervnc.rfb; import java.io.FileInputStream; import java.io.PrintWriter; +import java.lang.reflect.Field; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.*; +import com.tigervnc.vncviewer.VncViewer; + public class Configuration { static LogWriter vlog = new LogWriter("Configuration"); + private static final String IDENTIFIER_STRING = "TigerVNC Configuration file Version 1.0"; + public enum ConfigurationObject { ConfGlobal, ConfServer, ConfViewer }; // -=- The Global/server/viewer Configuration objects @@ -259,77 +264,6 @@ public class Configuration { list(79, 10); } - public void readAppletParams(java.applet.Applet applet) { - VoidParameter current = head; - while (current != null) { - String str = applet.getParameter(current.getName()); - if (str != null) - current.setParam(str); - current = current._next; - } - } - - public static void load(String filename) { - if (filename == null) - return; - - /* Read parameters from file */ - Properties props = new Properties(); - try { - props.load(new FileInputStream(filename)); - } catch(java.security.AccessControlException e) { - vlog.error("Cannot access system properties:"+e.getMessage()); - return; - } catch (java.lang.Exception e) { - vlog.error("Error opening config file:"+e.getMessage()); - return; - } - - for (Iterator i = props.stringPropertyNames().iterator(); i.hasNext();) { - String name = (String)i.next(); - if (name.startsWith("[")) { - // skip the section delimiters - continue; - } else if (name.equals("host")) { - setParam("Server", props.getProperty(name)); - } else if (name.equals("disableclipboard")) { - setParam("RecvClipboard", props.getProperty(name)); - setParam("SendClipboard", props.getProperty(name)); - } else if (name.equals("localcursor")) { - setParam("UseLocalCursor", props.getProperty(name)); - } else { - if (!setParam(name, props.getProperty(name))) - vlog.debug("Cannot set parameter: "+name); - } - } - } - - public static void save(String filename) { - PrintWriter pw = null; - try { - pw = new PrintWriter(filename, "UTF-8"); - } catch (java.lang.Exception e) { - vlog.error("Error opening config file:"+e.getMessage()); - return; - } - - pw.println("# TigerVNC viewer configuration"); - DateFormat dateFormat = new SimpleDateFormat("E MMM d k:m:s z yyyy"); - Date date = new Date(); - pw.println("# "+dateFormat.format(date)); - VoidParameter current = Configuration.global().head; - while (current != null) { - String name = current.getName(); - String value = current.getValueStr(); - if (!name.equals("Server") && !name.equals("Port") && - value != null && value != current.getDefaultStr()) - pw.println(name+"="+current.getValueStr()); - current = current._next; - } - pw.flush(); - pw.close(); - } - // Name for this Configuration private String name; diff --git a/java/com/tigervnc/rfb/Security.java b/java/com/tigervnc/rfb/Security.java index a68ae3e9..e256e6eb 100644 --- a/java/com/tigervnc/rfb/Security.java +++ b/java/com/tigervnc/rfb/Security.java @@ -60,6 +60,8 @@ public class Security { public static final int secResultFailed = 1; public static final int secResultTooMany = 2; // deprecated + public Security() { } + public Security(StringParameter secTypes) { String secTypesStr; @@ -70,9 +72,9 @@ public class Security { secTypesStr = null; } - public static List enabledSecTypes = new ArrayList(); + private List enabledSecTypes = new ArrayList(); - public static final List GetEnabledSecTypes() + public final List GetEnabledSecTypes() { List result = new ArrayList(); @@ -98,7 +100,7 @@ public class Security { return (result); } - public static final List GetEnabledExtSecTypes() + public final List GetEnabledExtSecTypes() { List result = new ArrayList(); @@ -111,7 +113,7 @@ public class Security { return (result); } - public static final void EnableSecType(int secType) + public final void EnableSecType(int secType) { for (Iterator i = enabledSecTypes.iterator(); i.hasNext(); ) @@ -134,7 +136,29 @@ public class Security { return false; } - public static void DisableSecType(int secType) { enabledSecTypes.remove((Object)secType); } + public String ToString() + { + Iterator i; + String out = new String(""); + boolean firstpass = true; + String name; + + for (i = enabledSecTypes.iterator(); i.hasNext(); ) { + name = secTypeName((Integer)i.next()); + if (name.startsWith("[")) /* Unknown security type */ + continue; + + if (!firstpass) + out = out.concat(","); + else + firstpass = false; + out = out.concat(name); + } + + return out; + } + + public void DisableSecType(int secType) { enabledSecTypes.remove((Object)secType); } public static int secTypeNum(String name) { if (name.equalsIgnoreCase("None")) return secTypeNone; @@ -203,7 +227,9 @@ public class Security { return (result); } - public final void SetSecTypes(List secTypes) { enabledSecTypes = secTypes; } + public final void SetSecTypes(List secTypes) { + enabledSecTypes = secTypes; + } static LogWriter vlog = new LogWriter("Security"); } diff --git a/java/com/tigervnc/rfb/SecurityClient.java b/java/com/tigervnc/rfb/SecurityClient.java index 59499b1e..ff2433c2 100644 --- a/java/com/tigervnc/rfb/SecurityClient.java +++ b/java/com/tigervnc/rfb/SecurityClient.java @@ -78,9 +78,9 @@ public class SecurityClient extends Security { //UserPasswdGetter upg = null; String msg = null; - static StringParameter secTypes + public static StringParameter secTypes = new StringParameter("SecurityTypes", - "Specify which security scheme to use (None, VncAuth)", - "Ident,TLSIdent,X509Ident,X509Plain,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone,VncAuth,None", Configuration.ConfigurationObject.ConfViewer); + "Specify which security scheme to use (None, VncAuth, Plain, Ident, TLSNone, TLSVnc, TLSPlain, TLSIdent, X509None, X509Vnc, X509Plain, X509Ident)", + "X509Ident,X509Plain,TLSIdent,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone,Ident,VncAuth,None", Configuration.ConfigurationObject.ConfViewer); } diff --git a/java/com/tigervnc/vncviewer/BIPixelBuffer.java b/java/com/tigervnc/vncviewer/BIPixelBuffer.java index 9612b36f..1634ebd1 100644 --- a/java/com/tigervnc/vncviewer/BIPixelBuffer.java +++ b/java/com/tigervnc/vncviewer/BIPixelBuffer.java @@ -27,8 +27,8 @@ import com.tigervnc.rfb.Exception; public class BIPixelBuffer extends PlatformPixelBuffer implements ImageObserver { - public BIPixelBuffer(int w, int h, CConn cc_, DesktopWindow desktop_) { - super(w, h, cc_, desktop_); + public BIPixelBuffer(PixelFormat pf, int w, int h, DesktopWindow desktop_) { + super(pf, w, h, desktop_); clip = new Rectangle(); } diff --git a/java/com/tigervnc/vncviewer/CConn.java b/java/com/tigervnc/vncviewer/CConn.java index b9680ef7..d7134344 100644 --- a/java/com/tigervnc/vncviewer/CConn.java +++ b/java/com/tigervnc/vncviewer/CConn.java @@ -35,7 +35,9 @@ package com.tigervnc.vncviewer; import java.awt.*; +import java.awt.datatransfer.StringSelection; import java.awt.event.*; +import java.awt.Toolkit; import java.io.IOException; import java.io.InputStream; @@ -49,6 +51,7 @@ import javax.swing.ImageIcon; import java.net.InetSocketAddress; import java.net.SocketException; import java.util.*; +import java.util.prefs.*; import com.tigervnc.rdr.*; import com.tigervnc.rfb.*; @@ -57,16 +60,18 @@ import com.tigervnc.rfb.Exception; import com.tigervnc.network.Socket; import com.tigervnc.network.TcpSocket; +import static com.tigervnc.vncviewer.Parameters.*; + public class CConn extends CConnection implements - UserPasswdGetter, UserMsgBox, OptionsDialogCallback, + UserPasswdGetter, UserMsgBox, FdInStreamBlockCallback, ActionListener { - public final PixelFormat getPreferredPF() { return fullColourPF; } - static final PixelFormat verylowColourPF = + public final PixelFormat getPreferredPF() { return fullColorPF; } + static final PixelFormat verylowColorPF = new PixelFormat(8, 3, false, true, 1, 1, 1, 2, 1, 0); - static final PixelFormat lowColourPF = + static final PixelFormat lowColorPF = new PixelFormat(8, 6, false, true, 3, 3, 3, 4, 2, 0); - static final PixelFormat mediumColourPF = + static final PixelFormat mediumColorPF = new PixelFormat(8, 8, false, false, 7, 7, 3, 0, 3, 6); static final int KEY_LOC_SHIFT_R = 0; static final int KEY_LOC_SHIFT_L = 16; @@ -75,67 +80,48 @@ public class CConn extends CConnection implements //////////////////////////////////////////////////////////////////// // The following methods are all called from the RFB thread - public CConn(VncViewer viewer_, Socket sock_, - String vncServerName) + public CConn(String vncServerName, Socket socket) { - sock = sock_; viewer = viewer_; pendingPFChange = false; currentEncoding = Encodings.encodingTight; lastServerEncoding = -1; - fullColour = viewer.fullColour.getValue(); - lowColourLevel = viewer.lowColourLevel.getValue(); - autoSelect = viewer.autoSelect.getValue(); formatChange = false; encodingChange = false; - fullScreen = viewer.fullScreen.getValue(); - menuKeyCode = MenuKey.getMenuKeyCode(); - options = new OptionsDialog(this); - options.initDialog(); - clipboardDialog = new ClipboardDialog(this); firstUpdate = true; pendingUpdate = false; continuousUpdates = false; forceNonincremental = true; supportsSyncFence = false; + + setShared(shared.getValue()); + sock = socket; downKeySym = new HashMap(); - setShared(viewer.shared.getValue()); upg = this; msg = this; - String encStr = viewer.preferredEncoding.getValue(); - int encNum = Encodings.encodingNum(encStr); - if (encNum != -1) { + int encNum = Encodings.encodingNum(preferredEncoding.getValue()); + if (encNum != -1) currentEncoding = encNum; - } + + cp.supportsLocalCursor = useLocalCursor.getValue(); + cp.supportsDesktopResize = true; cp.supportsExtendedDesktopSize = true; - cp.supportsSetDesktopSize = false; - cp.supportsClientRedirect = true; cp.supportsDesktopRename = true; - cp.supportsLocalCursor = viewer.useLocalCursor.getValue(); - cp.customCompressLevel = viewer.customCompressLevel.getValue(); - cp.compressLevel = viewer.compressLevel.getValue(); - cp.noJpeg = viewer.noJpeg.getValue(); - cp.qualityLevel = viewer.qualityLevel.getValue(); - initMenu(); - - if (sock != null) { - String name = sock.getPeerEndpoint(); - vlog.info("Accepted connection from " + name); - } else { - if (vncServerName != null && - !viewer.alwaysShowServerDialog.getValue()) { - setServerName(Hostname.getHost(vncServerName)); - setServerPort(Hostname.getPort(vncServerName)); - } else { - ServerDialog dlg = new ServerDialog(options, vncServerName, this); - boolean ret = dlg.showDialog(); - if (!ret) { - close(); - return; - } - setServerName(viewer.vncServerName.getValueStr()); - setServerPort(viewer.vncServerPort.getValue()); - } + cp.supportsSetDesktopSize = false; + cp.supportsClientRedirect = true; + if (customCompressLevel.getValue()) + cp.compressLevel = compressLevel.getValue(); + else + cp.compressLevel = -1; + + if (noJpeg.getValue()) + cp.qualityLevel = qualityLevel.getValue(); + else + cp.qualityLevel = -1; + + if (sock == null) { + setServerName(Hostname.getHost(vncServerName)); + setServerPort(Hostname.getPort(vncServerName)); try { - if (viewer.tunnel.getValue() || (viewer.via.getValue() != null)) { + if (tunnel.getValue() || !via.getValue().isEmpty()) { int localPort = TcpSocket.findFreeTcpPort(); if (localPort == 0) throw new Exception("Could not obtain free TCP port"); @@ -148,11 +134,21 @@ public class CConn extends CConnection implements throw new Exception(e.getMessage()); } vlog.info("connected to host "+getServerName()+" port "+getServerPort()); + } else { + String name = sock.getPeerEndpoint(); + if (listenMode.getValue()) + vlog.info("Accepted connection from " + name); + else + vlog.info("connected to host "+Hostname.getHost(name)+" port "+Hostname.getPort(name)); } sock.inStream().setBlockCallback(this); + setStreams(sock.inStream(), sock.outStream()); + initialiseProtocol(); + + OptionsDialog.addCallback("handleOptions", this); } public void refreshFramebuffer() @@ -196,7 +192,7 @@ public class CConn extends CConnection implements public final boolean getUserPasswd(StringBuffer user, StringBuffer passwd) { String title = ("VNC Authentication [" +csecurity.description() + "]"); - String passwordFileStr = viewer.passwordFile.getValue(); + String passwordFileStr = passwordFile.getValue(); PasswdDialog dlg; if (user == null && !passwordFileStr.equals("")) { @@ -222,16 +218,16 @@ public class CConn extends CConnection implements if (user == null) { dlg = new PasswdDialog(title, (user == null), (passwd == null)); } else { - if ((passwd == null) && viewer.sendLocalUsername.getValue()) { + if ((passwd == null) && sendLocalUsername.getValue()) { user.append((String)System.getProperties().get("user.name")); return true; } - dlg = new PasswdDialog(title, viewer.sendLocalUsername.getValue(), + dlg = new PasswdDialog(title, sendLocalUsername.getValue(), (passwd == null)); } - if (!dlg.showDialog()) return false; + dlg.showDialog(); if (user != null) { - if (viewer.sendLocalUsername.getValue()) { + if (sendLocalUsername.getValue()) { user.append((String)System.getProperties().get("user.name")); } else { user.append(dlg.userEntry.getText()); @@ -252,13 +248,13 @@ public class CConn extends CConnection implements // If using AutoSelect with old servers, start in FullColor // mode. See comment in autoSelectFormatAndEncoding. - if (cp.beforeVersion(3, 8) && autoSelect) - fullColour = true; + if (cp.beforeVersion(3, 8) && autoSelect.getValue()) + fullColor.setParam(true); serverPF = cp.pf(); - desktop = new DesktopWindow(cp.width, cp.height, serverPF, this); - fullColourPF = desktop.getPreferredPF(); + desktop = new DesktopWindow(cp.width, cp.height, cp.name(), serverPF, this); + fullColorPF = desktop.getPreferredPF(); // Force a switch to the format and encoding we'd like formatChange = true; encodingChange = true; @@ -273,55 +269,7 @@ public class CConn extends CConnection implements cp.setPF(pendingPF); pendingPFChange = false; - if (viewer.embed.getValue()) { - setupEmbeddedFrame(); - } else { - recreateViewport(); - } - } - - void setupEmbeddedFrame() { - UIManager.getDefaults().put("ScrollPane.ancestorInputMap", - new UIDefaults.LazyInputMap(new Object[]{})); - JScrollPane sp = new JScrollPane(); - sp.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - sp.getViewport().setBackground(Color.BLACK); - InputMap im = sp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); - int ctrlAltShiftMask = Event.SHIFT_MASK | Event.CTRL_MASK | Event.ALT_MASK; - if (im != null) { - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, ctrlAltShiftMask), - "unitScrollUp"); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, ctrlAltShiftMask), - "unitScrollDown"); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, ctrlAltShiftMask), - "unitScrollLeft"); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, ctrlAltShiftMask), - "unitScrollRight"); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, ctrlAltShiftMask), - "scrollUp"); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, ctrlAltShiftMask), - "scrollDown"); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, ctrlAltShiftMask), - "scrollLeft"); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, ctrlAltShiftMask), - "scrollRight"); - } - sp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); - sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); - desktop.setViewport(sp.getViewport()); - viewer.getContentPane().removeAll(); - viewer.add(sp); - viewer.addFocusListener(new FocusAdapter() { - public void focusGained(FocusEvent e) { - if (desktop.isAncestorOf(viewer)) - desktop.requestFocus(); - } - public void focusLost(FocusEvent e) { - releaseDownKeys(); - } - }); - viewer.validate(); - desktop.requestFocus(); + recreateViewport(); } // setDesktopSize() is called when the desktop size changes (including when @@ -350,10 +298,13 @@ public class CConn extends CConnection implements String x509subject) { try { sock.close(); - setServerPort(port); sock = new TcpSocket(host, port); vlog.info("Redirected to "+host+":"+port); - VncViewer.newViewer(viewer, sock, true); + setServerName(host); + setServerPort(port); + sock.inStream().setBlockCallback(this); + setStreams(sock.inStream(), sock.outStream()); + initialiseProtocol(); } catch (java.lang.Exception e) { throw new Exception(e.getMessage()); } @@ -362,10 +313,8 @@ public class CConn extends CConnection implements // setName() is called when the desktop name changes public void setName(String name) { super.setName(name); - - if (viewport != null) { + if (viewport != null) viewport.setTitle(name+" - TigerVNC"); - } } // framebufferUpdateStart() is called at the beginning of an update. @@ -374,6 +323,8 @@ public class CConn extends CConnection implements // one. public void framebufferUpdateStart() { + super.framebufferUpdateStart(); + // Note: This might not be true if sync fences are supported pendingUpdate = false; @@ -386,6 +337,7 @@ public class CConn extends CConnection implements // appropriately, and then request another incremental update. public void framebufferUpdateEnd() { + super.framebufferUpdateEnd(); desktop.updateWindow(); @@ -398,10 +350,10 @@ public class CConn extends CConnection implements writer().writeFence(fenceTypes.fenceFlagRequest | fenceTypes.fenceFlagSyncNext, 0, null); if (cp.supportsSetDesktopSize && - viewer.desktopSize.getValue() != null && - viewer.desktopSize.getValue().split("x").length == 2) { - width = Integer.parseInt(viewer.desktopSize.getValue().split("x")[0]); - height = Integer.parseInt(viewer.desktopSize.getValue().split("x")[1]); + !desktopSize.getValue().isEmpty() && + desktopSize.getValue().split("x").length == 2) { + width = Integer.parseInt(desktopSize.getValue().split("x")[0]); + height = Integer.parseInt(desktopSize.getValue().split("x")[1]); ScreenSet layout; layout = cp.screenLayout; @@ -442,24 +394,28 @@ public class CConn extends CConnection implements } // Compute new settings based on updated bandwidth values - if (autoSelect) + if (autoSelect.getValue()) autoSelectFormatAndEncoding(); } // The rest of the callbacks are fairly self-explanatory... - public void setColourMapEntries(int firstColour, int nColours, int[] rgbs) { - desktop.setColourMapEntries(firstColour, nColours, rgbs); + public void setColourMapEntries(int firstColor, int nColors, int[] rgbs) { + desktop.setColourMapEntries(firstColor, nColors, rgbs); } public void bell() { - if (viewer.acceptBell.getValue()) + if (acceptBell.getValue()) desktop.getToolkit().beep(); } public void serverCutText(String str, int len) { - if (viewer.acceptClipboard.getValue()) - clipboardDialog.serverCutText(str, len); + StringSelection buffer; + + if (!acceptClipboard.getValue()) + return; + + ClipboardDialog.serverCutText(str); } // We start timing on beginRect and stop timing on endRect, to @@ -543,50 +499,54 @@ public class CConn extends CConnection implements return; desktop.resize(); - if (viewer.embed.getValue()) { - setupEmbeddedFrame(); - } else { + if (!firstUpdate) recreateViewport(); - } } - public void setEmbeddedFeatures(boolean s) { - menu.restore.setEnabled(s); - menu.minimize.setEnabled(s); - menu.maximize.setEnabled(s); - menu.fullScreen.setEnabled(s); - menu.newConn.setEnabled(s); - options.fullScreen.setEnabled(s); - options.fullScreenAllMonitors.setEnabled(s); - options.scalingFactor.setEnabled(s); - } - // recreateViewport() recreates our top-level window. This seems to be // better than attempting to resize the existing window, at least with // various X window managers. public void recreateViewport() { - if (viewer.embed.getValue()) - return; - if (viewport != null) viewport.dispose(); - viewport = new Viewport(cp.name(), this); - viewport.setUndecorated(fullScreen); - desktop.setViewport(viewport.getViewport()); - reconfigureViewport(); - if ((cp.width > 0) && (cp.height > 0)) - viewport.setVisible(true); - desktop.requestFocusInWindow(); + if (embed.getValue()) { + desktop.setViewport(VncViewer.getViewport()); + Container viewer = + SwingUtilities.getAncestorOfClass(JApplet.class, desktop); + viewer.addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + Container c = + SwingUtilities.getAncestorOfClass(JApplet.class, desktop); + if (c != null && desktop.isAncestorOf(c)) + desktop.requestFocus(); + } + public void focusLost(FocusEvent e) { + releaseDownKeys(); + } + }); + viewer.validate(); + desktop.requestFocus(); + } else { + if (viewport != null) + viewport.dispose(); + viewport = new Viewport(cp.name(), this); + viewport.setUndecorated(fullScreen.getValue()); + desktop.setViewport(viewport.getViewport()); + reconfigureViewport(); + if ((cp.width > 0) && (cp.height > 0)) + viewport.setVisible(true); + desktop.requestFocusInWindow(); + } } private void reconfigureViewport() { Dimension dpySize = viewport.getScreenSize(); int w = desktop.scaledWidth; int h = desktop.scaledHeight; - if (fullScreen) { - if (!viewer.fullScreenAllMonitors.getValue()) + if (fullScreen.getValue()) { + if (!fullScreenAllMonitors.getValue()) viewport.setExtendedState(JFrame.MAXIMIZED_BOTH); viewport.setBounds(viewport.getScreenBounds()); - if (!viewer.fullScreenAllMonitors.getValue()) + if (!fullScreenAllMonitors.getValue()) Viewport.setFullScreenWindow(viewport); } else { int wmDecorationWidth = viewport.getInsets().left + viewport.getInsets().right; @@ -629,7 +589,7 @@ public class CConn extends CConnection implements private void autoSelectFormatAndEncoding() { long kbitsPerSecond = sock.inStream().kbitsPerSecond(); long timeWaited = sock.inStream().timeWaited(); - boolean newFullColour = fullColour; + boolean newFullColor = fullColor.getValue(); int newQualityLevel = cp.qualityLevel; // Always use Tight @@ -653,7 +613,7 @@ public class CConn extends CConnection implements vlog.info("Throughput "+kbitsPerSecond+ " kbit/s - changing to quality "+newQualityLevel); cp.qualityLevel = newQualityLevel; - viewer.qualityLevel.setParam(Integer.toString(newQualityLevel)); + qualityLevel.setParam(newQualityLevel); encodingChange = true; } } @@ -670,12 +630,12 @@ public class CConn extends CConnection implements } // Select best color level - newFullColour = (kbitsPerSecond > 256); - if (newFullColour != fullColour) { + newFullColor = (kbitsPerSecond > 256); + if (newFullColor != fullColor.getValue()) { vlog.info("Throughput "+kbitsPerSecond+ " kbit/s - full color is now "+ - (newFullColour ? "enabled" : "disabled")); - fullColour = newFullColour; + (newFullColor ? "enabled" : "disabled")); + fullColor.setParam(newFullColor); formatChange = true; forceNonincremental = true; } @@ -691,15 +651,15 @@ public class CConn extends CConnection implements /* Catch incorrect requestNewUpdate calls */ assert(!pendingUpdate || supportsSyncFence); - if (fullColour) { - pf = fullColourPF; + if (fullColor.getValue()) { + pf = fullColorPF; } else { - if (lowColourLevel == 0) { - pf = verylowColourPF; - } else if (lowColourLevel == 1) { - pf = lowColourPF; + if (lowColorLevel.getValue() == 0) { + pf = verylowColorPF; + } else if (lowColorLevel.getValue() == 1) { + pf = lowColorPF; } else { - pf = mediumColourPF; + pf = mediumColorPF; } } @@ -746,12 +706,11 @@ public class CConn extends CConnection implements // close() shuts down the socket, thus waking up the RFB thread. public void close() { if (closeListener != null) { - viewer.embed.setParam(true); - if (VncViewer.nViewers == 1) { - JFrame f = (JFrame)JOptionPane.getFrameForComponent(viewer); - if (f != null) - f.dispatchEvent(new WindowEvent(f, WindowEvent.WINDOW_CLOSING)); - } + embed.setParam(true); + JFrame f = + (JFrame)SwingUtilities.getAncestorOfClass(JFrame.class, desktop); + if (f != null) + f.dispatchEvent(new WindowEvent(f, WindowEvent.WINDOW_CLOSING)); } deleteWindow(); shuttingDown = true; @@ -763,47 +722,6 @@ public class CConn extends CConnection implements } } - // Menu callbacks. These are guaranteed only to be called after serverInit() - // has been called, since the menu is only accessible from the DesktopWindow - - private void initMenu() { - menu = new F8Menu(this); - } - - void showMenu(int x, int y) { - String os = System.getProperty("os.name"); - if (os.startsWith("Windows")) - com.sun.java.swing.plaf.windows.WindowsLookAndFeel.setMnemonicHidden(false); - menu.show(desktop, x, y); - } - - void showAbout() { - String pkgDate = ""; - String pkgTime = ""; - try { - Manifest manifest = new Manifest(VncViewer.timestamp); - Attributes attributes = manifest.getMainAttributes(); - pkgDate = attributes.getValue("Package-Date"); - pkgTime = attributes.getValue("Package-Time"); - } catch (java.lang.Exception e) { } - - Window fullScreenWindow = Viewport.getFullScreenWindow(); - if (fullScreenWindow != null) - Viewport.setFullScreenWindow(null); - String msg = - String.format(VncViewer.aboutText, VncViewer.version, VncViewer.build, - VncViewer.buildDate, VncViewer.buildTime); - JOptionPane op = - new JOptionPane(msg, JOptionPane.INFORMATION_MESSAGE, - JOptionPane.DEFAULT_OPTION, VncViewer.logoIcon); - JDialog dlg = op.createDialog(desktop, "About TigerVNC Viewer for Java"); - dlg.setIconImage(VncViewer.frameIcon); - dlg.setAlwaysOnTop(true); - dlg.setVisible(true); - if (fullScreenWindow != null) - Viewport.setFullScreenWindow(fullScreenWindow); - } - void showInfo() { Window fullScreenWindow = Viewport.getFullScreenWindow(); if (fullScreenWindow != null) @@ -830,8 +748,10 @@ public class CConn extends CConnection implements cp.majorVersion, cp.minorVersion, Security.secTypeName(csecurity.getType()), csecurity.description()); - JOptionPane op = new JOptionPane(msg, JOptionPane.PLAIN_MESSAGE, - JOptionPane.DEFAULT_OPTION); + Object[] options = {"Close \u21B5"}; + JOptionPane op = + new JOptionPane(msg, JOptionPane.PLAIN_MESSAGE, + JOptionPane.DEFAULT_OPTION, null, options); JDialog dlg = op.createDialog(desktop, "VNC connection info"); dlg.setIconImage(VncViewer.frameIcon); dlg.setAlwaysOnTop(true); @@ -845,507 +765,74 @@ public class CConn extends CConnection implements pendingUpdate = true; } - - // OptionsDialogCallback. setOptions() sets the options dialog's checkboxes - // etc to reflect our flags. getOptions() sets our flags according to the - // options dialog's checkboxes. They are both called from the GUI thread. - // Some of the flags are also accessed by the RFB thread. I believe that - // reading and writing boolean and int values in java is atomic, so there is - // no need for synchronization. - - public void setOptions() { - int digit; - options.autoSelect.setSelected(autoSelect); - options.fullColour.setSelected(fullColour); - options.veryLowColour.setSelected(!fullColour && lowColourLevel == 0); - options.lowColour.setSelected(!fullColour && lowColourLevel == 1); - options.mediumColour.setSelected(!fullColour && lowColourLevel == 2); - options.tight.setSelected(currentEncoding == Encodings.encodingTight); - options.zrle.setSelected(currentEncoding == Encodings.encodingZRLE); - options.hextile.setSelected(currentEncoding == Encodings.encodingHextile); - options.raw.setSelected(currentEncoding == Encodings.encodingRaw); - - options.customCompressLevel.setSelected(viewer.customCompressLevel.getValue()); - digit = 0 + viewer.compressLevel.getValue(); - if (digit >= 0 && digit <= 9) { - options.compressLevel.setSelectedItem(digit); - } else { - options.compressLevel.setSelectedItem(Integer.parseInt(viewer.compressLevel.getDefaultStr())); - } - options.noJpeg.setSelected(!viewer.noJpeg.getValue()); - digit = 0 + viewer.qualityLevel.getValue(); - if (digit >= 0 && digit <= 9) { - options.qualityLevel.setSelectedItem(digit); - } else { - options.qualityLevel.setSelectedItem(Integer.parseInt(viewer.qualityLevel.getDefaultStr())); - } - - options.viewOnly.setSelected(viewer.viewOnly.getValue()); - options.acceptClipboard.setSelected(viewer.acceptClipboard.getValue()); - options.sendClipboard.setSelected(viewer.sendClipboard.getValue()); - options.menuKey.setSelectedItem(KeyEvent.getKeyText(MenuKey.getMenuKeyCode())); - options.sendLocalUsername.setSelected(viewer.sendLocalUsername.getValue()); - - if (state() == RFBSTATE_NORMAL) { - options.shared.setEnabled(false); - options.secVeNCrypt.setEnabled(false); - options.encNone.setEnabled(false); - options.encTLS.setEnabled(false); - options.encX509.setEnabled(false); - options.x509ca.setEnabled(false); - options.caButton.setEnabled(false); - options.x509crl.setEnabled(false); - options.crlButton.setEnabled(false); - options.secIdent.setEnabled(false); - options.secNone.setEnabled(false); - options.secVnc.setEnabled(false); - options.secPlain.setEnabled(false); - options.sendLocalUsername.setEnabled(false); - options.cfLoadButton.setEnabled(false); - options.cfSaveAsButton.setEnabled(true); - options.sshTunnel.setEnabled(false); - options.sshUseGateway.setEnabled(false); - options.sshUser.setEnabled(false); - options.sshHost.setEnabled(false); - options.sshPort.setEnabled(false); - options.sshUseExt.setEnabled(false); - options.sshClient.setEnabled(false); - options.sshClientBrowser.setEnabled(false); - options.sshArgsDefault.setEnabled(false); - options.sshArgsCustom.setEnabled(false); - options.sshArguments.setEnabled(false); - options.sshConfig.setEnabled(false); - options.sshConfigBrowser.setEnabled(false); - options.sshKeyFile.setEnabled(false); - options.sshKeyFileBrowser.setEnabled(false); - } else { - options.shared.setSelected(viewer.shared.getValue()); - options.sendLocalUsername.setSelected(viewer.sendLocalUsername.getValue()); - options.cfSaveAsButton.setEnabled(false); - if (viewer.tunnel.getValue() || viewer.via.getValue() != null) - options.sshTunnel.setSelected(true); - if (viewer.via.getValue() != null) - options.sshUseGateway.setSelected(true); - options.sshUser.setText(Tunnel.getSshUser(this)); - options.sshHost.setText(Tunnel.getSshHost(this)); - options.sshPort.setText(Integer.toString(Tunnel.getSshPort(this))); - options.sshUseExt.setSelected(viewer.extSSH.getValue()); - File client = new File(viewer.extSSHClient.getValue()); - if (client.exists() && client.canRead()) - options.sshClient.setText(client.getAbsolutePath()); - if (viewer.extSSHArgs.getValue() == null) { - options.sshArgsDefault.setSelected(true); - options.sshArguments.setText(""); - } else { - options.sshArgsCustom.setSelected(true); - options.sshArguments.setText(viewer.extSSHArgs.getValue()); - } - File config = new File(viewer.sshConfig.getValue()); - if (config.exists() && config.canRead()) - options.sshConfig.setText(config.getAbsolutePath()); - options.sshKeyFile.setText(Tunnel.getSshKeyFile(this)); - - /* Process non-VeNCrypt sectypes */ - java.util.List secTypes = new ArrayList(); - secTypes = Security.GetEnabledSecTypes(); - for (Iterator i = secTypes.iterator(); i.hasNext();) { - switch ((Integer)i.next()) { - case Security.secTypeVeNCrypt: - options.secVeNCrypt.setSelected(UserPreferences.getBool("viewer", "secVeNCrypt", true)); - break; - case Security.secTypeNone: - options.encNone.setSelected(true); - options.secNone.setSelected(UserPreferences.getBool("viewer", "secTypeNone", true)); - break; - case Security.secTypeVncAuth: - options.encNone.setSelected(true); - options.secVnc.setSelected(UserPreferences.getBool("viewer", "secTypeVncAuth", true)); - break; - } - } - - /* Process VeNCrypt subtypes */ - if (options.secVeNCrypt.isSelected()) { - java.util.List secTypesExt = new ArrayList(); - secTypesExt = Security.GetEnabledExtSecTypes(); - for (Iterator iext = secTypesExt.iterator(); iext.hasNext();) { - switch ((Integer)iext.next()) { - case Security.secTypePlain: - options.encNone.setSelected(UserPreferences.getBool("viewer", "encNone", true)); - options.secPlain.setSelected(UserPreferences.getBool("viewer", "secPlain", true)); - break; - case Security.secTypeIdent: - options.encNone.setSelected(UserPreferences.getBool("viewer", "encNone", true)); - options.secIdent.setSelected(UserPreferences.getBool("viewer", "secIdent", true)); - break; - case Security.secTypeTLSNone: - options.encTLS.setSelected(UserPreferences.getBool("viewer", "encTLS", true)); - options.secNone.setSelected(UserPreferences.getBool("viewer", "secNone", true)); - break; - case Security.secTypeTLSVnc: - options.encTLS.setSelected(UserPreferences.getBool("viewer", "encTLS", true)); - options.secVnc.setSelected(UserPreferences.getBool("viewer", "secVnc", true)); - break; - case Security.secTypeTLSPlain: - options.encTLS.setSelected(UserPreferences.getBool("viewer", "encTLS", true)); - options.secPlain.setSelected(UserPreferences.getBool("viewer", "secPlain", true)); - break; - case Security.secTypeTLSIdent: - options.encTLS.setSelected(UserPreferences.getBool("viewer", "encTLS", true)); - options.secIdent.setSelected(UserPreferences.getBool("viewer", "secIdent", true)); - break; - case Security.secTypeX509None: - options.encX509.setSelected(UserPreferences.getBool("viewer", "encX509", true)); - options.secNone.setSelected(UserPreferences.getBool("viewer", "secNone", true)); - break; - case Security.secTypeX509Vnc: - options.encX509.setSelected(UserPreferences.getBool("viewer", "encX509", true)); - options.secVnc.setSelected(UserPreferences.getBool("viewer", "secVnc", true)); - break; - case Security.secTypeX509Plain: - options.encX509.setSelected(UserPreferences.getBool("viewer", "encX509", true)); - options.secPlain.setSelected(UserPreferences.getBool("viewer", "secPlain", true)); - break; - case Security.secTypeX509Ident: - options.encX509.setSelected(UserPreferences.getBool("viewer", "encX509", true)); - options.secIdent.setSelected(UserPreferences.getBool("viewer", "secIdent", true)); - break; - } - } - } - File caFile = new File(viewer.x509ca.getValue()); - if (caFile.exists() && caFile.canRead()) - options.x509ca.setText(caFile.getAbsolutePath()); - File crlFile = new File(viewer.x509crl.getValue()); - if (crlFile.exists() && crlFile.canRead()) - options.x509crl.setText(crlFile.getAbsolutePath()); - options.encNone.setEnabled(options.secVeNCrypt.isSelected()); - options.encTLS.setEnabled(options.secVeNCrypt.isSelected()); - options.encX509.setEnabled(options.secVeNCrypt.isSelected()); - options.x509ca.setEnabled(options.secVeNCrypt.isSelected() && - options.encX509.isSelected()); - options.caButton.setEnabled(options.secVeNCrypt.isSelected() && - options.encX509.isSelected()); - options.x509crl.setEnabled(options.secVeNCrypt.isSelected() && - options.encX509.isSelected()); - options.crlButton.setEnabled(options.secVeNCrypt.isSelected() && - options.encX509.isSelected()); - options.secIdent.setEnabled(options.secVeNCrypt.isSelected()); - options.secPlain.setEnabled(options.secVeNCrypt.isSelected()); - options.sendLocalUsername.setEnabled(options.secPlain.isSelected()|| - options.secIdent.isSelected()); - options.sshTunnel.setEnabled(true); - options.sshUseGateway.setEnabled(options.sshTunnel.isSelected()); - options.sshUser.setEnabled(options.sshTunnel.isSelected() && - options.sshUseGateway.isEnabled() && - options.sshUseGateway.isSelected()); - options.sshHost.setEnabled(options.sshTunnel.isSelected() && - options.sshUseGateway.isEnabled() && - options.sshUseGateway.isSelected()); - options.sshPort.setEnabled(options.sshTunnel.isSelected() && - options.sshUseGateway.isEnabled() && - options.sshUseGateway.isSelected()); - options.sshUseExt.setEnabled(options.sshTunnel.isSelected()); - options.sshClient.setEnabled(options.sshTunnel.isSelected() && - options.sshUseExt.isEnabled() && - options.sshUseExt.isSelected()); - options.sshClientBrowser.setEnabled(options.sshTunnel.isSelected() && - options.sshUseExt.isEnabled() && - options.sshUseExt.isSelected()); - options.sshArgsDefault.setEnabled(options.sshTunnel.isSelected() && - options.sshUseExt.isEnabled() && - options.sshUseExt.isSelected()); - options.sshArgsCustom.setEnabled(options.sshTunnel.isSelected() && - options.sshUseExt.isEnabled() && - options.sshUseExt.isSelected()); - options.sshArguments.setEnabled(options.sshTunnel.isSelected() && - options.sshUseExt.isEnabled() && - options.sshUseExt.isSelected() && - options.sshArgsCustom.isSelected()); - options.sshConfig.setEnabled(options.sshTunnel.isSelected() && - options.sshUseExt.isEnabled() && - !options.sshUseExt.isSelected()); - options.sshConfigBrowser.setEnabled(options.sshTunnel.isSelected() && - options.sshUseExt.isEnabled() && - !options.sshUseExt.isSelected()); - options.sshKeyFile.setEnabled(options.sshTunnel.isSelected() && - options.sshUseExt.isEnabled() && - !options.sshUseExt.isSelected()); - options.sshKeyFileBrowser.setEnabled(options.sshTunnel.isSelected() && - options.sshUseExt.isEnabled() && - !options.sshUseExt.isSelected()); - } - - options.fullScreen.setSelected(fullScreen); - options.fullScreenAllMonitors.setSelected(viewer.fullScreenAllMonitors.getValue()); - options.useLocalCursor.setSelected(viewer.useLocalCursor.getValue()); - options.acceptBell.setSelected(viewer.acceptBell.getValue()); - String scaleString = viewer.scalingFactor.getValue(); - if (scaleString.equalsIgnoreCase("Auto")) { - options.scalingFactor.setSelectedItem("Auto"); - } else if(scaleString.equalsIgnoreCase("FixedRatio")) { - options.scalingFactor.setSelectedItem("Fixed Aspect Ratio"); - } else { - digit = Integer.parseInt(scaleString); - if (digit >= 1 && digit <= 1000) { - options.scalingFactor.setSelectedItem(digit+"%"); - } else { - digit = Integer.parseInt(viewer.scalingFactor.getDefaultStr()); - options.scalingFactor.setSelectedItem(digit+"%"); - } - int scaleFactor = - Integer.parseInt(scaleString.substring(0, scaleString.length())); - } - if (viewer.desktopSize.getValue() != null && - viewer.desktopSize.getValue().split("x").length == 2) { - options.desktopSize.setSelected(true); - String desktopWidth = viewer.desktopSize.getValue().split("x")[0]; - options.desktopWidth.setText(desktopWidth); - String desktopHeight = viewer.desktopSize.getValue().split("x")[1]; - options.desktopHeight.setText(desktopHeight); - } + public synchronized int currentEncoding() { + return currentEncoding; } - public void getOptions() { - autoSelect = options.autoSelect.isSelected(); - if (fullColour != options.fullColour.isSelected()) { - formatChange = true; - forceNonincremental = true; - } - fullColour = options.fullColour.isSelected(); - if (!fullColour) { - int newLowColourLevel = (options.veryLowColour.isSelected() ? 0 : - options.lowColour.isSelected() ? 1 : 2); - if (newLowColourLevel != lowColourLevel) { - lowColourLevel = newLowColourLevel; - formatChange = true; - forceNonincremental = true; - } - } - int newEncoding = (options.zrle.isSelected() ? Encodings.encodingZRLE : - options.hextile.isSelected() ? Encodings.encodingHextile : - options.tight.isSelected() ? Encodings.encodingTight : - Encodings.encodingRaw); - if (newEncoding != currentEncoding) { - currentEncoding = newEncoding; - encodingChange = true; - } + public void handleOptions() + { - viewer.customCompressLevel.setParam(options.customCompressLevel.isSelected()); - if (cp.customCompressLevel != viewer.customCompressLevel.getValue()) { - cp.customCompressLevel = viewer.customCompressLevel.getValue(); - encodingChange = true; - } - if (Integer.parseInt(options.compressLevel.getSelectedItem().toString()) >= 0 && - Integer.parseInt(options.compressLevel.getSelectedItem().toString()) <= 9) { - viewer.compressLevel.setParam(options.compressLevel.getSelectedItem().toString()); - } else { - viewer.compressLevel.setParam(viewer.compressLevel.getDefaultStr()); - } - if (cp.compressLevel != viewer.compressLevel.getValue()) { - cp.compressLevel = viewer.compressLevel.getValue(); - encodingChange = true; - } - viewer.noJpeg.setParam(!options.noJpeg.isSelected()); - if (cp.noJpeg != viewer.noJpeg.getValue()) { - cp.noJpeg = viewer.noJpeg.getValue(); - encodingChange = true; - } - viewer.qualityLevel.setParam(options.qualityLevel.getSelectedItem().toString()); - if (cp.qualityLevel != viewer.qualityLevel.getValue()) { - cp.qualityLevel = viewer.qualityLevel.getValue(); - encodingChange = true; - } - if (!options.x509ca.getText().equals("")) - CSecurityTLS.x509ca.setParam(options.x509ca.getText()); - if (!options.x509crl.getText().equals("")) - CSecurityTLS.x509crl.setParam(options.x509crl.getText()); - viewer.sendLocalUsername.setParam(options.sendLocalUsername.isSelected()); - - viewer.viewOnly.setParam(options.viewOnly.isSelected()); - viewer.acceptClipboard.setParam(options.acceptClipboard.isSelected()); - viewer.sendClipboard.setParam(options.sendClipboard.isSelected()); - viewer.acceptBell.setParam(options.acceptBell.isSelected()); - String scaleString = - options.scalingFactor.getSelectedItem().toString(); - String oldScaleFactor = viewer.scalingFactor.getValue(); - if (scaleString.equalsIgnoreCase("Fixed Aspect Ratio")) { - scaleString = new String("FixedRatio"); - } else if (scaleString.equalsIgnoreCase("Auto")) { - scaleString = new String("Auto"); - } else { - scaleString=scaleString.substring(0, scaleString.length()-1); - } - if (!oldScaleFactor.equals(scaleString)) { - viewer.scalingFactor.setParam(scaleString); - if ((options.fullScreen.isSelected() == fullScreen) && - (desktop != null)) - recreateViewport(); + if (viewport != null && viewport.isVisible()) { + viewport.toFront(); + viewport.requestFocus(); } - clipboardDialog.setSendingEnabled(viewer.sendClipboard.getValue()); - viewer.menuKey.setParam(MenuKey.getMenuKeySymbols()[options.menuKey.getSelectedIndex()].name); - F8Menu.f8.setText("Send "+KeyEvent.getKeyText(MenuKey.getMenuKeyCode())); + // Checking all the details of the current set of encodings is just + // a pain. Assume something has changed, as resending the encoding + // list is cheap. Avoid overriding what the auto logic has selected + // though. + if (!autoSelect.getValue()) { + int encNum = Encodings.encodingNum(preferredEncoding.getValue()); - setShared(options.shared.isSelected()); - viewer.useLocalCursor.setParam(options.useLocalCursor.isSelected()); - if (cp.supportsLocalCursor != viewer.useLocalCursor.getValue()) { - cp.supportsLocalCursor = viewer.useLocalCursor.getValue(); - encodingChange = true; - if (desktop != null) - desktop.resetLocalCursor(); + if (encNum != -1) + this.currentEncoding = encNum; } - viewer.extSSH.setParam(options.sshUseExt.isSelected()); - checkEncodings(); + this.cp.supportsLocalCursor = useLocalCursor.getValue(); - if (state() != RFBSTATE_NORMAL) { - /* Process security types which don't use encryption */ - if (options.encNone.isSelected()) { - if (options.secNone.isSelected()) - Security.EnableSecType(Security.secTypeNone); - if (options.secVnc.isSelected()) - Security.EnableSecType(Security.secTypeVncAuth); - if (options.secPlain.isSelected()) - Security.EnableSecType(Security.secTypePlain); - if (options.secIdent.isSelected()) - Security.EnableSecType(Security.secTypeIdent); - } else { - Security.DisableSecType(Security.secTypeNone); - Security.DisableSecType(Security.secTypeVncAuth); - Security.DisableSecType(Security.secTypePlain); - Security.DisableSecType(Security.secTypeIdent); - } + if (customCompressLevel.getValue()) + this.cp.compressLevel = compressLevel.getValue(); + else + this.cp.compressLevel = -1; - /* Process security types which use TLS encryption */ - if (options.encTLS.isSelected()) { - if (options.secNone.isSelected()) - Security.EnableSecType(Security.secTypeTLSNone); - if (options.secVnc.isSelected()) - Security.EnableSecType(Security.secTypeTLSVnc); - if (options.secPlain.isSelected()) - Security.EnableSecType(Security.secTypeTLSPlain); - if (options.secIdent.isSelected()) - Security.EnableSecType(Security.secTypeTLSIdent); - } else { - Security.DisableSecType(Security.secTypeTLSNone); - Security.DisableSecType(Security.secTypeTLSVnc); - Security.DisableSecType(Security.secTypeTLSPlain); - Security.DisableSecType(Security.secTypeTLSIdent); - } + if (!noJpeg.getValue() && !autoSelect.getValue()) + this.cp.qualityLevel = qualityLevel.getValue(); + else + this.cp.qualityLevel = -1; - /* Process security types which use X509 encryption */ - if (options.encX509.isSelected()) { - if (options.secNone.isSelected()) - Security.EnableSecType(Security.secTypeX509None); - if (options.secVnc.isSelected()) - Security.EnableSecType(Security.secTypeX509Vnc); - if (options.secPlain.isSelected()) - Security.EnableSecType(Security.secTypeX509Plain); - if (options.secIdent.isSelected()) - Security.EnableSecType(Security.secTypeX509Ident); - } else { - Security.DisableSecType(Security.secTypeX509None); - Security.DisableSecType(Security.secTypeX509Vnc); - Security.DisableSecType(Security.secTypeX509Plain); - Security.DisableSecType(Security.secTypeX509Ident); - } + this.encodingChange = true; - /* Process *None security types */ - if (options.secNone.isSelected()) { - if (options.encNone.isSelected()) - Security.EnableSecType(Security.secTypeNone); - if (options.encTLS.isSelected()) - Security.EnableSecType(Security.secTypeTLSNone); - if (options.encX509.isSelected()) - Security.EnableSecType(Security.secTypeX509None); - } else { - Security.DisableSecType(Security.secTypeNone); - Security.DisableSecType(Security.secTypeTLSNone); - Security.DisableSecType(Security.secTypeX509None); - } + // Format changes refreshes the entire screen though and are therefore + // very costly. It's probably worth the effort to see if it is necessary + // here. + PixelFormat pf; - /* Process *Vnc security types */ - if (options.secVnc.isSelected()) { - if (options.encNone.isSelected()) - Security.EnableSecType(Security.secTypeVncAuth); - if (options.encTLS.isSelected()) - Security.EnableSecType(Security.secTypeTLSVnc); - if (options.encX509.isSelected()) - Security.EnableSecType(Security.secTypeX509Vnc); - } else { - Security.DisableSecType(Security.secTypeVncAuth); - Security.DisableSecType(Security.secTypeTLSVnc); - Security.DisableSecType(Security.secTypeX509Vnc); - } + if (fullColor.getValue()) { + pf = fullColorPF; + } else { + if (lowColorLevel.getValue() == 0) + pf = verylowColorPF; + else if (lowColorLevel.getValue() == 1) + pf = lowColorPF; + else + pf = mediumColorPF; + } - /* Process *Plain security types */ - if (options.secPlain.isSelected()) { - if (options.encNone.isSelected()) - Security.EnableSecType(Security.secTypePlain); - if (options.encTLS.isSelected()) - Security.EnableSecType(Security.secTypeTLSPlain); - if (options.encX509.isSelected()) - Security.EnableSecType(Security.secTypeX509Plain); - } else { - Security.DisableSecType(Security.secTypePlain); - Security.DisableSecType(Security.secTypeTLSPlain); - Security.DisableSecType(Security.secTypeX509Plain); - } + if (!pf.equal(this.cp.pf())) { + this.formatChange = true; - /* Process *Ident security types */ - if (options.secIdent.isSelected()) { - if (options.encNone.isSelected()) - Security.EnableSecType(Security.secTypeIdent); - if (options.encTLS.isSelected()) - Security.EnableSecType(Security.secTypeTLSIdent); - if (options.encX509.isSelected()) - Security.EnableSecType(Security.secTypeX509Ident); - } else { - Security.DisableSecType(Security.secTypeIdent); - Security.DisableSecType(Security.secTypeTLSIdent); - Security.DisableSecType(Security.secTypeX509Ident); - } - if (options.sshTunnel.isSelected()) { - if (options.sshUseGateway.isSelected()) { - String user = options.sshUser.getText(); - String host = options.sshHost.getText(); - String port = options.sshPort.getText(); - viewer.via.setParam(user+"@"+host+":"+port); - } else { - viewer.tunnel.setParam(true); - } - } - viewer.extSSH.setParam(options.sshUseExt.isSelected()); - viewer.extSSHClient.setParam(options.sshClient.getText()); - if (options.sshArgsCustom.isSelected()) - viewer.extSSHArgs.setParam(options.sshArguments.getText()); - viewer.sshConfig.setParam(options.sshConfig.getText()); - viewer.sshKeyFile.setParam(options.sshKeyFile.getText()); - } - String desktopSize = (options.desktopSize.isSelected()) ? - options.desktopWidth.getText() + "x" + options.desktopHeight.getText() : ""; - viewer.desktopSize.setParam(desktopSize); - if (options.fullScreen.isSelected() ^ fullScreen) { - viewer.fullScreenAllMonitors.setParam(options.fullScreenAllMonitors.isSelected()); - toggleFullScreen(); - } else { - if (viewer.fullScreenAllMonitors.getValue() != - options.fullScreenAllMonitors.isSelected()) { - viewer.fullScreenAllMonitors.setParam(options.fullScreenAllMonitors.isSelected()); - if (desktop != null) - recreateViewport(); - } else { - viewer.fullScreenAllMonitors.setParam(options.fullScreenAllMonitors.isSelected()); - } + // Without fences, we cannot safely trigger an update request directly + // but must wait for the next update to arrive. + if (this.supportsSyncFence) + this.requestNewUpdate(); } + } public void toggleFullScreen() { - if (viewer.embed.getValue()) + if (embed.getValue()) return; - fullScreen = !fullScreen; - menu.fullScreen.setSelected(fullScreen); + fullScreen.setParam(!fullScreen.getValue()); if (viewport != null) { if (!viewport.lionFSSupported()) { recreateViewport(); @@ -1369,7 +856,7 @@ public class CConn extends CConnection implements } public void writeKeyEvent(KeyEvent ev) { - if (viewer.viewOnly.getValue() || shuttingDown) + if (viewOnly.getValue() || shuttingDown) return; boolean down = (ev.getID() == KeyEvent.KEY_PRESSED); @@ -1539,12 +1026,6 @@ public class CConn extends CConnection implements // the following never change so need no synchronization: - - // viewer object is only ever accessed by the GUI thread so needs no - // synchronization (except for one test in DesktopWindow - see comment - // there). - VncViewer viewer; - // access to desktop by different threads is specified in DesktopWindow // the following need no synchronization: @@ -1559,27 +1040,21 @@ public class CConn extends CConnection implements // reading and writing int and boolean is atomic in java, so no // synchronization of the following flags is needed: - int lowColourLevel; - // All menu, options, about and info stuff is done in the GUI thread (apart // from when constructed). - F8Menu menu; - OptionsDialog options; - - // clipboard sync issues? - ClipboardDialog clipboardDialog; // the following are only ever accessed by the GUI thread: int buttonMask; + private String serverHost; + private int serverPort; private Socket sock; protected DesktopWindow desktop; - // FIXME: should be private - public PixelFormat serverPF; - private PixelFormat fullColourPF; + private PixelFormat serverPF; + private PixelFormat fullColorPF; private boolean pendingPFChange; private PixelFormat pendingPF; @@ -1597,11 +1072,7 @@ public class CConn extends CConnection implements private boolean supportsSyncFence; - public int menuKeyCode; Viewport viewport; - private boolean fullColour; - private boolean autoSelect; - boolean fullScreen; private HashMap downKeySym; public ActionListener closeListener = null; diff --git a/java/com/tigervnc/vncviewer/ClipboardDialog.java b/java/com/tigervnc/vncviewer/ClipboardDialog.java index 441846cf..1404c227 100644 --- a/java/com/tigervnc/vncviewer/ClipboardDialog.java +++ b/java/com/tigervnc/vncviewer/ClipboardDialog.java @@ -26,83 +26,126 @@ import java.io.*; import java.nio.*; import javax.swing.*; import javax.swing.border.*; +import javax.swing.event.*; import javax.swing.text.*; import com.tigervnc.rfb.LogWriter; +import static com.tigervnc.vncviewer.Parameters.*; + class ClipboardDialog extends Dialog { - private class VncTransferHandler extends TransferHandler { - // Custom TransferHandler designed to limit the size of outbound - // clipboard transfers to VncViewer.maxCutText.getValue() bytes. - private LogWriter vlog = new LogWriter("VncTransferHandler"); - - public void exportToClipboard(JComponent c, Clipboard clip, int a) - throws IllegalStateException { - if (!(c instanceof JTextComponent)) return; - StringSelection selection = - new StringSelection(((JTextComponent)c).getText()); - clip.setContents(selection, null); - } + protected static class MyJTextArea extends JTextArea { - public boolean importData(JComponent c, Transferable t) { - if (canImport(c, t.getTransferDataFlavors())) { + private class VncTransferHandler extends TransferHandler { + // Custom TransferHandler designed to limit the size of outbound + // clipboard transfers to VncViewer.maxCutText.getValue() bytes. + private LogWriter vlog = new LogWriter("VncTransferHandler"); + + public void exportToClipboard(JComponent c, Clipboard clip, int a) + throws IllegalStateException { + if (!(c instanceof JTextComponent)) return; + String text = ((JTextComponent)c).getText(); try { - DataFlavor VncFlavor = null; - for (DataFlavor f : t.getTransferDataFlavors()) - if (f.isFlavorTextType() && f.isRepresentationClassInputStream()) - VncFlavor = f; - if (VncFlavor == null) return false; - Reader reader = (Reader)VncFlavor.getReaderForText(t); - CharBuffer cbuf = - CharBuffer.allocate(VncViewer.maxCutText.getValue()); - cbuf.limit(reader.read(cbuf.array(), 0, cbuf.length())); - reader.close(); - if (c instanceof JTextComponent) - ((JTextComponent)c).setText(cbuf.toString()); - return true; - } catch (OutOfMemoryError oome) { - vlog.error("ERROR: Too much data on local clipboard!"); - } catch (UnsupportedFlavorException ufe) { - // Skip import - vlog.info(ufe.toString()); - } catch (IOException ioe) { - // Skip import - vlog.info(ioe.toString()); + if (text.equals((String)clip.getData(DataFlavor.stringFlavor))) return; + } catch (IOException e) { + // worst case we set the clipboard contents unnecessarily + vlog.info(e.toString()); + } catch (UnsupportedFlavorException e) { + // worst case we set the clipboard contents unnecessarily + vlog.info(e.toString()); } - } - return false; - } + StringSelection selection = new StringSelection(text); + clip.setContents(selection, null); + } + + public boolean importData(JComponent c, Transferable t) { + if (canImport(c, t.getTransferDataFlavors())) { + try { + DataFlavor VncFlavor = null; + for (DataFlavor f : t.getTransferDataFlavors()) { + if (f.isMimeTypeEqual("text/plain") && + f.isRepresentationClassInputStream()) { + VncFlavor = f; + break; + } + } + if (VncFlavor == null) return false; + CharBuffer cbuf = + CharBuffer.allocate(maxCutText.getValue()); + Reader reader = (Reader)VncFlavor.getReaderForText(t); + int n = reader.read(cbuf.array(), 0, cbuf.length()); + reader.close(); + // reader returns -1 (EOF) for empty clipboard + cbuf.limit(n < 0 ? 0 : n); + if (c instanceof JTextComponent) + if (!cbuf.toString().equals(((JTextComponent)c).getText())) + ((JTextComponent)c).setText(cbuf.toString()); + return true; + } catch (OutOfMemoryError e) { + vlog.error("ERROR: Too much data on local clipboard!"); + } catch (UnsupportedFlavorException e) { + // Skip import + vlog.info(e.toString()); + } catch (IOException e) { + // Skip import + vlog.info(e.toString()); + } + } + return false; + } + + public boolean canImport(JComponent c, DataFlavor[] flavors) { + for (DataFlavor f : flavors) + if (f.isMimeTypeEqual("text/plain") && + f.isRepresentationClassReader()) { + return true; + } + return false; + } + } + + private class MyTextListener implements DocumentListener { + public MyTextListener() { } + + public void changedUpdate(DocumentEvent e) { } + + public void insertUpdate(DocumentEvent e) { + if (!listen) return; + String text = textArea.getText(); + if (sendClipboard.getValue()) + VncViewer.cc.writeClientCutText(text, text.length()); + } + + public void removeUpdate(DocumentEvent e) { } + } - public boolean canImport(JComponent c, DataFlavor[] flavors) { - for (DataFlavor f : flavors) - if (f.isFlavorTextType() && f.isRepresentationClassReader()) - return true; - return false; + public MyJTextArea() { + super(); + setTransferHandler(new VncTransferHandler()); + getDocument().addDocumentListener(new MyTextListener()); + // If the textArea can receive the focus, then text within the textArea + // can be selected. On platforms that don't support separate selection + // and clipboard buffers, this triggers a replacement of the textAra's + // contents with the selected text. + setFocusable(false); + setLineWrap(false); + setWrapStyleWord(true); } } - public ClipboardDialog(CConn cc_) { + public ClipboardDialog() { super(false); setTitle("VNC Clipboard Viewer"); setPreferredSize(new Dimension(640, 480)); - addWindowFocusListener(new WindowAdapter() { + addWindowFocusListener(new WindowFocusListener() { // Necessary to ensure that updates from the system clipboard - // still occur when the ClipboardDialog has the focus. - public void WindowGainedFocus(WindowEvent e) { + // are propagated to the textArea when the dialog is visible. + public void windowGainedFocus(WindowEvent e) { clientCutText(); } + public void windowLostFocus(WindowEvent e) { } }); - cc = cc_; - textArea = new JTextArea(); - textArea.setTransferHandler(new VncTransferHandler()); - // If the textArea can receive the focus, then text within the textArea - // can be selected. On platforms that don't support separate selection - // and clipboard buffers, this triggers a replacement of the textAra's - // contents with the selected text. - textArea.setFocusable(false); - textArea.setLineWrap(false); - textArea.setWrapStyleWord(true); JScrollPane sp = new JScrollPane(textArea); getContentPane().add(sp, BorderLayout.CENTER); // button panel placed below the scrollpane @@ -118,19 +161,40 @@ class ClipboardDialog extends Dialog { pack(); } - public void serverCutText(String str, int len) { + public static void showDialog(Container c) { + if (dialog == null) + dialog = new ClipboardDialog(); + dialog.show(c); + } + + public void show(Container c) { + super.showDialog(c); + } + + public void endDialog() { + super.endDialog(); + dialog.dispose(); + } + + public static void serverCutText(String str) { + if (textArea.getText().equals(str)) + return; + // Update the text area with incoming serverCutText. We need to diable + // the DocumentListener temporarily to prevent an clientCutText msg from + // being sent back to the server when the textArea is updated. + listen = false; textArea.setText(str); textArea.copy(); + listen = true; } - public void clientCutText() { - int hc = textArea.getText().hashCode(); + public static void clientCutText() { + // Update the textArea with the current contents of the system clipboard. + // The TransferHandler ensures that the textArea's contents are only + // changed when they differ from the clipboard's. If the textArea is + // updated, the DocumentListener will trigger an RFB clientCutText msg. textArea.paste(); textArea.setCaretPosition(0); - String text = textArea.getText(); - if (cc.viewer.sendClipboard.getValue()) - if (hc != text.hashCode()) - cc.writeClientCutText(text, text.length()); } public void setSendingEnabled(boolean b) { @@ -140,18 +204,20 @@ class ClipboardDialog extends Dialog { public void actionPerformed(ActionEvent e) { Object s = e.getSource(); if (s instanceof JButton && (JButton)s == clearButton) { - serverCutText(new String(""), 0); + serverCutText(new String("")); } else if (s instanceof JButton && (JButton)s == sendButton) { String text = textArea.getText(); - cc.writeClientCutText(text, text.length()); + VncViewer.cc.writeClientCutText(text, text.length()); endDialog(); } else if (s instanceof JButton && (JButton)s == cancelButton) { endDialog(); } } - CConn cc; - JTextArea textArea; - JButton clearButton, sendButton, cancelButton; + private JButton clearButton, sendButton, cancelButton; + private static boolean listen = true; + static ClipboardDialog dialog; + static MyJTextArea textArea = new MyJTextArea(); + static Toolkit tk = Toolkit.getDefaultToolkit(); static LogWriter vlog = new LogWriter("ClipboardDialog"); } diff --git a/java/com/tigervnc/vncviewer/DesktopWindow.java b/java/com/tigervnc/vncviewer/DesktopWindow.java index ff48fc1c..de2d2cd5 100644 --- a/java/com/tigervnc/vncviewer/DesktopWindow.java +++ b/java/com/tigervnc/vncviewer/DesktopWindow.java @@ -43,18 +43,22 @@ import com.tigervnc.rfb.*; import com.tigervnc.rfb.Cursor; import com.tigervnc.rfb.Point; +import static com.tigervnc.vncviewer.Parameters.*; + class DesktopWindow extends JPanel implements Runnable, MouseListener, MouseMotionListener, MouseWheelListener, KeyListener { //////////////////////////////////////////////////////////////////// // The following methods are all called from the RFB thread - public DesktopWindow(int width, int height, PixelFormat serverPF, + public DesktopWindow(int width, int height, String name, PixelFormat serverPF, CConn cc_) { cc = cc_; setSize(width, height); setScaledSize(); setOpaque(false); + if (cc.viewport != null) + cc.viewport.setName(name); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice gd = ge.getDefaultScreenDevice(); @@ -67,7 +71,7 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener, } else { vlog.debug("GraphicsDevice does not support HW acceleration."); } - im = new BIPixelBuffer(width, height, cc, this); + im = new BIPixelBuffer(serverPF, width, height, this); cursor = new Cursor(); cursorBacking = new ManagedPixelBuffer(); @@ -86,7 +90,7 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener, addKeyListener(this); addFocusListener(new FocusAdapter() { public void focusGained(FocusEvent e) { - cc.clipboardDialog.clientCutText(); + ClipboardDialog.clientCutText(); } public void focusLost(FocusEvent e) { cc.releaseDownKeys(); @@ -94,6 +98,7 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener, }); setFocusTraversalKeysEnabled(false); setFocusable(true); + OptionsDialog.addCallback("handleOptions", this); } public int width() { @@ -125,10 +130,8 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener, // might be being altered by the GUI thread. However it's only a single // boolean and it doesn't matter if we get the wrong value anyway. - synchronized(cc.viewer.useLocalCursor) { - if (!cc.viewer.useLocalCursor.getValue()) - return; - } + if (!useLocalCursor.getValue()) + return; hideLocalCursor(); @@ -319,7 +322,7 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener, } public void setScaledSize() { - String scaleString = cc.viewer.scalingFactor.getValue(); + String scaleString = scalingFactor.getValue(); if (!scaleString.equalsIgnoreCase("Auto") && !scaleString.equalsIgnoreCase("FixedRatio")) { int scalingFactor = Integer.parseInt(scaleString); @@ -369,7 +372,7 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener, // Mouse-Motion callback function private void mouseMotionCB(MouseEvent e) { - if (!cc.viewer.viewOnly.getValue() && + if (!viewOnly.getValue() && e.getX() >= 0 && e.getX() <= scaledWidth && e.getY() >= 0 && e.getY() <= scaledHeight) cc.writePointerEvent(e); @@ -394,7 +397,7 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener, // Mouse callback function private void mouseCB(MouseEvent e) { - if (!cc.viewer.viewOnly.getValue()) { + if (!viewOnly.getValue()) { if ((e.getID() == MouseEvent.MOUSE_RELEASED) || (e.getX() >= 0 && e.getX() <= scaledWidth && e.getY() >= 0 && e.getY() <= scaledHeight)) @@ -407,14 +410,14 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener, public void mousePressed(MouseEvent e) { mouseCB(e); } public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) { - if (cc.viewer.embed.getValue()) + if (embed.getValue()) requestFocus(); } public void mouseExited(MouseEvent e) {} // MouseWheel callback function private void mouseWheelCB(MouseWheelEvent e) { - if (!cc.viewer.viewOnly.getValue()) + if (!viewOnly.getValue()) cc.writeWheelEvent(e); } @@ -443,14 +446,15 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener, lastY : (int)Math.floor(lastY * scaleHeightRatio); java.awt.Point ev = new java.awt.Point(lastX, lastY); ev.translate(sx - lastX, sy - lastY); - cc.showMenu((int)ev.getX(), (int)ev.getY()); + F8Menu menu = new F8Menu(cc); + menu.show(this, (int)ev.getX(), (int)ev.getY()); return; } int ctrlAltShiftMask = Event.SHIFT_MASK | Event.CTRL_MASK | Event.ALT_MASK; if ((e.getModifiers() & ctrlAltShiftMask) == ctrlAltShiftMask) { switch (e.getKeyCode()) { case KeyEvent.VK_A: - cc.showAbout(); + VncViewer.showAbout(this); return; case KeyEvent.VK_F: cc.toggleFullScreen(); @@ -462,10 +466,10 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener, cc.showInfo(); return; case KeyEvent.VK_O: - cc.options.showDialog(cc.viewport); + OptionsDialog.showDialog(cc.viewport); return; case KeyEvent.VK_W: - VncViewer.newViewer(cc.viewer); + VncViewer.newViewer(); return; case KeyEvent.VK_LEFT: case KeyEvent.VK_RIGHT: @@ -564,6 +568,14 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener, setColourMapEntriesTimerThread = null; } + public void handleOptions() + { + if (fullScreen.getValue() && Viewport.getFullScreenWindow() == null) + cc.toggleFullScreen(); + else if (!fullScreen.getValue() && Viewport.getFullScreenWindow() != null) + cc.toggleFullScreen(); + } + // access to cc by different threads is specified in CConn CConn cc; diff --git a/java/com/tigervnc/vncviewer/Dialog.java b/java/com/tigervnc/vncviewer/Dialog.java index 8bf19799..4419159e 100644 --- a/java/com/tigervnc/vncviewer/Dialog.java +++ b/java/com/tigervnc/vncviewer/Dialog.java @@ -31,6 +31,7 @@ package com.tigervnc.vncviewer; import java.awt.*; import java.awt.Dialog.*; import java.awt.event.*; +import java.io.File; import javax.swing.*; import javax.swing.border.*; import javax.swing.text.*; @@ -52,7 +53,7 @@ class Dialog extends JDialog implements ActionListener, } } - public boolean showDialog(Component c) { + public void showDialog(Component c) { initDialog(); if (c != null) { setLocationRelativeTo(c); @@ -70,11 +71,10 @@ class Dialog extends JDialog implements ActionListener, if (getModalityType() == ModalityType.APPLICATION_MODAL) setAlwaysOnTop(true); setVisible(true); - return ret; } - public boolean showDialog() { - return showDialog(null); + public void showDialog() { + showDialog(null); } public void endDialog() { @@ -137,6 +137,21 @@ class Dialog extends JDialog implements ActionListener, return width + gap; } + public static File showChooser(String title, File defFile, Container c) { + JFileChooser fc = new JFileChooser(defFile); + fc.setDialogTitle(title); + fc.setApproveButtonText("OK \u21B5"); + fc.setFileHidingEnabled(false); + if (fc.showOpenDialog(c) == JFileChooser.APPROVE_OPTION) + return fc.getSelectedFile(); + else + return null; + } + + protected File showChooser(String title, File defFile) { + return showChooser(title, defFile, this); + } + protected class GroupedJRadioButton extends JRadioButton { public GroupedJRadioButton(String l, ButtonGroup g, JComponent c) { super(l); @@ -181,9 +196,9 @@ class Dialog extends JDialog implements ActionListener, JTextField jtf = (JTextField)editor.getEditorComponent(); jtf.setDocument(doc); } + } private Window fullScreenWindow; - protected boolean ret = true; } diff --git a/java/com/tigervnc/vncviewer/F8Menu.java b/java/com/tigervnc/vncviewer/F8Menu.java index 472f11f8..6aadb2d3 100644 --- a/java/com/tigervnc/vncviewer/F8Menu.java +++ b/java/com/tigervnc/vncviewer/F8Menu.java @@ -22,48 +22,58 @@ package com.tigervnc.vncviewer; import java.awt.*; import java.awt.Cursor; import java.awt.event.*; +import java.io.File; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JFileChooser; -import javax.swing.JPopupMenu; import javax.swing.JMenuItem; -import javax.swing.JCheckBoxMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPopupMenu; import com.tigervnc.rfb.*; +import static com.tigervnc.vncviewer.Parameters.*; + public class F8Menu extends JPopupMenu implements ActionListener { - public F8Menu(CConn cc_) { + public F8Menu(CConn cc) { super("VNC Menu"); setLightWeightPopupEnabled(false); - cc = cc_; + String os = System.getProperty("os.name"); + if (os.startsWith("Windows")) + com.sun.java.swing.plaf.windows.WindowsLookAndFeel.setMnemonicHidden(false); + this.cc = cc; restore = addMenuItem("Restore",KeyEvent.VK_R); - restore.setEnabled(!cc.viewer.embed.getValue()); + restore.setEnabled(!embed.getValue()); move = addMenuItem("Move"); move.setEnabled(false); size = addMenuItem("Size"); size.setEnabled(false); minimize = addMenuItem("Minimize", KeyEvent.VK_N); - minimize.setEnabled(!cc.viewer.embed.getValue()); + minimize.setEnabled(!embed.getValue()); maximize = addMenuItem("Maximize", KeyEvent.VK_X); - maximize.setEnabled(!cc.viewer.embed.getValue()); + maximize.setEnabled(!embed.getValue()); addSeparator(); exit = addMenuItem("Close Viewer", KeyEvent.VK_C); addSeparator(); - fullScreen = new JCheckBoxMenuItem("Full Screen"); - fullScreen.setMnemonic(KeyEvent.VK_F); - fullScreen.setSelected(cc.fullScreen); - fullScreen.addActionListener(this); - fullScreen.setEnabled(!cc.viewer.embed.getValue()); - add(fullScreen); + fullScreenCheckbox = new JCheckBoxMenuItem("Full Screen"); + fullScreenCheckbox.setMnemonic(KeyEvent.VK_F); + fullScreenCheckbox.setSelected(fullScreen.getValue()); + fullScreenCheckbox.addActionListener(this); + fullScreenCheckbox.setEnabled(!embed.getValue()); + add(fullScreenCheckbox); addSeparator(); clipboard = addMenuItem("Clipboard..."); addSeparator(); - f8 = addMenuItem("Send "+KeyEvent.getKeyText(MenuKey.getMenuKeyCode()), MenuKey.getMenuKeyCode()); + int keyCode = MenuKey.getMenuKeyCode(); + String keyText = KeyEvent.getKeyText(keyCode); + f8 = addMenuItem("Send "+keyText, keyCode); ctrlAltDel = addMenuItem("Send Ctrl-Alt-Del"); addSeparator(); refresh = addMenuItem("Refresh Screen", KeyEvent.VK_H); addSeparator(); newConn = addMenuItem("New connection...", KeyEvent.VK_W); - newConn.setEnabled(!cc.viewer.embed.getValue()); + newConn.setEnabled(!embed.getValue()); options = addMenuItem("Options...", KeyEvent.VK_O); save = addMenuItem("Save connection info as...", KeyEvent.VK_S); info = addMenuItem("Connection info...", KeyEvent.VK_I); @@ -94,19 +104,20 @@ public class F8Menu extends JPopupMenu implements ActionListener { public void actionPerformed(ActionEvent ev) { if (actionMatch(ev, exit)) { cc.close(); - } else if (actionMatch(ev, fullScreen)) { + } else if (actionMatch(ev, fullScreenCheckbox)) { cc.toggleFullScreen(); } else if (actionMatch(ev, restore)) { - if (cc.fullScreen) cc.toggleFullScreen(); + if (fullScreen.getValue()) cc.toggleFullScreen(); cc.viewport.setExtendedState(JFrame.NORMAL); } else if (actionMatch(ev, minimize)) { - if (cc.fullScreen) cc.toggleFullScreen(); + if (fullScreen.getValue()) cc.toggleFullScreen(); cc.viewport.setExtendedState(JFrame.ICONIFIED); } else if (actionMatch(ev, maximize)) { - if (cc.fullScreen) cc.toggleFullScreen(); + if (fullScreen.getValue()) cc.toggleFullScreen(); cc.viewport.setExtendedState(JFrame.MAXIMIZED_BOTH); } else if (actionMatch(ev, clipboard)) { - cc.clipboardDialog.showDialog(cc.viewport); + //ClipboardDialog dlg = new ClipboardDialog(cc); + ClipboardDialog.showDialog(cc.viewport); } else if (actionMatch(ev, f8)) { cc.writeKeyEvent(MenuKey.getMenuKeySym(), true); cc.writeKeyEvent(MenuKey.getMenuKeySym(), false); @@ -120,29 +131,37 @@ public class F8Menu extends JPopupMenu implements ActionListener { } else if (actionMatch(ev, refresh)) { cc.refresh(); } else if (actionMatch(ev, newConn)) { - VncViewer.newViewer(cc.viewer); + VncViewer.newViewer(); } else if (actionMatch(ev, options)) { - cc.options.showDialog(cc.viewport); + OptionsDialog.showDialog(cc.viewport); } else if (actionMatch(ev, save)) { - JFileChooser fc = new JFileChooser(); - fc.setDialogTitle("Save current configuration as:"); - fc.setApproveButtonText("OK"); - fc.setFileHidingEnabled(false); - Window fullScreenWindow = Viewport.getFullScreenWindow(); - if (fullScreenWindow != null) - Viewport.setFullScreenWindow(null); - int ret = fc.showOpenDialog(cc.viewport); - if (fullScreenWindow != null) - Viewport.setFullScreenWindow(fullScreenWindow); - if (ret == JFileChooser.APPROVE_OPTION) { - String filename = fc.getSelectedFile().toString(); - if (filename != null) - Configuration.save(filename); - } + String title = "Save the TigerVNC configuration to file"; + File dflt = new File(FileUtils.getVncHomeDir().concat("default.tigervnc")); + if (!dflt.exists() || !dflt.isFile()) + dflt = new File(FileUtils.getVncHomeDir()); + File f = Dialog.showChooser(title, dflt, this); + while (f != null && f.exists() && f.isFile()) { + String msg = f.getAbsolutePath(); + msg = msg.concat(" already exists. Do you want to overwrite?"); + Object[] options = {"Overwrite", "No \u21B5"}; + JOptionPane op = + new JOptionPane(msg, JOptionPane.QUESTION_MESSAGE, + JOptionPane.OK_CANCEL_OPTION, null, options, options[1]); + JDialog dlg = op.createDialog(this, "TigerVNC Viewer"); + dlg.setIconImage(VncViewer.frameIcon); + dlg.setAlwaysOnTop(true); + dlg.setVisible(true); + if (op.getValue() == options[0]) + break; + else + f = Dialog.showChooser(title, f, this); + } + if (f != null && (!f.exists() || f.canWrite())) + saveViewerParameters(f.getAbsolutePath(), vncServerName.getValue()); } else if (actionMatch(ev, info)) { cc.showInfo(); } else if (actionMatch(ev, about)) { - cc.showAbout(); + VncViewer.showAbout(cc.desktop); } else if (actionMatch(ev, dismiss)) { firePopupMenuCanceled(); } @@ -153,6 +172,6 @@ public class F8Menu extends JPopupMenu implements ActionListener { JMenuItem exit, clipboard, ctrlAltDel, refresh; JMenuItem newConn, options, save, info, about, dismiss; static JMenuItem f8; - JCheckBoxMenuItem fullScreen; + JCheckBoxMenuItem fullScreenCheckbox; static LogWriter vlog = new LogWriter("F8Menu"); } diff --git a/java/com/tigervnc/vncviewer/OptionsDialog.java b/java/com/tigervnc/vncviewer/OptionsDialog.java index 369b965d..8d49a2e3 100644 --- a/java/com/tigervnc/vncviewer/OptionsDialog.java +++ b/java/com/tigervnc/vncviewer/OptionsDialog.java @@ -22,6 +22,7 @@ package com.tigervnc.vncviewer; import java.awt.*; import java.awt.event.*; import java.io.File; +import java.lang.reflect.*; import java.text.Format; import java.text.NumberFormat; import javax.swing.*; @@ -29,7 +30,9 @@ import javax.swing.border.*; import javax.swing.UIManager.*; import javax.swing.text.*; import java.util.*; +import java.util.List; import java.util.Map.Entry; +import java.util.prefs.*; import com.tigervnc.rfb.*; @@ -44,6 +47,8 @@ import static java.awt.GridBagConstraints.RELATIVE; import static java.awt.GridBagConstraints.REMAINDER; import static java.awt.GridBagConstraints.VERTICAL; +import static com.tigervnc.vncviewer.Parameters.*; + class OptionsDialog extends Dialog { private class IntegerDocument extends PlainDocument { @@ -88,47 +93,541 @@ class OptionsDialog extends Dialog { } } - // Constants - static LogWriter vlog = new LogWriter("OptionsDialog"); + private static Map callbacks = new HashMap(); + /* Compression */ + JCheckBox autoselectCheckbox; + + ButtonGroup encodingGroup; + JRadioButton tightButton; + JRadioButton zrleButton; + JRadioButton hextileButton; + JRadioButton rawButton; + + ButtonGroup colorlevelGroup; + JRadioButton fullcolorButton; + JRadioButton mediumcolorButton; + JRadioButton lowcolorButton; + JRadioButton verylowcolorButton; + + JCheckBox compressionCheckbox; + JCheckBox jpegCheckbox; + JComboBox compressionInput; + JComboBox jpegInput; + + /* Security */ + JCheckBox encNoneCheckbox; + JCheckBox encTLSCheckbox; + JCheckBox encX509Checkbox; + JTextField caInput; + JTextField crlInput; + JButton caChooser; + JButton crlChooser; + + JCheckBox authNoneCheckbox; + JCheckBox authVncCheckbox; + JCheckBox authPlainCheckbox; + JCheckBox authIdentCheckbox; + JCheckBox sendLocalUsernameCheckbox; - CConn cc; - @SuppressWarnings({"rawtypes"}) - JComboBox menuKey, compressLevel, qualityLevel, scalingFactor; - ButtonGroup encodingGroup, colourGroup, sshArgsGroup; - JRadioButton zrle, hextile, tight, raw, fullColour, mediumColour, - lowColour, veryLowColour, sshArgsDefault, sshArgsCustom; - JCheckBox autoSelect, customCompressLevel, noJpeg, viewOnly, - acceptClipboard, sendClipboard, acceptBell, desktopSize, - fullScreen, fullScreenAllMonitors, shared, useLocalCursor, - secVeNCrypt, encNone, encTLS, encX509, secNone, secVnc, - secPlain, secIdent, sendLocalUsername, sshTunnel, sshUseExt, - sshUseGateway; - JButton okButton, cancelButton, caButton, crlButton, cfLoadButton, - cfSaveAsButton, defSaveButton, defReloadButton, defClearButton, - sshConfigBrowser, sshKeyFileBrowser, sshClientBrowser; - JTextField desktopWidth, desktopHeight, x509ca, x509crl, sshUser, sshHost, - sshPort, sshClient, sshArguments, sshConfig, sshKeyFile; - JTabbedPane tabPane; + /* Input */ + JCheckBox viewOnlyCheckbox; + JCheckBox acceptClipboardCheckbox; + JCheckBox sendClipboardCheckbox; + JComboBox menuKeyChoice; + + /* Screen */ + JCheckBox desktopSizeCheckbox; + JTextField desktopWidthInput; + JTextField desktopHeightInput; + JCheckBox fullScreenCheckbox; + JCheckBox fullScreenAllMonitorsCheckbox; + JComboBox scalingFactorInput; + + /* Misc. */ + JCheckBox sharedCheckbox; + JCheckBox localCursorCheckbox; + JCheckBox acceptBellCheckbox; + + /* SSH */ + JCheckBox tunnelCheckbox; + JCheckBox viaCheckbox; + JTextField viaUserInput; + JTextField viaHostInput; + JTextField viaPortInput; + JCheckBox extSSHCheckbox; + JTextField sshClientInput; + JButton sshClientChooser; + JRadioButton sshArgsDefaultButton; + JRadioButton sshArgsCustomButton; + JTextField sshArgsInput; + JTextField sshConfigInput; + JTextField sshKeyFileInput; + JButton sshConfigChooser; + JButton sshKeyFileChooser; @SuppressWarnings({"rawtypes","unchecked"}) - public OptionsDialog(CConn cc_) { + public OptionsDialog() { super(true); - cc = cc_; setTitle("VNC Viewer Options"); setResizable(false); getContentPane().setLayout( new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS)); - tabPane = new JTabbedPane(); + JTabbedPane tabPane = new JTabbedPane(); tabPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); encodingGroup = new ButtonGroup(); - colourGroup = new ButtonGroup(); - sshArgsGroup = new ButtonGroup(); - int indent = 0; + colorlevelGroup = new ButtonGroup(); + + // tabPane + tabPane.addTab("Compression", createCompressionPanel()); + tabPane.addTab("Security", createSecurityPanel()); + tabPane.addTab("Input", createInputPanel()); + tabPane.addTab("Screen", createScreenPanel()); + tabPane.addTab("Misc", createMiscPanel()); + tabPane.addTab("SSH", createSshPanel()); + tabPane.setBorder(BorderFactory.createEmptyBorder()); + // Resize the tabPane if necessary to prevent scrolling + Insets tpi = + (Insets)UIManager.get("TabbedPane:TabbedPaneTabArea.contentMargins"); + int minWidth = tpi.left + tpi.right; + for (int i = 0; i < tabPane.getTabCount(); i++) + minWidth += tabPane.getBoundsAt(i).width; + int minHeight = tabPane.getPreferredSize().height; + if (tabPane.getPreferredSize().width < minWidth) + tabPane.setPreferredSize(new Dimension(minWidth, minHeight)); + + // button pane + JButton okButton = new JButton("OK \u21B5"); + okButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + storeOptions(); + endDialog(); + } + }); + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + endDialog(); + } + }); + + JPanel buttonPane = new JPanel(new GridLayout(1, 5, 10, 10)); + buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 5)); + buttonPane.add(Box.createRigidArea(new Dimension())); + buttonPane.add(Box.createRigidArea(new Dimension())); + buttonPane.add(Box.createRigidArea(new Dimension())); + buttonPane.add(cancelButton); + buttonPane.add(okButton); + + this.add(tabPane); + this.add(buttonPane); + addListeners(this); + pack(); + } + + public static void showDialog(Container c) { + OptionsDialog dialog = new OptionsDialog(); + dialog.show(c); + } + + public void show(Container c) { + loadOptions(); + super.showDialog(c); + } + + public static void addCallback(String cb, Object obj) + { + callbacks.put(cb, obj); + } + + public static void removeCallback(String cb) + { + callbacks.remove(cb); + } + + public void endDialog() { + super.endDialog(); + // Making a new dialog is so cheap that it's not worth keeping + this.dispose(); + } + + public void setEmbeddedFeatures(boolean s) { + fullScreenCheckbox.setEnabled(s); + fullScreenAllMonitorsCheckbox.setEnabled(s); + scalingFactorInput.setEnabled(s); + } + + private void loadOptions() + { + /* Compression */ + autoselectCheckbox.setSelected(autoSelect.getValue()); + + int encNum = Encodings.encodingNum(preferredEncoding.getValueStr()); + + switch (encNum) { + case Encodings.encodingTight: + tightButton.setSelected(true); + break; + case Encodings.encodingZRLE: + zrleButton.setSelected(true); + break; + case Encodings.encodingHextile: + hextileButton.setSelected(true); + break; + case Encodings.encodingRaw: + rawButton.setSelected(true); + break; + } + + if (fullColor.getValue()) + fullcolorButton.setSelected(true); + else { + switch (lowColorLevel.getValue()) { + case 0: + verylowcolorButton.setSelected(true); + break; + case 1: + lowcolorButton.setSelected(true); + break; + case 2: + mediumcolorButton.setSelected(true); + break; + } + } + + int digit = 0; + + compressionCheckbox.setSelected(customCompressLevel.getValue()); + jpegCheckbox.setSelected(!noJpeg.getValue()); + digit = 0 + compressLevel.getValue(); + compressionInput.setSelectedItem(digit); + digit = 0 + qualityLevel.getValue(); + jpegInput.setSelectedItem(digit); + + handleAutoselect(); + handleCompression(); + handleJpeg(); + + /* Security */ + Security security = new Security(SecurityClient.secTypes); + + List secTypes; + Iterator iter; + + List secTypesExt; + Iterator iterExt; + + encNoneCheckbox.setSelected(false); + encTLSCheckbox.setSelected(false); + encX509Checkbox.setSelected(false); + + authNoneCheckbox.setSelected(false); + authVncCheckbox.setSelected(false); + authPlainCheckbox.setSelected(false); + authIdentCheckbox.setSelected(false); + sendLocalUsernameCheckbox.setSelected(sendLocalUsername.getValue()); + + secTypes = security.GetEnabledSecTypes(); + for (iter = secTypes.iterator(); iter.hasNext(); ) { + switch ((Integer)iter.next()) { + case Security.secTypeNone: + encNoneCheckbox.setSelected(true); + authNoneCheckbox.setSelected(true); + break; + case Security.secTypeVncAuth: + encNoneCheckbox.setSelected(true); + authVncCheckbox.setSelected(true); + break; + } + } + + secTypesExt = security.GetEnabledExtSecTypes(); + for (iterExt = secTypesExt.iterator(); iterExt.hasNext(); ) { + switch ((Integer)iterExt.next()) { + case Security.secTypePlain: + encNoneCheckbox.setSelected(true); + authPlainCheckbox.setSelected(true); + break; + case Security.secTypeIdent: + encNoneCheckbox.setSelected(true); + authIdentCheckbox.setSelected(true); + break; + case Security.secTypeTLSNone: + encTLSCheckbox.setSelected(true); + authNoneCheckbox.setSelected(true); + break; + case Security.secTypeTLSVnc: + encTLSCheckbox.setSelected(true); + authVncCheckbox.setSelected(true); + break; + case Security.secTypeTLSPlain: + encTLSCheckbox.setSelected(true); + authPlainCheckbox.setSelected(true); + break; + case Security.secTypeTLSIdent: + encTLSCheckbox.setSelected(true); + authIdentCheckbox.setSelected(true); + break; + case Security.secTypeX509None: + encX509Checkbox.setSelected(true); + authNoneCheckbox.setSelected(true); + break; + case Security.secTypeX509Vnc: + encX509Checkbox.setSelected(true); + authVncCheckbox.setSelected(true); + break; + case Security.secTypeX509Plain: + encX509Checkbox.setSelected(true); + authPlainCheckbox.setSelected(true); + break; + case Security.secTypeX509Ident: + encX509Checkbox.setSelected(true); + authIdentCheckbox.setSelected(true); + break; + } + } + + File caFile = new File(CSecurityTLS.X509CA.getValueStr()); + if (caFile.exists() && caFile.canRead()) + caInput.setText(caFile.getAbsolutePath()); + File crlFile = new File(CSecurityTLS.X509CRL.getValueStr()); + if (crlFile.exists() && crlFile.canRead()) + crlInput.setText(crlFile.getAbsolutePath()); + + handleX509(); + handleSendLocalUsername(); + + /* Input */ + viewOnlyCheckbox.setSelected(viewOnly.getValue()); + acceptClipboardCheckbox.setSelected(acceptClipboard.getValue()); + sendClipboardCheckbox.setSelected(sendClipboard.getValue()); + + menuKeyChoice.setSelectedIndex(0); + + String menuKeyStr = menuKey.getValueStr(); + for (int i = 0; i < menuKeyChoice.getItemCount(); i++) + if (menuKeyStr.equals(menuKeyChoice.getItemAt(i))) + menuKeyChoice.setSelectedIndex(i); + + /* Screen */ + String width, height; + + if (desktopSize.getValueStr().isEmpty() || + desktopSize.getValueStr().split("x").length != 2) { + desktopSizeCheckbox.setSelected(false); + desktopWidthInput.setText("1024"); + desktopHeightInput.setText("768"); + } else { + desktopSizeCheckbox.setSelected(true); + width = desktopSize.getValueStr().split("x")[0]; + desktopWidthInput.setText(width); + height = desktopSize.getValueStr().split("x")[1]; + desktopHeightInput.setText(height); + } + fullScreenCheckbox.setSelected(fullScreen.getValue()); + fullScreenAllMonitorsCheckbox.setSelected(fullScreenAllMonitors.getValue()); + + scalingFactorInput.setSelectedItem("100%"); + String scaleStr = scalingFactor.getValueStr(); + if (scaleStr.matches("^[0-9]+$")) + scaleStr = scaleStr.concat("%"); + for (int i = 0; i < scalingFactorInput.getItemCount(); i++) + if (scaleStr.equals(scalingFactorInput.getItemAt(i))) + scalingFactorInput.setSelectedIndex(i); + + handleDesktopSize(); + + /* Misc. */ + sharedCheckbox.setSelected(shared.getValue()); + localCursorCheckbox.setSelected(useLocalCursor.getValue()); + acceptBellCheckbox.setSelected(acceptBell.getValue()); + + /* SSH */ + File f; + tunnelCheckbox.setSelected(tunnel.getValue() || !via.getValueStr().isEmpty()); + viaCheckbox.setSelected(!via.getValueStr().isEmpty()); + if (viaCheckbox.isSelected()) { + viaUserInput.setText(Tunnel.getSshUser()); + viaHostInput.setText(Tunnel.getSshHost()); + viaPortInput.setText(Integer.toString(Tunnel.getSshPort())); + } + extSSHCheckbox.setSelected(extSSH.getValue()); + f = new File(extSSHClient.getValueStr()); + if (f.exists() && f.isFile() && f.canExecute()) + sshClientInput.setText(f.getAbsolutePath()); + if (extSSHArgs.getValueStr().isEmpty()) { + sshArgsDefaultButton.setSelected(true); + } else { + sshArgsCustomButton.setSelected(true); + sshArgsInput.setText(extSSHArgs.getValueStr()); + } + f = new File(sshKeyFile.getValueStr()); + if (f.exists() && f.isFile() && f.canRead()) + sshKeyFileInput.setText(f.getAbsolutePath()); + f = new File(sshConfig.getValueStr()); + if (f.exists() && f.isFile() && f.canRead()) + sshConfigInput.setText(f.getAbsolutePath()); + + handleTunnel(); + handleVia(); + handleExtSSH(); + handleEmbed(); + handleRfbState(); + } + + private void storeOptions() { + /* Compression */ + autoSelect.setParam(autoselectCheckbox.isSelected()); + + if (tightButton.isSelected()) + preferredEncoding.setParam(Encodings.encodingName(Encodings.encodingTight)); + else if (zrleButton.isSelected()) + preferredEncoding.setParam(Encodings.encodingName(Encodings.encodingZRLE)); + else if (hextileButton.isSelected()) + preferredEncoding.setParam(Encodings.encodingName(Encodings.encodingHextile)); + else if (rawButton.isSelected()) + preferredEncoding.setParam(Encodings.encodingName(Encodings.encodingRaw)); + + fullColor.setParam(fullcolorButton.isSelected()); + if (verylowcolorButton.isSelected()) + lowColorLevel.setParam(0); + else if (lowcolorButton.isSelected()) + lowColorLevel.setParam(1); + else if (mediumcolorButton.isSelected()) + lowColorLevel.setParam(2); + + customCompressLevel.setParam(compressionCheckbox.isSelected()); + noJpeg.setParam(!jpegCheckbox.isSelected()); + compressLevel.setParam((Integer)compressionInput.getSelectedItem()); + qualityLevel.setParam((Integer)jpegInput.getSelectedItem()); + + /* Security */ + Security security = new Security(); + + /* Process security types which don't use encryption */ + if (encNoneCheckbox.isSelected()) { + if (authNoneCheckbox.isSelected()) + security.EnableSecType(Security.secTypeNone); + if (authVncCheckbox.isSelected()) + security.EnableSecType(Security.secTypeVncAuth); + if (authPlainCheckbox.isSelected()) + security.EnableSecType(Security.secTypePlain); + if (authIdentCheckbox.isSelected()) + security.EnableSecType(Security.secTypeIdent); + } + + /* Process security types which use TLS encryption */ + if (encTLSCheckbox.isSelected()) { + if (authNoneCheckbox.isSelected()) + security.EnableSecType(Security.secTypeTLSNone); + if (authVncCheckbox.isSelected()) + security.EnableSecType(Security.secTypeTLSVnc); + if (authPlainCheckbox.isSelected()) + security.EnableSecType(Security.secTypeTLSPlain); + if (authIdentCheckbox.isSelected()) + security.EnableSecType(Security.secTypeTLSIdent); + } + + /* Process security types which use X509 encryption */ + if (encX509Checkbox.isSelected()) { + if (authNoneCheckbox.isSelected()) + security.EnableSecType(Security.secTypeX509None); + if (authVncCheckbox.isSelected()) + security.EnableSecType(Security.secTypeX509Vnc); + if (authPlainCheckbox.isSelected()) + security.EnableSecType(Security.secTypeX509Plain); + if (authIdentCheckbox.isSelected()) + security.EnableSecType(Security.secTypeX509Ident); + } + + if (authIdentCheckbox.isSelected() || + authPlainCheckbox.isSelected()) { + sendLocalUsername.setParam(sendLocalUsernameCheckbox.isSelected()); + } + + SecurityClient.secTypes.setParam(security.ToString()); + + File caFile = new File(caInput.getText()); + if (caFile.exists() && caFile.canRead()) + CSecurityTLS.X509CA.setParam(caFile.getAbsolutePath()); + File crlFile = new File(crlInput.getText()); + if (crlFile.exists() && crlFile.canRead()) + CSecurityTLS.X509CRL.setParam(crlFile.getAbsolutePath()); + + /* Input */ + viewOnly.setParam(viewOnlyCheckbox.isSelected()); + acceptClipboard.setParam(acceptClipboardCheckbox.isSelected()); + sendClipboard.setParam(sendClipboardCheckbox.isSelected()); + + String menuKeyStr = + MenuKey.getMenuKeySymbols()[menuKeyChoice.getSelectedIndex()].name; + menuKey.setParam(menuKeyStr); - // Compression tab + /* Screen */ + if (desktopSizeCheckbox.isSelected() && + !desktopWidthInput.getText().isEmpty() && + !desktopHeightInput.getText().isEmpty()) { + String width = desktopWidthInput.getText(); + String height = desktopHeightInput.getText(); + desktopSize.setParam(width.concat("x").concat(height)); + } else { + desktopSize.setParam(""); + } + fullScreen.setParam(fullScreenCheckbox.isSelected()); + fullScreenAllMonitors.setParam(fullScreenAllMonitorsCheckbox.isSelected()); + + String scaleStr = + ((String)scalingFactorInput.getSelectedItem()).replace("%", ""); + if (scaleStr.equals("Fixed Aspect Ratio")) + scaleStr = "FixedRatio"; + scalingFactor.setParam(scaleStr); + + /* Misc. */ + shared.setParam(sharedCheckbox.isSelected()); + useLocalCursor.setParam(localCursorCheckbox.isSelected()); + acceptBell.setParam(acceptBellCheckbox.isSelected()); + + /* SSH */ + tunnel.setParam(tunnelCheckbox.isSelected()); + if (viaCheckbox.isSelected() && + !viaUserInput.getText().isEmpty() && + !viaHostInput.getText().isEmpty() && + !viaPortInput.getText().isEmpty()) { + String sshUser = viaUserInput.getText(); + String sshHost = viaHostInput.getText(); + String sshPort = viaPortInput.getText(); + String viaStr = sshUser.concat("@").concat(sshHost).concat(":").concat(sshPort); + via.setParam(viaStr); + } + extSSH.setParam(extSSHCheckbox.isSelected()); + if (!sshClientInput.getText().isEmpty()) + extSSHClient.setParam(sshClientInput.getText()); + if (sshArgsDefaultButton.isSelected()) + if (!sshArgsInput.getText().isEmpty()) + extSSHArgs.setParam(sshArgsInput.getText()); + if (!sshConfigInput.getText().isEmpty()) + sshConfig.setParam(sshConfigInput.getText()); + if (!sshKeyFileInput.getText().isEmpty()) + sshKeyFile.setParam(sshKeyFileInput.getText()); + + try { + for (Map.Entry iter : callbacks.entrySet()) { + Object obj = iter.getValue(); + Method cb = obj.getClass().getMethod(iter.getKey(), new Class[]{}); + cb.invoke(obj); + } + } catch (NoSuchMethodException e) { + vlog.error("NoSuchMethodException: "+e.getMessage()); + } catch (IllegalAccessException e) { + vlog.error("IllegalAccessException: "+e.getMessage()); + } catch (InvocationTargetException e) { + vlog.error("InvocationTargetException: "+e.getMessage()); + } + } + + private JPanel createCompressionPanel() { JPanel FormatPanel = new JPanel(); FormatPanel.setLayout(new BoxLayout(FormatPanel, BoxLayout.PAGE_AXIS)); @@ -138,61 +637,72 @@ class OptionsDialog extends Dialog { autoSelectPane.setLayout(new BoxLayout(autoSelectPane, BoxLayout.LINE_AXIS)); autoSelectPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); - autoSelect = new JCheckBox("Auto Select"); - autoSelectPane.add(autoSelect); + autoselectCheckbox = new JCheckBox("Auto Select"); + autoselectCheckbox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + handleAutoselect(); + } + }); + autoSelectPane.add(autoselectCheckbox); autoSelectPane.add(Box.createHorizontalGlue()); JPanel encodingPanel = new JPanel(new GridLayout(4, 1)); - encodingPanel. - setBorder(BorderFactory.createTitledBorder("Preferred encoding")); - tight = new GroupedJRadioButton("Tight", - encodingGroup, encodingPanel); - zrle = new GroupedJRadioButton("ZRLE", - encodingGroup, encodingPanel); - hextile = new GroupedJRadioButton("Hextile", - encodingGroup, encodingPanel); - raw = new GroupedJRadioButton("Raw", encodingGroup, encodingPanel); - - JPanel colourPanel = new JPanel(new GridLayout(4, 1)); - colourPanel.setBorder(BorderFactory.createTitledBorder("Color level")); - fullColour = new GroupedJRadioButton("Full (all available colors)", - colourGroup, colourPanel); - mediumColour = new GroupedJRadioButton("Medium (256 colors)", - colourGroup, colourPanel); - lowColour = new GroupedJRadioButton("Low (64 colours)", - colourGroup, colourPanel); - veryLowColour = new GroupedJRadioButton("Very low(8 colors)", - colourGroup, colourPanel); + encodingPanel.setBorder(BorderFactory.createTitledBorder("Preferred encoding")); + tightButton = new GroupedJRadioButton("Tight", encodingGroup, encodingPanel); + zrleButton = new GroupedJRadioButton("ZRLE", encodingGroup, encodingPanel); + hextileButton = new GroupedJRadioButton("Hextile", encodingGroup, encodingPanel); + rawButton = new GroupedJRadioButton("Raw", encodingGroup, encodingPanel); + + JPanel colorPanel = new JPanel(new GridLayout(4, 1)); + colorPanel.setBorder(BorderFactory.createTitledBorder("Color level")); + fullcolorButton = new GroupedJRadioButton("Full (all available colors)", + colorlevelGroup, colorPanel); + mediumcolorButton = new GroupedJRadioButton("Medium (256 colors)", + colorlevelGroup, colorPanel); + lowcolorButton = new GroupedJRadioButton("Low (64 colors)", + colorlevelGroup, colorPanel); + verylowcolorButton = new GroupedJRadioButton("Very low (8 colors)", + colorlevelGroup, colorPanel); JPanel encodingPane = new JPanel(new GridLayout(1, 2, 5, 0)); encodingPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); encodingPane.add(encodingPanel); - encodingPane.add(colourPanel); + encodingPane.add(colorPanel); JPanel tightPanel = new JPanel(new GridBagLayout()); - customCompressLevel = new JCheckBox("Custom Compression Level"); + compressionCheckbox = new JCheckBox("Custom Compression Level"); + compressionCheckbox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + handleCompression(); + } + }); Object[] compressionLevels = { 1, 2, 3, 4, 5, 6 }; - compressLevel = new MyJComboBox(compressionLevels); - ((MyJComboBox)compressLevel).setDocument(new IntegerDocument(1)); - compressLevel.setPrototypeDisplayValue("0."); - compressLevel.setEditable(true); + compressionInput = new MyJComboBox(compressionLevels); + ((MyJComboBox)compressionInput).setDocument(new IntegerDocument(1)); + compressionInput.setPrototypeDisplayValue("0."); + compressionInput.setEditable(true); JLabel compressionLabel = new JLabel("Level (1=fast, 6=best [4-6 are rarely useful])"); - noJpeg = new JCheckBox("Allow JPEG Compression"); + jpegCheckbox = new JCheckBox("Allow JPEG Compression"); + jpegCheckbox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + handleJpeg(); + } + }); Object[] qualityLevels = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - qualityLevel = new MyJComboBox(qualityLevels); - qualityLevel.setPrototypeDisplayValue("0."); + jpegInput = new MyJComboBox(qualityLevels); + jpegInput.setPrototypeDisplayValue("0."); JLabel qualityLabel = new JLabel("Quality (0=poor, 9=best)"); - tightPanel.add(customCompressLevel, + tightPanel.add(compressionCheckbox, new GridBagConstraints(0, 0, REMAINDER, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 0, 0), NONE, NONE)); - indent = getButtonLabelInset(customCompressLevel); - tightPanel.add(compressLevel, + int indent = getButtonLabelInset(compressionCheckbox); + tightPanel.add(compressionInput, new GridBagConstraints(0, 1, 1, 1, LIGHT, LIGHT, @@ -206,15 +716,15 @@ class OptionsDialog extends Dialog { LINE_START, HORIZONTAL, new Insets(0, 5, 0, 0), NONE, NONE)); - tightPanel.add(noJpeg, + tightPanel.add(jpegCheckbox, new GridBagConstraints(0, 2, REMAINDER, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(5, 0, 0, 0), NONE, NONE)); - indent = getButtonLabelInset(noJpeg); - tightPanel.add(qualityLevel, + indent = getButtonLabelInset(jpegCheckbox); + tightPanel.add(jpegInput, new GridBagConstraints(0, 3, 1, 1, LIGHT, LIGHT, @@ -238,8 +748,10 @@ class OptionsDialog extends Dialog { FormatPanel.add(autoSelectPane); FormatPanel.add(encodingPane); FormatPanel.add(tightPanel); + return FormatPanel; + } - // security tab + private JPanel createSecurityPanel() { JPanel SecPanel = new JPanel(); SecPanel.setLayout(new BoxLayout(SecPanel, BoxLayout.PAGE_AXIS)); @@ -249,46 +761,63 @@ class OptionsDialog extends Dialog { vencryptPane.setLayout(new BoxLayout(vencryptPane, BoxLayout.LINE_AXIS)); vencryptPane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0)); - secVeNCrypt = new JCheckBox("Extended encryption and "+ - "authentication methods (VeNCrypt)"); - vencryptPane.add(secVeNCrypt); - vencryptPane.add(Box.createHorizontalGlue()); JPanel encrPanel = new JPanel(new GridBagLayout()); encrPanel.setBorder(BorderFactory.createTitledBorder("Encryption")); - encNone = new JCheckBox("None"); - encTLS = new JCheckBox("Anonymous TLS"); - encX509 = new JCheckBox("TLS with X.509 certificates"); + encNoneCheckbox = new JCheckBox("None"); + encTLSCheckbox = new JCheckBox("Anonymous TLS"); + encX509Checkbox = new JCheckBox("TLS with X.509 certificates"); + encX509Checkbox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + handleX509(); + } + }); JLabel caLabel = new JLabel("X.509 CA Certificate"); - x509ca = new JTextField(); - x509ca.setName(Configuration.getParam("x509ca").getName()); - caButton = new JButton("Browse"); + caInput = new JTextField(); + caChooser = new JButton("Browse"); + caChooser.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JComponent c = ((JButton)e.getSource()).getRootPane(); + File dflt = new File(CSecurityTLS.X509CA.getValueStr()); + File f = showChooser("Path to X509 CA certificate", dflt, c); + if (f != null && f.exists() && f.canRead()) + caInput.setText(f.getAbsolutePath()); + } + }); JLabel crlLabel = new JLabel("X.509 CRL file"); - x509crl = new JTextField(); - x509crl.setName(Configuration.getParam("x509crl").getName()); - crlButton = new JButton("Browse"); - encrPanel.add(encNone, + crlInput = new JTextField(); + crlChooser = new JButton("Browse"); + crlChooser.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JComponent c = ((JButton)e.getSource()).getRootPane(); + File dflt = new File(CSecurityTLS.X509CRL.getValueStr()); + File f = showChooser("Path to X509 CRL file", dflt, c); + if (f != null && f.exists() && f.canRead()) + crlInput.setText(f.getAbsolutePath()); + } + }); + encrPanel.add(encNoneCheckbox, new GridBagConstraints(0, 0, REMAINDER, 1, HEAVY, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - encrPanel.add(encTLS, + encrPanel.add(encTLSCheckbox, new GridBagConstraints(0, 1, REMAINDER, 1, HEAVY, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - encrPanel.add(encX509, + encrPanel.add(encX509Checkbox, new GridBagConstraints(0, 2, 3, 1, HEAVY, LIGHT, LINE_START, NONE, new Insets(0, 0, 0, 0), NONE, NONE)); - indent = getButtonLabelInset(encX509); + int indent = getButtonLabelInset(encX509Checkbox); encrPanel.add(caLabel, new GridBagConstraints(0, 3, 1, 1, @@ -296,14 +825,14 @@ class OptionsDialog extends Dialog { LINE_END, NONE, new Insets(0, indent, 5, 0), 0, 0)); - encrPanel.add(x509ca, + encrPanel.add(caInput, new GridBagConstraints(1, 3, 1, 1, HEAVY, LIGHT, LINE_START, HORIZONTAL, new Insets(0, 5, 5, 0), 0, 0)); - encrPanel.add(caButton, + encrPanel.add(caChooser, new GridBagConstraints(2, 3, 1, 1, LIGHT, LIGHT, @@ -317,14 +846,14 @@ class OptionsDialog extends Dialog { LINE_END, NONE, new Insets(0, indent, 0, 0), 0, 0)); - encrPanel.add(x509crl, + encrPanel.add(crlInput, new GridBagConstraints(1, 4, 1, 1, HEAVY, LIGHT, LINE_START, HORIZONTAL, new Insets(0, 5, 0, 0), 0, 0)); - encrPanel.add(crlButton, + encrPanel.add(crlChooser, new GridBagConstraints(2, 4, 1, 1, LIGHT, LIGHT, @@ -335,40 +864,50 @@ class OptionsDialog extends Dialog { JPanel authPanel = new JPanel(new GridBagLayout()); authPanel.setBorder(BorderFactory.createTitledBorder("Authentication")); - secNone = new JCheckBox("None"); - secVnc = new JCheckBox("Standard VNC"); - secPlain = new JCheckBox("Plaintext"); - secIdent = new JCheckBox("Ident"); - sendLocalUsername = new JCheckBox("Send Local Username"); - authPanel.add(secNone, + authNoneCheckbox = new JCheckBox("None"); + authVncCheckbox = new JCheckBox("Standard VNC"); + authPlainCheckbox = new JCheckBox("Plaintext"); + authPlainCheckbox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + handleSendLocalUsername(); + } + }); + authIdentCheckbox = new JCheckBox("Ident"); + authIdentCheckbox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + handleSendLocalUsername(); + } + }); + sendLocalUsernameCheckbox = new JCheckBox("Send Local Username"); + authPanel.add(authNoneCheckbox, new GridBagConstraints(0, 0, REMAINDER, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - authPanel.add(secVnc, + authPanel.add(authVncCheckbox, new GridBagConstraints(0, 1, REMAINDER, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - authPanel.add(secPlain, + authPanel.add(authPlainCheckbox, new GridBagConstraints(0, 2, 1, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 2, 0), NONE, NONE)); - authPanel.add(secIdent, + authPanel.add(authIdentCheckbox, new GridBagConstraints(0, 3, 1, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(2, 0, 0, 0), NONE, NONE)); - authPanel.add(sendLocalUsername, + authPanel.add(sendLocalUsernameCheckbox, new GridBagConstraints(1, 2, 1, 2, HEAVY, LIGHT, @@ -404,35 +943,40 @@ class OptionsDialog extends Dialog { LINE_START, BOTH, new Insets(0, 0, 0, 0), NONE, NONE)); + return SecPanel; + } - // Input tab + private JPanel createInputPanel() { JPanel inputPanel = new JPanel(new GridBagLayout()); inputPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5)); - viewOnly = new JCheckBox("View Only (ignore mouse & keyboard)"); - acceptClipboard = new JCheckBox("Accept clipboard from server"); - sendClipboard = new JCheckBox("Send clipboard to server"); - JLabel menuKeyLabel = new JLabel("Menu Key"); + viewOnlyCheckbox = new JCheckBox("View only (ignore mouse and keyboard)"); + acceptClipboardCheckbox = new JCheckBox("Accept clipboard from server"); + sendClipboardCheckbox = new JCheckBox("Send clipboard to server"); + JLabel menuKeyLabel = new JLabel("Menu key"); String[] menuKeys = new String[MenuKey.getMenuKeySymbolCount()]; + //String[] menuKeys = new String[MenuKey.getMenuKeySymbolCount()+1]; + //menuKeys[0] = "None"; for (int i = 0; i < MenuKey.getMenuKeySymbolCount(); i++) menuKeys[i] = MenuKey.getKeyText(MenuKey.getMenuKeySymbols()[i]); - menuKey = new JComboBox(menuKeys); + //menuKeys[i+1] = MenuKey.getKeyText(MenuKey.getMenuKeySymbols()[i]); + menuKeyChoice = new JComboBox(menuKeys); - inputPanel.add(viewOnly, + inputPanel.add(viewOnlyCheckbox, new GridBagConstraints(0, 0, REMAINDER, 1, HEAVY, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - inputPanel.add(acceptClipboard, + inputPanel.add(acceptClipboardCheckbox, new GridBagConstraints(0, 1, REMAINDER, 1, HEAVY, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - inputPanel.add(sendClipboard, + inputPanel.add(sendClipboardCheckbox, new GridBagConstraints(0, 2, REMAINDER, 1, HEAVY, LIGHT, @@ -446,7 +990,7 @@ class OptionsDialog extends Dialog { LINE_START, NONE, new Insets(0, 0, 0, 0), NONE, NONE)); - inputPanel.add(menuKey, + inputPanel.add(menuKeyChoice, new GridBagConstraints(1, 3, 1, 1, HEAVY, LIGHT, @@ -460,42 +1004,42 @@ class OptionsDialog extends Dialog { LINE_START, BOTH, new Insets(0, 0, 0, 0), NONE, NONE)); + return inputPanel; + } - // Screen tab + private JPanel createScreenPanel() { JPanel ScreenPanel = new JPanel(new GridBagLayout()); ScreenPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5)); - desktopSize = new JCheckBox("Resize remote session on connect"); - desktopSize.setEnabled(!cc.viewer.embed.getValue() && - (cc.viewer.desktopSize.getValue() != null)); - desktopWidth = new IntegerTextField(5); - desktopWidth.setEnabled(desktopSize.isSelected()); - desktopHeight = new IntegerTextField(5); - desktopHeight.setEnabled(desktopSize.isSelected()); + desktopSizeCheckbox = new JCheckBox("Resize remote session on connect"); + desktopSizeCheckbox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + handleDesktopSize(); + } + }); + desktopWidthInput = new IntegerTextField(5); + desktopHeightInput = new IntegerTextField(5); JPanel desktopSizePanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0)); - desktopSizePanel.add(desktopWidth); + desktopSizePanel.add(desktopWidthInput); desktopSizePanel.add(new JLabel(" x ")); - desktopSizePanel.add(desktopHeight); - fullScreen = new JCheckBox("Full-screen mode"); - fullScreen.setEnabled(!cc.viewer.embed.getValue()); - fullScreenAllMonitors = + desktopSizePanel.add(desktopHeightInput); + fullScreenCheckbox = new JCheckBox("Full-screen mode"); + fullScreenAllMonitorsCheckbox = new JCheckBox("Enable full-screen mode over all monitors"); - fullScreenAllMonitors.setEnabled(!cc.viewer.embed.getValue()); JLabel scalingFactorLabel = new JLabel("Scaling Factor"); Object[] scalingFactors = { "Auto", "Fixed Aspect Ratio", "50%", "75%", "95%", "100%", "105%", "125%", "150%", "175%", "200%", "250%", "300%", "350%", "400%" }; - scalingFactor = new MyJComboBox(scalingFactors); - scalingFactor.setEditable(true); - scalingFactor.setEnabled(!cc.viewer.embed.getValue()); - ScreenPanel.add(desktopSize, + scalingFactorInput = new MyJComboBox(scalingFactors); + scalingFactorInput.setEditable(true); + ScreenPanel.add(desktopSizeCheckbox, new GridBagConstraints(0, 0, REMAINDER, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 0, 0), NONE, NONE)); - indent = getButtonLabelInset(desktopSize); + int indent = getButtonLabelInset(desktopSizeCheckbox); ScreenPanel.add(desktopSizePanel, new GridBagConstraints(0, 1, REMAINDER, 1, @@ -503,15 +1047,15 @@ class OptionsDialog extends Dialog { LINE_START, NONE, new Insets(0, indent, 0, 0), NONE, NONE)); - ScreenPanel.add(fullScreen, + ScreenPanel.add(fullScreenCheckbox, new GridBagConstraints(0, 2, REMAINDER, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - indent = getButtonLabelInset(fullScreen); - ScreenPanel.add(fullScreenAllMonitors, + indent = getButtonLabelInset(fullScreenCheckbox); + ScreenPanel.add(fullScreenAllMonitorsCheckbox, new GridBagConstraints(0, 3, REMAINDER, 1, LIGHT, LIGHT, @@ -525,7 +1069,7 @@ class OptionsDialog extends Dialog { LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - ScreenPanel.add(scalingFactor, + ScreenPanel.add(scalingFactorInput, new GridBagConstraints(1, 4, 1, 1, HEAVY, LIGHT, @@ -539,29 +1083,31 @@ class OptionsDialog extends Dialog { LINE_START, BOTH, new Insets(0, 0, 0, 0), NONE, NONE)); + return ScreenPanel; + } - // Misc tab + private JPanel createMiscPanel() { JPanel MiscPanel = new JPanel(new GridBagLayout()); MiscPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5)); - shared = - new JCheckBox("Shared connection (do not disconnect other viewers)"); - useLocalCursor = new JCheckBox("Render cursor locally"); - acceptBell = new JCheckBox("Beep when requested by the server"); - MiscPanel.add(shared, + sharedCheckbox = + new JCheckBox("Shared (don't disconnect other viewers)"); + localCursorCheckbox = new JCheckBox("Render cursor locally"); + acceptBellCheckbox = new JCheckBox("Beep when requested by the server"); + MiscPanel.add(sharedCheckbox, new GridBagConstraints(0, 0, 1, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - MiscPanel.add(useLocalCursor, + MiscPanel.add(localCursorCheckbox, new GridBagConstraints(0, 1, 1, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - MiscPanel.add(acceptBell, + MiscPanel.add(acceptBellCheckbox, new GridBagConstraints(0, 2, 1, 1, LIGHT, LIGHT, @@ -575,52 +1121,101 @@ class OptionsDialog extends Dialog { LINE_START, BOTH, new Insets(0, 0, 0, 0), NONE, NONE)); + return MiscPanel; + } - // SSH tab + private JPanel createSshPanel() { JPanel sshPanel = new JPanel(new GridBagLayout()); sshPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5)); - sshTunnel = new JCheckBox("Tunnel VNC over SSH"); + ButtonGroup sshArgsGroup = new ButtonGroup(); + tunnelCheckbox = new JCheckBox("Tunnel VNC over SSH"); + tunnelCheckbox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + handleTunnel(); + } + }); JPanel tunnelPanel = new JPanel(new GridBagLayout()); - sshUseGateway = new JCheckBox("Use SSH gateway"); + viaCheckbox = new JCheckBox("Use SSH gateway"); + viaCheckbox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + handleVia(); + } + }); JLabel sshUserLabel = new JLabel("Username"); - sshUser = new JTextField(); + viaUserInput = new JTextField(); JLabel sshUserAtLabel = new JLabel("@"); JLabel sshHostLabel = new JLabel("Hostname (or IP address)"); - sshHost = new JTextField(""); + viaHostInput = new JTextField(""); JLabel sshPortLabel = new JLabel("Port"); - sshPort = new IntegerTextField(5); + viaPortInput = new IntegerTextField(5); - sshUseExt = new JCheckBox("Use external SSH client"); - sshClient = new JTextField(); - sshClient.setName(Configuration.getParam("extSSHClient").getName()); - sshClientBrowser = new JButton("Browse"); + extSSHCheckbox = new JCheckBox("Use external SSH client"); + extSSHCheckbox.addItemListener(new ItemListener() { + public void itemStateChanged(ItemEvent e) { + handleExtSSH(); + } + }); + sshClientInput = new JTextField(); + sshClientChooser = new JButton("Browse"); + sshClientChooser.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JComponent c = ((JButton)e.getSource()).getRootPane(); + File dflt = new File(extSSHClient.getValueStr()); + File f = showChooser("Path to external SSH client", dflt, c); + if (f != null && f.exists() && f.isFile() && f.canExecute()) + sshClientInput.setText(f.getAbsolutePath()); + } + }); JLabel sshConfigLabel = new JLabel("SSH config file"); - sshConfig = new JTextField(); - sshConfig.setName(Configuration.getParam("sshConfig").getName()); - sshConfigBrowser = new JButton("Browse"); + sshConfigInput = new JTextField(); + sshConfigChooser = new JButton("Browse"); + sshConfigChooser.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JComponent c = ((JButton)e.getSource()).getRootPane(); + File dflt = new File(sshConfig.getValueStr()); + File f = showChooser("Path to OpenSSH client config file", dflt, c); + if (f != null && f.exists() && f.isFile() && f.canRead()) + sshConfigInput.setText(f.getAbsolutePath()); + } + }); JLabel sshKeyFileLabel = new JLabel("SSH identity file"); - sshKeyFile = new JTextField(); - sshKeyFile.setName(Configuration.getParam("sshKeyFile").getName()); - sshKeyFileBrowser = new JButton("Browse"); + sshKeyFileInput = new JTextField(); + sshKeyFileChooser = new JButton("Browse"); + sshKeyFileChooser.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JComponent c = ((JButton)e.getSource()).getRootPane(); + File f = showChooser("Path to SSH key file", null, c); + if (f != null && f.exists() && f.isFile() && f.canRead()) + sshKeyFileInput.setText(f.getAbsolutePath()); + } + }); JPanel sshArgsPanel = new JPanel(new GridBagLayout()); JLabel sshArgsLabel = new JLabel("Arguments:"); - sshArgsDefault = - new GroupedJRadioButton("Default", sshArgsGroup, sshArgsPanel); - sshArgsCustom = - new GroupedJRadioButton("Custom", sshArgsGroup, sshArgsPanel); - sshArguments = new JTextField(); + sshArgsDefaultButton = new GroupedJRadioButton("Default", sshArgsGroup, sshArgsPanel); + sshArgsDefaultButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + sshArgsInput.setEnabled(sshArgsCustomButton.isSelected()); + } + }); + sshArgsCustomButton = new GroupedJRadioButton("Custom", sshArgsGroup, sshArgsPanel); + sshArgsCustomButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + sshArgsInput.setEnabled(sshArgsCustomButton.isSelected()); + } + }); + sshArgsInput = new JTextField(); JPanel gatewayPanel = new JPanel(new GridBagLayout()); - gatewayPanel.add(sshUseGateway, + gatewayPanel.add(viaCheckbox, new GridBagConstraints(0, 0, REMAINDER, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - indent = getButtonLabelInset(sshUseGateway); + int indent = getButtonLabelInset(viaCheckbox); gatewayPanel.add(sshUserLabel, new GridBagConstraints(0, 1, 1, 1, @@ -642,7 +1237,7 @@ class OptionsDialog extends Dialog { LINE_START, HORIZONTAL, new Insets(0, 5, 4, 0), NONE, NONE)); - gatewayPanel.add(sshUser, + gatewayPanel.add(viaUserInput, new GridBagConstraints(0, 2, 1, 1, LIGHT, LIGHT, @@ -656,14 +1251,14 @@ class OptionsDialog extends Dialog { LINE_START, HORIZONTAL, new Insets(0, 2, 0, 2), NONE, NONE)); - gatewayPanel.add(sshHost, + gatewayPanel.add(viaHostInput, new GridBagConstraints(2, 2, 1, 1, HEAVY, LIGHT, LINE_START, HORIZONTAL, new Insets(0, 0, 0, 0), NONE, NONE)); - gatewayPanel.add(sshPort, + gatewayPanel.add(viaPortInput, new GridBagConstraints(3, 2, 1, 1, LIGHT, LIGHT, @@ -672,21 +1267,21 @@ class OptionsDialog extends Dialog { NONE, NONE)); JPanel clientPanel = new JPanel(new GridBagLayout()); - clientPanel.add(sshUseExt, + clientPanel.add(extSSHCheckbox, new GridBagConstraints(0, 0, 1, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 0, 0), NONE, NONE)); - clientPanel.add(sshClient, + clientPanel.add(sshClientInput, new GridBagConstraints(1, 0, 1, 1, HEAVY, LIGHT, LINE_START, HORIZONTAL, new Insets(0, 5, 0, 0), NONE, NONE)); - clientPanel.add(sshClientBrowser, + clientPanel.add(sshClientChooser, new GridBagConstraints(2, 0, 1, 1, LIGHT, LIGHT, @@ -700,28 +1295,28 @@ class OptionsDialog extends Dialog { LINE_START, NONE, new Insets(0, 0, 0, 0), NONE, NONE)); - sshArgsPanel.add(sshArgsDefault, + sshArgsPanel.add(sshArgsDefaultButton, new GridBagConstraints(1, 1, 1, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 5, 0, 0), NONE, NONE)); - sshArgsPanel.add(sshArgsCustom, + sshArgsPanel.add(sshArgsCustomButton, new GridBagConstraints(2, 1, 1, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 5, 0, 0), NONE, NONE)); - sshArgsPanel.add(sshArguments, + sshArgsPanel.add(sshArgsInput, new GridBagConstraints(3, 1, 1, 1, HEAVY, LIGHT, LINE_START, HORIZONTAL, new Insets(0, 5, 0, 0), NONE, NONE)); - indent = getButtonLabelInset(sshUseExt); + indent = getButtonLabelInset(extSSHCheckbox); clientPanel.add(sshArgsPanel, new GridBagConstraints(0, 1, REMAINDER, 1, @@ -731,7 +1326,9 @@ class OptionsDialog extends Dialog { NONE, NONE)); JPanel opensshPanel = new JPanel(new GridBagLayout()); - opensshPanel.setBorder(BorderFactory.createTitledBorder("Embedded SSH client configuration")); + TitledBorder border = + BorderFactory.createTitledBorder("Embedded SSH client configuration"); + opensshPanel.setBorder(border); opensshPanel.add(sshConfigLabel, new GridBagConstraints(0, 0, 1, 1, @@ -739,14 +1336,14 @@ class OptionsDialog extends Dialog { LINE_START, NONE, new Insets(0, 0, 5, 0), NONE, NONE)); - opensshPanel.add(sshConfig, + opensshPanel.add(sshConfigInput, new GridBagConstraints(1, 0, 1, 1, HEAVY, LIGHT, LINE_START, HORIZONTAL, new Insets(0, 5, 5, 0), NONE, NONE)); - opensshPanel.add(sshConfigBrowser, + opensshPanel.add(sshConfigChooser, new GridBagConstraints(2, 0, 1, 1, LIGHT, LIGHT, @@ -760,14 +1357,14 @@ class OptionsDialog extends Dialog { LINE_START, NONE, new Insets(0, 0, 0, 0), NONE, NONE)); - opensshPanel.add(sshKeyFile, + opensshPanel.add(sshKeyFileInput, new GridBagConstraints(1, 1, 1, 1, HEAVY, LIGHT, LINE_START, HORIZONTAL, new Insets(0, 5, 0, 0), NONE, NONE)); - opensshPanel.add(sshKeyFileBrowser, + opensshPanel.add(sshKeyFileChooser, new GridBagConstraints(2, 1, 1, 1, LIGHT, LIGHT, @@ -796,14 +1393,14 @@ class OptionsDialog extends Dialog { new Insets(0, 0, 0, 0), NONE, NONE)); - sshPanel.add(sshTunnel, + sshPanel.add(tunnelCheckbox, new GridBagConstraints(0, 0, REMAINDER, 1, LIGHT, LIGHT, LINE_START, NONE, new Insets(0, 0, 4, 0), NONE, NONE)); - indent = getButtonLabelInset(sshTunnel); + indent = getButtonLabelInset(tunnelCheckbox); sshPanel.add(tunnelPanel, new GridBagConstraints(0, 2, REMAINDER, 1, @@ -818,601 +1415,142 @@ class OptionsDialog extends Dialog { LINE_START, BOTH, new Insets(0, 0, 0, 0), NONE, NONE)); + return sshPanel; + } - // load/save tab - JPanel loadSavePanel = new JPanel(new GridBagLayout()); - loadSavePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5)); - JPanel configPanel = new JPanel(new GridBagLayout()); - configPanel. - setBorder(BorderFactory.createTitledBorder("Configuration File")); - cfLoadButton = new JButton("Load"); - cfSaveAsButton = new JButton("Save As..."); - configPanel.add(cfLoadButton, - new GridBagConstraints(0, 0, - 1, 1, - HEAVY, LIGHT, - CENTER, HORIZONTAL, - new Insets(0, 0, 5, 0), - NONE, NONE)); - configPanel.add(cfSaveAsButton, - new GridBagConstraints(0, 1, - 1, 1, - HEAVY, HEAVY, - CENTER, HORIZONTAL, - new Insets(0, 0, 0, 0), - NONE, NONE)); + private void handleAutoselect() + { + ButtonGroup[] groups = { encodingGroup, colorlevelGroup }; + for (ButtonGroup grp : groups) { + Enumeration elems = grp.getElements(); + while (elems.hasMoreElements()) + elems.nextElement().setEnabled(!autoselectCheckbox.isSelected()); + } - JPanel defaultsPanel = new JPanel(new GridBagLayout()); - defaultsPanel.setBorder(BorderFactory.createTitledBorder("Defaults")); - defClearButton = new JButton("Clear"); - defReloadButton = new JButton("Reload"); - defSaveButton = new JButton("Save"); - defaultsPanel.add(defClearButton, - new GridBagConstraints(0, 0, - 1, 1, - HEAVY, LIGHT, - CENTER, HORIZONTAL, - new Insets(0, 0, 5, 0), - NONE, NONE)); - defaultsPanel.add(defReloadButton, - new GridBagConstraints(0, 1, - 1, 1, - HEAVY, LIGHT, - CENTER, HORIZONTAL, - new Insets(0, 0, 5, 0), - NONE, NONE)); - defaultsPanel.add(defSaveButton, - new GridBagConstraints(0, 2, - 1, 1, - HEAVY, HEAVY, - CENTER, HORIZONTAL, - new Insets(0, 0, 0, 0), - NONE, NONE)); - - loadSavePanel.add(configPanel, - new GridBagConstraints(0, 0, - 1, 1, - HEAVY, LIGHT, - PAGE_START, HORIZONTAL, - new Insets(0, 0, 0, 0), - NONE, NONE)); - loadSavePanel.add(Box.createRigidArea(new Dimension(5, 0)), - new GridBagConstraints(1, 1, - 1, 1, - LIGHT, LIGHT, - LINE_START, NONE, - new Insets(0, 0, 0, 0), - NONE, NONE)); - loadSavePanel.add(defaultsPanel, - new GridBagConstraints(2, 0, - 1, 1, - HEAVY, LIGHT, - PAGE_START, HORIZONTAL, - new Insets(0, 0, 0, 0), - NONE, NONE)); - loadSavePanel.add(Box.createRigidArea(new Dimension(5, 0)), - new GridBagConstraints(0, 1, - REMAINDER, REMAINDER, - HEAVY, HEAVY, - LINE_START, BOTH, - new Insets(0, 0, 0, 0), - NONE, NONE)); + // JPEG setting is also affected by autoselection + jpegCheckbox.setEnabled(!autoselectCheckbox.isSelected()); + handleJpeg(); + } - // tabPane - tabPane.addTab("Compression", FormatPanel); - tabPane.addTab("Security", SecPanel); - tabPane.addTab("Input", inputPanel); - tabPane.addTab("Screen", ScreenPanel); - tabPane.addTab("Misc", MiscPanel); - tabPane.addTab("SSH", sshPanel); - tabPane.addTab("Load / Save", loadSavePanel); - tabPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); - // Resize the tabPane if necessary to prevent scrolling - Insets tpi = - (Insets)UIManager.get("TabbedPane:TabbedPaneTabArea.contentMargins"); - int minWidth = tpi.left + tpi.right; - for (int i = 0; i < tabPane.getTabCount(); i++) - minWidth += tabPane.getBoundsAt(i).width; - int minHeight = tabPane.getPreferredSize().height; - if (tabPane.getPreferredSize().width < minWidth) - tabPane.setPreferredSize(new Dimension(minWidth, minHeight)); + private void handleCompression() + { + compressionInput.setEnabled(compressionCheckbox.isSelected()); + } - // button pane - okButton = new JButton("OK"); - cancelButton = new JButton("Cancel"); + private void handleJpeg() + { + if (jpegCheckbox.isSelected() && + !autoselectCheckbox.isSelected()) + jpegInput.setEnabled(true); + else + jpegInput.setEnabled(false); + } - JPanel buttonPane = new JPanel(new GridLayout(1, 5, 10, 10)); - buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 5)); - buttonPane.add(Box.createRigidArea(new Dimension())); - buttonPane.add(Box.createRigidArea(new Dimension())); - buttonPane.add(Box.createRigidArea(new Dimension())); - buttonPane.add(okButton); - buttonPane.add(cancelButton); + private void handleX509() + { + caInput.setEnabled(encX509Checkbox.isSelected()); + caChooser.setEnabled(encX509Checkbox.isSelected()); + crlInput.setEnabled(encX509Checkbox.isSelected()); + crlChooser.setEnabled(encX509Checkbox.isSelected()); + } - this.add(tabPane); - this.add(buttonPane); - addListeners(this); - pack(); + private void handleSendLocalUsername() + { + boolean value = authIdentCheckbox.isSelected() || + authPlainCheckbox.isSelected(); + sendLocalUsernameCheckbox.setEnabled(value); } - public void initDialog() { - if (cc != null) cc.setOptions(); - zrle.setEnabled(!autoSelect.isSelected()); - hextile.setEnabled(!autoSelect.isSelected()); - tight.setEnabled(!autoSelect.isSelected()); - raw.setEnabled(!autoSelect.isSelected()); - fullColour.setEnabled(!autoSelect.isSelected()); - mediumColour.setEnabled(!autoSelect.isSelected()); - lowColour.setEnabled(!autoSelect.isSelected()); - veryLowColour.setEnabled(!autoSelect.isSelected()); - compressLevel.setEnabled(customCompressLevel.isSelected()); - qualityLevel.setEnabled(noJpeg.isSelected()); - sendLocalUsername.setEnabled(secVeNCrypt.isEnabled() && - (secPlain.isSelected() || secIdent.isSelected())); - sshArguments.setEnabled(sshTunnel.isSelected() && - (sshUseExt.isSelected() && sshArgsCustom.isSelected())); + private void handleDesktopSize() + { + desktopWidthInput.setEnabled(desktopSizeCheckbox.isSelected()); + desktopHeightInput.setEnabled(desktopSizeCheckbox.isSelected()); } - private void updatePreferences() { - if (autoSelect.isSelected()) { - UserPreferences.set("global", "AutoSelect", true); - } else { - UserPreferences.set("global", "AutoSelect", false); - if (zrle.isSelected()) { - UserPreferences.set("global", "PreferredEncoding", "ZRLE"); - } else if (hextile.isSelected()) { - UserPreferences.set("global", "PreferredEncoding", "hextile"); - } else if (tight.isSelected()) { - UserPreferences.set("global", "PreferredEncoding", "Tight"); - } else if (raw.isSelected()) { - UserPreferences.set("global", "PreferredEncoding", "raw"); - } - } - if (fullColour.isSelected()) { - UserPreferences.set("global", "FullColour", true); - } else { - UserPreferences.set("global", "FullColour", false); - if (mediumColour.isSelected()) { - UserPreferences.set("global", "LowColorLevel", 2); - } else if (lowColour.isSelected()) { - UserPreferences.set("global", "LowColorLevel", 1); - } else if (veryLowColour.isSelected()) { - UserPreferences.set("global", "LowColorLevel", 0); - } - } - UserPreferences.set("global", "NoJPEG", !noJpeg.isSelected()); - UserPreferences.set("global", - "QualityLevel", (Integer)qualityLevel.getSelectedItem()); - UserPreferences.set("global", - "CustomCompressLevel", customCompressLevel.isSelected()); - UserPreferences.set("global", - "CompressLevel", (Integer)compressLevel.getSelectedItem()); - UserPreferences.set("global", "ViewOnly", viewOnly.isSelected()); - UserPreferences.set("global", - "AcceptClipboard", acceptClipboard.isSelected()); - UserPreferences.set("global", "SendClipboard", sendClipboard.isSelected()); - String menuKeyStr = - MenuKey.getMenuKeySymbols()[menuKey.getSelectedIndex()].name; - UserPreferences.set("global", "MenuKey", menuKeyStr); - String desktopSizeString = - desktopSize.isSelected() ? - desktopWidth.getText() + "x" + desktopHeight.getText() : ""; - UserPreferences.set("global", "DesktopSize", desktopSizeString); - UserPreferences.set("global", "FullScreen", fullScreen.isSelected()); - UserPreferences.set("global", - "FullScreenAllMonitors", fullScreenAllMonitors.isSelected()); - UserPreferences.set("global", "Shared", shared.isSelected()); - UserPreferences.set("global", - "UseLocalCursor", useLocalCursor.isSelected()); - UserPreferences.set("global", "AcceptBell", acceptBell.isSelected()); - String scaleString = scalingFactor.getSelectedItem().toString(); - if (scaleString.equalsIgnoreCase("Auto")) { - UserPreferences.set("global", "ScalingFactor", "Auto"); - } else if(scaleString.equalsIgnoreCase("Fixed Aspect Ratio")) { - UserPreferences.set("global", "ScalingFactor", "FixedRatio"); + private void handleTunnel() + { + viaCheckbox.setEnabled(tunnelCheckbox.isSelected()); + extSSHCheckbox.setEnabled(tunnelCheckbox.isSelected()); + if (tunnelCheckbox.isSelected()) { + JComponent[] components = { viaUserInput, viaHostInput, viaPortInput }; + for (JComponent c : components) + c.setEnabled(viaCheckbox.isSelected()); + sshClientInput.setEnabled(extSSHCheckbox.isSelected()); + sshClientChooser.setEnabled(extSSHCheckbox.isSelected()); + sshArgsDefaultButton.setEnabled(extSSHCheckbox.isSelected()); + sshArgsCustomButton.setEnabled(extSSHCheckbox.isSelected()); + sshArgsInput.setEnabled(extSSHCheckbox.isSelected()); + sshConfigInput.setEnabled(!extSSHCheckbox.isSelected()); + sshConfigChooser.setEnabled(!extSSHCheckbox.isSelected()); + sshKeyFileInput.setEnabled(!extSSHCheckbox.isSelected()); + sshKeyFileChooser.setEnabled(!extSSHCheckbox.isSelected()); } else { - scaleString=scaleString.substring(0, scaleString.length()-1); - UserPreferences.set("global", "ScalingFactor", scaleString); - } - UserPreferences.set("viewer", "secVeNCrypt", secVeNCrypt.isSelected()); - UserPreferences.set("viewer", "encNone", encNone.isSelected()); - UserPreferences.set("viewer", "encTLS", encTLS.isSelected()); - UserPreferences.set("viewer", "encX509", encX509.isSelected()); - UserPreferences.set("viewer", "secNone", secNone.isSelected()); - UserPreferences.set("viewer", "secVnc", secVnc.isSelected()); - UserPreferences.set("viewer", "secPlain", secPlain.isSelected()); - UserPreferences.set("viewer", "secIdent", secIdent.isSelected()); - UserPreferences.set("global", - "SendLocalUsername", sendLocalUsername.isSelected()); - if (!CSecurityTLS.x509ca.getValueStr().equals("")) - UserPreferences.set("viewer", "x509ca", - CSecurityTLS.x509ca.getValueStr()); - if (!CSecurityTLS.x509crl.getValueStr().equals("")) - UserPreferences.set("viewer", "x509crl", - CSecurityTLS.x509crl.getValueStr()); - UserPreferences.set("global", "Tunnel", sshTunnel.isSelected()); - if (sshUseGateway.isSelected()) { - String via = sshUser.getText()+"@"+sshHost.getText()+":"+sshPort.getText(); - UserPreferences.set("global", "Via", via); + JComponent[] components = { + viaUserInput, viaHostInput, viaPortInput, sshClientInput, + sshClientChooser, sshArgsDefaultButton, sshArgsCustomButton, + sshArgsInput, sshConfigInput, sshConfigChooser, sshKeyFileInput, + sshKeyFileChooser, }; + for (JComponent c : components) + c.setEnabled(false); } - if (sshUseExt.isSelected()) { - UserPreferences.set("global", "extSSH", sshUseExt.isSelected()); - UserPreferences.set("global", "extSSHClient", sshClient.getText()); - if (!sshArguments.getText().isEmpty()) - UserPreferences.set("global", "extSSHArgs", sshArguments.getText()); - } - UserPreferences.set("global", "SSHConfig", sshConfig.getText()); - UserPreferences.set("global", "SSHKeyFile", sshKeyFile.getText()); } - private void restorePreferences() { - autoSelect.setSelected(UserPreferences.getBool("global", "AutoSelect")); - if (!autoSelect.isSelected()) { - if (UserPreferences.getBool("global", "FullColour")) { - fullColour.setSelected(true); - } else { - switch (UserPreferences.getInt("global", "LowColorLevel")) { - case 2: - mediumColour.setSelected(true); - break; - case 1: - lowColour.setSelected(true); - break; - case 0: - veryLowColour.setSelected(true); - break; - } - } - String encoding = UserPreferences.get("global", "PreferredEncoding"); - if (encoding != null) { - switch (Encodings.encodingNum(encoding)) { - case Encodings.encodingZRLE: - zrle.setSelected(true); - break; - case Encodings.encodingHextile: - hextile.setSelected(true); - break; - case Encodings.encodingRaw: - raw.setSelected(true); - break; - default: - tight.setSelected(true); - } - } + private void handleVia() + { + if (tunnelCheckbox.isSelected()) { + viaUserInput.setEnabled(viaCheckbox.isSelected()); + viaHostInput.setEnabled(viaCheckbox.isSelected()); + viaPortInput.setEnabled(viaCheckbox.isSelected()); } - noJpeg.setSelected(!UserPreferences.getBool("global", "NoJPEG")); - qualityLevel.setSelectedItem(UserPreferences.getInt("global", - "QualityLevel")); - customCompressLevel.setSelected(UserPreferences.getBool("global", - "CustomCompressLevel")); - compressLevel.setSelectedItem(UserPreferences.getInt("global", - "CompressLevel")); - viewOnly.setSelected(UserPreferences.getBool("global", "ViewOnly")); - acceptClipboard.setSelected(UserPreferences.getBool("global", - "AcceptClipboard")); - sendClipboard.setSelected(UserPreferences.getBool("global", - "SendClipboard")); - menuKey.setSelectedItem(UserPreferences.get("global", "MenuKey")); - desktopSize.setSelected(!UserPreferences.get("global", "DesktopSize").isEmpty()); - if (desktopSize.isSelected()) { - String desktopSizeString = UserPreferences.get("global", "DesktopSize"); - desktopWidth.setText(desktopSizeString.split("x")[0]); - desktopHeight.setText(desktopSizeString.split("x")[1]); - } - fullScreen.setSelected(UserPreferences.getBool("global", "FullScreen")); - fullScreenAllMonitors.setSelected(UserPreferences.getBool("global", - "FullScreenAllMonitors")); - if (shared.isEnabled()) - shared.setSelected(UserPreferences.getBool("global", "Shared")); - useLocalCursor.setSelected(UserPreferences.getBool("global", - "UseLocalCursor")); - acceptBell.setSelected(UserPreferences.getBool("global", "AcceptBell")); - String scaleString = UserPreferences.get("global", "ScalingFactor"); - if (scaleString != null) { - if (scaleString.equalsIgnoreCase("Auto")) { - scalingFactor.setSelectedItem("Auto"); - } else if (scaleString.equalsIgnoreCase("FixedRatio")) { - scalingFactor.setSelectedItem("Fixed Aspect Ratio"); - } else { - scalingFactor.setSelectedItem(scaleString+"%"); - } - } - if (secVeNCrypt.isEnabled()) { - secVeNCrypt.setSelected(UserPreferences.getBool("viewer", - "secVeNCrypt", true)); - if (secVeNCrypt.isSelected()) { - encNone.setSelected(UserPreferences.getBool("viewer", "encNone", true)); - encTLS.setSelected(UserPreferences.getBool("viewer", "encTLS", true)); - encX509.setSelected(UserPreferences.getBool("viewer", "encX509", true)); - secPlain.setSelected(UserPreferences.getBool("viewer", "secPlain", true)); - secIdent.setSelected(UserPreferences.getBool("viewer", "secIdent", true)); - sendLocalUsername.setSelected(UserPreferences.getBool("global", - "SendLocalUsername")); - } - } - if (secNone.isEnabled()) - secNone.setSelected(UserPreferences.getBool("viewer", "secNone", true)); - if (secVnc.isEnabled()) - secVnc.setSelected(UserPreferences.getBool("viewer", "secVnc", true)); - sshTunnel.setSelected(UserPreferences.getBool("global", "Tunnel")); - sshUseGateway.setSelected(UserPreferences.get("global", "Via") != null); - if (sshUseGateway.isSelected()) - cc.viewer.via.setParam(UserPreferences.get("global", "Via")); - sshUser.setText(Tunnel.getSshUser(cc)); - sshHost.setText(Tunnel.getSshHost(cc)); - sshPort.setText(Integer.toString(Tunnel.getSshPort(cc))); - sshUseExt.setSelected(UserPreferences.getBool("global", "extSSH")); - File f = new File(UserPreferences.get("global", "extSSHClient")); - if (f.exists() && f.canExecute()) - sshClient.setText(f.getAbsolutePath()); - sshArguments.setText(UserPreferences.get("global", "extSSHArgs")); - if (sshArguments.getText().isEmpty()) - sshArgsDefault.setSelected(true); - else - sshArgsCustom.setSelected(true); - f = new File(UserPreferences.get("global", "SSHConfig")); - if (f.exists() && f.canRead()) - sshConfig.setText(f.getAbsolutePath()); - if (UserPreferences.get("global", "SSHKeyFile") != null) { - f = new File(UserPreferences.get("global", "SSHKeyFile")); - if (f.exists() && f.canRead()) - sshKeyFile.setText(f.getAbsolutePath()); - } else { - sshKeyFile.setText(Tunnel.getSshKeyFile(cc)); - } - sshUseGateway.setEnabled(sshTunnel.isSelected()); - sshUser.setEnabled(sshTunnel.isSelected() && - sshUseGateway.isEnabled() && - sshUseGateway.isSelected()); - sshHost.setEnabled(sshTunnel.isSelected() && - sshUseGateway.isEnabled() && - sshUseGateway.isSelected()); - sshPort.setEnabled(sshTunnel.isSelected() && - sshUseGateway.isEnabled() && - sshUseGateway.isSelected()); - sshUseExt.setEnabled(sshTunnel.isSelected()); - sshClient.setEnabled(sshTunnel.isSelected() && - sshUseExt.isEnabled()); - sshClientBrowser.setEnabled(sshTunnel.isSelected() && - sshUseExt.isEnabled() && - sshUseExt.isSelected()); - sshArgsDefault.setEnabled(sshTunnel.isSelected() && - sshUseExt.isEnabled() && - sshUseExt.isSelected()); - sshArgsCustom.setEnabled(sshTunnel.isSelected() && - sshUseExt.isEnabled() && - sshUseExt.isSelected()); - sshArguments.setEnabled(sshTunnel.isSelected() && - sshUseExt.isEnabled() && - sshUseExt.isSelected() && - sshArgsCustom.isSelected()); - sshConfig.setEnabled(sshTunnel.isSelected() && - sshUseExt.isEnabled() && - !sshUseExt.isSelected()); - sshConfigBrowser.setEnabled(sshTunnel.isSelected() && - sshUseExt.isEnabled() && - !sshUseExt.isSelected()); - sshKeyFile.setEnabled(sshTunnel.isSelected() && - sshUseExt.isEnabled() && - !sshUseExt.isSelected()); - sshKeyFileBrowser.setEnabled(sshTunnel.isSelected() && - sshUseExt.isEnabled() && - !sshUseExt.isSelected()); } - public void endDialog() { - super.endDialog(); - if (cc.viewport != null && cc.viewport.isVisible()) { - cc.viewport.toFront(); - cc.viewport.requestFocus(); + private void handleExtSSH() + { + if (tunnelCheckbox.isSelected()) { + sshClientInput.setEnabled(extSSHCheckbox.isSelected()); + sshClientChooser.setEnabled(extSSHCheckbox.isSelected()); + sshArgsDefaultButton.setEnabled(extSSHCheckbox.isSelected()); + sshArgsCustomButton.setEnabled(extSSHCheckbox.isSelected()); + sshConfigInput.setEnabled(!extSSHCheckbox.isSelected()); + sshConfigChooser.setEnabled(!extSSHCheckbox.isSelected()); + sshKeyFileInput.setEnabled(!extSSHCheckbox.isSelected()); + sshKeyFileChooser.setEnabled(!extSSHCheckbox.isSelected()); + if (sshArgsCustomButton.isSelected()) + sshArgsInput.setEnabled(extSSHCheckbox.isSelected()); + else + sshArgsInput.setEnabled(false); } } - public void actionPerformed(ActionEvent e) { - Object s = e.getSource(); - if (s instanceof JButton) { - JButton button = (JButton)s; - if (button == okButton) { - JTextField[] fields = - { x509ca, x509crl, sshClient, sshConfig, sshKeyFile }; - for (JTextField field : fields) { - if (field.getText() != null && !field.getText().equals("")) { - File f = new File(field.getText()); - if (!f.exists() || !f.canRead()) { - String msg = new String("The file "+f.getAbsolutePath()+ - " specified for option "+field.getName()+ - " does not exist or cannot be read. Please"+ - " correct before proceeding."); - JOptionPane.showMessageDialog(this, msg, "WARNING", - JOptionPane.WARNING_MESSAGE); - return; - } - } - } - if (cc != null) cc.getOptions(); - endDialog(); - } else if (button == cancelButton) { - endDialog(); - } else if (button == cfLoadButton) { - JFileChooser fc = new JFileChooser(); - fc.setDialogTitle("Path to configuration file"); - fc.setApproveButtonText("OK"); - fc.setFileHidingEnabled(false); - int ret = fc.showOpenDialog(this); - if (ret == JFileChooser.APPROVE_OPTION) { - String filename = fc.getSelectedFile().toString(); - if (filename != null) - Configuration.load(filename); - cc.setOptions(); - } - } else if (button == cfSaveAsButton) { - JFileChooser fc = new JFileChooser(); - fc.setDialogTitle("Save current configuration as:"); - fc.setApproveButtonText("OK"); - fc.setFileHidingEnabled(false); - int ret = fc.showOpenDialog(this); - if (ret == JFileChooser.APPROVE_OPTION) { - String filename = fc.getSelectedFile().toString(); - if (filename != null) - Configuration.save(filename); - } - } else if (button == defSaveButton) { - updatePreferences(); - UserPreferences.save(); - } else if (button == defReloadButton) { - restorePreferences(); - } else if (button == defClearButton) { - UserPreferences.clear(); - cc.setOptions(); - } else if (button == caButton) { - JFileChooser fc = - new JFileChooser(new File(CSecurityTLS.getDefaultCA())); - fc.setDialogTitle("Path to X509 CA certificate"); - fc.setApproveButtonText("OK"); - fc.setFileHidingEnabled(false); - int ret = fc.showOpenDialog(this); - if (ret == JFileChooser.APPROVE_OPTION) - x509ca.setText(fc.getSelectedFile().toString()); - } else if (button == crlButton) { - JFileChooser fc = - new JFileChooser(new File(CSecurityTLS.getDefaultCRL())); - fc.setDialogTitle("Path to X509 CRL file"); - fc.setApproveButtonText("OK"); - fc.setFileHidingEnabled(false); - int ret = fc.showOpenDialog(this); - if (ret == JFileChooser.APPROVE_OPTION) - x509crl.setText(fc.getSelectedFile().toString()); - } else if (button == sshClientBrowser) { - JFileChooser fc = new JFileChooser(); - fc.setDialogTitle("Path to external SSH client"); - fc.setApproveButtonText("OK"); - fc.setFileHidingEnabled(false); - int ret = fc.showOpenDialog(this); - if (ret == JFileChooser.APPROVE_OPTION) - sshClient.setText(fc.getSelectedFile().toString()); - } else if (button == sshConfigBrowser) { - JFileChooser fc = new JFileChooser(); - fc.setDialogTitle("Path to OpenSSH client config file"); - fc.setApproveButtonText("OK"); - fc.setFileHidingEnabled(false); - int ret = fc.showOpenDialog(this); - if (ret == JFileChooser.APPROVE_OPTION) - sshConfig.setText(fc.getSelectedFile().toString()); - } else if (button == sshKeyFileBrowser) { - JFileChooser fc = new JFileChooser(); - fc.setDialogTitle("Path to SSH key file"); - fc.setApproveButtonText("OK"); - fc.setFileHidingEnabled(false); - int ret = fc.showOpenDialog(this); - if (ret == JFileChooser.APPROVE_OPTION) - sshKeyFile.setText(fc.getSelectedFile().toString()); - } - } else if (s instanceof JRadioButton) { - JRadioButton button = (JRadioButton)s; - if (button == sshArgsCustom || button == sshArgsDefault) { - sshArguments.setEnabled(sshArgsCustom.isSelected()); - } + private void handleEmbed() + { + if (embed.getValue()) { + desktopSizeCheckbox.setEnabled(false); + desktopWidthInput.setEnabled(false); + desktopHeightInput.setEnabled(false); + fullScreenCheckbox.setEnabled(false); + fullScreenAllMonitorsCheckbox.setEnabled(false); + scalingFactorInput.setEnabled(false); } } - public void itemStateChanged(ItemEvent e) { - Object s = e.getSource(); - if (s instanceof JCheckBox) { - JCheckBox item = (JCheckBox)s; - boolean enable = item.isSelected(); - if (item == autoSelect) { - ButtonGroup[] groups = { encodingGroup, colourGroup }; - for (ButtonGroup grp : groups) { - Enumeration elems = grp.getElements(); - while (elems.hasMoreElements()) - elems.nextElement().setEnabled(!enable); - } - } else if (item == customCompressLevel) { - compressLevel.setEnabled(enable); - } else if (item == desktopSize) { - desktopWidth.setEnabled(enable); - desktopHeight.setEnabled(enable); - } else if (item == noJpeg) { - qualityLevel.setEnabled(enable); - } else if (item == encX509) { - x509ca.setEnabled(enable); - caButton.setEnabled(enable); - x509crl.setEnabled(enable); - crlButton.setEnabled(enable); - } else if (item == secVeNCrypt) { - encNone.setEnabled(enable); - encTLS.setEnabled(enable); - encX509.setEnabled(enable); - x509ca.setEnabled(enable && encX509.isSelected()); - caButton.setEnabled(enable && encX509.isSelected()); - x509crl.setEnabled(enable && encX509.isSelected()); - crlButton.setEnabled(enable && encX509.isSelected()); - secIdent.setEnabled(enable); - secPlain.setEnabled(enable); - sendLocalUsername.setEnabled(enable); - } else if (item == encNone) { - secNone.setSelected(enable && - UserPreferences.getBool("viewer", "secNone", true)); - secVnc.setSelected(enable && - UserPreferences.getBool("viewer", "secVnc", true)); - } else if (item == secIdent || item == secPlain) { - sendLocalUsername.setEnabled(secIdent.isSelected() || - secPlain.isSelected()); - } else if (item == sshTunnel) { - sshUseGateway.setEnabled(enable); - sshUser.setEnabled(enable && - sshUseGateway.isEnabled() && - sshUseGateway.isSelected()); - sshHost.setEnabled(enable && - sshUseGateway.isEnabled() && - sshUseGateway.isSelected()); - sshPort.setEnabled(enable && - sshUseGateway.isEnabled() && - sshUseGateway.isSelected()); - sshUseExt.setEnabled(enable); - sshClient.setEnabled(enable && - sshUseExt.isEnabled() && - sshUseExt.isSelected()); - sshClientBrowser.setEnabled(enable && - sshUseExt.isEnabled() && - sshUseExt.isSelected()); - sshArgsDefault.setEnabled(enable && - sshUseExt.isEnabled() && - sshUseExt.isSelected()); - sshArgsCustom.setEnabled(enable && - sshUseExt.isEnabled() && - sshUseExt.isSelected()); - sshArguments.setEnabled(enable && - sshUseExt.isEnabled() && - sshUseExt.isSelected() && - sshArgsCustom.isSelected()); - sshConfig.setEnabled(enable && - sshUseExt.isEnabled() && - !sshUseExt.isSelected()); - sshConfigBrowser.setEnabled(enable && - sshUseExt.isEnabled() && - !sshUseExt.isSelected()); - sshKeyFile.setEnabled(enable && - sshUseExt.isEnabled() && - !sshUseExt.isSelected()); - sshKeyFileBrowser.setEnabled(enable && - sshUseExt.isEnabled() && - !sshUseExt.isSelected()); - } else if (item == sshUseExt) { - sshClient.setEnabled(enable); - sshClientBrowser.setEnabled(enable); - sshArgsDefault.setEnabled(enable); - sshArgsCustom.setEnabled(enable); - sshArguments.setEnabled(enable && sshArgsCustom.isSelected()); - sshConfig.setEnabled(!enable); - sshConfigBrowser.setEnabled(!enable); - sshKeyFile.setEnabled(!enable); - sshKeyFileBrowser.setEnabled(!enable); - } else if (item == sshUseGateway) { - sshUser.setEnabled(enable); - sshHost.setEnabled(enable); - sshPort.setEnabled(enable); - } + private void handleRfbState() + { + CConn cc = VncViewer.cc; + if (cc != null && cc.state() == CConnection.RFBSTATE_NORMAL) { + JComponent[] components = { + encNoneCheckbox, encTLSCheckbox, encX509Checkbox, authNoneCheckbox, + authVncCheckbox, authVncCheckbox, authIdentCheckbox, authPlainCheckbox, + sendLocalUsernameCheckbox, caInput, caChooser, crlInput, crlChooser, + sharedCheckbox, tunnelCheckbox, viaCheckbox, viaUserInput, viaHostInput, + viaPortInput, extSSHCheckbox, sshClientInput, sshClientChooser, + sshArgsDefaultButton, sshArgsCustomButton, sshArgsInput, sshConfigInput, + sshKeyFileInput, sshConfigChooser, sshKeyFileChooser, + }; + for (JComponent c : components) + c.setEnabled(false); } } + + static LogWriter vlog = new LogWriter("OptionsDialog"); } diff --git a/java/com/tigervnc/vncviewer/OptionsDialogCallback.java b/java/com/tigervnc/vncviewer/OptionsDialogCallback.java deleted file mode 100644 index efb8c069..00000000 --- a/java/com/tigervnc/vncviewer/OptionsDialogCallback.java +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright (C) 2002-2005 RealVNC Ltd. 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, - * USA. - */ - -package com.tigervnc.vncviewer; - -public interface OptionsDialogCallback { - public void setOptions(); - public void getOptions(); -} diff --git a/java/com/tigervnc/vncviewer/PasswdDialog.java b/java/com/tigervnc/vncviewer/PasswdDialog.java index fbaf9911..9d5cc7cb 100644 --- a/java/com/tigervnc/vncviewer/PasswdDialog.java +++ b/java/com/tigervnc/vncviewer/PasswdDialog.java @@ -149,7 +149,6 @@ class PasswdDialog extends Dialog implements UserInfo, String instruction, String[] prompt, boolean[] echo) { - vlog.info("OK"); Container panel = new JPanel(new GridBagLayout()); ((JPanel)panel).setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); diff --git a/java/com/tigervnc/vncviewer/PlatformPixelBuffer.java b/java/com/tigervnc/vncviewer/PlatformPixelBuffer.java index e0e24f06..8fc2760b 100644 --- a/java/com/tigervnc/vncviewer/PlatformPixelBuffer.java +++ b/java/com/tigervnc/vncviewer/PlatformPixelBuffer.java @@ -27,12 +27,11 @@ import com.tigervnc.rfb.*; abstract public class PlatformPixelBuffer extends PixelBuffer { - public PlatformPixelBuffer(int w, int h, CConn cc_, DesktopWindow desktop_) { - cc = cc_; + public PlatformPixelBuffer(PixelFormat pf, int w, int h, DesktopWindow desktop_) { desktop = desktop_; PixelFormat nativePF = getNativePF(); - if (nativePF.depth > cc.serverPF.depth) { - setPF(cc.serverPF); + if (nativePF.depth > pf.depth) { + setPF(pf); } else { setPF(nativePF); } @@ -103,7 +102,6 @@ abstract public class PlatformPixelBuffer extends PixelBuffer byte[] greens; byte[] blues; - CConn cc; DesktopWindow desktop; static LogWriter vlog = new LogWriter("PlatformPixelBuffer"); } diff --git a/java/com/tigervnc/vncviewer/ServerDialog.java b/java/com/tigervnc/vncviewer/ServerDialog.java index 172bde6f..338e6266 100644 --- a/java/com/tigervnc/vncviewer/ServerDialog.java +++ b/java/com/tigervnc/vncviewer/ServerDialog.java @@ -21,6 +21,7 @@ package com.tigervnc.vncviewer; import java.awt.*; import java.awt.event.*; +import java.io.File; import javax.swing.*; import javax.swing.border.*; import javax.swing.WindowConstants.*; @@ -30,38 +31,41 @@ import com.tigervnc.rfb.*; import static java.awt.GridBagConstraints.HORIZONTAL; import static java.awt.GridBagConstraints.LINE_START; +import static java.awt.GridBagConstraints.LINE_END; import static java.awt.GridBagConstraints.NONE; import static java.awt.GridBagConstraints.REMAINDER; -class ServerDialog extends Dialog { +import static com.tigervnc.vncviewer.Parameters.*; - @SuppressWarnings({"unchecked","rawtypes"}) - public ServerDialog(OptionsDialog options_, - String defaultServerName, CConn cc_) { +class ServerDialog extends Dialog implements Runnable { + @SuppressWarnings({"unchecked","rawtypes"}) + public ServerDialog() { super(true); - cc = cc_; setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); setTitle("VNC Viewer: Connection Details"); setResizable(false); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { - if (VncViewer.nViewers == 1) { - cc.viewer.exit(1); - } else { - ret = false; - endDialog(); - } + endDialog(); + System.exit(1); } }); - options = options_; - - JLabel serverLabel = new JLabel("VNC Server:", JLabel.RIGHT); + JLabel serverLabel = new JLabel("VNC server:", JLabel.RIGHT); String valueStr = new String(""); if (UserPreferences.get("ServerDialog", "history") != null) valueStr = UserPreferences.get("ServerDialog", "history"); server = new MyJComboBox(valueStr.split(",")); + server.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JComboBox s = (JComboBox)e.getSource(); + if (e.getActionCommand().equals("comboBoxEdited")) { + s.insertItemAt(editor.getItem(), 0); + s.setSelectedIndex(0); + } + } + }); if (valueStr.equals("")) server.setPrototypeDisplayValue("255.255.255.255:5900"); @@ -74,7 +78,7 @@ class ServerDialog extends Dialog { if (e.getKeyCode() == KeyEvent.VK_ENTER) { server.insertItemAt(editor.getItem(), 0); server.setSelectedIndex(0); - commit(); + handleConnect(); } } }); @@ -84,9 +88,41 @@ class ServerDialog extends Dialog { JLabel icon = new JLabel(VncViewer.logoIcon); optionsButton = new JButton("Options..."); + optionsButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleOptions(); + } + }); + JButton loadButton = new JButton("Load..."); + loadButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleLoad(); + } + }); + JButton saveAsButton = new JButton("Save As..."); + saveAsButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleSaveAs(); + } + }); aboutButton = new JButton("About..."); - okButton = new JButton("OK"); + aboutButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleAbout(); + } + }); cancelButton = new JButton("Cancel"); + cancelButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleCancel(); + } + }); + connectButton = new JButton("Connect \u21B5"); + connectButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + handleConnect(); + } + }); contentPane.add(icon, new GridBagConstraints(0, 0, @@ -109,81 +145,133 @@ class ServerDialog extends Dialog { LINE_START, HORIZONTAL, new Insets(5, 0, 5, 5), NONE, NONE)); - JPanel buttonPane = new JPanel(); - buttonPane.setLayout(new GridLayout(1, 4, 5, 5)); - buttonPane.add(aboutButton); - buttonPane.add(optionsButton); - buttonPane.add(okButton); - buttonPane.add(cancelButton); - contentPane.add(buttonPane, + JPanel buttonPane1 = new JPanel(); + Box box = Box.createHorizontalBox(); + JSeparator separator1 = new JSeparator(); + JSeparator separator2 = new JSeparator(); + GroupLayout layout = new GroupLayout(buttonPane1); + buttonPane1.setLayout(layout); + layout.setAutoCreateGaps(false); + layout.setAutoCreateContainerGaps(false); + layout.setHorizontalGroup( + layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(10) + .addComponent(optionsButton)) + .addComponent(separator1) + .addGroup(layout.createSequentialGroup() + .addGap(10) + .addComponent(aboutButton))) + .addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING) + .addGroup(layout.createSequentialGroup() + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(loadButton) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(saveAsButton) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(box) + .addGap(10)) + .addComponent(separator2) + .addGroup(layout.createSequentialGroup() + .addComponent(cancelButton) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(connectButton) + .addGap(10))) + ); + layout.setVerticalGroup( + layout.createSequentialGroup() + .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(optionsButton) + .addComponent(loadButton) + .addComponent(saveAsButton) + .addComponent(box)) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(separator1) + .addComponent(separator2)) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE) + .addComponent(aboutButton) + .addComponent(cancelButton) + .addComponent(connectButton)) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + ); + layout.linkSize(SwingConstants.HORIZONTAL, + optionsButton, loadButton, saveAsButton, + aboutButton, cancelButton, box); + contentPane.add(buttonPane1, new GridBagConstraints(0, 1, REMAINDER, 1, LIGHT, LIGHT, LINE_START, HORIZONTAL, - new Insets(5, 5, 5, 5), + new Insets(5, 0, 10, 0), NONE, NONE)); - addListeners(this); pack(); } - @SuppressWarnings({"unchecked","rawtypes"}) - public void actionPerformed(ActionEvent e) { - Object s = e.getSource(); - if (s instanceof JButton && (JButton)s == okButton) { - commit(); - } else if (s instanceof JButton && (JButton)s == cancelButton) { - if (VncViewer.nViewers == 1) - cc.viewer.exit(1); - ret = false; - endDialog(); - } else if (s instanceof JButton && (JButton)s == optionsButton) { - options.showDialog(this); - } else if (s instanceof JButton && (JButton)s == aboutButton) { - cc.showAbout(); - } else if (s instanceof JComboBox && (JComboBox)s == server) { - if (e.getActionCommand().equals("comboBoxEdited")) { - server.insertItemAt(editor.getItem(), 0); - server.setSelectedIndex(0); - } + public void run() { + this.showDialog(); + } + + private void handleOptions() { + OptionsDialog.showDialog(this); + } + + private void handleLoad() { + String title = "Select a TigerVNC configuration file"; + File dflt = new File(FileUtils.getVncHomeDir().concat("default.tigervnc")); + File f = showChooser(title, dflt); + if (f != null && f.exists() && f.canRead()) + loadViewerParameters(f.getAbsolutePath()); + } + + private void handleSaveAs() { + String title = "Save the TigerVNC configuration to file"; + File dflt = new File(FileUtils.getVncHomeDir().concat("default.tigervnc")); + if (!dflt.exists() || !dflt.isFile()) + dflt = new File(FileUtils.getVncHomeDir()); + File f = showChooser(title, dflt); + while (f != null && f.exists() && f.isFile()) { + String msg = f.getAbsolutePath(); + msg = msg.concat(" already exists. Do you want to overwrite?"); + Object[] options = {"Overwrite", "No \u21B5"}; + JOptionPane op = + new JOptionPane(msg, JOptionPane.QUESTION_MESSAGE, + JOptionPane.OK_CANCEL_OPTION, null, options, options[1]); + JDialog dlg = op.createDialog(this, "TigerVNC Viewer"); + dlg.setIconImage(VncViewer.frameIcon); + dlg.setAlwaysOnTop(true); + dlg.setVisible(true); + if (op.getValue() == options[0]) + break; + else + f = showChooser(title, f); } + if (f != null && (!f.exists() || f.canWrite())) + saveViewerParameters(f.getAbsolutePath(), (String)server.getSelectedItem()); + } + + private void handleAbout() { + VncViewer.showAbout(this); } - private void commit() { + private void handleCancel() { + vncServerName.setParam(""); + endDialog(); + } + + private void handleConnect() { String serverName = (String)server.getSelectedItem(); - if (serverName == null || serverName.equals("")) { - vlog.error("Invalid servername specified"); - if (VncViewer.nViewers == 1) - cc.viewer.exit(1); - ret = false; - endDialog(); - } - // set params - Configuration.setParam("Server", Hostname.getHost(serverName)); - Configuration.setParam("Port", - Integer.toString(Hostname.getPort(serverName))); - // Update the history list - String valueStr = UserPreferences.get("ServerDialog", "history"); - String t = (valueStr == null) ? "" : valueStr; - StringTokenizer st = new StringTokenizer(t, ","); - StringBuffer sb = - new StringBuffer().append((String)server.getSelectedItem()); - while (st.hasMoreTokens()) { - String str = st.nextToken(); - if (!str.equals((String)server.getSelectedItem()) && !str.equals("")) { - sb.append(','); - sb.append(str); - } - } - UserPreferences.set("ServerDialog", "history", sb.toString()); - UserPreferences.save("ServerDialog"); + vncServerName.setParam(serverName); + saveViewerParameters(null, serverName); endDialog(); } - CConn cc; @SuppressWarnings("rawtypes") MyJComboBox server; ComboBoxEditor editor; - JButton aboutButton, optionsButton, okButton, cancelButton; + JButton aboutButton, optionsButton, connectButton, cancelButton; OptionsDialog options; static LogWriter vlog = new LogWriter("ServerDialog"); diff --git a/java/com/tigervnc/vncviewer/Tunnel.java b/java/com/tigervnc/vncviewer/Tunnel.java index 90ab38ef..f036c1a5 100644 --- a/java/com/tigervnc/vncviewer/Tunnel.java +++ b/java/com/tigervnc/vncviewer/Tunnel.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2016 All Rights Reserved. + * Copyright (C) 2012-2016 Brian P. Hinz. All Rights Reserved. * Copyright (C) 2000 Const Kaplinsky. All Rights Reserved. * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. * @@ -43,6 +43,8 @@ import com.jcraft.jsch.Logger; import com.jcraft.jsch.OpenSSHConfig; import com.jcraft.jsch.Session; +import static com.tigervnc.vncviewer.Parameters.*; + public class Tunnel { private final static String DEFAULT_TUNNEL_TEMPLATE @@ -56,27 +58,27 @@ public class Tunnel { String remoteHost; remotePort = cc.getServerPort(); - if (cc.viewer.tunnel.getValue()) { + if (tunnel.getValue()) { gatewayHost = cc.getServerName(); remoteHost = "localhost"; } else { - gatewayHost = getSshHost(cc); + gatewayHost = getSshHost(); remoteHost = cc.getServerName(); } - String pattern = cc.viewer.extSSHArgs.getValue(); - if (pattern == null) { - if (cc.viewer.tunnel.getValue()) + String pattern = extSSHArgs.getValue(); + if (pattern == null || pattern.isEmpty()) { + if (tunnel.getValue()) pattern = System.getProperty("VNC_TUNNEL_CMD"); else pattern = System.getProperty("VNC_VIA_CMD"); } - if (cc.viewer.extSSH.getValue() || + if (extSSH.getValue() || (pattern != null && pattern.length() > 0)) { - createTunnelExt(gatewayHost, remoteHost, remotePort, localPort, pattern, cc); + createTunnelExt(gatewayHost, remoteHost, remotePort, localPort, pattern); } else { - createTunnelJSch(gatewayHost, remoteHost, remotePort, localPort, cc); + createTunnelJSch(gatewayHost, remoteHost, remotePort, localPort); } } @@ -99,10 +101,10 @@ public class Tunnel { } } - public static String getSshHost(CConn cc) { - String sshHost = cc.viewer.via.getValue(); - if (sshHost == null) - return cc.getServerName(); + public static String getSshHost() { + String sshHost = via.getValue(); + if (sshHost.isEmpty()) + return vncServerName.getValue(); int end = sshHost.indexOf(":"); if (end < 0) end = sshHost.length(); @@ -110,37 +112,42 @@ public class Tunnel { return sshHost; } - public static String getSshUser(CConn cc) { + public static String getSshUser() { String sshUser = (String)System.getProperties().get("user.name"); - String via = cc.viewer.via.getValue(); - if (via != null && via.indexOf("@") > 0) - sshUser = via.substring(0, via.indexOf("@")); + String viaStr = via.getValue(); + if (!viaStr.isEmpty() && viaStr.indexOf("@") > 0) + sshUser = viaStr.substring(0, viaStr.indexOf("@")); return sshUser; } - public static int getSshPort(CConn cc) { + public static int getSshPort() { String sshPort = "22"; - String via = cc.viewer.via.getValue(); - if (via != null && via.indexOf(":") > 0) - sshPort = via.substring(via.indexOf(":")+1, via.length()); + String viaStr = via.getValue(); + if (!viaStr.isEmpty() && viaStr.indexOf(":") > 0) + sshPort = viaStr.substring(viaStr.indexOf(":")+1, viaStr.length()); return Integer.parseInt(sshPort); } - public static String getSshKeyFile(CConn cc) { - if (cc.viewer.sshKeyFile.getValue() != null) - return cc.viewer.sshKeyFile.getValue(); + public static String getSshKeyFile() { + if (!sshKeyFile.getValue().isEmpty()) + return sshKeyFile.getValue(); String[] ids = { "id_dsa", "id_rsa" }; for (String id : ids) { File f = new File(FileUtils.getHomeDir()+".ssh/"+id); if (f.exists() && f.canRead()) return(f.getAbsolutePath()); } - return null; + return ""; + } + + public static String getSshKey() { + if (!sshKey.getValue().isEmpty()) + return sshKeyFile.getValue().replaceAll("\\\\n", "\n"); + return ""; } private static void createTunnelJSch(String gatewayHost, String remoteHost, - int remotePort, int localPort, - CConn cc) throws Exception { + int remotePort, int localPort) throws Exception { JSch.setLogger(new MyJSchLogger()); JSch jsch=new JSch(); @@ -152,44 +159,39 @@ public class Tunnel { if (knownHosts.exists() && knownHosts.canRead()) jsch.setKnownHosts(knownHosts.getAbsolutePath()); ArrayList privateKeys = new ArrayList(); - String sshKeyFile = cc.options.sshKeyFile.getText(); - String sshKey = cc.viewer.sshKey.getValue(); - if (sshKey != null) { - String sshKeyPass = cc.viewer.sshKeyPass.getValue(); + if (!getSshKey().isEmpty()) { byte[] keyPass = null, key; - if (sshKeyPass != null) - keyPass = sshKeyPass.getBytes(); - sshKey = sshKey.replaceAll("\\\\n", "\n"); - key = sshKey.getBytes(); - jsch.addIdentity("TigerVNC", key, null, keyPass); - } else if (!sshKeyFile.equals("")) { - File f = new File(sshKeyFile); + if (!sshKeyPass.getValue().isEmpty()) + keyPass = sshKeyPass.getValue().getBytes(); + jsch.addIdentity("TigerVNC", getSshKey().getBytes(), null, keyPass); + } else if (!getSshKeyFile().isEmpty()) { + File f = new File(getSshKeyFile()); if (!f.exists() || !f.canRead()) - throw new Exception("Cannot access SSH key file "+ sshKeyFile); + throw new Exception("Cannot access SSH key file "+getSshKeyFile()); privateKeys.add(f); } for (Iterator i = privateKeys.iterator(); i.hasNext();) { File privateKey = (File)i.next(); if (privateKey.exists() && privateKey.canRead()) - if (cc.viewer.sshKeyPass.getValue() != null) + if (!sshKeyPass.getValue().isEmpty()) jsch.addIdentity(privateKey.getAbsolutePath(), - cc.viewer.sshKeyPass.getValue()); + sshKeyPass.getValue()); else jsch.addIdentity(privateKey.getAbsolutePath()); } - String user = getSshUser(cc); + String user = getSshUser(); String label = new String("SSH Authentication"); PasswdDialog dlg = new PasswdDialog(label, (user == null ? false : true), false); dlg.userEntry.setText(user != null ? user : ""); - File ssh_config = new File(cc.viewer.sshConfig.getValue()); + File ssh_config = new File(sshConfig.getValue()); if (ssh_config.exists() && ssh_config.canRead()) { ConfigRepository repo = OpenSSHConfig.parse(ssh_config.getAbsolutePath()); jsch.setConfigRepository(repo); } - Session session=jsch.getSession(user, gatewayHost, getSshPort(cc)); + Session session=jsch.getSession(user, gatewayHost, getSshPort()); session.setUserInfo(dlg); // OpenSSHConfig doesn't recognize StrictHostKeyChecking if (session.getConfig("StrictHostKeyChecking") == null) @@ -201,93 +203,19 @@ public class Tunnel { } } - private static class MyExtProcess implements Runnable { - - private String cmd = null; - private Process pid = null; - - private static class MyProcessLogger extends Thread { - private final BufferedReader err; - - public MyProcessLogger(Process p) { - InputStreamReader reader = - new InputStreamReader(p.getErrorStream()); - err = new BufferedReader(reader); - } - - @Override - public void run() { - try { - while (true) { - String msg = err.readLine(); - if (msg != null) - vlog.info(msg); - } - } catch(java.io.IOException e) { - vlog.info(e.getMessage()); - } finally { - try { - if (err != null) - err.close(); - } catch (java.io.IOException e ) { } - } - } - } - - private static class MyShutdownHook extends Thread { - - private Process proc = null; - - public MyShutdownHook(Process p) { - proc = p; - } - - @Override - public void run() { - try { - proc.exitValue(); - } catch (IllegalThreadStateException e) { - try { - // wait for CConn to shutdown the socket - Thread.sleep(500); - } catch(InterruptedException ie) { } - proc.destroy(); - } - } - } - - public MyExtProcess(String command) { - cmd = command; - } - - public void run() { - try { - Runtime runtime = Runtime.getRuntime(); - pid = runtime.exec(cmd); - runtime.addShutdownHook(new MyShutdownHook(pid)); - new MyProcessLogger(pid).start(); - pid.waitFor(); - } catch(InterruptedException e) { - vlog.info(e.getMessage()); - } catch(java.io.IOException e) { - vlog.info(e.getMessage()); - } - } - } - private static void createTunnelExt(String gatewayHost, String remoteHost, int remotePort, int localPort, - String pattern, CConn cc) throws Exception { + String pattern) throws Exception { if (pattern == null || pattern.length() < 1) { - if (cc.viewer.tunnel.getValue()) + if (tunnel.getValue()) pattern = DEFAULT_TUNNEL_TEMPLATE; else pattern = DEFAULT_VIA_TEMPLATE; } String cmd = fillCmdPattern(pattern, gatewayHost, remoteHost, - remotePort, localPort, cc); + remotePort, localPort); try { - Thread t = new Thread(new MyExtProcess(cmd)); + Thread t = new Thread(new ExtProcess(cmd, vlog, true)); t.start(); // wait for the ssh process to start Thread.sleep(1000); @@ -298,21 +226,21 @@ public class Tunnel { private static String fillCmdPattern(String pattern, String gatewayHost, String remoteHost, int remotePort, - int localPort, CConn cc) { + int localPort) { boolean H_found = false, G_found = false, R_found = false, L_found = false; boolean P_found = false; - String cmd = cc.options.sshClient.getText() + " "; + String cmd = extSSHClient.getValue() + " "; pattern.replaceAll("^\\s+", ""); - String user = getSshUser(cc); - int sshPort = getSshPort(cc); + String user = getSshUser(); + int sshPort = getSshPort(); gatewayHost = user + "@" + gatewayHost; for (int i = 0; i < pattern.length(); i++) { if (pattern.charAt(i) == '%') { switch (pattern.charAt(++i)) { case 'H': - cmd += (cc.viewer.tunnel.getValue() ? gatewayHost : remoteHost); + cmd += (tunnel.getValue() ? gatewayHost : remoteHost); H_found = true; continue; case 'G': @@ -342,7 +270,7 @@ public class Tunnel { if (!H_found || !R_found || !L_found) throw new Exception("%H, %R or %L absent in tunneling command template."); - if (!cc.viewer.tunnel.getValue() && !G_found) + if (!tunnel.getValue() && !G_found) throw new Exception("%G pattern absent in tunneling command template."); vlog.info("SSH command line: "+cmd); diff --git a/java/com/tigervnc/vncviewer/Viewport.java b/java/com/tigervnc/vncviewer/Viewport.java index b55744bd..3a5fb54c 100644 --- a/java/com/tigervnc/vncviewer/Viewport.java +++ b/java/com/tigervnc/vncviewer/Viewport.java @@ -42,6 +42,8 @@ import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER; import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED; import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; +import static com.tigervnc.vncviewer.Parameters.*; + public class Viewport extends JFrame { public Viewport(String name, CConn cc_) { @@ -58,7 +60,7 @@ public class Viewport extends JFrame sp.setBorder(BorderFactory.createEmptyBorder(0,0,0,0)); getContentPane().add(sp); if (VncViewer.os.startsWith("mac os x")) { - if (!VncViewer.noLionFS.getValue()) + if (!noLionFS.getValue()) enableLionFS(); } addWindowFocusListener(new WindowAdapter() { @@ -72,20 +74,12 @@ public class Viewport extends JFrame }); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { - if (VncViewer.nViewers == 1) { - if (cc.closeListener != null) { - cc.close(); - } else { - cc.viewer.exit(1); - } - } else { - cc.close(); - } + cc.close(); } }); addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { - String scaleString = cc.viewer.scalingFactor.getValue(); + String scaleString = scalingFactor.getValue(); if (scaleString.equalsIgnoreCase("Auto") || scaleString.equalsIgnoreCase("FixedRatio")) { if ((sp.getSize().width != cc.desktop.scaledWidth) || @@ -95,7 +89,7 @@ public class Viewport extends JFrame sp.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_NEVER); sp.validate(); if (getExtendedState() != JFrame.MAXIMIZED_BOTH && - !cc.fullScreen) { + !fullScreen.getValue()) { sp.setSize(new Dimension(cc.desktop.scaledWidth, cc.desktop.scaledHeight)); int w = cc.desktop.scaledWidth + getInsets().left + @@ -120,6 +114,10 @@ public class Viewport extends JFrame }); } + public void setName(String name) { + setTitle(name + "- TigerVNC"); + } + boolean lionFSSupported() { return canDoLionFS; } void enableLionFS() { @@ -168,7 +166,7 @@ public class Viewport extends JFrame public void setGeometry(int x, int y, int w, int h) { pack(); - if (!cc.fullScreen) + if (!fullScreen.getValue()) setLocation(x, y); } @@ -181,7 +179,7 @@ public class Viewport extends JFrame GraphicsEnvironment.getLocalGraphicsEnvironment(); Rectangle r = new Rectangle(); setMaximizedBounds(null); - if (cc.viewer.fullScreenAllMonitors.getValue()) { + if (fullScreenAllMonitors.getValue()) { for (GraphicsDevice gd : ge.getScreenDevices()) for (GraphicsConfiguration gc : gd.getConfigurations()) r = r.union(gc.getBounds()); diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java index fc9c7b59..a3daef31 100644 --- a/java/com/tigervnc/vncviewer/VncViewer.java +++ b/java/com/tigervnc/vncviewer/VncViewer.java @@ -1,7 +1,7 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. * Copyright 2011 Pierre Ossman for Cendio AB * Copyright (C) 2011-2013 D. R. Commander. All Rights Reserved. - * Copyright (C) 2011-2015 Brian P. Hinz + * Copyright (C) 2011-2016 Brian P. Hinz * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,22 +35,28 @@ import java.awt.event.*; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; +import java.io.BufferedReader; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.IOException; import java.io.File; import java.lang.Character; import java.lang.reflect.*; +import java.net.URL; import java.util.jar.Attributes; import java.util.jar.Manifest; import java.util.*; import javax.swing.*; import javax.swing.plaf.FontUIResource; +import javax.swing.SwingUtilities; import javax.swing.UIManager.*; import com.tigervnc.rdr.*; import com.tigervnc.rfb.*; import com.tigervnc.network.*; +import static com.tigervnc.vncviewer.Parameters.*; + public class VncViewer extends javax.swing.JApplet implements Runnable, ActionListener { @@ -133,12 +139,12 @@ public class VncViewer extends javax.swing.JApplet viewer.start(); } + public VncViewer() { + //this(new String[0]); + embed.setParam(true); + } public VncViewer(String[] argv) { - embed.setParam(false); - - // load user preferences - UserPreferences.load("global"); SecurityClient.setDefaults(); @@ -150,14 +156,24 @@ public class VncViewer extends javax.swing.JApplet Configuration.enableViewerParams(); + /* Load the default parameter settings */ + String defaultServerName; + try { + defaultServerName = loadViewerParameters(null); + } catch (com.tigervnc.rfb.Exception e) { + defaultServerName = ""; + vlog.info(e.getMessage()); + } + // Override defaults with command-line options for (int i = 0; i < argv.length; i++) { if (argv[i].length() == 0) continue; if (argv[i].equalsIgnoreCase("-config")) { - if (++i >= argv.length) usage(); - Configuration.load(argv[i]); + if (++i >= argv.length) + usage(); + defaultServerName = loadViewerParameters(argv[i]); continue; } @@ -181,28 +197,11 @@ public class VncViewer extends javax.swing.JApplet usage(); } - if (vncServerName.getValue() != null) + if (!vncServerName.getValue().isEmpty()) usage(); vncServerName.setParam(argv[i]); } - if (!autoSelect.hasBeenSet()) { - // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used - autoSelect.setParam(!preferredEncoding.hasBeenSet() && - !fullColour.hasBeenSet() && - !fullColourAlias.hasBeenSet()); - } - if (!fullColour.hasBeenSet() && !fullColourAlias.hasBeenSet()) { - // Default to FullColor=0 if AutoSelect=0 && LowColorLevel is set - if (!autoSelect.getValue() && (lowColourLevel.hasBeenSet() || - lowColourLevelAlias.hasBeenSet())) { - fullColour.setParam(false); - } - } - if (!customCompressLevel.hasBeenSet()) { - // Default to CustomCompressLevel=1 if CompressLevel is used. - customCompressLevel.setParam(compressLevel.hasBeenSet()); - } } public static void usage() { @@ -272,26 +271,27 @@ public class VncViewer extends javax.swing.JApplet System.exit(1); } - public VncViewer() { - UserPreferences.load("global"); - embed.setParam(true); - } - - public static void newViewer(VncViewer oldViewer, Socket sock, boolean close) { - VncViewer viewer = new VncViewer(); - viewer.embed.setParam(oldViewer.embed.getValue()); - viewer.sock = sock; - viewer.start(); - if (close) - oldViewer.exit(0); - } - - public static void newViewer(VncViewer oldViewer, Socket sock) { - newViewer(oldViewer, sock, false); - } - - public static void newViewer(VncViewer oldViewer) { - newViewer(oldViewer, null); + public static void newViewer() { + String cmd = "java -jar "; + try { + URL url = + VncViewer.class.getProtectionDomain().getCodeSource().getLocation(); + File f = new File(url.toURI()); + if (!f.exists() || !f.canRead()) { + String msg = new String("The jar file "+f.getAbsolutePath()+ + " does not exist or cannot be read."); + JOptionPane.showMessageDialog(null, msg, "ERROR", + JOptionPane.ERROR_MESSAGE); + return; + } + cmd = cmd.concat(f.getAbsolutePath()); + Thread t = new Thread(new ExtProcess(cmd, vlog)); + t.start(); + } catch (java.net.URISyntaxException e) { + vlog.info(e.getMessage()); + } catch (java.lang.Exception e) { + vlog.info(e.getMessage()); + } } public boolean isAppletDragStart(MouseEvent e) { @@ -320,7 +320,6 @@ public class VncViewer extends javax.swing.JApplet } public void appletDragFinished() { - cc.setEmbeddedFeatures(true); JFrame f = (JFrame)JOptionPane.getFrameForComponent(this); if (f != null) f.dispose(); @@ -331,7 +330,6 @@ public class VncViewer extends javax.swing.JApplet } public void appletRestored() { - cc.setEmbeddedFeatures(false); cc.setCloseListener(null); } @@ -361,37 +359,91 @@ public class VncViewer extends javax.swing.JApplet } } + public static void showAbout(Container parent) { + String pkgDate = ""; + String pkgTime = ""; + try { + Manifest manifest = new Manifest(VncViewer.timestamp); + Attributes attributes = manifest.getMainAttributes(); + pkgDate = attributes.getValue("Package-Date"); + pkgTime = attributes.getValue("Package-Time"); + } catch (java.lang.Exception e) { } + + Window fullScreenWindow = Viewport.getFullScreenWindow(); + if (fullScreenWindow != null) + Viewport.setFullScreenWindow(null); + String msg = + String.format(VncViewer.aboutText, VncViewer.version, VncViewer.build, + VncViewer.buildDate, VncViewer.buildTime); + Object[] options = {"Close \u21B5"}; + JOptionPane op = + new JOptionPane(msg, JOptionPane.INFORMATION_MESSAGE, + JOptionPane.DEFAULT_OPTION, VncViewer.logoIcon, options); + JDialog dlg = op.createDialog(parent, "About TigerVNC Viewer for Java"); + dlg.setIconImage(VncViewer.frameIcon); + dlg.setAlwaysOnTop(true); + dlg.setVisible(true); + if (fullScreenWindow != null) + Viewport.setFullScreenWindow(fullScreenWindow); + } + public void start() { vlog.debug("start called"); getTimestamp(); - if (embed.getValue() && nViewers == 0) { + if (embed.getValue()) { + setupEmbeddedFrame(); alwaysShowServerDialog.setParam(false); - Configuration.global().readAppletParams(this); + String servername = loadAppletParameters(this); + vncServerName.setParam(servername); fullScreen.setParam(false); scalingFactor.setParam("100"); - String host = getCodeBase().getHost(); - if (vncServerName.getValue() == null && vncServerPort.getValue() != 0) { - int port = vncServerPort.getValue(); - vncServerName.setParam(host + ((port >= 5900 && port <= 5999) - ? (":"+(port-5900)) - : ("::"+port))); - } } - nViewers++; thread = new Thread(this); thread.start(); } public void exit(int n) { - nViewers--; - if (nViewers > 0) - return; if (embed.getValue()) destroy(); else System.exit(n); } + private void setupEmbeddedFrame() { + UIManager.getDefaults().put("ScrollPane.ancestorInputMap", + new UIDefaults.LazyInputMap(new Object[]{})); + sp = new JScrollPane(); + sp.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + sp.getViewport().setBackground(Color.BLACK); + InputMap im = sp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); + int ctrlAltShiftMask = Event.SHIFT_MASK | Event.CTRL_MASK | Event.ALT_MASK; + if (im != null) { + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, ctrlAltShiftMask), + "unitScrollUp"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, ctrlAltShiftMask), + "unitScrollDown"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, ctrlAltShiftMask), + "unitScrollLeft"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, ctrlAltShiftMask), + "unitScrollRight"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, ctrlAltShiftMask), + "scrollUp"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, ctrlAltShiftMask), + "scrollDown"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, ctrlAltShiftMask), + "scrollLeft"); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, ctrlAltShiftMask), + "scrollRight"); + } + sp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + add(sp); + } + + public static JViewport getViewport() { + return sp.getViewport(); + } + // If "Reconnect" button is pressed public void actionPerformed(ActionEvent e) { getContentPane().removeAll(); @@ -425,14 +477,13 @@ public class VncViewer extends javax.swing.JApplet } } - CConn cc; public void run() { cc = null; if (listenMode.getValue()) { int port = 5500; - if (vncServerName.getValue() != null && + if (!vncServerName.getValue().isEmpty() && Character.isDigit(vncServerName.getValue().charAt(0))) port = Integer.parseInt(vncServerName.getValue()); @@ -446,15 +497,26 @@ public class VncViewer extends javax.swing.JApplet vlog.info("Listening on port "+port); - while (true) { - Socket new_sock = listener.accept(); - if (new_sock != null) - newViewer(this, new_sock, true); + while (sock == null) + sock = listener.accept(); + } else { + if (alwaysShowServerDialog.getValue() || sock == null) { + if (vncServerName.getValue().isEmpty()) { + try { + SwingUtilities.invokeAndWait(new ServerDialog()); + } catch (InvocationTargetException e) { + reportException(e); + } catch (InterruptedException e) { + reportException(e); + } + if (vncServerName.getValue().isEmpty()) + exit(0); + } } } try { - cc = new CConn(this, sock, vncServerName.getValue()); + cc = new CConn(vncServerName.getValue(), sock); while (!cc.shuttingDown) cc.processMsg(); exit(0); @@ -471,243 +533,13 @@ public class VncViewer extends javax.swing.JApplet } } - static BoolParameter noLionFS - = new BoolParameter("NoLionFS", - "On Mac systems, setting this parameter will force the use of the old "+ - "(pre-Lion) full-screen mode, even if the viewer is running on OS X 10.7 "+ - "Lion or later.", - false); - - BoolParameter embed - = new BoolParameter("Embed", - "If the viewer is being run as an applet, display its output to " + - "an embedded frame in the browser window rather than to a dedicated " + - "window. Embed=1 implies FullScreen=0 and Scale=100.", - false); - - BoolParameter useLocalCursor - = new BoolParameter("UseLocalCursor", - "Render the mouse cursor locally", - true); - BoolParameter sendLocalUsername - = new BoolParameter("SendLocalUsername", - "Send the local username for SecurityTypes "+ - "such as Plain rather than prompting", - true); - StringParameter passwordFile - = new StringParameter("PasswordFile", - "Password file for VNC authentication", - ""); - AliasParameter passwd - = new AliasParameter("passwd", - "Alias for PasswordFile", - passwordFile); - BoolParameter autoSelect - = new BoolParameter("AutoSelect", - "Auto select pixel format and encoding", - true); - BoolParameter fullColour - = new BoolParameter("FullColour", - "Use full colour - otherwise 6-bit colour is "+ - "used until AutoSelect decides the link is "+ - "fast enough", - true); - AliasParameter fullColourAlias - = new AliasParameter("FullColor", - "Alias for FullColour", - fullColour); - IntParameter lowColourLevel - = new IntParameter("LowColorLevel", - "Color level to use on slow connections. "+ - "0 = Very Low (8 colors), 1 = Low (64 colors), "+ - "2 = Medium (256 colors)", - 2); - AliasParameter lowColourLevelAlias - = new AliasParameter("LowColourLevel", - "Alias for LowColorLevel", - lowColourLevel); - StringParameter preferredEncoding - = new StringParameter("PreferredEncoding", - "Preferred encoding to use (Tight, ZRLE, "+ - "hextile or raw) - implies AutoSelect=0", - "Tight"); - BoolParameter viewOnly - = new BoolParameter("ViewOnly", - "Don't send any mouse or keyboard events to "+ - "the server", - false); - BoolParameter shared - = new BoolParameter("Shared", - "Don't disconnect other viewers upon "+ - "connection - share the desktop instead", - false); - BoolParameter fullScreen - = new BoolParameter("FullScreen", - "Full Screen Mode", - false); - BoolParameter fullScreenAllMonitors - = new BoolParameter("FullScreenAllMonitors", - "Enable full screen over all monitors", - true); - BoolParameter acceptClipboard - = new BoolParameter("AcceptClipboard", - "Accept clipboard changes from the server", - true); - BoolParameter sendClipboard - = new BoolParameter("SendClipboard", - "Send clipboard changes to the server", - true); - static IntParameter maxCutText - = new IntParameter("MaxCutText", - "Maximum permitted length of an outgoing clipboard update", - 262144); - StringParameter menuKey - = new StringParameter("MenuKey", - "The key which brings up the popup menu", - "F8"); - StringParameter desktopSize - = new StringParameter("DesktopSize", - "Reconfigure desktop size on the server on "+ - "connect (if possible)", ""); - BoolParameter listenMode - = new BoolParameter("listen", - "Listen for connections from VNC servers", - false); - StringParameter scalingFactor - = new StringParameter("ScalingFactor", - "Reduce or enlarge the remote desktop image. "+ - "The value is interpreted as a scaling factor "+ - "in percent. If the parameter is set to "+ - "\"Auto\", then automatic scaling is "+ - "performed. Auto-scaling tries to choose a "+ - "scaling factor in such a way that the whole "+ - "remote desktop will fit on the local screen. "+ - "If the parameter is set to \"FixedRatio\", "+ - "then automatic scaling is performed, but the "+ - "original aspect ratio is preserved.", - "100"); - BoolParameter alwaysShowServerDialog - = new BoolParameter("AlwaysShowServerDialog", - "Always show the server dialog even if a server "+ - "has been specified in an applet parameter or on "+ - "the command line", - false); - StringParameter vncServerName - = new StringParameter("Server", - "The VNC server [:] or "+ - "::", - null); - IntParameter vncServerPort - = new IntParameter("Port", - "The VNC server's port number, assuming it is on "+ - "the host from which the applet was downloaded", - 0); - BoolParameter acceptBell - = new BoolParameter("AcceptBell", - "Produce a system beep when requested to by the server.", - true); - StringParameter via - = new StringParameter("Via", - "Automatically create an encrypted TCP tunnel to "+ - "the gateway machine, then connect to the VNC host "+ - "through that tunnel. By default, this option invokes "+ - "SSH local port forwarding using the embedded JSch "+ - "client, however an external SSH client may be specified "+ - "using the \"-extSSH\" parameter. Note that when using "+ - "the -via option, the VNC host machine name should be "+ - "specified from the point of view of the gateway machine, "+ - "e.g. \"localhost\" denotes the gateway, "+ - "not the machine on which the viewer was launched. "+ - "See the System Properties section below for "+ - "information on configuring the -Via option.", null); - BoolParameter tunnel - = new BoolParameter("Tunnel", - "The -Tunnel command is basically a shorthand for the "+ - "-via command when the VNC server and SSH gateway are "+ - "one and the same. -Tunnel creates an SSH connection "+ - "to the server and forwards the VNC through the tunnel "+ - "without the need to specify anything else.", false); - BoolParameter extSSH - = new BoolParameter("extSSH", - "By default, SSH tunneling uses the embedded JSch client "+ - "for tunnel creation. This option causes the client to "+ - "invoke an external SSH client application for all tunneling "+ - "operations. By default, \"/usr/bin/ssh\" is used, however "+ - "the path to the external application may be specified using "+ - "the -SSHClient option.", false); - StringParameter extSSHClient - = new StringParameter("extSSHClient", - "Specifies the path to an external SSH client application "+ - "that is to be used for tunneling operations when the -extSSH "+ - "option is in effect.", "/usr/bin/ssh"); - StringParameter extSSHArgs - = new StringParameter("extSSHArgs", - "Specifies the arguments string or command template to be used "+ - "by the external SSH client application when the -extSSH option "+ - "is in effect. The string will be processed according to the same "+ - "pattern substitution rules as the VNC_TUNNEL_CMD and VNC_VIA_CMD "+ - "system properties, and can be used to override those in a more "+ - "command-line friendly way. If not specified, then the appropriate "+ - "VNC_TUNNEL_CMD or VNC_VIA_CMD command template will be used.", null); - StringParameter sshConfig - = new StringParameter("SSHConfig", - "Specifies the path to an OpenSSH configuration file that to "+ - "be parsed by the embedded JSch SSH client during tunneling "+ - "operations.", FileUtils.getHomeDir()+".ssh/config"); - StringParameter sshKey - = new StringParameter("SSHKey", - "When using the Via or Tunnel options with the embedded SSH client, "+ - "this parameter specifies the text of the SSH private key to use when "+ - "authenticating with the SSH server. You can use \\n within the string "+ - "to specify a new line.", null); - StringParameter sshKeyFile - = new StringParameter("SSHKeyFile", - "When using the Via or Tunnel options with the embedded SSH client, "+ - "this parameter specifies a file that contains an SSH private key "+ - "(or keys) to use when authenticating with the SSH server. If not "+ - "specified, ~/.ssh/id_dsa or ~/.ssh/id_rsa will be used (if they exist). "+ - "Otherwise, the client will fallback to prompting for an SSH password.", - null); - StringParameter sshKeyPass - = new StringParameter("SSHKeyPass", - "When using the Via or Tunnel options with the embedded SSH client, "+ - "this parameter specifies the passphrase for the SSH key.", null); - BoolParameter customCompressLevel - = new BoolParameter("CustomCompressLevel", - "Use custom compression level. "+ - "Default if CompressLevel is specified.", - false); - IntParameter compressLevel - = new IntParameter("CompressLevel", - "Use specified compression level "+ - "0 = Low, 6 = High", - 1); - BoolParameter noJpeg - = new BoolParameter("NoJPEG", - "Disable lossy JPEG compression in Tight encoding.", - false); - IntParameter qualityLevel - = new IntParameter("QualityLevel", - "JPEG quality level. "+ - "0 = Low, 9 = High", - 8); - StringParameter x509ca - = new StringParameter("X509CA", - "Path to CA certificate to use when authenticating remote servers "+ - "using any of the X509 security schemes (X509None, X509Vnc, etc.). "+ - "Must be in PEM format.", - FileUtils.getHomeDir()+".vnc/x509_ca.pem"); - StringParameter x509crl - = new StringParameter("X509CRL", - "Path to certificate revocation list to use in conjunction with "+ - "-X509CA. Must also be in PEM format.", - FileUtils.getHomeDir()+".vnc/x509_crl.pem"); - StringParameter config - = new StringParameter("config", + public static CConn cc; + private static JScrollPane sp; + public static StringParameter config + = new StringParameter("Config", "Specifies a configuration file to load.", null); Thread thread; Socket sock; - static int nViewers; static LogWriter vlog = new LogWriter("VncViewer"); } -- 2.39.5