diff options
author | Brian P. Hinz <bphinz@users.sf.net> | 2016-08-27 17:33:04 -0400 |
---|---|---|
committer | Brian P. Hinz <bphinz@users.sf.net> | 2016-08-27 17:33:04 -0400 |
commit | c456387fe1ed922c5bfb5daf0e780f0000cab2fb (patch) | |
tree | 269a8b95eff08344098afdf89a511ce443ec2bf1 /java | |
parent | da33c36638482d014996a3d99c90e2781304cebb (diff) | |
download | tigervnc-c456387fe1ed922c5bfb5daf0e780f0000cab2fb.tar.gz tigervnc-c456387fe1ed922c5bfb5daf0e780f0000cab2fb.zip |
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.
Diffstat (limited to 'java')
21 files changed, 1748 insertions, 2230 deletions
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<Integer> secTypes = new ArrayList<Integer>(); - 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<Integer> j; List<Integer> secTypes = new ArrayList<Integer>(); - 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<String> 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<Integer> enabledSecTypes = new ArrayList<Integer>(); + private List<Integer> enabledSecTypes = new ArrayList<Integer>(); - public static final List<Integer> GetEnabledSecTypes() + public final List<Integer> GetEnabledSecTypes() { List<Integer> result = new ArrayList<Integer>(); @@ -98,7 +100,7 @@ public class Security { return (result); } - public static final List<Integer> GetEnabledExtSecTypes() + public final List<Integer> GetEnabledExtSecTypes() { List<Integer> result = new ArrayList<Integer>(); @@ -111,7 +113,7 @@ public class Security { return (result); } - public static final void EnableSecType(int secType) + public final void EnableSecType(int secType) { for (Iterator<Integer> 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<Integer> 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<Integer> secTypes) { enabledSecTypes = secTypes; } + public final void SetSecTypes(List<Integer> 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<Integer, Integer>(); - 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<Integer> secTypes = new ArrayList<Integer>(); - secTypes = Security.GetEnabledSecTypes(); - for (Iterator<Integer> 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<Integer> secTypesExt = new ArrayList<Integer>(); - secTypesExt = Security.GetEnabledExtSecTypes(); - for (Iterator<Integer> 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<Integer, Integer> 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<String, Object> callbacks = new HashMap<String, Object>(); + /* 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<Integer> secTypes; + Iterator<Integer> iter; + + List<Integer> secTypesExt; + Iterator<Integer> 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<String, Object> 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<AbstractButton> 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<AbstractButton> 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<File> privateKeys = new ArrayList<File>(); - 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<File> 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 <ossman@cendio.se> 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 <host>[:<dpyNum>] or "+ - "<host>::<port>", - 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"); } |