Completely reworked Java viewer (contributed by Brian Hinz)
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4413 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/java/src/com/tigervnc/vncviewer/AuthPanel.java b/java/src/com/tigervnc/vncviewer/AuthPanel.java
deleted file mode 100644
index a23758e..0000000
--- a/java/src/com/tigervnc/vncviewer/AuthPanel.java
+++ /dev/null
@@ -1,125 +0,0 @@
-//
-// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
-// Copyright (C) 2002-2006 Constantin Kaplinsky. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.awt.*;
-import java.awt.event.*;
-
-//
-// The panel which implements the user authentication scheme
-//
-
-class AuthPanel extends Panel implements ActionListener {
-
- TextField passwordField;
- Button okButton;
- boolean AskPassword;
-
- //
- // Constructor.
- //
-
- public AuthPanel(VncViewer viewer, boolean askpassword)
- {
- AskPassword = askpassword;
- Label titleLabel = new Label("VNC Authentication", Label.CENTER);
- titleLabel.setFont(new Font("Helvetica", Font.BOLD, 18));
-
- Label promptLabel;
- if (AskPassword)
- promptLabel = new Label("Password:", Label.CENTER);
- else
- promptLabel = new Label("User:", Label.CENTER);
-
- passwordField = new TextField(10);
- passwordField.setForeground(Color.black);
- passwordField.setBackground(Color.white);
- if (AskPassword)
- passwordField.setEchoChar('*');
-
- okButton = new Button("OK");
-
- GridBagLayout gridbag = new GridBagLayout();
- GridBagConstraints gbc = new GridBagConstraints();
-
- setLayout(gridbag);
-
- gbc.gridwidth = GridBagConstraints.REMAINDER;
- gbc.insets = new Insets(0,0,20,0);
- gridbag.setConstraints(titleLabel,gbc);
- add(titleLabel);
-
- gbc.fill = GridBagConstraints.NONE;
- gbc.gridwidth = 1;
- gbc.insets = new Insets(0,0,0,0);
- gridbag.setConstraints(promptLabel,gbc);
- add(promptLabel);
-
- gridbag.setConstraints(passwordField,gbc);
- add(passwordField);
- passwordField.addActionListener(this);
-
- // gbc.ipady = 10;
- gbc.gridwidth = GridBagConstraints.REMAINDER;
- gbc.fill = GridBagConstraints.BOTH;
- gbc.insets = new Insets(0,20,0,0);
- gbc.ipadx = 30;
- gridbag.setConstraints(okButton,gbc);
- add(okButton);
- okButton.addActionListener(this);
- }
-
- //
- // Move keyboard focus to the default object, that is, the password
- // text field.
- //
-
- public void moveFocusToDefaultField()
- {
- passwordField.requestFocus();
- }
-
- //
- // This method is called when a button is pressed or return is
- // pressed in the password text field.
- //
-
- public synchronized void actionPerformed(ActionEvent evt)
- {
- if (evt.getSource() == passwordField || evt.getSource() == okButton) {
- passwordField.setEnabled(false);
- notify();
- }
- }
-
- //
- // Wait for user entering a password, and return it as String.
- //
-
- public synchronized String getPassword() throws Exception
- {
- try {
- wait();
- } catch (InterruptedException e) { }
- return passwordField.getText();
- }
-
-}
diff --git a/java/src/com/tigervnc/vncviewer/ButtonPanel.java b/java/src/com/tigervnc/vncviewer/ButtonPanel.java
deleted file mode 100644
index 1ef6412..0000000
--- a/java/src/com/tigervnc/vncviewer/ButtonPanel.java
+++ /dev/null
@@ -1,154 +0,0 @@
-//
-// Copyright (C) 2001,2002 HorizonLive.com, Inc. All Rights Reserved.
-// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
-
-//
-// ButtonPanel class implements panel with four buttons in the
-// VNCViewer desktop window.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.io.*;
-
-class ButtonPanel extends Panel implements ActionListener {
-
- VncViewer viewer;
- Button disconnectButton;
- Button optionsButton;
- Button recordButton;
- Button clipboardButton;
- Button ctrlAltDelButton;
- Button refreshButton;
-
- ButtonPanel(VncViewer v) {
- viewer = v;
-
- setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
- disconnectButton = new Button("Disconnect");
- disconnectButton.setEnabled(false);
- add(disconnectButton);
- disconnectButton.addActionListener(this);
- optionsButton = new Button("Options");
- add(optionsButton);
- optionsButton.addActionListener(this);
- clipboardButton = new Button("Clipboard");
- clipboardButton.setEnabled(false);
- add(clipboardButton);
- clipboardButton.addActionListener(this);
- if (viewer.rec != null) {
- recordButton = new Button("Record");
- add(recordButton);
- recordButton.addActionListener(this);
- }
- ctrlAltDelButton = new Button("Send Ctrl-Alt-Del");
- ctrlAltDelButton.setEnabled(false);
- add(ctrlAltDelButton);
- ctrlAltDelButton.addActionListener(this);
- refreshButton = new Button("Refresh");
- refreshButton.setEnabled(false);
- add(refreshButton);
- refreshButton.addActionListener(this);
- }
-
- //
- // Enable buttons on successful connection.
- //
-
- public void enableButtons() {
- disconnectButton.setEnabled(true);
- clipboardButton.setEnabled(true);
- refreshButton.setEnabled(true);
- }
-
- //
- // Disable all buttons on disconnect.
- //
-
- public void disableButtonsOnDisconnect() {
- remove(disconnectButton);
- disconnectButton = new Button("Hide desktop");
- disconnectButton.setEnabled(true);
- add(disconnectButton, 0);
- disconnectButton.addActionListener(this);
-
- optionsButton.setEnabled(false);
- clipboardButton.setEnabled(false);
- ctrlAltDelButton.setEnabled(false);
- refreshButton.setEnabled(false);
- }
-
- //
- // Enable/disable controls that should not be available in view-only
- // mode.
- //
-
- public void enableRemoteAccessControls(boolean enable) {
- ctrlAltDelButton.setEnabled(enable);
- }
-
- //
- // Event processing.
- //
-
- public void actionPerformed(ActionEvent evt) {
-
- viewer.moveFocusToDesktop();
-
- if (evt.getSource() == disconnectButton) {
- viewer.disconnect();
-
- } else if (evt.getSource() == optionsButton) {
- viewer.options.setVisible(!viewer.options.isVisible());
-
- } else if (evt.getSource() == recordButton) {
- viewer.rec.setVisible(!viewer.rec.isVisible());
-
- } else if (evt.getSource() == clipboardButton) {
- viewer.clipboard.setVisible(!viewer.clipboard.isVisible());
-
- } else if (evt.getSource() == ctrlAltDelButton) {
- try {
- final int modifiers = InputEvent.CTRL_MASK | InputEvent.ALT_MASK;
-
- KeyEvent ctrlAltDelEvent =
- new KeyEvent(this, KeyEvent.KEY_PRESSED, 0, modifiers, 127);
- viewer.rfb.writeKeyEvent(ctrlAltDelEvent);
-
- ctrlAltDelEvent =
- new KeyEvent(this, KeyEvent.KEY_RELEASED, 0, modifiers, 127);
- viewer.rfb.writeKeyEvent(ctrlAltDelEvent);
-
- } catch (IOException e) {
- e.printStackTrace();
- }
- } else if (evt.getSource() == refreshButton) {
- try {
- RfbProto rfb = viewer.rfb;
- rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
- rfb.framebufferHeight, false);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-}
-
diff --git a/java/src/com/tigervnc/vncviewer/CConn.java b/java/src/com/tigervnc/vncviewer/CConn.java
new file mode 100644
index 0000000..e4da169
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/CConn.java
@@ -0,0 +1,1137 @@
+/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+//
+// CConn
+//
+// Methods on CConn are called from both the GUI thread and the thread which
+// processes incoming RFB messages ("the RFB thread"). This means we need to
+// be careful with synchronization here.
+//
+// Any access to writer() must not only be synchronized, but we must also make
+// sure that the connection is in RFBSTATE_NORMAL. We are guaranteed this for
+// any code called after serverInit() has been called. Since the DesktopWindow
+// isn't created until then, any methods called only from DesktopWindow can
+// assume that we are in RFBSTATE_NORMAL.
+
+package com.tigervnc.vncviewer;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Event;
+import java.awt.Frame;
+import java.awt.ScrollPane;
+
+import java.io.*;
+import javax.net.ssl.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.File;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import javax.swing.*;
+import javax.swing.filechooser.*;
+import javax.swing.ImageIcon;
+import java.net.URL;
+import java.net.ServerSocket;
+import javax.swing.border.*;
+import java.util.*;
+
+import com.tigervnc.rdr.*;
+import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Exception;
+import com.tigervnc.rfb.Point;
+import com.tigervnc.rfb.Rect;
+
+class ViewportFrame extends JFrame
+{
+ public ViewportFrame(String name, CConn cc_) {
+ cc = cc_;
+ setTitle("TigerVNC: "+name);
+ setFocusable(false);
+ setFocusTraversalKeysEnabled(false);
+ addWindowFocusListener(new WindowAdapter() {
+ public void windowGainedFocus(WindowEvent e) {
+ sp.getViewport().getView().requestFocusInWindow();
+ }
+ });
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {
+ cc.close();
+ }
+ });
+ }
+
+ public void addChild(DesktopWindow child) {
+ sp = new JScrollPane(child);
+ child.setBackground(Color.BLACK);
+ child.setOpaque(true);
+ sp.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
+ getContentPane().add(sp);
+ }
+
+ public void setGeometry(int x, int y, int w, int h) {
+ pack();
+ if (cc.fullScreen) setSize(w, h);
+ setLocation(x, y);
+ setBackground(Color.BLACK);
+ }
+
+
+ CConn cc;
+ Graphics g;
+ JScrollPane sp;
+ static LogWriter vlog = new LogWriter("ViewportFrame");
+}
+
+public class CConn extends CConnection
+ implements UserPasswdGetter, UserMsgBox, OptionsDialogCallback
+{
+ ////////////////////////////////////////////////////////////////////
+ // The following methods are all called from the RFB thread
+
+ public CConn(VncViewer viewer_, java.net.Socket sock_,
+ String vncServerName, boolean reverse)
+ {
+ serverHost = null; serverPort = 0; sock = sock_; viewer = viewer_;
+ currentEncoding = Encodings.encodingTight; lastServerEncoding = -1;
+ fullColour = viewer.fullColour.getValue();
+ lowColourLevel = 2;
+ autoSelect = viewer.autoSelect.getValue();
+ shared = viewer.shared.getValue(); formatChange = false;
+ encodingChange = false; sameMachine = false;
+ fullScreen = viewer.fullScreen.getValue();
+ menuKey = Keysyms.F8;
+ options = new OptionsDialog(this);
+ clipboardDialog = new ClipboardDialog(this);
+ firstUpdate = true; pendingUpdate = false;
+
+ setShared(shared);
+ upg = this;
+ msg = this;
+
+ String encStr = viewer.preferredEncoding.getValue();
+ int encNum = Encodings.encodingNum(encStr);
+ if (encNum != -1) {
+ currentEncoding = encNum;
+ }
+ cp.supportsDesktopResize = true;
+ cp.supportsExtendedDesktopSize = 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.getRemoteSocketAddress()+"::"+sock.getPort();
+ vlog.info("Accepted connection from "+name);
+ } else {
+ if (vncServerName != null) {
+ serverHost = Hostname.getHost(vncServerName);
+ serverPort = Hostname.getPort(vncServerName);
+ } else {
+ ServerDialog dlg = new ServerDialog(options, vncServerName, this);
+ if (!dlg.showDialog() || dlg.server.getSelectedItem().equals("")) {
+ System.exit(1);
+ }
+ vncServerName = (String)dlg.server.getSelectedItem();
+ serverHost = Hostname.getHost(vncServerName);
+ serverPort = Hostname.getPort(vncServerName);
+ }
+
+ try {
+ sock = new java.net.Socket(serverHost, serverPort);
+ } catch (java.io.IOException e) {
+ throw new Exception(e.toString());
+ }
+ vlog.info("connected to host "+serverHost+" port "+serverPort);
+ }
+
+ sameMachine = (sock.getLocalSocketAddress() == sock.getRemoteSocketAddress());
+ try {
+ sock.setTcpNoDelay(true);
+ sock.setTrafficClass(0x10);
+ setServerName(serverHost);
+ jis = new JavaInStream(sock.getInputStream());
+ jos = new JavaOutStream(sock.getOutputStream());
+ } catch (java.net.SocketException e) {
+ throw new Exception(e.toString());
+ } catch (java.io.IOException e) {
+ throw new Exception(e.toString());
+ }
+ setStreams(jis, jos);
+ initialiseProtocol();
+ }
+
+ public boolean showMsgBox(int flags, String title, String text)
+ {
+ StringBuffer titleText = new StringBuffer("VNC Viewer: "+title);
+
+ return true;
+ }
+
+ // deleteWindow() is called when the user closes the desktop or menu windows.
+
+ void deleteWindow() {
+ if (viewport != null)
+ viewport.dispose();
+ viewport = null;
+ }
+
+ // getUserPasswd() is called by the CSecurity object when it needs us to read
+ // a password from the user.
+
+ public final boolean getUserPasswd(StringBuffer user, StringBuffer passwd) {
+ String title = ("VNC Authentication ["
+ +csecurity.description() + "]");
+ PasswdDialog dlg;
+ if (user == null) {
+ dlg = new PasswdDialog(title, (user == null), (passwd == null));
+ } else {
+ if ((passwd == null) && viewer.sendLocalUsername.getValue()) {
+ user.append((String)System.getProperties().get("user.name"));
+ return true;
+ }
+ dlg = new PasswdDialog(title, viewer.sendLocalUsername.getValue(),
+ (passwd == null));
+ }
+ if (!dlg.showDialog()) return false;
+ if (user != null) {
+ if (viewer.sendLocalUsername.getValue()) {
+ user.append((String)System.getProperties().get("user.name"));
+ } else {
+ user.append(dlg.userEntry.getText());
+ }
+ }
+ if (passwd != null)
+ passwd.append(dlg.passwdEntry.getText());
+ return true;
+ }
+
+ // CConnection callback methods
+
+ // serverInit() is called when the serverInit message has been received. At
+ // this point we create the desktop window and display it. We also tell the
+ // server the pixel format and encodings to use and request the first update.
+ public void serverInit() {
+ super.serverInit();
+
+ // If using AutoSelect with old servers, start in FullColor
+ // mode. See comment in autoSelectFormatAndEncoding.
+ if (cp.beforeVersion(3, 8) && autoSelect) {
+ fullColour = true;
+ }
+
+ serverPF = cp.pf();
+ desktop = new DesktopWindow(cp.width, cp.height, serverPF, this);
+ //desktopEventHandler = desktop.setEventHandler(this);
+ //desktop.addEventMask(KeyPressMask | KeyReleaseMask);
+ fullColourPF = desktop.getPF();
+ if (!serverPF.trueColour)
+ fullColour = true;
+ recreateViewport();
+ formatChange = true; encodingChange = true;
+ requestNewUpdate();
+ }
+
+ // setDesktopSize() is called when the desktop size changes (including when
+ // it is set initially).
+ public void setDesktopSize(int w, int h) {
+ super.setDesktopSize(w,h);
+ resizeFramebuffer();
+ }
+
+ // setExtendedDesktopSize() is a more advanced version of setDesktopSize()
+ public void setExtendedDesktopSize(int reason, int result, int w, int h,
+ ScreenSet layout) {
+ super.setExtendedDesktopSize(reason, result, w, h, layout);
+
+ if ((reason == screenTypes.reasonClient) &&
+ (result != screenTypes.resultSuccess)) {
+ vlog.error("SetDesktopSize failed: "+result);
+ return;
+ }
+
+ resizeFramebuffer();
+ }
+
+ // setName() is called when the desktop name changes
+ public void setName(String name) {
+ super.setName(name);
+
+ if (viewport != null) {
+ viewport.setTitle("TigerVNC: "+name);
+ }
+ }
+
+ // framebufferUpdateStart() is called at the beginning of an update.
+ // Here we try to send out a new framebuffer update request so that the
+ // next update can be sent out in parallel with us decoding the current
+ // one. We cannot do this if we're in the middle of a format change
+ // though.
+ public void framebufferUpdateStart() {
+ if (!formatChange) {
+ pendingUpdate = true;
+ requestNewUpdate();
+ } else
+ pendingUpdate = false;
+ }
+
+ // framebufferUpdateEnd() is called at the end of an update.
+ // For each rectangle, the FdInStream will have timed the speed
+ // of the connection, allowing us to select format and encoding
+ // appropriately, and then request another incremental update.
+ public void framebufferUpdateEnd() {
+ desktop.framebufferUpdateEnd();
+
+ if (firstUpdate) {
+ //int width = (int)viewer.desktopSize.getValue().split("x")[0];
+ //int height = (int)viewer.desktopSize.getValue().split("x")[1];
+
+/*
+ if (cp.supportsSetDesktopSize &&
+ ((width != 0) && (height != 0))) {
+ ScreenSet layout;
+
+ layout = cp.screenLayout;
+
+ if (layout.num_screens() == 0)
+ layout.add_screen(new Screen());
+ else if (layout.num_screens() != 1) {
+
+ while (true) {
+ Iterator iter = layout.screens.iterator();
+ iter.next();
+
+ if (!iter.hasNext())
+ break;
+
+ layout.remove_screen(iter.id);
+ }
+ }
+
+ layout.screens.iterator().dimensions.tl.x = 0;
+ layout.screens.iterator().dimensions.tl.y = 0;
+ layout.screens.iterator().dimensions.by.x = width;
+ layout.screens.iterator().dimensions.by.y = height;
+
+ writer().writeSetDesktopSize(width, height, layout);
+ }
+*/
+
+ firstUpdate = false;
+ }
+
+ // A format change prevented us from sending this before the update,
+ // so make sure to send it now.
+ if (formatChange && !pendingUpdate)
+ requestNewUpdate();
+
+ // Compute new settings based on updated bandwidth values
+ if (autoSelect)
+ 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 bell() { desktop.getToolkit().beep(); }
+
+ public void serverCutText(String str, int len) {
+ if (viewer.acceptClipboard.getValue())
+ clipboardDialog.serverCutText(str, len);
+ }
+
+ // We start timing on beginRect and stop timing on endRect, to
+ // avoid skewing the bandwidth estimation as a result of the server
+ // being slow or the network having high latency
+ public void beginRect(Rect r, int encoding) {
+ ((JavaInStream)getInStream()).startTiming();
+ if (encoding != Encodings.encodingCopyRect) {
+ lastServerEncoding = encoding;
+ }
+ }
+
+ public void endRect(Rect r, int encoding) {
+ ((JavaInStream)getInStream()).stopTiming();
+ }
+
+ public void fillRect(Rect r, int p) {
+ desktop.fillRect(r.tl.x, r.tl.y, r.width(), r.height(), p);
+ }
+ public void imageRect(Rect r, int[] p) {
+ desktop.imageRect(r.tl.x, r.tl.y, r.width(), r.height(), p);
+ }
+ public void copyRect(Rect r, int sx, int sy) {
+ desktop.copyRect(r.tl.x, r.tl.y, r.width(), r.height(), sx, sy);
+ }
+
+ public void setCursor(int width, int height, Point hotspot,
+ int[] data, byte[] mask) {
+ desktop.setCursor(width, height, hotspot, data, mask);
+ }
+
+ private void resizeFramebuffer()
+ {
+ if (desktop == null)
+ return;
+ if ((desktop.width() == cp.width) && (desktop.height() == cp.height))
+ return;
+
+ desktop.resize();
+ recreateViewport();
+ }
+
+ // 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.
+
+ private void recreateViewport()
+ {
+ if (viewport != null) viewport.dispose();
+ viewport = new ViewportFrame(cp.name(), this);
+ viewport.setUndecorated(fullScreen);
+ ClassLoader loader = this.getClass().getClassLoader();
+ URL url = loader.getResource("tigervnc.ico");
+ ImageIcon icon = null;
+ if (url != null) {
+ icon = new ImageIcon(url);
+ viewport.setIconImage(icon.getImage());
+ }
+ viewport.addChild(desktop);
+ reconfigureViewport();
+ viewport.setVisible(true);
+ desktop.initGraphics();
+ desktop.requestFocusInWindow();
+ }
+
+ private void reconfigureViewport()
+ {
+ //viewport->setMaxSize(cp.width, cp.height);
+ int w = cp.width;
+ int h = cp.height;
+ Dimension dpySize = viewport.getToolkit().getScreenSize();
+ int wmDecorationWidth = 0;
+ int wmDecorationHeight = 24;
+ if (w + wmDecorationWidth >= dpySize.width)
+ w = dpySize.width - wmDecorationWidth;
+ if (h + wmDecorationHeight >= dpySize.height)
+ h = dpySize.height - wmDecorationHeight;
+
+ int x = (dpySize.width - w - wmDecorationWidth) / 2;
+ int y = (dpySize.height - h - wmDecorationHeight)/2;
+
+ if (fullScreen) {
+ viewport.setExtendedState(JFrame.MAXIMIZED_BOTH);
+ viewport.setGeometry(0, 0, dpySize.width, dpySize.height);
+ } else {
+ viewport.setExtendedState(JFrame.NORMAL);
+ viewport.setGeometry(x, y, w, h);
+ }
+ }
+
+ // autoSelectFormatAndEncoding() chooses the format and encoding appropriate
+ // to the connection speed:
+ //
+ // First we wait for at least one second of bandwidth measurement.
+ //
+ // Above 16Mbps (i.e. LAN), we choose the second highest JPEG quality,
+ // which should be perceptually lossless.
+ //
+ // If the bandwidth is below that, we choose a more lossy JPEG quality.
+ //
+ // If the bandwidth drops below 256 Kbps, we switch to palette mode.
+ //
+ // Note: The system here is fairly arbitrary and should be replaced
+ // with something more intelligent at the server end.
+ //
+ private void autoSelectFormatAndEncoding() {
+ long kbitsPerSecond = ((JavaInStream)getInStream()).kbitsPerSecond();
+ long timeWaited = ((JavaInStream)getInStream()).timeWaited();
+ boolean newFullColour = fullColour;
+ int newQualityLevel = cp.qualityLevel;
+
+ // Always use Tight
+ if (currentEncoding != Encodings.encodingTight) {
+ currentEncoding = Encodings.encodingTight;
+ encodingChange = true;
+ }
+
+ // Check that we have a decent bandwidth measurement
+ if ((kbitsPerSecond == 0) || (timeWaited < 10000))
+ return;
+
+ // Select appropriate quality level
+ if (!cp.noJpeg) {
+ if (kbitsPerSecond > 16000)
+ newQualityLevel = 8;
+ else
+ newQualityLevel = 6;
+
+ if (newQualityLevel != cp.qualityLevel) {
+ vlog.info("Throughput "+kbitsPerSecond+
+ " kbit/s - changing to quality "+newQualityLevel);
+ cp.qualityLevel = newQualityLevel;
+ viewer.qualityLevel.setParam(Integer.toString(newQualityLevel));
+ encodingChange = true;
+ }
+ }
+
+ if (cp.beforeVersion(3, 8)) {
+ // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
+ // cursors "asynchronously". If this happens in the middle of a
+ // pixel format change, the server will encode the cursor with
+ // the old format, but the client will try to decode it
+ // according to the new format. This will lead to a
+ // crash. Therefore, we do not allow automatic format change for
+ // old servers.
+ return;
+ }
+
+ // Select best color level
+ newFullColour = (kbitsPerSecond > 256);
+ if (newFullColour != fullColour) {
+ vlog.info("Throughput "+kbitsPerSecond+
+ " kbit/s - full color is now "+
+ (newFullColour ? "enabled" : "disabled"));
+ fullColour = newFullColour;
+ formatChange = true;
+ }
+ }
+
+ // requestNewUpdate() requests an update from the server, having set the
+ // format and encoding appropriately.
+ private void requestNewUpdate()
+ {
+ if (formatChange) {
+ if (fullColour) {
+ desktop.setPF(fullColourPF);
+ } else {
+ if (lowColourLevel == 0) {
+ desktop.setPF(new PixelFormat(8,3,false,true,1,1,1,2,1,0));
+ } else if (lowColourLevel == 1) {
+ desktop.setPF(new PixelFormat(8,6,false,true,3,3,3,4,2,0));
+ } else {
+ desktop.setPF(new PixelFormat(8,8,false,true,7,7,3,0,3,6));
+ }
+ }
+ String str = desktop.getPF().print();
+ vlog.info("Using pixel format "+str);
+ cp.setPF(desktop.getPF());
+ synchronized (this) {
+ writer().writeSetPixelFormat(cp.pf());
+ }
+ }
+ checkEncodings();
+ synchronized (this) {
+ writer().writeFramebufferUpdateRequest(new Rect(0,0,cp.width,cp.height),
+ !formatChange);
+ }
+ formatChange = false;
+ }
+
+
+ ////////////////////////////////////////////////////////////////////
+ // The following methods are all called from the GUI thread
+
+ // close() closes the socket, thus waking up the RFB thread.
+ public void close() {
+ try {
+ shuttingDown = true;
+ sock.close();
+ } catch (java.io.IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // 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() {
+ InputStream stream = cl.getResourceAsStream("manifest");
+ String pkgVersion = "";
+ String pkgDate = "";
+ String pkgTime = "";
+ try {
+ Manifest manifest = new Manifest(stream);
+ Attributes attributes = manifest.getMainAttributes();
+ pkgVersion = attributes.getValue("Package-Version");
+ pkgDate = attributes.getValue("Package-Date");
+ pkgTime = attributes.getValue("Package-Time");
+ } catch (IOException e) { }
+ JOptionPane.showMessageDialog((viewport != null ? viewport : null),
+ VncViewer.about1+"\n"
+ +"Built on "+pkgDate+" at "+pkgTime+"\n"
+ +VncViewer.about2+"\n"
+ +VncViewer.about3,
+ "About TigerVNC Viewer for Java",
+ JOptionPane.INFORMATION_MESSAGE,
+ logo);
+ }
+
+ void showInfo() {
+ JOptionPane.showMessageDialog(viewport,
+ "Desktop name: "+cp.name()+"\n"
+ +"Host: "+serverHost+":"+sock.getPort()+"\n"
+ +"Size: "+cp.width+"x"+cp.height+"\n"
+ +"Pixel format: "+desktop.getPF().print()+"\n"
+ +"(server default "+serverPF.print()+")\n"
+ +"Requested encoding: "+Encodings.encodingName(currentEncoding)+"\n"
+ +"Last used encoding: "+Encodings.encodingName(lastServerEncoding)+"\n"
+ +"Line speed estimate: "+((JavaInStream)getInStream()).kbitsPerSecond()+" kbit/s"+"\n"
+ +"Protocol version: "+cp.majorVersion+"."+cp.minorVersion+"\n"
+ +"Security method: "+Security.secTypeName(csecurity.getType())
+ +" ["+csecurity.description()+"]",
+ "VNC connection info",
+ JOptionPane.PLAIN_MESSAGE);
+ }
+
+ synchronized public void refresh() {
+ writer().writeFramebufferUpdateRequest(new Rect(0,0,cp.width,cp.height), false);
+ }
+
+
+ // 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 = -1 + viewer.compressLevel.getValue();
+ options.compressLevel.setSelectedIndex(digit);
+ options.noJpeg.setSelected(!viewer.noJpeg.getValue());
+ digit = -1 + viewer.qualityLevel.getValue();
+ options.qualityLevel.setSelectedIndex(digit);
+
+ options.viewOnly.setSelected(viewer.viewOnly.getValue());
+ options.acceptClipboard.setSelected(viewer.acceptClipboard.getValue());
+ options.sendClipboard.setSelected(viewer.sendClipboard.getValue());
+ options.menuKey.setSelectedIndex(menuKey-0xFFBE);
+
+ 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.ca.setEnabled(false);
+ options.crl.setEnabled(false);
+ options.secManaged.setEnabled(false);
+ options.secNone.setEnabled(false);
+ options.secVnc.setEnabled(false);
+ options.secPlain.setEnabled(false);
+ options.sendLocalUsername.setEnabled(false);
+ } else {
+ options.shared.setSelected(shared);
+
+ /* Process non-VeNCrypt sectypes */
+ java.util.List<Integer> secTypes = new ArrayList<Integer>();
+ secTypes = security.GetEnabledSecTypes();
+ for (Iterator i = secTypes.iterator(); i.hasNext();) {
+ switch ((Integer)i.next()) {
+ case Security.secTypeVeNCrypt:
+ options.secVeNCrypt.setSelected(true);
+ break;
+ case Security.secTypeManaged:
+ options.encNone.setSelected(true);
+ options.secManaged.setSelected(true);
+ options.sendLocalUsername.setSelected(true);
+ break;
+ case Security.secTypeNone:
+ options.encNone.setSelected(true);
+ options.secNone.setSelected(true);
+ break;
+ case Security.secTypeVncAuth:
+ options.encNone.setSelected(true);
+ options.secVnc.setSelected(true);
+ break;
+ }
+ }
+
+ /* Process VeNCrypt subtypes */
+ if (options.secVeNCrypt.isSelected()) {
+ java.util.List<Integer> secTypesExt = new ArrayList<Integer>();
+ secTypesExt = security.GetEnabledExtSecTypes();
+ for (Iterator iext = secTypesExt.iterator(); iext.hasNext();) {
+ switch ((Integer)iext.next()) {
+ case Security.secTypePlain:
+ options.secPlain.setSelected(true);
+ options.sendLocalUsername.setSelected(true);
+ break;
+ case Security.secTypeTLSNone:
+ options.encTLS.setSelected(true);
+ options.secNone.setSelected(true);
+ break;
+ case Security.secTypeTLSVnc:
+ options.encTLS.setSelected(true);
+ options.secVnc.setSelected(true);
+ break;
+ case Security.secTypeTLSPlain:
+ options.encTLS.setSelected(true);
+ options.secPlain.setSelected(true);
+ options.sendLocalUsername.setSelected(true);
+ break;
+ case Security.secTypeX509None:
+ options.encX509.setSelected(true);
+ options.secNone.setSelected(true);
+ break;
+ case Security.secTypeX509Vnc:
+ options.encX509.setSelected(true);
+ options.secVnc.setSelected(true);
+ break;
+ case Security.secTypeX509Plain:
+ options.encX509.setSelected(true);
+ options.secPlain.setSelected(true);
+ options.sendLocalUsername.setSelected(true);
+ break;
+ }
+ }
+ }
+ options.sendLocalUsername.setEnabled(options.secPlain.isSelected()||
+ options.secManaged.isSelected());
+ }
+
+ options.fullScreen.setSelected(fullScreen);
+ options.useLocalCursor.setSelected(viewer.useLocalCursor.getValue());
+ options.fastCopyRect.setSelected(viewer.fastCopyRect.getValue());
+ }
+
+ public void getOptions() {
+ autoSelect = options.autoSelect.isSelected();
+ if (fullColour != options.fullColour.isSelected())
+ formatChange = 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;
+ }
+ }
+ 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;
+ }
+
+ viewer.customCompressLevel.setParam(options.customCompressLevel.isSelected());
+ if (cp.customCompressLevel != viewer.customCompressLevel.getValue()) {
+ cp.customCompressLevel = viewer.customCompressLevel.getValue();
+ encodingChange = true;
+ }
+ viewer.compressLevel.setParam(Integer.toString(options.compressLevel.getSelectedIndex()));
+ 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(Integer.toString(options.qualityLevel.getSelectedIndex()));
+ if (cp.qualityLevel != viewer.qualityLevel.getValue()) {
+ cp.qualityLevel = viewer.qualityLevel.getValue();
+ encodingChange = true;
+ }
+ viewer.sendLocalUsername.setParam(options.sendLocalUsername.isSelected());
+
+ viewer.viewOnly.setParam(options.viewOnly.isSelected());
+ viewer.acceptClipboard.setParam(options.acceptClipboard.isSelected());
+ viewer.sendClipboard.setParam(options.sendClipboard.isSelected());
+ clipboardDialog.setSendingEnabled(viewer.sendClipboard.getValue());
+ menuKey = (int)(options.menuKey.getSelectedIndex()+0xFFBE);
+ F8Menu.f8.setLabel("Send F"+(menuKey-Keysyms.F1+1));
+
+ shared = options.shared.isSelected();
+ setShared(shared);
+ viewer.useLocalCursor.setParam(options.useLocalCursor.isSelected());
+ if (cp.supportsLocalCursor != viewer.useLocalCursor.getValue()) {
+ cp.supportsLocalCursor = viewer.useLocalCursor.getValue();
+ encodingChange = true;
+ if (desktop != null)
+ desktop.resetLocalCursor();
+ }
+
+ checkEncodings();
+
+ if (state() != RFBSTATE_NORMAL) {
+ /* Process security types which don't use encryption */
+ if (options.encNone.isSelected()) {
+ if (options.secManaged.isSelected())
+ Security.EnableSecType(Security.secTypeManaged);
+ if (options.secNone.isSelected())
+ Security.EnableSecType(Security.secTypeNone);
+ if (options.secVnc.isSelected())
+ Security.EnableSecType(Security.secTypeVncAuth);
+ if (options.secPlain.isSelected())
+ Security.EnableSecType(Security.secTypePlain);
+ } else {
+ Security.DisableSecType(Security.secTypeManaged);
+ Security.DisableSecType(Security.secTypeNone);
+ Security.DisableSecType(Security.secTypeVncAuth);
+ Security.DisableSecType(Security.secTypePlain);
+ }
+
+ /* 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);
+ } else {
+ Security.DisableSecType(Security.secTypeTLSNone);
+ Security.DisableSecType(Security.secTypeTLSVnc);
+ Security.DisableSecType(Security.secTypeTLSPlain);
+ }
+
+ /* 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);
+ } else {
+ Security.DisableSecType(Security.secTypeX509None);
+ Security.DisableSecType(Security.secTypeX509Vnc);
+ Security.DisableSecType(Security.secTypeX509Plain);
+ }
+
+ /* 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);
+ }
+
+ /* 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);
+ }
+
+ /* 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);
+ }
+
+ CSecurityTLS.x509ca.setParam(options.ca.getText());
+ CSecurityTLS.x509crl.setParam(options.crl.getText());
+ }
+ }
+
+ public void toggleFullScreen() {
+ fullScreen = !fullScreen;
+ if (!fullScreen) menu.fullScreen.setSelected(false);
+ recreateViewport();
+ }
+
+ // writeClientCutText() is called from the clipboard dialog
+ synchronized public void writeClientCutText(String str, int len) {
+ if (state() != RFBSTATE_NORMAL) return;
+ writer().writeClientCutText(str,len);
+ }
+
+ synchronized public void writeKeyEvent(int keysym, boolean down) {
+ if (state() != RFBSTATE_NORMAL) return;
+ writer().writeKeyEvent(keysym, down);
+ }
+
+ synchronized public void writeKeyEvent(KeyEvent ev) {
+ if (ev.getID() != KeyEvent.KEY_PRESSED && !ev.isActionKey())
+ return;
+
+ int keysym;
+
+ if (!ev.isActionKey()) {
+ vlog.debug("key press "+ev.getKeyChar());
+ if (ev.getKeyChar() < 32) {
+ // if the ctrl modifier key is down, send the equivalent ASCII since we
+ // will send the ctrl modifier anyway
+
+ if ((ev.getModifiers() & KeyEvent.CTRL_MASK) != 0) {
+ if ((ev.getModifiers() & KeyEvent.SHIFT_MASK) != 0) {
+ keysym = ev.getKeyChar() + 64;
+ if (keysym == -1)
+ return;
+ } else {
+ keysym = ev.getKeyChar() + 96;
+ if (keysym == 127) keysym = 95;
+ }
+ } else {
+ switch (ev.getKeyCode()) {
+ case KeyEvent.VK_BACK_SPACE: keysym = Keysyms.BackSpace; break;
+ case KeyEvent.VK_TAB: keysym = Keysyms.Tab; break;
+ case KeyEvent.VK_ENTER: keysym = Keysyms.Return; break;
+ case KeyEvent.VK_ESCAPE: keysym = Keysyms.Escape; break;
+ default: return;
+ }
+ }
+
+ } else if (ev.getKeyChar() == 127) {
+ keysym = Keysyms.Delete;
+
+ } else {
+ keysym = UnicodeToKeysym.translate(ev.getKeyChar());
+ if (keysym == -1)
+ return;
+ }
+
+ } else {
+ // KEY_ACTION
+ vlog.debug("key action "+ev.getKeyCode());
+ switch (ev.getKeyCode()) {
+ case KeyEvent.VK_HOME: keysym = Keysyms.Home; break;
+ case KeyEvent.VK_END: keysym = Keysyms.End; break;
+ case KeyEvent.VK_PAGE_UP: keysym = Keysyms.Page_Up; break;
+ case KeyEvent.VK_PAGE_DOWN: keysym = Keysyms.Page_Down; break;
+ case KeyEvent.VK_UP: keysym = Keysyms.Up; break;
+ case KeyEvent.VK_DOWN: keysym = Keysyms.Down; break;
+ case KeyEvent.VK_LEFT: keysym = Keysyms.Left; break;
+ case KeyEvent.VK_RIGHT: keysym = Keysyms.Right; break;
+ case KeyEvent.VK_F1: keysym = Keysyms.F1; break;
+ case KeyEvent.VK_F2: keysym = Keysyms.F2; break;
+ case KeyEvent.VK_F3: keysym = Keysyms.F3; break;
+ case KeyEvent.VK_F4: keysym = Keysyms.F4; break;
+ case KeyEvent.VK_F5: keysym = Keysyms.F5; break;
+ case KeyEvent.VK_F6: keysym = Keysyms.F6; break;
+ case KeyEvent.VK_F7: keysym = Keysyms.F7; break;
+ case KeyEvent.VK_F8: keysym = Keysyms.F8; break;
+ case KeyEvent.VK_F9: keysym = Keysyms.F9; break;
+ case KeyEvent.VK_F10: keysym = Keysyms.F10; break;
+ case KeyEvent.VK_F11: keysym = Keysyms.F11; break;
+ case KeyEvent.VK_F12: keysym = Keysyms.F12; break;
+ case KeyEvent.VK_PRINTSCREEN: keysym = Keysyms.Print; break;
+ case KeyEvent.VK_PAUSE: keysym = Keysyms.Pause; break;
+ case KeyEvent.VK_INSERT: keysym = Keysyms.Insert; break;
+ default: return;
+ }
+ }
+
+ writeModifiers(ev.getModifiers());
+ writeKeyEvent(keysym, true);
+ writeKeyEvent(keysym, false);
+ writeModifiers(0);
+ }
+
+
+ synchronized public void writePointerEvent(MouseEvent ev) {
+ if (state() != RFBSTATE_NORMAL) return;
+ int x, y;
+
+ switch (ev.getID()) {
+ case MouseEvent.MOUSE_PRESSED:
+ buttonMask = 1;
+ if ((ev.getModifiers() & KeyEvent.ALT_MASK) != 0) buttonMask = 2;
+ if ((ev.getModifiers() & KeyEvent.META_MASK) != 0) buttonMask = 4;
+ break;
+ case MouseEvent.MOUSE_RELEASED:
+ buttonMask = 0;
+ break;
+ }
+
+ writeModifiers(ev.getModifiers() & ~KeyEvent.ALT_MASK & ~KeyEvent.META_MASK);
+
+ x = ev.getX();
+ y = ev.getY();
+ if (x < 0) x = 0;
+ if (x > cp.width-1) x = cp.width-1;
+ if (y < 0) y = 0;
+ if (y > cp.height-1) y = cp.height-1;
+
+ writer().writePointerEvent(new Point(x, y), buttonMask);
+
+ if (buttonMask == 0) writeModifiers(0);
+ }
+
+
+ synchronized public void writeWheelEvent(MouseWheelEvent ev) {
+ if (state() != RFBSTATE_NORMAL) return;
+ int x, y;
+ int clicks = ev.getWheelRotation();
+ if (clicks < 0) {
+ buttonMask = 8;
+ } else {
+ buttonMask = 16;
+ }
+ writeModifiers(ev.getModifiers() & ~KeyEvent.ALT_MASK & ~KeyEvent.META_MASK);
+ for (int i=0;i<java.lang.Math.abs(clicks);i++) {
+ x = ev.getX();
+ y = ev.getY();
+ writer().writePointerEvent(new Point(x, y), buttonMask);
+ buttonMask = 0;
+ writer().writePointerEvent(new Point(x, y), buttonMask);
+ }
+ writeModifiers(0);
+
+ }
+
+
+ void writeModifiers(int m) {
+ if ((m & Event.SHIFT_MASK) != (pressedModifiers & Event.SHIFT_MASK))
+ writeKeyEvent(Keysyms.Shift_L, (m & Event.SHIFT_MASK) != 0);
+ if ((m & Event.CTRL_MASK) != (pressedModifiers & Event.CTRL_MASK))
+ writeKeyEvent(Keysyms.Control_L, (m & Event.CTRL_MASK) != 0);
+ if ((m & Event.ALT_MASK) != (pressedModifiers & Event.ALT_MASK))
+ writeKeyEvent(Keysyms.Alt_L, (m & Event.ALT_MASK) != 0);
+ if ((m & Event.META_MASK) != (pressedModifiers & Event.META_MASK))
+ writeKeyEvent(Keysyms.Meta_L, (m & Event.META_MASK) != 0);
+ pressedModifiers = m;
+ }
+
+
+ ////////////////////////////////////////////////////////////////////
+ // The following methods are called from both RFB and GUI threads
+
+ // checkEncodings() sends a setEncodings message if one is needed.
+ synchronized private void checkEncodings() {
+ if (encodingChange && state() == RFBSTATE_NORMAL) {
+ vlog.info("Using "+Encodings.encodingName(currentEncoding)+" encoding");
+ writer().writeSetEncodings(currentEncoding, true);
+ encodingChange = false;
+ }
+ }
+
+ // the following never change so need no synchronization:
+ JavaInStream jis;
+ JavaOutStream jos;
+
+
+ // 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:
+
+ ClassLoader cl = this.getClass().getClassLoader();
+ ImageIcon logo = new ImageIcon(cl.getResource("com/tigervnc/vncviewer/tigervnc.png"));
+ public static UserPasswdGetter upg;
+ public UserMsgBox msg;
+
+ // shuttingDown is set by the GUI thread and only ever tested by the RFB
+ // thread after the window has been destroyed.
+ boolean shuttingDown;
+
+ // reading and writing int and boolean is atomic in java, so no
+ // synchronization of the following flags is needed:
+ int currentEncoding, lastServerEncoding;
+
+ 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;
+ int pressedModifiers;
+
+ public String serverHost;
+ public int serverPort;
+ public int menuKey;
+ PixelFormat serverPF;
+ ViewportFrame viewport;
+ DesktopWindow desktop;
+ PixelFormat fullColourPF;
+ boolean fullColour;
+ boolean autoSelect;
+ boolean shared;
+ boolean formatChange;
+ boolean encodingChange;
+ boolean sameMachine;
+ boolean fullScreen;
+ boolean reverseConnection;
+ boolean firstUpdate;
+ boolean pendingUpdate;
+
+ static LogWriter vlog = new LogWriter("CConn");
+}
diff --git a/java/src/com/tigervnc/vncviewer/ClipboardDialog.java b/java/src/com/tigervnc/vncviewer/ClipboardDialog.java
new file mode 100644
index 0000000..9753b15
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/ClipboardDialog.java
@@ -0,0 +1,107 @@
+/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.vncviewer;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import javax.swing.*;
+import com.tigervnc.rfb.LogWriter;
+
+class ClipboardDialog extends Dialog implements ActionListener {
+
+ public ClipboardDialog(CConn cc_) {
+ super(false);
+ cc = cc_;
+ setTitle("VNC clipboard");
+ textArea = new JTextArea(5,50);
+ getContentPane().add("Center", textArea);
+
+ JPanel pb = new JPanel();
+ clearButton = new JButton("Clear");
+ pb.add(clearButton);
+ clearButton.addActionListener(this);
+ sendButton = new JButton("Send to VNC server");
+ pb.add(sendButton);
+ sendButton.addActionListener(this);
+ cancelButton = new JButton("Cancel");
+ pb.add(cancelButton);
+ cancelButton.addActionListener(this);
+ getContentPane().add("South", pb);
+
+ pack();
+ }
+
+ static Clipboard systemClipboard;
+ static {
+ try {
+ systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ } catch (Exception e) { }
+ }
+
+ public void initDialog() {
+ textArea.setText(current);
+ textArea.selectAll();
+ }
+
+ public void setContents(String str) {
+ current = str;
+ textArea.setText(str);
+ textArea.selectAll();
+ }
+
+ public void serverCutText(String str, int len) {
+ setContents(str);
+ if (systemClipboard != null) {
+ StringSelection ss = new StringSelection(str);
+ try {
+ systemClipboard.setContents(ss, ss);
+ } catch(Exception e) {
+ vlog.debug(e.toString());
+ }
+ }
+ }
+
+ public void setSendingEnabled(boolean b) {
+ sendButton.setEnabled(b);
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ Object s = e.getSource();
+ if (s instanceof JButton && (JButton)s == clearButton) {
+ current = "";
+ textArea.setText(current);
+ } else if (s instanceof JButton && (JButton)s == sendButton) {
+ ok = true;
+ current = textArea.getText();
+ cc.writeClientCutText(current, current.length());
+ endDialog();
+ } else if (s instanceof JButton && (JButton)s == cancelButton) {
+ ok = false;
+ endDialog();
+ }
+ }
+
+ CConn cc;
+ String current;
+ JTextArea textArea;
+ JButton clearButton, sendButton, cancelButton;
+ static LogWriter vlog = new LogWriter("ClipboardDialog");
+}
diff --git a/java/src/com/tigervnc/vncviewer/ClipboardFrame.java b/java/src/com/tigervnc/vncviewer/ClipboardFrame.java
deleted file mode 100644
index a09519c..0000000
--- a/java/src/com/tigervnc/vncviewer/ClipboardFrame.java
+++ /dev/null
@@ -1,135 +0,0 @@
-//
-// Copyright (C) 2001 HorizonLive.com, Inc. All Rights Reserved.
-// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
-
-//
-// Clipboard frame.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.awt.*;
-import java.awt.event.*;
-
-class ClipboardFrame extends Frame
- implements WindowListener, ActionListener {
-
- TextArea textArea;
- Button clearButton, closeButton;
- String selection;
- VncViewer viewer;
-
- //
- // Constructor.
- //
-
- ClipboardFrame(VncViewer v) {
- super("TigerVNC Clipboard");
-
- viewer = v;
-
- GridBagLayout gridbag = new GridBagLayout();
- setLayout(gridbag);
-
- GridBagConstraints gbc = new GridBagConstraints();
- gbc.gridwidth = GridBagConstraints.REMAINDER;
- gbc.fill = GridBagConstraints.BOTH;
- gbc.weighty = 1.0;
-
- textArea = new TextArea(5, 40);
- gridbag.setConstraints(textArea, gbc);
- add(textArea);
-
- gbc.fill = GridBagConstraints.HORIZONTAL;
- gbc.weightx = 1.0;
- gbc.weighty = 0.0;
- gbc.gridwidth = 1;
-
- clearButton = new Button("Clear");
- gridbag.setConstraints(clearButton, gbc);
- add(clearButton);
- clearButton.addActionListener(this);
-
- closeButton = new Button("Close");
- gridbag.setConstraints(closeButton, gbc);
- add(closeButton);
- closeButton.addActionListener(this);
-
- pack();
-
- addWindowListener(this);
- }
-
-
- //
- // Set the cut text from the RFB server.
- //
-
- void setCutText(String text) {
- selection = text;
- textArea.setText(text);
- if (isVisible()) {
- textArea.selectAll();
- }
- }
-
-
- //
- // When the focus leaves the window, see if we have new cut text and
- // if so send it to the RFB server.
- //
-
- public void windowDeactivated (WindowEvent evt) {
- if (selection != null && !selection.equals(textArea.getText())) {
- selection = textArea.getText();
- viewer.setCutText(selection);
- }
- }
-
- //
- // Close our window properly.
- //
-
- public void windowClosing(WindowEvent evt) {
- setVisible(false);
- }
-
- //
- // Ignore window events we're not interested in.
- //
-
- public void windowActivated(WindowEvent evt) {}
- public void windowOpened(WindowEvent evt) {}
- public void windowClosed(WindowEvent evt) {}
- public void windowIconified(WindowEvent evt) {}
- public void windowDeiconified(WindowEvent evt) {}
-
-
- //
- // Respond to button presses
- //
-
- public void actionPerformed(ActionEvent evt) {
- if (evt.getSource() == clearButton) {
- textArea.setText("");
- } else if (evt.getSource() == closeButton) {
- setVisible(false);
- }
- }
-}
diff --git a/java/src/com/tigervnc/vncviewer/DesCipher.java b/java/src/com/tigervnc/vncviewer/DesCipher.java
deleted file mode 100644
index 25362fe..0000000
--- a/java/src/com/tigervnc/vncviewer/DesCipher.java
+++ /dev/null
@@ -1,498 +0,0 @@
-//
-// This DES class has been extracted from package Acme.Crypto for use in VNC.
-// The bytebit[] array has been reversed so that the most significant bit
-// in each byte of the key is ignored, not the least significant. Also the
-// unnecessary odd parity code has been removed.
-//
-// These changes are:
-// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
-//
-// 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.
-//
-
-// DesCipher - the DES encryption method
-//
-// The meat of this code is by Dave Zimmerman <dzimm@widget.com>, and is:
-//
-// Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved.
-//
-// Permission to use, copy, modify, and distribute this software
-// and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
-// without fee is hereby granted, provided that this copyright notice is kept
-// intact.
-//
-// WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
-// OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
-// TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
-// PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE
-// FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
-// DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
-//
-// THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
-// CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
-// PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
-// NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
-// SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
-// SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
-// PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET WORKSHOP
-// SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
-// HIGH RISK ACTIVITIES.
-//
-//
-// The rest is:
-//
-// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions
-// are met:
-// 1. Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// 2. Redistributions in binary form must reproduce the above copyright
-// notice, this list of conditions and the following disclaimer in the
-// documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-// SUCH DAMAGE.
-//
-// Visit the ACME Labs Java page for up-to-date versions of this and other
-// fine Java utilities: http://www.acme.com/java/
-
-
-package com.tigervnc.vncviewer;
-
-import java.io.*;
-
-/// The DES encryption method.
-// <P>
-// This is surprisingly fast, for pure Java. On a SPARC 20, wrapped
-// in Acme.Crypto.EncryptedOutputStream or Acme.Crypto.EncryptedInputStream,
-// it does around 7000 bytes/second.
-// <P>
-// Most of this code is by Dave Zimmerman <dzimm@widget.com>, and is
-// Copyright (c) 1996 Widget Workshop, Inc. See the source file for details.
-// <P>
-// <A HREF="/resources/classes/Acme/Crypto/DesCipher.java">Fetch the software.</A><BR>
-// <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A>
-// <P>
-// @see Des3Cipher
-// @see EncryptedOutputStream
-// @see EncryptedInputStream
-
-public class DesCipher
- {
-
- // Constructor, byte-array key.
- public DesCipher( byte[] key )
- {
- setKey( key );
- }
-
- // Key routines.
-
- private int[] encryptKeys = new int[32];
- private int[] decryptKeys = new int[32];
-
- /// Set the key.
- public void setKey( byte[] key )
- {
- deskey( key, true, encryptKeys );
- deskey( key, false, decryptKeys );
- }
-
- // Turn an 8-byte key into internal keys.
- private void deskey( byte[] keyBlock, boolean encrypting, int[] KnL )
- {
- int i, j, l, m, n;
- int[] pc1m = new int[56];
- int[] pcr = new int[56];
- int[] kn = new int[32];
-
- for ( j = 0; j < 56; ++j )
- {
- l = pc1[j];
- m = l & 07;
- pc1m[j] = ( (keyBlock[l >>> 3] & bytebit[m]) != 0 )? 1: 0;
- }
-
- for ( i = 0; i < 16; ++i )
- {
- if ( encrypting )
- m = i << 1;
- else
- m = (15-i) << 1;
- n = m+1;
- kn[m] = kn[n] = 0;
- for ( j = 0; j < 28; ++j )
- {
- l = j+totrot[i];
- if ( l < 28 )
- pcr[j] = pc1m[l];
- else
- pcr[j] = pc1m[l-28];
- }
- for ( j=28; j < 56; ++j )
- {
- l = j+totrot[i];
- if ( l < 56 )
- pcr[j] = pc1m[l];
- else
- pcr[j] = pc1m[l-28];
- }
- for ( j = 0; j < 24; ++j )
- {
- if ( pcr[pc2[j]] != 0 )
- kn[m] |= bigbyte[j];
- if ( pcr[pc2[j+24]] != 0 )
- kn[n] |= bigbyte[j];
- }
- }
- cookey( kn, KnL );
- }
-
- private void cookey( int[] raw, int KnL[] )
- {
- int raw0, raw1;
- int rawi, KnLi;
- int i;
-
- for ( i = 0, rawi = 0, KnLi = 0; i < 16; ++i )
- {
- raw0 = raw[rawi++];
- raw1 = raw[rawi++];
- KnL[KnLi] = (raw0 & 0x00fc0000) << 6;
- KnL[KnLi] |= (raw0 & 0x00000fc0) << 10;
- KnL[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
- KnL[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
- ++KnLi;
- KnL[KnLi] = (raw0 & 0x0003f000) << 12;
- KnL[KnLi] |= (raw0 & 0x0000003f) << 16;
- KnL[KnLi] |= (raw1 & 0x0003f000) >>> 4;
- KnL[KnLi] |= (raw1 & 0x0000003f);
- ++KnLi;
- }
- }
-
-
- // Block encryption routines.
-
- private int[] tempInts = new int[2];
-
- /// Encrypt a block of eight bytes.
- public void encrypt( byte[] clearText, int clearOff, byte[] cipherText, int cipherOff )
- {
- squashBytesToInts( clearText, clearOff, tempInts, 0, 2 );
- des( tempInts, tempInts, encryptKeys );
- spreadIntsToBytes( tempInts, 0, cipherText, cipherOff, 2 );
- }
-
- /// Decrypt a block of eight bytes.
- public void decrypt( byte[] cipherText, int cipherOff, byte[] clearText, int clearOff )
- {
- squashBytesToInts( cipherText, cipherOff, tempInts, 0, 2 );
- des( tempInts, tempInts, decryptKeys );
- spreadIntsToBytes( tempInts, 0, clearText, clearOff, 2 );
- }
-
- // The DES function.
- private void des( int[] inInts, int[] outInts, int[] keys )
- {
- int fval, work, right, leftt;
- int round;
- int keysi = 0;
-
- leftt = inInts[0];
- right = inInts[1];
-
- work = ((leftt >>> 4) ^ right) & 0x0f0f0f0f;
- right ^= work;
- leftt ^= (work << 4);
-
- work = ((leftt >>> 16) ^ right) & 0x0000ffff;
- right ^= work;
- leftt ^= (work << 16);
-
- work = ((right >>> 2) ^ leftt) & 0x33333333;
- leftt ^= work;
- right ^= (work << 2);
-
- work = ((right >>> 8) ^ leftt) & 0x00ff00ff;
- leftt ^= work;
- right ^= (work << 8);
- right = (right << 1) | ((right >>> 31) & 1);
-
- work = (leftt ^ right) & 0xaaaaaaaa;
- leftt ^= work;
- right ^= work;
- leftt = (leftt << 1) | ((leftt >>> 31) & 1);
-
- for ( round = 0; round < 8; ++round )
- {
- work = (right << 28) | (right >>> 4);
- work ^= keys[keysi++];
- fval = SP7[ work & 0x0000003f ];
- fval |= SP5[(work >>> 8) & 0x0000003f ];
- fval |= SP3[(work >>> 16) & 0x0000003f ];
- fval |= SP1[(work >>> 24) & 0x0000003f ];
- work = right ^ keys[keysi++];
- fval |= SP8[ work & 0x0000003f ];
- fval |= SP6[(work >>> 8) & 0x0000003f ];
- fval |= SP4[(work >>> 16) & 0x0000003f ];
- fval |= SP2[(work >>> 24) & 0x0000003f ];
- leftt ^= fval;
- work = (leftt << 28) | (leftt >>> 4);
- work ^= keys[keysi++];
- fval = SP7[ work & 0x0000003f ];
- fval |= SP5[(work >>> 8) & 0x0000003f ];
- fval |= SP3[(work >>> 16) & 0x0000003f ];
- fval |= SP1[(work >>> 24) & 0x0000003f ];
- work = leftt ^ keys[keysi++];
- fval |= SP8[ work & 0x0000003f ];
- fval |= SP6[(work >>> 8) & 0x0000003f ];
- fval |= SP4[(work >>> 16) & 0x0000003f ];
- fval |= SP2[(work >>> 24) & 0x0000003f ];
- right ^= fval;
- }
-
- right = (right << 31) | (right >>> 1);
- work = (leftt ^ right) & 0xaaaaaaaa;
- leftt ^= work;
- right ^= work;
- leftt = (leftt << 31) | (leftt >>> 1);
- work = ((leftt >>> 8) ^ right) & 0x00ff00ff;
- right ^= work;
- leftt ^= (work << 8);
- work = ((leftt >>> 2) ^ right) & 0x33333333;
- right ^= work;
- leftt ^= (work << 2);
- work = ((right >>> 16) ^ leftt) & 0x0000ffff;
- leftt ^= work;
- right ^= (work << 16);
- work = ((right >>> 4) ^ leftt) & 0x0f0f0f0f;
- leftt ^= work;
- right ^= (work << 4);
- outInts[0] = right;
- outInts[1] = leftt;
- }
-
-
- // Tables, permutations, S-boxes, etc.
-
- private static byte[] bytebit = {
- (byte)0x01, (byte)0x02, (byte)0x04, (byte)0x08,
- (byte)0x10, (byte)0x20, (byte)0x40, (byte)0x80
- };
- private static int[] bigbyte = {
- 0x800000, 0x400000, 0x200000, 0x100000,
- 0x080000, 0x040000, 0x020000, 0x010000,
- 0x008000, 0x004000, 0x002000, 0x001000,
- 0x000800, 0x000400, 0x000200, 0x000100,
- 0x000080, 0x000040, 0x000020, 0x000010,
- 0x000008, 0x000004, 0x000002, 0x000001
- };
- private static byte[] pc1 = {
- (byte)56, (byte)48, (byte)40, (byte)32, (byte)24, (byte)16, (byte) 8,
- (byte) 0, (byte)57, (byte)49, (byte)41, (byte)33, (byte)25, (byte)17,
- (byte) 9, (byte) 1, (byte)58, (byte)50, (byte)42, (byte)34, (byte)26,
- (byte)18, (byte)10, (byte) 2, (byte)59, (byte)51, (byte)43, (byte)35,
- (byte)62, (byte)54, (byte)46, (byte)38, (byte)30, (byte)22, (byte)14,
- (byte) 6, (byte)61, (byte)53, (byte)45, (byte)37, (byte)29, (byte)21,
- (byte)13, (byte) 5, (byte)60, (byte)52, (byte)44, (byte)36, (byte)28,
- (byte)20, (byte)12, (byte) 4, (byte)27, (byte)19, (byte)11, (byte)3
- };
- private static int[] totrot = {
- 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28
- };
-
- private static byte[] pc2 = {
- (byte)13, (byte)16, (byte)10, (byte)23, (byte) 0, (byte) 4,
- (byte) 2, (byte)27, (byte)14, (byte) 5, (byte)20, (byte) 9,
- (byte)22, (byte)18, (byte)11, (byte)3 , (byte)25, (byte) 7,
- (byte)15, (byte) 6, (byte)26, (byte)19, (byte)12, (byte) 1,
- (byte)40, (byte)51, (byte)30, (byte)36, (byte)46, (byte)54,
- (byte)29, (byte)39, (byte)50, (byte)44, (byte)32, (byte)47,
- (byte)43, (byte)48, (byte)38, (byte)55, (byte)33, (byte)52,
- (byte)45, (byte)41, (byte)49, (byte)35, (byte)28, (byte)31,
- };
-
- private static int[] SP1 = {
- 0x01010400, 0x00000000, 0x00010000, 0x01010404,
- 0x01010004, 0x00010404, 0x00000004, 0x00010000,
- 0x00000400, 0x01010400, 0x01010404, 0x00000400,
- 0x01000404, 0x01010004, 0x01000000, 0x00000004,
- 0x00000404, 0x01000400, 0x01000400, 0x00010400,
- 0x00010400, 0x01010000, 0x01010000, 0x01000404,
- 0x00010004, 0x01000004, 0x01000004, 0x00010004,
- 0x00000000, 0x00000404, 0x00010404, 0x01000000,
- 0x00010000, 0x01010404, 0x00000004, 0x01010000,
- 0x01010400, 0x01000000, 0x01000000, 0x00000400,
- 0x01010004, 0x00010000, 0x00010400, 0x01000004,
- 0x00000400, 0x00000004, 0x01000404, 0x00010404,
- 0x01010404, 0x00010004, 0x01010000, 0x01000404,
- 0x01000004, 0x00000404, 0x00010404, 0x01010400,
- 0x00000404, 0x01000400, 0x01000400, 0x00000000,
- 0x00010004, 0x00010400, 0x00000000, 0x01010004
- };
- private static int[] SP2 = {
- 0x80108020, 0x80008000, 0x00008000, 0x00108020,
- 0x00100000, 0x00000020, 0x80100020, 0x80008020,
- 0x80000020, 0x80108020, 0x80108000, 0x80000000,
- 0x80008000, 0x00100000, 0x00000020, 0x80100020,
- 0x00108000, 0x00100020, 0x80008020, 0x00000000,
- 0x80000000, 0x00008000, 0x00108020, 0x80100000,
- 0x00100020, 0x80000020, 0x00000000, 0x00108000,
- 0x00008020, 0x80108000, 0x80100000, 0x00008020,
- 0x00000000, 0x00108020, 0x80100020, 0x00100000,
- 0x80008020, 0x80100000, 0x80108000, 0x00008000,
- 0x80100000, 0x80008000, 0x00000020, 0x80108020,
- 0x00108020, 0x00000020, 0x00008000, 0x80000000,
- 0x00008020, 0x80108000, 0x00100000, 0x80000020,
- 0x00100020, 0x80008020, 0x80000020, 0x00100020,
- 0x00108000, 0x00000000, 0x80008000, 0x00008020,
- 0x80000000, 0x80100020, 0x80108020, 0x00108000
- };
- private static int[] SP3 = {
- 0x00000208, 0x08020200, 0x00000000, 0x08020008,
- 0x08000200, 0x00000000, 0x00020208, 0x08000200,
- 0x00020008, 0x08000008, 0x08000008, 0x00020000,
- 0x08020208, 0x00020008, 0x08020000, 0x00000208,
- 0x08000000, 0x00000008, 0x08020200, 0x00000200,
- 0x00020200, 0x08020000, 0x08020008, 0x00020208,
- 0x08000208, 0x00020200, 0x00020000, 0x08000208,
- 0x00000008, 0x08020208, 0x00000200, 0x08000000,
- 0x08020200, 0x08000000, 0x00020008, 0x00000208,
- 0x00020000, 0x08020200, 0x08000200, 0x00000000,
- 0x00000200, 0x00020008, 0x08020208, 0x08000200,
- 0x08000008, 0x00000200, 0x00000000, 0x08020008,
- 0x08000208, 0x00020000, 0x08000000, 0x08020208,
- 0x00000008, 0x00020208, 0x00020200, 0x08000008,
- 0x08020000, 0x08000208, 0x00000208, 0x08020000,
- 0x00020208, 0x00000008, 0x08020008, 0x00020200
- };
- private static int[] SP4 = {
- 0x00802001, 0x00002081, 0x00002081, 0x00000080,
- 0x00802080, 0x00800081, 0x00800001, 0x00002001,
- 0x00000000, 0x00802000, 0x00802000, 0x00802081,
- 0x00000081, 0x00000000, 0x00800080, 0x00800001,
- 0x00000001, 0x00002000, 0x00800000, 0x00802001,
- 0x00000080, 0x00800000, 0x00002001, 0x00002080,
- 0x00800081, 0x00000001, 0x00002080, 0x00800080,
- 0x00002000, 0x00802080, 0x00802081, 0x00000081,
- 0x00800080, 0x00800001, 0x00802000, 0x00802081,
- 0x00000081, 0x00000000, 0x00000000, 0x00802000,
- 0x00002080, 0x00800080, 0x00800081, 0x00000001,
- 0x00802001, 0x00002081, 0x00002081, 0x00000080,
- 0x00802081, 0x00000081, 0x00000001, 0x00002000,
- 0x00800001, 0x00002001, 0x00802080, 0x00800081,
- 0x00002001, 0x00002080, 0x00800000, 0x00802001,
- 0x00000080, 0x00800000, 0x00002000, 0x00802080
- };
- private static int[] SP5 = {
- 0x00000100, 0x02080100, 0x02080000, 0x42000100,
- 0x00080000, 0x00000100, 0x40000000, 0x02080000,
- 0x40080100, 0x00080000, 0x02000100, 0x40080100,
- 0x42000100, 0x42080000, 0x00080100, 0x40000000,
- 0x02000000, 0x40080000, 0x40080000, 0x00000000,
- 0x40000100, 0x42080100, 0x42080100, 0x02000100,
- 0x42080000, 0x40000100, 0x00000000, 0x42000000,
- 0x02080100, 0x02000000, 0x42000000, 0x00080100,
- 0x00080000, 0x42000100, 0x00000100, 0x02000000,
- 0x40000000, 0x02080000, 0x42000100, 0x40080100,
- 0x02000100, 0x40000000, 0x42080000, 0x02080100,
- 0x40080100, 0x00000100, 0x02000000, 0x42080000,
- 0x42080100, 0x00080100, 0x42000000, 0x42080100,
- 0x02080000, 0x00000000, 0x40080000, 0x42000000,
- 0x00080100, 0x02000100, 0x40000100, 0x00080000,
- 0x00000000, 0x40080000, 0x02080100, 0x40000100
- };
- private static int[] SP6 = {
- 0x20000010, 0x20400000, 0x00004000, 0x20404010,
- 0x20400000, 0x00000010, 0x20404010, 0x00400000,
- 0x20004000, 0x00404010, 0x00400000, 0x20000010,
- 0x00400010, 0x20004000, 0x20000000, 0x00004010,
- 0x00000000, 0x00400010, 0x20004010, 0x00004000,
- 0x00404000, 0x20004010, 0x00000010, 0x20400010,
- 0x20400010, 0x00000000, 0x00404010, 0x20404000,
- 0x00004010, 0x00404000, 0x20404000, 0x20000000,
- 0x20004000, 0x00000010, 0x20400010, 0x00404000,
- 0x20404010, 0x00400000, 0x00004010, 0x20000010,
- 0x00400000, 0x20004000, 0x20000000, 0x00004010,
- 0x20000010, 0x20404010, 0x00404000, 0x20400000,
- 0x00404010, 0x20404000, 0x00000000, 0x20400010,
- 0x00000010, 0x00004000, 0x20400000, 0x00404010,
- 0x00004000, 0x00400010, 0x20004010, 0x00000000,
- 0x20404000, 0x20000000, 0x00400010, 0x20004010
- };
- private static int[] SP7 = {
- 0x00200000, 0x04200002, 0x04000802, 0x00000000,
- 0x00000800, 0x04000802, 0x00200802, 0x04200800,
- 0x04200802, 0x00200000, 0x00000000, 0x04000002,
- 0x00000002, 0x04000000, 0x04200002, 0x00000802,
- 0x04000800, 0x00200802, 0x00200002, 0x04000800,
- 0x04000002, 0x04200000, 0x04200800, 0x00200002,
- 0x04200000, 0x00000800, 0x00000802, 0x04200802,
- 0x00200800, 0x00000002, 0x04000000, 0x00200800,
- 0x04000000, 0x00200800, 0x00200000, 0x04000802,
- 0x04000802, 0x04200002, 0x04200002, 0x00000002,
- 0x00200002, 0x04000000, 0x04000800, 0x00200000,
- 0x04200800, 0x00000802, 0x00200802, 0x04200800,
- 0x00000802, 0x04000002, 0x04200802, 0x04200000,
- 0x00200800, 0x00000000, 0x00000002, 0x04200802,
- 0x00000000, 0x00200802, 0x04200000, 0x00000800,
- 0x04000002, 0x04000800, 0x00000800, 0x00200002
- };
- private static int[] SP8 = {
- 0x10001040, 0x00001000, 0x00040000, 0x10041040,
- 0x10000000, 0x10001040, 0x00000040, 0x10000000,
- 0x00040040, 0x10040000, 0x10041040, 0x00041000,
- 0x10041000, 0x00041040, 0x00001000, 0x00000040,
- 0x10040000, 0x10000040, 0x10001000, 0x00001040,
- 0x00041000, 0x00040040, 0x10040040, 0x10041000,
- 0x00001040, 0x00000000, 0x00000000, 0x10040040,
- 0x10000040, 0x10001000, 0x00041040, 0x00040000,
- 0x00041040, 0x00040000, 0x10041000, 0x00001000,
- 0x00000040, 0x10040040, 0x00001000, 0x00041040,
- 0x10001000, 0x00000040, 0x10000040, 0x10040000,
- 0x10040040, 0x10000000, 0x00040000, 0x10001040,
- 0x00000000, 0x10041040, 0x00040040, 0x10000040,
- 0x10040000, 0x10001000, 0x10001040, 0x00000000,
- 0x10041040, 0x00041000, 0x00041000, 0x00001040,
- 0x00001040, 0x00040040, 0x10000000, 0x10041000
- };
-
- // Routines taken from other parts of the Acme utilities.
-
- /// Squash bytes down to ints.
- public static void squashBytesToInts( byte[] inBytes, int inOff, int[] outInts, int outOff, int intLen )
- {
- for ( int i = 0; i < intLen; ++i )
- outInts[outOff + i] =
- ( ( inBytes[inOff + i * 4 ] & 0xff ) << 24 ) |
- ( ( inBytes[inOff + i * 4 + 1] & 0xff ) << 16 ) |
- ( ( inBytes[inOff + i * 4 + 2] & 0xff ) << 8 ) |
- ( inBytes[inOff + i * 4 + 3] & 0xff );
- }
-
- /// Spread ints into bytes.
- public static void spreadIntsToBytes( int[] inInts, int inOff, byte[] outBytes, int outOff, int intLen )
- {
- for ( int i = 0; i < intLen; ++i )
- {
- outBytes[outOff + i * 4 ] = (byte) ( inInts[inOff + i] >>> 24 );
- outBytes[outOff + i * 4 + 1] = (byte) ( inInts[inOff + i] >>> 16 );
- outBytes[outOff + i * 4 + 2] = (byte) ( inInts[inOff + i] >>> 8 );
- outBytes[outOff + i * 4 + 3] = (byte) inInts[inOff + i];
- }
- }
- }
diff --git a/java/src/com/tigervnc/vncviewer/DesktopWindow.java b/java/src/com/tigervnc/vncviewer/DesktopWindow.java
new file mode 100644
index 0000000..4fd80d3
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/DesktopWindow.java
@@ -0,0 +1,480 @@
+/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+//
+// DesktopWindow is an AWT Canvas representing a VNC desktop.
+//
+// Methods on DesktopWindow are called from both the GUI thread and the thread
+// which processes incoming RFB messages ("the RFB thread"). This means we
+// need to be careful with synchronization here.
+//
+
+package com.tigervnc.vncviewer;
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import javax.swing.*;
+
+import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Cursor;
+import com.tigervnc.rfb.Exception;
+import com.tigervnc.rfb.Point;
+
+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, CConn cc_) {
+ cc = cc_;
+ setSize(width, height);
+ im = new PixelBufferImage(width, height, cc, this);
+
+ cursor = new Cursor();
+ cursorBacking = new ManagedPixelBuffer();
+ addMouseListener(this);
+ addMouseWheelListener(this);
+ addMouseMotionListener(this);
+ addKeyListener(this);
+ addFocusListener(new FocusAdapter() {
+ public void focusGained(FocusEvent e) {
+ checkClipboard();
+ }
+ });
+ setFocusTraversalKeysEnabled(false);
+ setFocusable(true);
+ setDoubleBuffered(true);
+ }
+
+ public int width() {
+ return getWidth();
+ }
+
+ public int height() {
+ return getHeight();
+ }
+
+ // initGraphics() is needed because for some reason you can't call
+ // getGraphics() on a newly-created awt Component. It is called when the
+ // DesktopWindow has actually been made visible so that getGraphics() ought
+ // to work.
+
+ public void initGraphics() {
+ cc.viewport.g = cc.viewport.getGraphics();
+ graphics = getComponentGraphics(cc.viewport.g);
+ prepareImage(im.image, -1, -1, this);
+ }
+
+ final public PixelFormat getPF() { return im.getPF(); }
+
+ synchronized public void setPF(PixelFormat pf) {
+ im.setPF(pf);
+ }
+
+ // Methods called from the RFB thread - these need to be synchronized
+ // wherever they access data shared with the GUI thread.
+
+ public void setCursor(int w, int h, Point hotspot,
+ int[] data, byte[] mask) {
+ // strictly we should use a mutex around this test since useLocalCursor
+ // 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(this) {
+ if (!cc.viewer.useLocalCursor.getValue()) return;
+
+ hideLocalCursor();
+
+ cursor.hotspot = hotspot;
+
+ Dimension bsc = tk.getBestCursorSize(w, h);
+
+ cursor.setSize(((int)bsc.getWidth() > w ? (int)bsc.getWidth() : w),
+ ((int)bsc.getHeight() > h ? (int)bsc.getHeight() : h));
+ cursor.setPF(getPF());
+
+ cursorBacking.setSize(cursor.width(), cursor.height());
+ cursorBacking.setPF(getPF());
+
+ cursor.data = new int[cursor.width() * cursor.height()];
+ cursor.mask = new byte[cursor.maskLen()];
+
+ // set the masked pixels of the cursor transparent by using an extra bit in
+ // the colormap. We'll OR this into the data based on the values in the mask.
+ if (cursor.getPF().bpp == 8) {
+ cursor.cm = new DirectColorModel(9, 7, (7 << 3), (3 << 6), (1 << 8));
+ }
+
+ int maskBytesPerRow = (w + 7) / 8;
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ int byte_ = y * maskBytesPerRow + x / 8;
+ int bit = 7 - x % 8;
+ if ((mask[byte_] & (1 << bit)) > 0) {
+ cursor.data[y * cursor.width() + x] = (cursor.getPF().bpp == 8) ?
+ data[y * w + x] | (1 << 8) : data[y * w + x];
+ }
+ }
+ System.arraycopy(mask, y * maskBytesPerRow, cursor.mask,
+ y * ((cursor.width() + 7) / 8), maskBytesPerRow);
+ }
+
+ MemoryImageSource bitmap =
+ new MemoryImageSource(cursor.width(), cursor.height(), cursor.cm,
+ cursor.data, 0, cursor.width());
+ softCursor =
+ tk.createCustomCursor(tk.createImage(bitmap), new java.awt.Point(hotspot.x,hotspot.y), "Cursor");
+ }
+
+ if (softCursor != null) {
+ setCursor(softCursor);
+ cursorAvailable = true;
+ return;
+ }
+
+ if (!cursorAvailable) {
+ cursorAvailable = true;
+ }
+
+ showLocalCursor();
+ return;
+ }
+
+ // setColourMapEntries() changes some of the entries in the colourmap.
+ // Unfortunately these messages are often sent one at a time, so we delay the
+ // settings taking effect unless the whole colourmap has changed. This is
+ // because getting java to recalculate its internal translation table and
+ // redraw the screen is expensive.
+
+ synchronized public void setColourMapEntries(int firstColour, int nColours,
+ int[] rgbs) {
+ im.setColourMapEntries(firstColour, nColours, rgbs);
+ if (nColours <= 256) {
+ im.updateColourMap();
+ im.put(0, 0, im.width(), im.height(), graphics);
+ } else {
+ if (setColourMapEntriesTimerThread == null) {
+ setColourMapEntriesTimerThread = new Thread(this);
+ setColourMapEntriesTimerThread.start();
+ }
+ }
+ }
+
+// Update the actual window with the changed parts of the framebuffer.
+
+ public void framebufferUpdateEnd()
+ {
+ drawInvalidRect();
+ }
+
+ // resize() is called when the desktop has changed size
+ synchronized public void resize() {
+ vlog.debug("DesktopWindow.resize() called");
+ int w = cc.cp.width;
+ int h = cc.cp.height;
+ hideLocalCursor();
+ setSize(w, h);
+ im.resize(w, h);
+ }
+
+ final void drawInvalidRect() {
+ if (!invalidRect) return;
+ int x = invalidLeft;
+ int w = invalidRight - x;
+ int y = invalidTop;
+ int h = invalidBottom - y;
+ invalidRect = false;
+
+ synchronized (this) {
+ im.put(x, y, w, h, graphics);
+ }
+ }
+
+ final void invalidate(int x, int y, int w, int h) {
+ if (invalidRect) {
+ if (x < invalidLeft) invalidLeft = x;
+ if (x + w > invalidRight) invalidRight = x + w;
+ if (y < invalidTop) invalidTop = y;
+ if (y + h > invalidBottom) invalidBottom = y + h;
+ } else {
+ invalidLeft = x;
+ invalidRight = x + w;
+ invalidTop = y;
+ invalidBottom = y + h;
+ invalidRect = true;
+ }
+
+ if ((invalidRight - invalidLeft) * (invalidBottom - invalidTop) > 100000)
+ drawInvalidRect();
+ }
+
+ public void beginRect(int x, int y, int w, int h, int encoding) {
+ invalidRect = false;
+ }
+
+ public void endRect(int x, int y, int w, int h, int encoding) {
+ drawInvalidRect();
+ }
+
+ synchronized final public void fillRect(int x, int y, int w, int h, int pix)
+ {
+ if (overlapsCursor(x, y, w, h)) hideLocalCursor();
+ im.fillRect(x, y, w, h, pix);
+ invalidate(x, y, w, h);
+ if (softCursor == null)
+ showLocalCursor();
+ }
+
+ synchronized final public void imageRect(int x, int y, int w, int h,
+ int[] pix) {
+ if (overlapsCursor(x, y, w, h)) hideLocalCursor();
+ im.imageRect(x, y, w, h, pix);
+ invalidate(x, y, w, h);
+ if (softCursor == null)
+ showLocalCursor();
+ }
+
+ synchronized final public void copyRect(int x, int y, int w, int h,
+ int srcX, int srcY) {
+ if (overlapsCursor(x, y, w, h) || overlapsCursor(srcX, srcY, w, h))
+ hideLocalCursor();
+ im.copyRect(x, y, w, h, srcX, srcY);
+ if (!cc.viewer.fastCopyRect.getValue()) {
+ invalidate(x, y, w, h);
+ }
+ }
+
+
+ // mutex MUST be held when overlapsCursor() is called
+ final boolean overlapsCursor(int x, int y, int w, int h) {
+ return (x < cursorBackingX + cursorBacking.width() &&
+ y < cursorBackingY + cursorBacking.height() &&
+ x+w > cursorBackingX && y+h > cursorBackingY);
+ }
+
+
+ ////////////////////////////////////////////////////////////////////
+ // The following methods are all called from the GUI thread
+
+ synchronized void resetLocalCursor() {
+ hideLocalCursor();
+ cursorAvailable = false;
+ }
+
+ synchronized public Dimension getPreferredSize() {
+ return new Dimension(im.width(), im.height());
+ }
+
+ synchronized public Dimension getMinimumSize() {
+ return new Dimension(im.width(), im.height());
+ }
+
+ public void update(Graphics g) {
+ //repaint();
+ }
+
+ synchronized public void paintComponent(Graphics g) {
+ Graphics2D g2 = (Graphics2D) g;
+ g2.drawImage(im.image, 0, 0, this);
+ }
+
+
+ String oldContents = "";
+
+ synchronized public void checkClipboard() {
+ if (ClipboardDialog.systemClipboard != null &&
+ cc.viewer.sendClipboard.getValue()) {
+ Transferable t = ClipboardDialog.systemClipboard.getContents(this);
+ if ((t != null) && t.isDataFlavorSupported(DataFlavor.stringFlavor)) {
+ try {
+ String newContents = (String) t.getTransferData(DataFlavor.stringFlavor);
+ if (newContents != null && !newContents.equals(oldContents)) {
+ cc.writeClientCutText(newContents, newContents.length());
+ oldContents = newContents;
+ cc.clipboardDialog.setContents(newContents);
+ }
+ } catch (java.lang.Exception e) {
+ System.out.println("Exception getting clipboard data: " + e.getMessage());
+ }
+ }
+ }
+ }
+
+ /** Mouse-Motion callback function */
+ private void mouseMotionCB(MouseEvent e) {
+ if (!cc.viewer.viewOnly.getValue())
+ cc.writePointerEvent(e);
+ // - If local cursor rendering is enabled then use it
+ synchronized(this) {
+ if (cursorAvailable) {
+ // - Render the cursor!
+ if (e.getX() != cursorPosX || e.getY() != cursorPosY) {
+ hideLocalCursor();
+ if (e.getX() >= 0 && e.getX() < im.width() &&
+ e.getY() >= 0 && e.getY() < im.height()) {
+ cursorPosX = e.getX();
+ cursorPosY = e.getY();
+ if (softCursor == null)
+ showLocalCursor();
+ }
+ }
+ }
+ }
+ lastX = e.getX();
+ lastY = e.getY();
+ }
+ public void mouseDragged(MouseEvent e) { mouseMotionCB(e);}
+ public void mouseMoved(MouseEvent e) { mouseMotionCB(e);}
+
+ /** Mouse callback function */
+ private void mouseCB(MouseEvent e) {
+ if (!cc.viewer.viewOnly.getValue())
+ cc.writePointerEvent(e);
+ lastX = e.getX();
+ lastY = e.getY();
+ }
+ public void mouseReleased(MouseEvent e){ mouseCB(e);}
+ public void mousePressed(MouseEvent e) { mouseCB(e);}
+ public void mouseClicked(MouseEvent e){}
+ public void mouseEntered(MouseEvent e){}
+ public void mouseExited(MouseEvent e){}
+
+ /** MouseWheel callback function */
+ private void mouseWheelCB(MouseWheelEvent e) {
+ if (!cc.viewer.viewOnly.getValue())
+ cc.writeWheelEvent(e);
+ }
+ public void mouseWheelMoved(MouseWheelEvent e){
+ mouseWheelCB(e);
+ }
+
+ /** Handle the key-typed event. */
+ public void keyTyped(KeyEvent e) {}
+ /** Handle the key-released event. */
+ public void keyReleased(KeyEvent e) {}
+ /** Handle the key-pressed event. */
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyCode() ==
+ (KeyEvent.VK_F1+cc.menuKey-Keysyms.F1)) {
+ cc.showMenu(lastX, lastY);
+ return;
+ }
+ if (!cc.viewer.viewOnly.getValue())
+ cc.writeKeyEvent(e);
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // The following methods are called from both RFB and GUI threads
+
+ // Note that mutex MUST be held when hideLocalCursor() and showLocalCursor()
+ // are called.
+
+ private void hideLocalCursor() {
+ // - Blit the cursor backing store over the cursor
+ if (cursorVisible) {
+ cursorVisible = false;
+ im.imageRect(cursorBackingX, cursorBackingY, cursorBacking.width(),
+ cursorBacking.height(), cursorBacking.data);
+ im.put(cursorBackingX, cursorBackingY, cursorBacking.width(),
+ cursorBacking.height(), graphics);
+ }
+ }
+
+ private void showLocalCursor() {
+ if (cursorAvailable && !cursorVisible) {
+ if (!im.getPF().equal(cursor.getPF()) ||
+ cursor.width() == 0 || cursor.height() == 0) {
+ vlog.debug("attempting to render invalid local cursor");
+ cursorAvailable = false;
+ return;
+ }
+ cursorVisible = true;
+ if (softCursor != null) return;
+
+ int cursorLeft = (int)cursor.hotspot.x;
+ int cursorTop = (int)cursor.hotspot.y;
+ int cursorRight = cursorLeft + cursor.width();
+ int cursorBottom = cursorTop + cursor.height();
+
+ int x = (cursorLeft >= 0 ? cursorLeft : 0);
+ int y = (cursorTop >= 0 ? cursorTop : 0);
+ int w = ((cursorRight < im.width() ? cursorRight : im.width()) - x);
+ int h = ((cursorBottom < im.height() ? cursorBottom : im.height()) - y);
+
+ cursorBackingX = x;
+ cursorBackingY = y;
+ cursorBacking.setSize(w, h);
+
+ for (int j = 0; j < h; j++)
+ System.arraycopy(im.data, (y+j) * im.width() + x,
+ cursorBacking.data, j*w, w);
+
+ im.maskRect(cursorLeft, cursorTop, cursor.width(), cursor.height(),
+ cursor.data, cursor.mask);
+ im.put(x, y, w, h, graphics);
+ }
+ }
+
+
+ // run() is executed by the setColourMapEntriesTimerThread - it sleeps for
+ // 100ms before actually updating the colourmap.
+ public void run() {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {}
+ synchronized (this) {
+ im.updateColourMap();
+ im.put(0, 0, im.width(), im.height(), graphics);
+ setColourMapEntriesTimerThread = null;
+ }
+ }
+
+ // access to cc by different threads is specified in CConn
+ CConn cc;
+
+ // access to the following must be synchronized:
+ PixelBufferImage im;
+ Graphics graphics;
+ Thread setColourMapEntriesTimerThread;
+
+ Cursor cursor;
+ boolean cursorVisible; // Is cursor currently rendered?
+ boolean cursorAvailable; // Is cursor available for rendering?
+ int cursorPosX, cursorPosY;
+ ManagedPixelBuffer cursorBacking;
+ int cursorBackingX, cursorBackingY;
+ java.awt.Cursor softCursor;
+ static Toolkit tk = Toolkit.getDefaultToolkit();
+
+ // the following are only ever accessed by the RFB thread:
+ boolean invalidRect;
+ int invalidLeft, invalidRight, invalidTop, invalidBottom;
+
+ // the following are only ever accessed by the GUI thread:
+ int lastX, lastY;
+
+ static LogWriter vlog = new LogWriter("DesktopWindow");
+}
diff --git a/java/src/com/tigervnc/vncviewer/Dialog.java b/java/src/com/tigervnc/vncviewer/Dialog.java
index 9572b9f..6a11fb3 100644
--- a/java/src/com/tigervnc/vncviewer/Dialog.java
+++ b/java/src/com/tigervnc/vncviewer/Dialog.java
@@ -1,5 +1,4 @@
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright (C) 2010 TigerVNC Team
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,6 +15,7 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
+
//
// This Dialog class implements a pop-up dialog. This is needed because
// apparently you can't use the standard AWT Dialog from within an applet. The
@@ -27,11 +27,24 @@
package com.tigervnc.vncviewer;
+import java.io.*;
+import java.net.*;
import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import javax.swing.*;
+import javax.swing.filechooser.*;
-class Dialog extends Frame {
+//class Dialog extends JFrame implements WindowListener {
+class Dialog extends JFrame {
- public Dialog(boolean modal_) { modal = modal_; }
+ protected boolean ok, done;
+ boolean modal;
+
+ public Dialog(boolean modal_) {
+ modal = modal_;
+ //addWindowListener(this);
+ }
public boolean showDialog() {
ok = false;
@@ -42,7 +55,14 @@
int x = (dpySize.width - mySize.width) / 2;
int y = (dpySize.height - mySize.height) / 2;
setLocation(x, y);
- show();
+ ClassLoader cl = this.getClass().getClassLoader();
+ ImageIcon icon = new ImageIcon(cl.getResource("com/tigervnc/vncviewer/tigervnc.ico"));
+ setIconImage(icon.getImage());
+ //setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ //setFont(new Font("SansSerif", Font.PLAIN, 11));
+
+ setVisible(true);
+ setFocusable(true);
if (!modal) return true;
synchronized(this) {
try {
@@ -56,7 +76,8 @@
public void endDialog() {
done = true;
- hide();
+ setVisible(false);
+ setFocusable(false);
if (modal) {
synchronized (this) {
notify();
@@ -66,16 +87,57 @@
// initDialog() can be overridden in a derived class. Typically it is used
// to make sure that checkboxes have the right state, etc.
- public void initDialog() {}
-
- public boolean handleEvent(Event event) {
- if (event.id == Event.WINDOW_DESTROY) {
- ok = false;
- endDialog();
- }
- return super.handleEvent(event);
+ public void initDialog() {
}
- protected boolean ok, done;
- boolean modal;
+ //------------------------------------------------------------------
+ // implemented blank methods
+ //public void windowClosed(WindowEvent event){}
+ //public void windowDeiconified(WindowEvent event){}
+ //public void windowIconified(WindowEvent event){}
+ //public void windowActivated(WindowEvent event){}
+ //public void windowDeactivated(WindowEvent event){}
+ //public void windowOpened(WindowEvent event){}
+
+ //------------------------------------------------------------------
+
+ // method to check which window was closing
+ //public void windowClosing(WindowEvent event) {
+ // ok = false;
+ // endDialog();
+ //}
+
+ public void addGBComponent(JComponent c, JComponent cp,
+ int gx, int gy,
+ int gw, int gh,
+ int gipx, int gipy,
+ double gwx, double gwy,
+ int fill, int anchor,
+ Insets insets)
+ {
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.anchor = anchor;
+ gbc.fill = fill;
+ gbc.gridx = gx;
+ gbc.gridy = gy;
+ gbc.gridwidth = gw;
+ gbc.gridheight = gh;
+ gbc.insets = insets;
+ gbc.ipadx = gipx;
+ gbc.ipady = gipy;
+ gbc.weightx = gwx;
+ gbc.weighty = gwy;
+ cp.add(c, gbc);
+ }
+
+ final public String getFileSeperator() {
+ String seperator = System.getProperties().get("file.separator").toString();
+ return seperator;
+ }
+
+ final public String getUserName() {
+ String userName = (String)System.getProperties().get("user.name");
+ return userName;
+ }
+
}
diff --git a/java/src/com/tigervnc/vncviewer/F8Menu.java b/java/src/com/tigervnc/vncviewer/F8Menu.java
new file mode 100644
index 0000000..5838b38
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/F8Menu.java
@@ -0,0 +1,133 @@
+/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.vncviewer;
+
+import java.awt.Cursor;
+import java.awt.event.*;
+import javax.swing.JFrame;
+import javax.swing.JPopupMenu;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JCheckBoxMenuItem;
+
+import com.tigervnc.rfb.*;
+
+public class F8Menu extends JPopupMenu implements ActionListener {
+ public F8Menu(CConn cc_) {
+ super("VNC Menu");
+ setLightWeightPopupEnabled(false);
+ cc = cc_;
+ restore = addMenuItem("Restore",KeyEvent.VK_R);
+ move = addMenuItem("Move");
+ move.setEnabled(false);
+ size = addMenuItem("Size");
+ size.setEnabled(false);
+ minimize = addMenuItem("Minimize", KeyEvent.VK_N);
+ maximize = addMenuItem("Maximize", KeyEvent.VK_X);
+ addSeparator();
+ exit = addMenuItem("Close Viewer", KeyEvent.VK_C);
+ addSeparator();
+ fullScreen = new JCheckBoxMenuItem("Full Screen");
+ fullScreen.setMnemonic(KeyEvent.VK_F);
+ fullScreen.addActionListener(this);
+ add(fullScreen);
+ addSeparator();
+ clipboard = addMenuItem("Clipboard...");
+ addSeparator();
+ f8 = addMenuItem("Send F"+(cc.menuKey-Keysyms.F1+1));
+ ctrlAltDel = addMenuItem("Send Ctrl-Alt-Del");
+ addSeparator();
+ refresh = addMenuItem("Refresh Screen", KeyEvent.VK_H);
+ addSeparator();
+ newConn = addMenuItem("New connection...", KeyEvent.VK_W);
+ options = addMenuItem("Options...", KeyEvent.VK_O);
+ info = addMenuItem("Connection info...", KeyEvent.VK_I);
+ about = addMenuItem("About VNCviewer...", KeyEvent.VK_A);
+ addSeparator();
+ dismiss = addMenuItem("Dismiss menu");
+ setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+ }
+
+ JMenuItem addMenuItem(String str, int mnemonic) {
+ JMenuItem item = new JMenuItem(str, mnemonic);
+ item.addActionListener(this);
+ add(item);
+ return item;
+ }
+
+ JMenuItem addMenuItem(String str) {
+ JMenuItem item = new JMenuItem(str);
+ item.addActionListener(this);
+ add(item);
+ return item;
+ }
+
+ boolean actionMatch(ActionEvent ev, JMenuItem item) {
+ return ev.getActionCommand().equals(item.getActionCommand());
+ }
+
+ public void actionPerformed(ActionEvent ev) {
+ if (actionMatch(ev, exit)) {
+ cc.close();
+ } else if (actionMatch(ev, fullScreen)) {
+ cc.toggleFullScreen();
+ } else if (actionMatch(ev, restore)) {
+ if (cc.fullScreen) cc.toggleFullScreen();
+ cc.viewport.setExtendedState(JFrame.NORMAL);
+ } else if (actionMatch(ev, minimize)) {
+ if (cc.fullScreen) cc.toggleFullScreen();
+ cc.viewport.setExtendedState(JFrame.ICONIFIED);
+ } else if (actionMatch(ev, maximize)) {
+ if (cc.fullScreen) cc.toggleFullScreen();
+ cc.viewport.setExtendedState(JFrame.MAXIMIZED_BOTH);
+ } else if (actionMatch(ev, clipboard)) {
+ cc.clipboardDialog.showDialog();
+ } else if (actionMatch(ev, f8)) {
+ cc.writeKeyEvent(cc.menuKey, true);
+ cc.writeKeyEvent(cc.menuKey, false);
+ } else if (actionMatch(ev, ctrlAltDel)) {
+ cc.writeKeyEvent(Keysyms.Control_L, true);
+ cc.writeKeyEvent(Keysyms.Alt_L, true);
+ cc.writeKeyEvent(Keysyms.Delete, true);
+ cc.writeKeyEvent(Keysyms.Delete, false);
+ cc.writeKeyEvent(Keysyms.Alt_L, false);
+ cc.writeKeyEvent(Keysyms.Control_L, false);
+ } else if (actionMatch(ev, refresh)) {
+ cc.refresh();
+ } else if (actionMatch(ev, newConn)) {
+ VncViewer.newViewer(cc.viewer);
+ } else if (actionMatch(ev, options)) {
+ cc.options.showDialog();
+ } else if (actionMatch(ev, info)) {
+ cc.showInfo();
+ } else if (actionMatch(ev, about)) {
+ cc.showAbout();
+ } else if (actionMatch(ev, dismiss)) {
+ firePopupMenuCanceled();
+ }
+ }
+
+ CConn cc;
+ JMenuItem restore, move, size, minimize, maximize;
+ JMenuItem exit, clipboard, ctrlAltDel, refresh;
+ JMenuItem newConn, options, info, about, dismiss;
+ static JMenuItem f8;
+ JCheckBoxMenuItem fullScreen;
+ static LogWriter vlog = new LogWriter("F8Menu");
+}
diff --git a/java/src/com/tigervnc/vncviewer/HTTPConnectSocket.java b/java/src/com/tigervnc/vncviewer/HTTPConnectSocket.java
deleted file mode 100644
index 2a66b25..0000000
--- a/java/src/com/tigervnc/vncviewer/HTTPConnectSocket.java
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-// Copyright (C) 2002 Constantin Kaplinsky, Inc. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
-
-//
-// HTTPConnectSocket.java together with HTTPConnectSocketFactory.java
-// implement an alternate way to connect to VNC servers via one or two
-// HTTP proxies supporting the HTTP CONNECT method.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.net.*;
-import java.io.*;
-
-class HTTPConnectSocket extends Socket {
-
- public HTTPConnectSocket(String host, int port,
- String proxyHost, int proxyPort)
- throws IOException {
-
- // Connect to the specified HTTP proxy
- super(proxyHost, proxyPort);
-
- // Send the CONNECT request
- getOutputStream().write(("CONNECT " + host + ":" + port +
- " HTTP/1.0\r\n\r\n").getBytes());
-
- // Read the first line of the response
- DataInputStream is = new DataInputStream(getInputStream());
- String str = is.readLine();
-
- // Check the HTTP error code -- it should be "200" on success
- if (!str.startsWith("HTTP/1.0 200 ")) {
- if (str.startsWith("HTTP/1.0 "))
- str = str.substring(9);
- throw new IOException("Proxy reports \"" + str + "\"");
- }
-
- // Success -- skip remaining HTTP headers
- do {
- str = is.readLine();
- } while (str.length() != 0);
- }
-}
-
diff --git a/java/src/com/tigervnc/vncviewer/HTTPConnectSocketFactory.java b/java/src/com/tigervnc/vncviewer/HTTPConnectSocketFactory.java
deleted file mode 100644
index 56c8b97..0000000
--- a/java/src/com/tigervnc/vncviewer/HTTPConnectSocketFactory.java
+++ /dev/null
@@ -1,88 +0,0 @@
-//
-// Copyright (C) 2002 Constantin Kaplinsky, Inc. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
-
-//
-// HTTPConnectSocketFactory.java together with HTTPConnectSocket.java
-// implement an alternate way to connect to VNC servers via one or two
-// HTTP proxies supporting the HTTP CONNECT method.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.applet.*;
-import java.net.*;
-import java.io.*;
-
-class HTTPConnectSocketFactory implements SocketFactory {
-
- public Socket createSocket(String host, int port, Applet applet)
- throws IOException {
-
- return createSocket(host, port,
- applet.getParameter("PROXYHOST1"),
- applet.getParameter("PROXYPORT1"));
- }
-
- public Socket createSocket(String host, int port, String[] args)
- throws IOException {
-
- return createSocket(host, port,
- readArg(args, "PROXYHOST1"),
- readArg(args, "PROXYPORT1"));
- }
-
- public Socket createSocket(String host, int port,
- String proxyHost, String proxyPortStr)
- throws IOException {
-
- int proxyPort = 0;
- if (proxyPortStr != null) {
- try {
- proxyPort = Integer.parseInt(proxyPortStr);
- } catch (NumberFormatException e) { }
- }
-
- if (proxyHost == null || proxyPort == 0) {
- System.out.println("Incomplete parameter list for HTTPConnectSocket");
- return new Socket(host, port);
- }
-
- System.out.println("HTTP CONNECT via proxy " + proxyHost +
- " port " + proxyPort);
- HTTPConnectSocket s =
- new HTTPConnectSocket(host, port, proxyHost, proxyPort);
-
- return (Socket)s;
- }
-
- private String readArg(String[] args, String name) {
-
- for (int i = 0; i < args.length; i += 2) {
- if (args[i].equalsIgnoreCase(name)) {
- try {
- return args[i+1];
- } catch (Exception e) {
- return null;
- }
- }
- }
- return null;
- }
-}
-
diff --git a/java/src/com/tigervnc/vncviewer/InStream.java b/java/src/com/tigervnc/vncviewer/InStream.java
deleted file mode 100644
index 75ff91a..0000000
--- a/java/src/com/tigervnc/vncviewer/InStream.java
+++ /dev/null
@@ -1,179 +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-//
-// rdr::InStream marshalls data from a buffer stored in RDR (RFB Data
-// Representation).
-//
-
-package com.tigervnc.vncviewer;
-
-abstract public class InStream {
-
- // check() ensures there is buffer data for at least one item of size
- // itemSize bytes. Returns the number of items in the buffer (up to a
- // maximum of nItems).
-
- public final int check(int itemSize, int nItems) throws Exception {
- if (ptr + itemSize * nItems > end) {
- if (ptr + itemSize > end)
- return overrun(itemSize, nItems);
-
- nItems = (end - ptr) / itemSize;
- }
- return nItems;
- }
-
- public final void check(int itemSize) throws Exception {
- if (ptr + itemSize > end)
- overrun(itemSize, 1);
- }
-
- // readU/SN() methods read unsigned and signed N-bit integers.
-
- public final int readS8() throws Exception {
- check(1); return b[ptr++];
- }
-
- public final int readS16() throws Exception {
- check(2); int b0 = b[ptr++];
- int b1 = b[ptr++] & 0xff; return b0 << 8 | b1;
- }
-
- public final int readS32() throws Exception {
- check(4); int b0 = b[ptr++];
- int b1 = b[ptr++] & 0xff;
- int b2 = b[ptr++] & 0xff;
- int b3 = b[ptr++] & 0xff;
- return b0 << 24 | b1 << 16 | b2 << 8 | b3;
- }
-
- public final int readU8() throws Exception {
- return readS8() & 0xff;
- }
-
- public final int readU16() throws Exception {
- return readS16() & 0xffff;
- }
-
- public final int readU32() throws Exception {
- return readS32() & 0xffffffff;
- }
-
- // readString() reads a string - a U32 length followed by the data.
-
- public final String readString() throws Exception {
- int len = readU32();
- if (len > maxStringLength)
- throw new Exception("InStream max string length exceeded");
-
- char[] str = new char[len];
- int i = 0;
- while (i < len) {
- int j = i + check(1, len - i);
- while (i < j) {
- str[i++] = (char)b[ptr++];
- }
- }
-
- return new String(str);
- }
-
- // maxStringLength protects against allocating a huge buffer. Set it
- // higher if you need longer strings.
-
- public static int maxStringLength = 65535;
-
- public final void skip(int bytes) throws Exception {
- while (bytes > 0) {
- int n = check(1, bytes);
- ptr += n;
- bytes -= n;
- }
- }
-
- // readBytes() reads an exact number of bytes into an array at an offset.
-
- public void readBytes(byte[] data, int offset, int length) throws Exception {
- int offsetEnd = offset + length;
- while (offset < offsetEnd) {
- int n = check(1, offsetEnd - offset);
- System.arraycopy(b, ptr, data, offset, n);
- ptr += n;
- offset += n;
- }
- }
-
- // readOpaqueN() reads a quantity "without byte-swapping". Because java has
- // no byte-ordering, we just use big-endian.
-
- public final int readOpaque8() throws Exception {
- return readU8();
- }
-
- public final int readOpaque16() throws Exception {
- return readU16();
- }
-
- public final int readOpaque32() throws Exception {
- return readU32();
- }
-
- public final int readOpaque24A() throws Exception {
- check(3); int b0 = b[ptr++];
- int b1 = b[ptr++]; int b2 = b[ptr++];
- return b0 << 24 | b1 << 16 | b2 << 8;
- }
-
- public final int readOpaque24B() throws Exception {
- check(3); int b0 = b[ptr++];
- int b1 = b[ptr++]; int b2 = b[ptr++];
- return b0 << 16 | b1 << 8 | b2;
- }
-
- // pos() returns the position in the stream.
-
- abstract public int pos();
-
- // bytesAvailable() returns true if at least one byte can be read from the
- // stream without blocking. i.e. if false is returned then readU8() would
- // block.
-
- public boolean bytesAvailable() { return end != ptr; }
-
- // getbuf(), getptr(), getend() and setptr() are "dirty" methods which allow
- // you to manipulate the buffer directly. This is useful for a stream which
- // is a wrapper around an underlying stream.
-
- public final byte[] getbuf() { return b; }
- public final int getptr() { return ptr; }
- public final int getend() { return end; }
- public final void setptr(int p) { ptr = p; }
-
- // overrun() is implemented by a derived class to cope with buffer overrun.
- // It ensures there are at least itemSize bytes of buffer data. Returns
- // the number of items in the buffer (up to a maximum of nItems). itemSize
- // is supposed to be "small" (a few bytes).
-
- abstract protected int overrun(int itemSize, int nItems) throws Exception;
-
- protected InStream() {}
- protected byte[] b;
- protected int ptr;
- protected int end;
-}
diff --git a/java/src/com/tigervnc/vncviewer/Makefile b/java/src/com/tigervnc/vncviewer/Makefile
index 39ccbb7..99c04ac 100644
--- a/java/src/com/tigervnc/vncviewer/Makefile
+++ b/java/src/com/tigervnc/vncviewer/Makefile
@@ -11,25 +11,15 @@
PAGES = index.vnc
INSTALL_DIR = /usr/local/vnc/classes
-CLASSES = VncViewer.class RfbProto.class AuthPanel.class VncCanvas.class \
- VncCanvas2.class \
- OptionsFrame.class ClipboardFrame.class ButtonPanel.class \
- DesCipher.class \
- RecordingFrame.class SessionRecorder.class \
- SocketFactory.class HTTPConnectSocketFactory.class \
- HTTPConnectSocket.class ReloginPanel.class \
- InStream.class MemInStream.class ZlibInStream.class \
- TLSTunnelBase.class TLSTunnel.class X509Tunnel.class Dialog.class MessageBox.class
+CLASSES = CConn.class OptionsDialogCallback.class ClipboardDialog.class \
+ PasswdDialog.class DesktopWindow.class PixelBufferImage.class \
+ Dialog.class ServerDialog.class F8Menu.class UserPrefs.class \
+ OptionsDialog.class VncViewer.class
-SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \
- VncCanvas2.java \
- OptionsFrame.java ClipboardFrame.java ButtonPanel.java \
- DesCipher.java \
- RecordingFrame.java SessionRecorder.java \
- SocketFactory.java HTTPConnectSocketFactory.java \
- HTTPConnectSocket.java ReloginPanel.java \
- InStream.java MemInStream.java ZlibInStream.java \
- TLSTunnelBase.java TLSTunnel.java X509Tunnel.java Dialog.java MessageBox.java
+SOURCES = CConn.java OptionsDialogCallback.java ClipboardDialog.java \
+ PasswdDialog.java DesktopWindow.java PixelBufferImage.java \
+ Dialog.java ServerDialog.java F8Menu.java UserPrefs.java \
+ OptionsDialog.java VncViewer.java
all: $(CLASSES) $(ARCHIVE)
@@ -41,8 +31,10 @@
$(JAR) cfm com/tigervnc/vncviewer/$(ARCHIVE) \
com/tigervnc/vncviewer/$(MANIFEST) \
com/tigervnc/vncviewer/*.class \
- com/tigervnc/decoder/*.class \
- com/tigervnc/decoder/common/*.class
+ com/tigervnc/rfb/*.class \
+ com/tigervnc/rdr/*.class \
+ com/tigervnc/vncviewer/tigervnc.png \
+ com/tigervnc/vncviewer/tigervnc.ico
install: $(CLASSES) $(ARCHIVE)
$(CP) $(CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR)
@@ -51,4 +43,4 @@
@$(ExportJavaClasses)
clean::
- $(RM) *.class *.jar ../decoder/*.class ../decoder/common/*.class
+ $(RM) *.class *.jar ../rfb/*.class ../rdr/*.class
diff --git a/java/src/com/tigervnc/vncviewer/MessageBox.java b/java/src/com/tigervnc/vncviewer/MessageBox.java
deleted file mode 100644
index feac5ce..0000000
--- a/java/src/com/tigervnc/vncviewer/MessageBox.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright (C) 2010 TigerVNC Team
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-package com.tigervnc.vncviewer;
-
-import java.awt.*;
-
-public class MessageBox extends com.tigervnc.vncviewer.Dialog {
-
- public static final int MB_OK = 0;
- public static final int MB_OKAYCANCEL = 1;
- public static final int MB_YESNO = 2;
-
- public MessageBox(String msg, int flags) {
- super(true);
- GridLayout g = new GridLayout(0,1);
- setLayout(g);
- while (true) {
- int i = msg.indexOf('\n');
- int j = (i==-1) ? msg.length() : i;
- add(new Label(msg.substring(0, j)));
- if (i==-1) break;
- msg = msg.substring(j+1);
- }
- Panel p2 = new Panel();
- switch (flags & 3) {
- case MB_OKAYCANCEL:
- cancelButton = new Button("Cancel");
- // No break
- case MB_OK:
- okButton = new Button("OK");
- break;
- case MB_YESNO:
- okButton = new Button("Yes");
- cancelButton = new Button("No");
- break;
- }
- if (okButton != null) p2.add(okButton);
- if (cancelButton != null) p2.add(cancelButton);
- add("South", p2);
- pack();
- showDialog();
- }
-
- public MessageBox(String msg) {
- this(msg, MB_OK);
- }
-
-
- public boolean action(Event event, Object arg) {
- if (event.target == okButton) {
- ok = true;
- endDialog();
- } else if (event.target == cancelButton) {
- ok = false;
- endDialog();
- }
- return true;
- }
-
- Button okButton, cancelButton;
-
- public boolean result() {
- return ok;
- }
-
-}
diff --git a/java/src/com/tigervnc/vncviewer/OptionsDialog.java b/java/src/com/tigervnc/vncviewer/OptionsDialog.java
new file mode 100644
index 0000000..7e1828e
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/OptionsDialog.java
@@ -0,0 +1,380 @@
+/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.vncviewer;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.filechooser.*;
+import javax.swing.ImageIcon;
+import java.net.URL;
+import java.io.IOException;
+
+import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Exception;
+
+class OptionsDialog extends Dialog implements
+ ActionListener,
+ ItemListener
+{
+
+ // Constants
+ // Static variables
+ static LogWriter vlog = new LogWriter("OptionsDialog");
+
+ OptionsDialogCallback cb;
+ JPanel FormatPanel, InputsPanel, MiscPanel, DefaultsPanel, SecPanel;
+ JCheckBox autoSelect, customCompressLevel, noJpeg;
+ JComboBox menuKey, compressLevel, qualityLevel ;
+ ButtonGroup encodingGroup, colourGroup;
+ JRadioButton zrle, hextile, tight, raw;
+ JRadioButton fullColour, mediumColour, lowColour, veryLowColour;
+ JCheckBox viewOnly, acceptClipboard, sendClipboard;
+ JCheckBox fullScreen, shared, useLocalCursor, fastCopyRect;
+ JCheckBox secVeNCrypt, encNone, encTLS, encX509;
+ JCheckBox secNone, secVnc, secPlain, secManaged, sendLocalUsername;
+ JButton okButton, cancelButton;
+ JButton ca, crl;
+ JButton defSaveButton;
+ boolean encryption = true;
+ UserPrefs defaults;
+
+ public OptionsDialog(OptionsDialogCallback cb_) {
+ super(false);
+ cb = cb_;
+ setResizable(false);
+ setTitle("VNC Viewer Options");
+ defaults = new UserPrefs("vncviewer");
+
+ getContentPane().setLayout(
+ new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS));
+
+ JTabbedPane tabPane = new JTabbedPane();
+
+ ButtonGroup encodingGroup = new ButtonGroup();
+ ButtonGroup colourGroup = new ButtonGroup();
+
+ // Colour & Encoding tab
+ FormatPanel=new JPanel(new GridBagLayout());
+
+ autoSelect = new JCheckBox("Auto Select");
+ autoSelect.addItemListener(this);
+
+ JPanel encodingPanel = new JPanel(new GridBagLayout());
+ encodingPanel.setBorder(BorderFactory.createTitledBorder("Preferred encoding"));
+ zrle = addRadioCheckbox("ZRLE", encodingGroup, encodingPanel);
+ hextile = addRadioCheckbox("Hextile", encodingGroup, encodingPanel);
+ tight = addRadioCheckbox("Tight", encodingGroup, encodingPanel);
+ raw = addRadioCheckbox("Raw", encodingGroup, encodingPanel);
+
+ JPanel tightPanel = new JPanel(new GridBagLayout());
+ customCompressLevel = new JCheckBox("Custom Compression Level");
+ customCompressLevel.addItemListener(this);
+ String[] compressionLevels = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+ compressLevel = new JComboBox(compressionLevels);
+ JLabel compressionLabel = new JLabel("Level (1=fast, 9=best)");
+ noJpeg = new JCheckBox("Allow JPEG Compression");
+ noJpeg.addItemListener(this);
+ String[] qualityLevels = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+ qualityLevel = new JComboBox(qualityLevels);
+ JLabel qualityLabel = new JLabel("Level (1=poor, 9=best)");
+ addGBComponent(customCompressLevel, tightPanel, 0, 0, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,0));
+ addGBComponent(compressLevel, tightPanel, 0, 1, 1, 1, 2, 2, 0, 0, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(0,20,0,0));
+ addGBComponent(compressionLabel, tightPanel, 1, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,5,0,0));
+ addGBComponent(noJpeg, tightPanel, 0, 2, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,0));
+ addGBComponent(qualityLevel, tightPanel, 0, 3, 1, 1, 2, 2, 0, 0, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(0,20,0,0));
+ addGBComponent(qualityLabel, tightPanel, 1, 3, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,5,0,0));
+
+
+ JPanel colourPanel = new JPanel(new GridBagLayout());
+ colourPanel.setBorder(BorderFactory.createTitledBorder("Colour level"));
+ fullColour = addRadioCheckbox("Full (all available colours)", colourGroup, colourPanel);
+ mediumColour = addRadioCheckbox("Medium (256 colours)", colourGroup, colourPanel);
+ lowColour = addRadioCheckbox("Low (64 colours)", colourGroup, colourPanel);
+ veryLowColour = addRadioCheckbox("Very low(8 colours)", colourGroup, colourPanel);
+
+ addGBComponent(autoSelect,FormatPanel, 0, 0, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,0));
+ addGBComponent(encodingPanel,FormatPanel, 0, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,2,0,0));
+ addGBComponent(colourPanel,FormatPanel, 1, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_END, new Insets(0,2,0,0));
+ addGBComponent(tightPanel,FormatPanel, 0, 2, 2, GridBagConstraints.REMAINDER, 2, 2, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,0));
+
+ // Inputs tab
+ InputsPanel=new JPanel(new GridBagLayout());
+
+ viewOnly = new JCheckBox("View Only (ignore mouse & keyboard)");
+ viewOnly.addItemListener(this);
+ acceptClipboard = new JCheckBox("Accept clipboard from server");
+ acceptClipboard.addItemListener(this);
+ sendClipboard = new JCheckBox("Send clipboard to server");
+ sendClipboard.addItemListener(this);
+ JLabel menuKeyLabel = new JLabel("Menu Key");
+ String[] menuKeys =
+ { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12" };
+ menuKey = new JComboBox(menuKeys);
+ menuKey.addItemListener(this);
+ addGBComponent(viewOnly,InputsPanel, 0, 0, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));
+ addGBComponent(acceptClipboard,InputsPanel, 0, 1, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));
+ addGBComponent(sendClipboard,InputsPanel, 0, 2, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));
+ addGBComponent(menuKeyLabel,InputsPanel, 0, 3, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(8,10,0,4));
+ addGBComponent(menuKey,InputsPanel, 1, 3, 1, GridBagConstraints.REMAINDER, 0, 0, 2, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(4,4,0,125));
+ //((javax.swing.plaf.basic.BasicComboBoxRenderer)menuKey.getRenderer()).setBorder(new EmptyBorder(0,3,0,3));
+
+ // Misc tab
+ MiscPanel=new JPanel(new GridBagLayout());
+
+ fullScreen = new JCheckBox("Full-screen mode");
+ fullScreen.addItemListener(this);
+ shared = new JCheckBox("Shared connection (do not disconnect other viewers)");
+ shared.addItemListener(this);
+ useLocalCursor = new JCheckBox("Render cursor locally");
+ useLocalCursor.addItemListener(this);
+ fastCopyRect = new JCheckBox("Fast CopyRect");
+ fastCopyRect.addItemListener(this);
+ addGBComponent(fullScreen,MiscPanel, 0, 0, 1, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));
+ addGBComponent(shared,MiscPanel, 0, 1, 1, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));
+ addGBComponent(useLocalCursor,MiscPanel, 0, 2, 1, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));
+ addGBComponent(fastCopyRect,MiscPanel, 0, 3, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(4,4,0,4));
+
+ // load/save tab
+ DefaultsPanel=new JPanel(new GridBagLayout());
+
+ JPanel configPanel = new JPanel(new GridBagLayout());
+ configPanel.setBorder(BorderFactory.createTitledBorder("Configuration File"));
+ JButton cfReloadButton = new JButton("Reload");
+ cfReloadButton.addActionListener(this);
+ addGBComponent(cfReloadButton,configPanel, 0, 0, 1, 1, 0, 0, 0, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));
+ JButton cfSaveButton = new JButton("Save");
+ cfSaveButton.addActionListener(this);
+ addGBComponent(cfSaveButton,configPanel, 0, 1, 1, 1, 0, 0, 0, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));
+ JButton cfSaveAsButton = new JButton("Save As...");
+ cfSaveAsButton.addActionListener(this);
+ addGBComponent(cfSaveAsButton,configPanel, 0, 2, 1, 1, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));
+ cfReloadButton.setEnabled(false);
+ cfSaveButton.setEnabled(false);
+
+ JPanel defaultsPanel = new JPanel(new GridBagLayout());
+ defaultsPanel.setBorder(BorderFactory.createTitledBorder("Defaults"));
+ JButton defReloadButton = new JButton("Reload");
+ defReloadButton.addActionListener(this);
+ addGBComponent(defReloadButton,defaultsPanel, 0, 0, 1, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));
+ defSaveButton = new JButton("Save");
+ defSaveButton.addActionListener(this);
+ addGBComponent(defSaveButton,defaultsPanel, 0, 1, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));
+
+ addGBComponent(configPanel,DefaultsPanel, 0, 0, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.PAGE_START, new Insets(4,4,4,4));
+ addGBComponent(defaultsPanel,DefaultsPanel, 1, 0, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.PAGE_START, new Insets(4,4,4,4));
+
+ // security tab
+ SecPanel=new JPanel(new GridBagLayout());
+
+ JPanel encryptionPanel = new JPanel(new GridBagLayout());
+ encryptionPanel.setBorder(BorderFactory.createTitledBorder("Session Encryption"));
+ encNone = addCheckbox("None", null, encryptionPanel);
+ encTLS = addCheckbox("Anonymous TLS", null, encryptionPanel);
+ encX509 = addJCheckBox("TLS with X.509 certificates", null, encryptionPanel, new GridBagConstraints(0,2,1,1,1,1,GridBagConstraints.LINE_START,GridBagConstraints.REMAINDER,new Insets(0,0,0,60),0,0));
+
+ JPanel x509Panel = new JPanel(new GridBagLayout());
+ x509Panel.setBorder(BorderFactory.createTitledBorder("X.509 certificates"));
+ ca = new JButton("Load CA certificate");
+ ca.setPreferredSize(new Dimension(145,25));
+ ca.addActionListener(this);
+ crl = new JButton("Load CRL certificate");
+ crl.setPreferredSize(new Dimension(145,25));
+ crl.addActionListener(this);
+ addGBComponent(ca, x509Panel, 0, 0, 1, 1, 2, 2, 0, 1, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(2,2,2,2));
+ addGBComponent(crl, x509Panel, 1, 0, 1, 1, 2, 2, 1, 1, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(2,2,2,2));
+
+ JPanel authPanel = new JPanel(new GridBagLayout());
+ authPanel.setBorder(BorderFactory.createTitledBorder("Authentication"));
+ secNone = addCheckbox("None", null, authPanel);
+ secVnc = addCheckbox("Standard VNC", null, authPanel);
+ secPlain = addJCheckBox("Plaintext", null, authPanel, new GridBagConstraints(0,2,1,1,1,1,GridBagConstraints.LINE_START,GridBagConstraints.NONE,new Insets(0,0,0,5),0,0));
+ secManaged = addJCheckBox("Managed", null, authPanel, new GridBagConstraints(0,3,1,1,1,1,GridBagConstraints.LINE_START,GridBagConstraints.NONE,new Insets(0,0,0,5),0,0));
+ sendLocalUsername = new JCheckBox("Send Local Username");
+ sendLocalUsername.addItemListener(this);
+ addGBComponent(sendLocalUsername, authPanel, 1, 2, 1, 2, 0, 0, 2, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,20,0,0));
+
+ secVeNCrypt = new JCheckBox("Extended encryption and authentication methods (VeNCrypt)");
+ secVeNCrypt.addItemListener(this);
+ addGBComponent(secVeNCrypt,SecPanel, 0, 0, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,20));
+ addGBComponent(encryptionPanel,SecPanel, 0, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(0,4,2,4));
+ addGBComponent(x509Panel,SecPanel, 0, 2, 1, 1, 2, 2, 1, 0, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(2,4,2,4));
+ addGBComponent(authPanel,SecPanel, 0, 3, 1, 1, 2, 2, 1, 1, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(2,4,2,4));
+
+ tabPane.add(FormatPanel);
+ tabPane.add(InputsPanel);
+ tabPane.add(MiscPanel);
+ tabPane.add(DefaultsPanel);
+ tabPane.add(SecPanel);
+ tabPane.addTab("Colour & Encoding", FormatPanel);
+ tabPane.addTab("Inputs", InputsPanel);
+ tabPane.addTab("Misc", MiscPanel);
+ tabPane.addTab("Load / Save", DefaultsPanel);
+ tabPane.addTab("Security", SecPanel);
+ tabPane.setBorder(BorderFactory.createEmptyBorder(4,4,0,4));
+
+ okButton = new JButton("OK");
+ okButton.setPreferredSize(new Dimension(90,30));
+ okButton.addActionListener(this);
+ cancelButton = new JButton("Cancel");
+ cancelButton.setPreferredSize(new Dimension(90,30));
+ cancelButton.addActionListener(this);
+
+ JPanel buttonPane = new JPanel();
+ buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
+ buttonPane.setBorder(BorderFactory.createEmptyBorder(4,0,0,0));
+ buttonPane.add(Box.createHorizontalGlue());
+ buttonPane.add(okButton);
+ buttonPane.add(Box.createRigidArea(new Dimension(4,0)));
+ buttonPane.add(cancelButton);
+ buttonPane.add(Box.createRigidArea(new Dimension(4,0)));
+
+ this.getContentPane().add(tabPane);
+ this.getContentPane().add(buttonPane);
+
+ pack();
+
+ }
+
+ public void initDialog() {
+ if (cb != null) cb.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()||secManaged.isSelected()));
+ }
+
+ JRadioButton addRadioCheckbox(String str, ButtonGroup group, JPanel panel) {
+ JRadioButton c = new JRadioButton(str);
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.LINE_START;
+ gbc.gridwidth = GridBagConstraints.REMAINDER;
+ gbc.weightx = 1;
+ gbc.weighty = 1;
+ panel.add(c,gbc);
+ group.add(c);
+ c.addItemListener(this);
+ return c;
+ }
+
+ JCheckBox addCheckbox(String str, ButtonGroup group, JPanel panel) {
+ JCheckBox c = new JCheckBox(str);
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.LINE_START;
+ gbc.gridwidth = GridBagConstraints.REMAINDER;
+ gbc.weightx = 1;
+ gbc.weighty = 1;
+ panel.add(c,gbc);
+ if (group != null)
+ group.add(c);
+ c.addItemListener(this);
+ return c;
+ }
+
+ JCheckBox addJCheckBox(String str, ButtonGroup group, JPanel panel,
+ GridBagConstraints gbc) {
+ JCheckBox c = new JCheckBox(str);
+ panel.add(c,gbc);
+ if (group != null)
+ group.add(c);
+ c.addItemListener(this);
+
+ return c;
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ Object s = e.getSource();
+ if (s instanceof JButton && (JButton)s == okButton) {
+ ok = true;
+ if (cb != null) cb.getOptions();
+ endDialog();
+ } else if (s instanceof JButton && (JButton)s == cancelButton) {
+ ok = false;
+ endDialog();
+ } else if (s instanceof JButton && (JButton)s == defSaveButton) {
+ try {
+ defaults.Save();
+ } catch (java.lang.Exception x) { }
+ } else if (s instanceof JButton && (JButton)s == ca) {
+ JFileChooser fc = new JFileChooser();
+ fc.setDialogTitle("Path to X509 CA certificate");
+ int ret = fc.showOpenDialog(this);
+ if (ret == JFileChooser.APPROVE_OPTION)
+ CSecurityTLS.x509ca.setParam(fc.getSelectedFile().toString());
+ } else if (s instanceof JButton && (JButton)s == crl) {
+ JFileChooser fc = new JFileChooser();
+ fc.setDialogTitle("Path to X509 CRL file");
+ int ret = fc.showOpenDialog(this);
+ if (ret == JFileChooser.APPROVE_OPTION)
+ CSecurityTLS.x509crl.setParam(fc.getSelectedFile().toString());
+ }
+ }
+
+ public void itemStateChanged(ItemEvent e) {
+ Object s = e.getSource();
+ if (s instanceof JCheckBox && (JCheckBox)s == autoSelect) {
+ 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());
+ defaults.setPref("autoSelect",(autoSelect.isSelected()) ? "on" : "off");
+ }
+ if (s instanceof JCheckBox && (JCheckBox)s == customCompressLevel) {
+ compressLevel.setEnabled(customCompressLevel.isSelected());
+ defaults.setPref("customCompressLevel",(customCompressLevel.isSelected()) ? "on" : "off");
+ }
+ if (s instanceof JCheckBox && (JCheckBox)s == noJpeg) {
+ qualityLevel.setEnabled(noJpeg.isSelected());
+ defaults.setPref("noJpeg",(noJpeg.isSelected()) ? "on" : "off");
+ }
+ if (s instanceof JCheckBox && (JCheckBox)s == sendLocalUsername) {
+ defaults.setPref("sendLocalUsername",(sendLocalUsername.isSelected()) ? "on" : "off");
+ }
+ if (s instanceof JCheckBox && (JCheckBox)s == secVeNCrypt) {
+ encNone.setEnabled(secVeNCrypt.isSelected());
+ encTLS.setEnabled(secVeNCrypt.isSelected());
+ encX509.setEnabled(secVeNCrypt.isSelected());
+ ca.setEnabled(secVeNCrypt.isSelected());
+ crl.setEnabled(secVeNCrypt.isSelected());
+ secManaged.setEnabled(secVeNCrypt.isSelected());
+ secNone.setEnabled(secVeNCrypt.isSelected());
+ secVnc.setEnabled(secVeNCrypt.isSelected());
+ secPlain.setEnabled(secVeNCrypt.isSelected());
+ sendLocalUsername.setEnabled(secVeNCrypt.isSelected());
+ }
+ if (s instanceof JCheckBox && (JCheckBox)s == secManaged ||
+ s instanceof JCheckBox && (JCheckBox)s == secPlain) {
+ sendLocalUsername.setEnabled(secManaged.isSelected()||secPlain.isSelected());
+ }
+ }
+
+}
diff --git a/java/src/com/tigervnc/vncviewer/MemInStream.java b/java/src/com/tigervnc/vncviewer/OptionsDialogCallback.java
similarity index 70%
rename from java/src/com/tigervnc/vncviewer/MemInStream.java
rename to java/src/com/tigervnc/vncviewer/OptionsDialogCallback.java
index 41a4fc0..f6897e2 100644
--- a/java/src/com/tigervnc/vncviewer/MemInStream.java
+++ b/java/src/com/tigervnc/vncviewer/OptionsDialogCallback.java
@@ -18,17 +18,7 @@
package com.tigervnc.vncviewer;
-public class MemInStream extends InStream {
-
- public MemInStream(byte[] data, int offset, int len) {
- b = data;
- ptr = offset;
- end = offset + len;
- }
-
- public int pos() { return ptr; }
-
- protected int overrun(int itemSize, int nItems) throws Exception {
- throw new Exception("MemInStream overrun: end of stream");
- }
+public interface OptionsDialogCallback {
+ public void setOptions();
+ public void getOptions();
}
diff --git a/java/src/com/tigervnc/vncviewer/OptionsFrame.java b/java/src/com/tigervnc/vncviewer/OptionsFrame.java
deleted file mode 100644
index 573f21d..0000000
--- a/java/src/com/tigervnc/vncviewer/OptionsFrame.java
+++ /dev/null
@@ -1,441 +0,0 @@
-//
-// Copyright (C) 2001 HorizonLive.com, Inc. All Rights Reserved.
-// Copyright (C) 2001 Constantin Kaplinsky. All Rights Reserved.
-// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
-// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
-
-//
-// Options frame.
-//
-// This deals with all the options the user can play with.
-// It sets the encodings array and some booleans.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.awt.*;
-import java.awt.event.*;
-
-class OptionsFrame extends Frame
- implements WindowListener, ActionListener, ItemListener {
-
- static String[] names = {
- "Encoding",
- "Compression level",
- "JPEG image quality",
- "Cursor shape updates",
- "Use CopyRect",
- "Restricted colors",
- "Mouse buttons 2 and 3",
- "View only",
- "Scaling factor",
- "Scale remote cursor",
- "Share desktop"
- };
-
- static String[][] values = {
- { "Auto", "Raw", "RRE", "CoRRE", "Hextile", "Zlib", "Tight", "ZRLE" },
- { "Default", "1", "2", "3", "4", "5", "6", "7", "8", "9" },
- { "JPEG off", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" },
- { "Enable", "Ignore", "Disable" },
- { "Yes", "No" },
- { "Yes", "No" },
- { "Normal", "Reversed" },
- { "Yes", "No" },
- { "Auto", "1%", "5%", "10%", "20%", "25%", "50%", "75%", "100%"},
- { "No", "50%", "75%", "125%", "150%" },
- { "Yes", "No" }
- };
-
- final int
- encodingIndex = 0,
- compressLevelIndex = 1,
- jpegQualityIndex = 2,
- cursorUpdatesIndex = 3,
- useCopyRectIndex = 4,
- eightBitColorsIndex = 5,
- mouseButtonIndex = 6,
- viewOnlyIndex = 7,
- scalingFactorIndex = 8,
- scaleCursorIndex = 9,
- shareDesktopIndex = 10;
-
- Label[] labels = new Label[names.length];
- Choice[] choices = new Choice[names.length];
- Button closeButton;
- VncViewer viewer;
-
-
- //
- // The actual data which other classes look at:
- //
-
- int preferredEncoding;
- int compressLevel;
- int jpegQuality;
- boolean useCopyRect;
- boolean requestCursorUpdates;
- boolean ignoreCursorUpdates;
-
- boolean eightBitColors;
-
- boolean reverseMouseButtons2And3;
- boolean shareDesktop;
- boolean viewOnly;
- int scaleCursor;
-
- boolean autoScale;
- int scalingFactor;
-
- //
- // Constructor. Set up the labels and choices from the names and values
- // arrays.
- //
-
- OptionsFrame(VncViewer v) {
- super("TigerVNC Options");
-
- viewer = v;
-
- GridBagLayout gridbag = new GridBagLayout();
- setLayout(gridbag);
-
- GridBagConstraints gbc = new GridBagConstraints();
- gbc.fill = GridBagConstraints.BOTH;
-
- for (int i = 0; i < names.length; i++) {
- labels[i] = new Label(names[i]);
- gbc.gridwidth = 1;
- gridbag.setConstraints(labels[i],gbc);
- add(labels[i]);
-
- choices[i] = new Choice();
- gbc.gridwidth = GridBagConstraints.REMAINDER;
- gridbag.setConstraints(choices[i],gbc);
- add(choices[i]);
- choices[i].addItemListener(this);
-
- for (int j = 0; j < values[i].length; j++) {
- choices[i].addItem(values[i][j]);
- }
- }
-
- closeButton = new Button("Close");
- gbc.gridwidth = GridBagConstraints.REMAINDER;
- gridbag.setConstraints(closeButton, gbc);
- add(closeButton);
- closeButton.addActionListener(this);
-
- pack();
-
- addWindowListener(this);
-
- // Set up defaults
-
- choices[encodingIndex].select("Auto");
- choices[compressLevelIndex].select("Default");
- choices[jpegQualityIndex].select("6");
- choices[cursorUpdatesIndex].select("Enable");
- choices[useCopyRectIndex].select("Yes");
- choices[eightBitColorsIndex].select("No");
- choices[mouseButtonIndex].select("Normal");
- choices[viewOnlyIndex].select("No");
- choices[scaleCursorIndex].select("No");
- choices[shareDesktopIndex].select("Yes");
-
- // But let them be overridden by parameters
-
- for (int i = 0; i < names.length; i++) {
- String s = viewer.readParameter(names[i], false);
- if (s != null) {
- for (int j = 0; j < values[i].length; j++) {
- if (s.equalsIgnoreCase(values[i][j])) {
- choices[i].select(j);
- }
- }
- }
- }
-
- // Get scaling factor from parameters and set it
- // to gui and class member scalingFactor
-
- String s = viewer.readParameter("Scaling Factor", false);
- if (s == null) s = "100%";
- setScalingFactor(s);
- if (autoScale) {
- choices[scalingFactorIndex].select("Auto");
- } else {
- choices[scalingFactorIndex].select(s);
- }
-
- // Make the booleans and encodings array correspond to the state of the GUI
-
- setEncodings();
- setColorFormat();
- setOtherOptions();
- }
-
- //
- // Set scaling factor class member value
- //
-
- void setScalingFactor(int sf) {
- setScalingFactor(((Integer)sf).toString());
- }
-
- void setScalingFactor(String s) {
- autoScale = false;
- scalingFactor = 100;
- if (s != null) {
- if (s.equalsIgnoreCase("Auto")) {
- autoScale = true;
- } else {
- // Remove the '%' char at the end of string if present.
- if (s.charAt(s.length() - 1) == '%') {
- s = s.substring(0, s.length() - 1);
- }
- // Convert to an integer.
- try {
- scalingFactor = Integer.parseInt(s);
- }
- catch (NumberFormatException e) {
- scalingFactor = 100;
- }
- // Make sure scalingFactor is in the range of [1..1000].
- if (scalingFactor < 1) {
- scalingFactor = 1;
- } else if (scalingFactor > 1000) {
- scalingFactor = 1000;
- }
- }
- }
- }
-
-
- //
- // Disable the shareDesktop option
- //
-
- void disableShareDesktop() {
- labels[shareDesktopIndex].setEnabled(false);
- choices[shareDesktopIndex].setEnabled(false);
- }
-
- //
- // setEncodings looks at the encoding, compression level, JPEG
- // quality level, cursor shape updates and copyRect choices and sets
- // corresponding variables properly. Then it calls the VncViewer's
- // setEncodings method to send a SetEncodings message to the RFB
- // server.
- //
-
- void setEncodings() {
- useCopyRect = choices[useCopyRectIndex].getSelectedItem().equals("Yes");
-
- preferredEncoding = RfbProto.EncodingRaw;
- boolean enableCompressLevel = false;
- boolean enableQualityLevel = false;
-
- if (choices[encodingIndex].getSelectedItem().equals("RRE")) {
- preferredEncoding = RfbProto.EncodingRRE;
- } else if (choices[encodingIndex].getSelectedItem().equals("CoRRE")) {
- preferredEncoding = RfbProto.EncodingCoRRE;
- } else if (choices[encodingIndex].getSelectedItem().equals("Hextile")) {
- preferredEncoding = RfbProto.EncodingHextile;
- } else if (choices[encodingIndex].getSelectedItem().equals("ZRLE")) {
- preferredEncoding = RfbProto.EncodingZRLE;
- } else if (choices[encodingIndex].getSelectedItem().equals("Zlib")) {
- preferredEncoding = RfbProto.EncodingZlib;
- enableCompressLevel = true;
- } else if (choices[encodingIndex].getSelectedItem().equals("Tight")) {
- preferredEncoding = RfbProto.EncodingTight;
- enableCompressLevel = true;
- enableQualityLevel = !eightBitColors;
- } else if (choices[encodingIndex].getSelectedItem().equals("Auto")) {
- preferredEncoding = -1;
- enableQualityLevel = !eightBitColors;
- }
-
- // Handle compression level setting.
-
- try {
- compressLevel =
- Integer.parseInt(choices[compressLevelIndex].getSelectedItem());
- }
- catch (NumberFormatException e) {
- compressLevel = -1;
- }
- if (compressLevel < 1 || compressLevel > 9) {
- compressLevel = -1;
- }
- labels[compressLevelIndex].setEnabled(enableCompressLevel);
- choices[compressLevelIndex].setEnabled(enableCompressLevel);
-
- // Handle JPEG quality setting.
-
- try {
- jpegQuality =
- Integer.parseInt(choices[jpegQualityIndex].getSelectedItem());
- }
- catch (NumberFormatException e) {
- jpegQuality = -1;
- }
- if (jpegQuality < 0 || jpegQuality > 9) {
- jpegQuality = -1;
- }
- labels[jpegQualityIndex].setEnabled(enableQualityLevel);
- choices[jpegQualityIndex].setEnabled(enableQualityLevel);
-
- // Request cursor shape updates if necessary.
-
- requestCursorUpdates =
- !choices[cursorUpdatesIndex].getSelectedItem().equals("Disable");
-
- if (requestCursorUpdates) {
- ignoreCursorUpdates =
- choices[cursorUpdatesIndex].getSelectedItem().equals("Ignore");
- }
-
- viewer.setEncodings();
- }
-
- //
- // setColorFormat sets eightBitColors variable depending on the GUI
- // setting, causing switches between 8-bit and 24-bit colors mode if
- // necessary.
- //
-
- void setColorFormat() {
-
- eightBitColors =
- choices[eightBitColorsIndex].getSelectedItem().equals("Yes");
-
- boolean enableJPEG = !eightBitColors &&
- (choices[encodingIndex].getSelectedItem().equals("Tight") ||
- choices[encodingIndex].getSelectedItem().equals("Auto"));
-
- labels[jpegQualityIndex].setEnabled(enableJPEG);
- choices[jpegQualityIndex].setEnabled(enableJPEG);
- }
-
- //
- // setOtherOptions looks at the "other" choices (ones that do not
- // cause sending any protocol messages) and sets the boolean flags
- // appropriately.
- //
-
- void setOtherOptions() {
-
- reverseMouseButtons2And3
- = choices[mouseButtonIndex].getSelectedItem().equals("Reversed");
-
- viewOnly
- = choices[viewOnlyIndex].getSelectedItem().equals("Yes");
- if (viewer.vc != null)
- viewer.vc.enableInput(!viewOnly);
-
- shareDesktop
- = choices[shareDesktopIndex].getSelectedItem().equals("Yes");
-
- String scaleString = choices[scaleCursorIndex].getSelectedItem();
- if (scaleString.endsWith("%"))
- scaleString = scaleString.substring(0, scaleString.length() - 1);
- try {
- scaleCursor = Integer.parseInt(scaleString);
- }
- catch (NumberFormatException e) {
- scaleCursor = 0;
- }
- if (scaleCursor < 10 || scaleCursor > 500) {
- scaleCursor = 0;
- }
- if (requestCursorUpdates && !ignoreCursorUpdates && !viewOnly) {
- labels[scaleCursorIndex].setEnabled(true);
- choices[scaleCursorIndex].setEnabled(true);
- } else {
- labels[scaleCursorIndex].setEnabled(false);
- choices[scaleCursorIndex].setEnabled(false);
- }
- if (viewer.vc != null)
- viewer.vc.createSoftCursor(); // update cursor scaling
- }
-
-
- //
- // Respond to actions on Choice controls
- //
-
- public void itemStateChanged(ItemEvent evt) {
- Object source = evt.getSource();
-
- if (source == choices[encodingIndex] ||
- source == choices[compressLevelIndex] ||
- source == choices[jpegQualityIndex] ||
- source == choices[cursorUpdatesIndex] ||
- source == choices[useCopyRectIndex]) {
-
- setEncodings();
-
- if (source == choices[cursorUpdatesIndex]) {
- setOtherOptions(); // update scaleCursor state
- }
-
- } else if (source == choices[eightBitColorsIndex]) {
-
- setColorFormat();
-
- } else if (source == choices[mouseButtonIndex] ||
- source == choices[shareDesktopIndex] ||
- source == choices[viewOnlyIndex] ||
- source == choices[scaleCursorIndex]) {
-
- setOtherOptions();
-
- } else if (source == choices[scalingFactorIndex]){
- // Tell VNC canvas that scaling factor has changed
- setScalingFactor(choices[scalingFactorIndex].getSelectedItem());
- if (viewer.vc != null)
- viewer.vc.setScalingFactor(scalingFactor);
- }
- }
-
- //
- // Respond to button press
- //
-
- public void actionPerformed(ActionEvent evt) {
- if (evt.getSource() == closeButton)
- setVisible(false);
- }
-
- //
- // Respond to window events
- //
-
- public void windowClosing(WindowEvent evt) {
- setVisible(false);
- }
-
- public void windowActivated(WindowEvent evt) {}
- public void windowDeactivated(WindowEvent evt) {}
- public void windowOpened(WindowEvent evt) {}
- public void windowClosed(WindowEvent evt) {}
- public void windowIconified(WindowEvent evt) {}
- public void windowDeiconified(WindowEvent evt) {}
-}
diff --git a/java/src/com/tigervnc/vncviewer/PasswdDialog.java b/java/src/com/tigervnc/vncviewer/PasswdDialog.java
new file mode 100644
index 0000000..eaace69
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/PasswdDialog.java
@@ -0,0 +1,88 @@
+/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.vncviewer;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.net.URL;
+
+class PasswdDialog extends Dialog implements KeyListener{
+
+ public PasswdDialog(String title, boolean userDisabled, boolean passwdDisabled) {
+ super(true);
+ setResizable(false);
+ setTitle(title);
+ setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+
+ JPanel p1 = new JPanel();
+ userLabel = new JLabel("Username:");
+ p1.add(userLabel);
+ userEntry = new JTextField(30);
+ userEntry.setEnabled(!userDisabled);
+ userLabel.setEnabled(!userDisabled);
+ p1.add(userEntry);
+ userEntry.addKeyListener(this);
+
+ JPanel p2 = new JPanel();
+ passwdLabel = new JLabel("Password:");
+ passwdLabel.setPreferredSize(userLabel.getPreferredSize());
+ p2.add(passwdLabel);
+ passwdEntry = new JPasswordField(30);
+ passwdEntry.setEnabled(!passwdDisabled);
+ passwdLabel.setEnabled(!passwdDisabled);
+ p2.add(passwdEntry);
+ passwdEntry.addKeyListener(this);
+
+ getContentPane().setLayout(new BoxLayout(getContentPane(),BoxLayout.Y_AXIS));
+ getContentPane().add(p1);
+ getContentPane().add(p2);
+ pack();
+ if (userEntry.isEnabled()) {
+ userEntry.requestFocus();
+ } else {
+ passwdEntry.requestFocus();
+ }
+ }
+
+ /** Handle the key-typed event. */
+ public void keyTyped(KeyEvent event) { }
+ /** Handle the key-released event. */
+ public void keyReleased(KeyEvent event) { }
+ /** Handle the key-pressed event. */
+ public void keyPressed(KeyEvent event) {
+ Object s = event.getSource();
+ if (s instanceof JTextField && (JTextField)s == userEntry) {
+ if (event.getKeyCode() == KeyEvent.VK_ENTER) {
+ ok = true;
+ endDialog();
+ }
+ } else if (s instanceof JPasswordField && (JPasswordField)s == passwdEntry) {
+ if (event.getKeyCode() == KeyEvent.VK_ENTER) {
+ ok = true;
+ endDialog();
+ }
+ }
+ }
+
+ JLabel userLabel;
+ JTextField userEntry;
+ JLabel passwdLabel;
+ JTextField passwdEntry;
+}
diff --git a/java/src/com/tigervnc/vncviewer/PixelBufferImage.java b/java/src/com/tigervnc/vncviewer/PixelBufferImage.java
new file mode 100644
index 0000000..7e5e717
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/PixelBufferImage.java
@@ -0,0 +1,185 @@
+/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+//
+// PixelBufferImage is an PixelBuffer which also acts as an ImageProducer.
+// Currently it only supports 8-bit colourmapped pixel format.
+//
+
+package com.tigervnc.vncviewer;
+
+import java.awt.*;
+import java.awt.image.*;
+import java.nio.ByteOrder;
+import javax.swing.JScrollPane;
+
+import com.tigervnc.rfb.*;
+
+public class PixelBufferImage extends PixelBuffer implements ImageProducer
+{
+ public PixelBufferImage(int w, int h, CConn cc_, DesktopWindow desktop_) {
+ cc = cc_;
+ desktop = desktop_;
+ PixelFormat nativePF = getNativePF();
+ switch ((nativePF.depth > cc.serverPF.depth) ? cc.serverPF.depth : nativePF.depth) {
+ case 8:
+ setPF(new PixelFormat(8,8,false,true,7,7,3,0,3,6));
+ break;
+ case 16:
+ setPF(new PixelFormat(16,16,false,true,0xF800,0x07C0,0x003E,0,0,0));
+ break;
+ case 24:
+ setPF(new PixelFormat(32,24,false,true,0xff,0xff,0xff,16,8,0));
+ break;
+ default:
+ setPF(new PixelFormat(8,8,false,true,7,7,3,0,3,6));
+ vlog.debug("Unsupported native PF, defaulting to depth 8");
+ }
+ resize(w, h);
+ }
+
+ // resize() resizes the image, preserving the image data where possible.
+ public void resize(int w, int h) {
+ if (w == width() && h == height()) return;
+
+ int rowsToCopy = h < height() ? h : height();
+ int copyWidth = w < width() ? w : width();
+ int oldWidth = width();
+ int[] oldData = data;
+
+ width_ = w;
+ height_ = h;
+ image = desktop.createImage(this);
+ //image.setAccelerationPriority(1);
+
+ data = new int[width() * height()];
+
+ for (int i = 0; i < rowsToCopy; i++)
+ System.arraycopy(oldData, copyWidth * i,
+ data, width() * i, copyWidth);
+ }
+
+ private PixelFormat getNativePF() {
+ PixelFormat pf;
+ cm = java.awt.Toolkit.getDefaultToolkit().getColorModel();
+ if (cm.getColorSpace().getType() == java.awt.color.ColorSpace.TYPE_RGB) {
+ int depth = cm.getPixelSize();
+ int bpp = (depth > 16 ? 32 : (depth > 8 ? 16 : 8));
+ ByteOrder byteOrder = ByteOrder.nativeOrder();
+ boolean bigEndian = (byteOrder == ByteOrder.BIG_ENDIAN ? true : false);
+ boolean trueColour = (depth > 8 ? true : false);
+ int redShift = cm.getComponentSize()[0] + cm.getComponentSize()[1];
+ int greenShift = cm.getComponentSize()[0];
+ int blueShift = 0;
+ pf = new PixelFormat(bpp, depth, bigEndian, trueColour,
+ (depth > 8 ? 0xff : 0),
+ (depth > 8 ? 0xff : 0),
+ (depth > 8 ? 0xff : 0),
+ (depth > 8 ? redShift : 0),
+ (depth > 8 ? greenShift : 0),
+ (depth > 8 ? blueShift : 0));
+ } else {
+ pf = new PixelFormat(8, 8, false, false, 7, 7, 3, 0, 3, 6);
+ }
+ vlog.debug("Native pixel format is "+pf.print());
+ return pf;
+ }
+
+ // put() causes the given rectangle to be drawn using the given graphics
+ // context.
+ public void put(int x, int y, int w, int h, Graphics g) {
+ if (ic != null) {
+ ic.setPixels(x, y, w, h, cm, data, width() * y + x, width());
+ ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
+ }
+ }
+
+ // fillRect(), imageRect(), maskRect() are inherited from PixelBuffer. For
+ // copyRect() we also need to tell the ImageConsumer that the pixels have
+ // changed (this is done in the put() call for the others).
+
+ public void copyRect(int x, int y, int w, int h, int srcX, int srcY) {
+ super.copyRect(x, y, w, h, srcX, srcY);
+ if (ic == null) return;
+ ic.setPixels(x, y, w, h, cm, data, width() * y + x, width());
+ ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
+ }
+
+ // setColourMapEntries() changes some of the entries in the colourmap.
+ // However these settings won't take effect until updateColourMap() is
+ // called. This is because getting java to recalculate its internal
+ // translation table and redraw the screen is expensive.
+
+ public void setColourMapEntries(int firstColour, int nColours_,
+ int[] rgbs) {
+ nColours = nColours_;
+ reds = new byte[nColours];
+ blues = new byte[nColours];
+ greens = new byte[nColours];
+ for (int i = 0; i < nColours; i++) {
+ reds[firstColour+i] = (byte)(rgbs[i*3] >> 8);
+ greens[firstColour+i] = (byte)(rgbs[i*3+1] >> 8);
+ blues[firstColour+i] = (byte)(rgbs[i*3+2] >> 8);
+ }
+ }
+
+ // ImageProducer methods
+
+ public void updateColourMap() {
+ cm = new IndexColorModel(8, nColours, reds, greens, blues);
+ }
+
+ public void addConsumer(ImageConsumer c) {
+ if (ic == c) return;
+
+ vlog.debug("adding consumer "+c);
+
+ if (ic != null)
+ vlog.error("Only one ImageConsumer allowed - discarding old one");
+
+ ic = c;
+ ic.setDimensions(width(), height());
+ ic.setHints(ImageConsumer.RANDOMPIXELORDER);
+ // Calling ic.setColorModel(cm) seemed to help in some earlier versions of
+ // the JDK, but it shouldn't be necessary because we pass the ColorModel
+ // with each setPixels() call.
+ ic.setPixels(0, 0, width(), height(), cm, data, 0, width());
+ ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE);
+ }
+
+ public void removeConsumer(ImageConsumer c) {
+ System.err.println("removeConsumer "+c);
+ if (ic == c) ic = null;
+ }
+
+ public boolean isConsumer(ImageConsumer c) { return ic == c; }
+ public void requestTopDownLeftRightResend(ImageConsumer c) {}
+ public void startProduction(ImageConsumer c) { addConsumer(c); }
+
+ Image image;
+ ImageConsumer ic;
+
+ int nColours;
+ byte[] reds;
+ byte[] greens;
+ byte[] blues;
+
+ CConn cc;
+ DesktopWindow desktop;
+ static LogWriter vlog = new LogWriter("PixelBufferImage");
+}
diff --git a/java/src/com/tigervnc/vncviewer/README b/java/src/com/tigervnc/vncviewer/README
index 39ba825..c6949d5 100644
--- a/java/src/com/tigervnc/vncviewer/README
+++ b/java/src/com/tigervnc/vncviewer/README
@@ -9,7 +9,7 @@
Copyright (C) 1999 AT&T Laboratories Cambridge.
Copyright (C) 2000 Tridia Corp.
- Copyright (C) 2002-2003 RealVNC Ltd.
+ Copyright (C) 2002-2005 RealVNC Ltd.
Copyright (C) 2001-2004 HorizonLive.com, Inc.
Copyright (C) 2000-2007 Constantin Kaplinsky
Copyright (C) 2000-2007 TightVNC Group
diff --git a/java/src/com/tigervnc/vncviewer/RecordOutputStream.java b/java/src/com/tigervnc/vncviewer/RecordOutputStream.java
deleted file mode 100644
index 7f13249..0000000
--- a/java/src/com/tigervnc/vncviewer/RecordOutputStream.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package com.tigervnc.vncviewer;
-
-import java.io.DataOutput;
-import java.io.IOException;
-
-public class RecordOutputStream implements DataOutput {
-
- public RecordOutputStream(RfbProto rfbproto) {
- rfb = rfbproto;
- }
-
- private boolean canWrite() {
- return ((rfb != null) && (rfb.rec != null));
- }
-
- public void write(byte[] b) throws IOException {
- if (canWrite())
- rfb.rec.write(b);
- }
-
- public void write(byte[] b, int off, int len) throws IOException {
- if (canWrite())
- rfb.rec.write(b, off, len);
- }
-
- public void write(int b) throws IOException {
- if (canWrite())
- rfb.rec.writeIntBE(b);
- }
-
- public void writeBoolean(boolean v) { }
-
- public void writeByte(int v) throws IOException {
- if (canWrite()) {
- rfb.rec.writeByte(v);
- }
- }
-
- public void writeBytes(String s) { }
- public void writeChar(int v) { }
- public void writeChars(String s) { }
- public void writeDouble(double v) { }
- public void writeFloat(float v) { }
-
- public void writeInt(int v) throws IOException {
- if (canWrite())
- rfb.rec.writeIntBE(v);
- }
-
- public void writeLong(long v) { }
-
- public void writeShort(int v) throws IOException {
- if (canWrite())
- rfb.rec.writeShortBE(v);
- }
-
- public void writeUTF(String str) { }
-
- private RfbProto rfb = null;
-}
diff --git a/java/src/com/tigervnc/vncviewer/RecordingFrame.java b/java/src/com/tigervnc/vncviewer/RecordingFrame.java
deleted file mode 100644
index 6bf40ea..0000000
--- a/java/src/com/tigervnc/vncviewer/RecordingFrame.java
+++ /dev/null
@@ -1,313 +0,0 @@
-//
-// Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
-
-//
-// Recording frame. It allows to control recording RFB sessions into
-// FBS (FrameBuffer Stream) files.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.io.*;
-import java.awt.*;
-import java.awt.event.*;
-
-class RecordingFrame extends Frame
- implements WindowListener, ActionListener {
-
- boolean recording;
-
- TextField fnameField;
- Button browseButton;
-
- Label statusLabel;
-
- Button recordButton, nextButton, closeButton;
- VncViewer viewer;
-
- //
- // Check if current security manager allows to create a
- // RecordingFrame object.
- //
-
- public static boolean checkSecurity() {
- SecurityManager security = System.getSecurityManager();
- if (security != null) {
- try {
- security.checkPropertyAccess("user.dir");
- security.checkPropertyAccess("file.separator");
- // Work around (rare) checkPropertyAccess bug
- System.getProperty("user.dir");
- } catch (SecurityException e) {
- System.out.println("SecurityManager restricts session recording.");
- return false;
- }
- }
- return true;
- }
-
- //
- // Constructor.
- //
-
- RecordingFrame(VncViewer v) {
- super("TigerVNC Session Recording");
-
- viewer = v;
-
- // Determine initial filename for next saved session.
- // FIXME: Check SecurityManager.
-
- String fname = nextNewFilename(System.getProperty("user.dir") +
- System.getProperty("file.separator") +
- "vncsession.fbs");
-
- // Construct new panel with file name field and "Browse" button.
-
- Panel fnamePanel = new Panel();
- GridBagLayout fnameGridbag = new GridBagLayout();
- fnamePanel.setLayout(fnameGridbag);
-
- GridBagConstraints fnameConstraints = new GridBagConstraints();
- fnameConstraints.gridwidth = GridBagConstraints.RELATIVE;
- fnameConstraints.fill = GridBagConstraints.BOTH;
- fnameConstraints.weightx = 4.0;
-
- fnameField = new TextField(fname, 64);
- fnameGridbag.setConstraints(fnameField, fnameConstraints);
- fnamePanel.add(fnameField);
- fnameField.addActionListener(this);
-
- fnameConstraints.gridwidth = GridBagConstraints.REMAINDER;
- fnameConstraints.weightx = 1.0;
-
- browseButton = new Button("Browse");
- fnameGridbag.setConstraints(browseButton, fnameConstraints);
- fnamePanel.add(browseButton);
- browseButton.addActionListener(this);
-
- // Construct the frame.
-
- GridBagLayout gridbag = new GridBagLayout();
- setLayout(gridbag);
-
- GridBagConstraints gbc = new GridBagConstraints();
- gbc.gridwidth = GridBagConstraints.REMAINDER;
- gbc.fill = GridBagConstraints.BOTH;
- gbc.weighty = 1.0;
- gbc.insets = new Insets(10, 0, 0, 0);
-
- Label helpLabel =
- new Label("File name to save next recorded session in:", Label.CENTER);
- gridbag.setConstraints(helpLabel, gbc);
- add(helpLabel);
-
- gbc.fill = GridBagConstraints.HORIZONTAL;
- gbc.weighty = 0.0;
- gbc.insets = new Insets(0, 0, 0, 0);
-
- gridbag.setConstraints(fnamePanel, gbc);
- add(fnamePanel);
-
- gbc.fill = GridBagConstraints.BOTH;
- gbc.weighty = 1.0;
- gbc.insets = new Insets(10, 0, 10, 0);
-
- statusLabel = new Label("", Label.CENTER);
- gridbag.setConstraints(statusLabel, gbc);
- add(statusLabel);
-
- gbc.fill = GridBagConstraints.HORIZONTAL;
- gbc.weightx = 1.0;
- gbc.weighty = 0.0;
- gbc.gridwidth = 1;
- gbc.insets = new Insets(0, 0, 0, 0);
-
- recordButton = new Button("Record");
- gridbag.setConstraints(recordButton, gbc);
- add(recordButton);
- recordButton.addActionListener(this);
-
- nextButton = new Button("Next file");
- gridbag.setConstraints(nextButton, gbc);
- add(nextButton);
- nextButton.addActionListener(this);
-
- closeButton = new Button("Close");
- gridbag.setConstraints(closeButton, gbc);
- add(closeButton);
- closeButton.addActionListener(this);
-
- // Set correct text, font and color for the statusLabel.
- stopRecording();
-
- pack();
-
- addWindowListener(this);
- }
-
- //
- // If the given string ends with ".NNN" where NNN is a decimal
- // number, increase this number by one. Otherwise, append ".001"
- // to the given string.
- //
-
- protected String nextFilename(String fname) {
- int len = fname.length();
- int suffixPos = len;
- int suffixNum = 1;
-
- if (len > 4 && fname.charAt(len - 4) == '.') {
- try {
- suffixNum = Integer.parseInt(fname.substring(len - 3, len)) + 1;
- suffixPos = len - 4;
- } catch (NumberFormatException e) { }
- }
-
- char[] zeroes = {'0', '0', '0'};
- String suffix = String.valueOf(suffixNum);
- if (suffix.length() < 3) {
- suffix = new String(zeroes, 0, 3 - suffix.length()) + suffix;
- }
-
- return fname.substring(0, suffixPos) + '.' + suffix;
- }
-
- //
- // Find next name of a file which does not exist yet.
- //
-
- protected String nextNewFilename(String fname) {
- String newName = fname;
- File f;
- try {
- do {
- newName = nextFilename(newName);
- f = new File(newName);
- } while (f.exists());
- } catch (SecurityException e) { }
-
- return newName;
- }
-
- //
- // Let the user choose a file name showing a FileDialog.
- //
-
- protected boolean browseFile() {
- File currentFile = new File(fnameField.getText());
-
- FileDialog fd =
- new FileDialog(this, "Save next session as...", FileDialog.SAVE);
- fd.setDirectory(currentFile.getParent());
- fd.setVisible(true);
- if (fd.getFile() != null) {
- String newDir = fd.getDirectory();
- String sep = System.getProperty("file.separator");
- if (newDir.length() > 0) {
- if (!sep.equals(newDir.substring(newDir.length() - sep.length())))
- newDir += sep;
- }
- String newFname = newDir + fd.getFile();
- if (newFname.equals(fnameField.getText())) {
- fnameField.setText(newFname);
- return true;
- }
- }
- return false;
- }
-
- //
- // Start recording.
- //
-
- public void startRecording() {
- statusLabel.setText("Status: Recording...");
- statusLabel.setFont(new Font("Helvetica", Font.BOLD, 12));
- statusLabel.setForeground(Color.red);
- recordButton.setLabel("Stop recording");
-
- recording = true;
-
- viewer.setRecordingStatus(fnameField.getText());
- }
-
- //
- // Stop recording.
- //
-
- public void stopRecording() {
- statusLabel.setText("Status: Not recording.");
- statusLabel.setFont(new Font("Helvetica", Font.PLAIN, 12));
- statusLabel.setForeground(Color.black);
- recordButton.setLabel("Record");
-
- recording = false;
-
- viewer.setRecordingStatus(null);
- }
-
- //
- // Close our window properly.
- //
-
- public void windowClosing(WindowEvent evt) {
- setVisible(false);
- }
-
- //
- // Ignore window events we're not interested in.
- //
-
- public void windowActivated(WindowEvent evt) {}
- public void windowDeactivated (WindowEvent evt) {}
- public void windowOpened(WindowEvent evt) {}
- public void windowClosed(WindowEvent evt) {}
- public void windowIconified(WindowEvent evt) {}
- public void windowDeiconified(WindowEvent evt) {}
-
-
- //
- // Respond to button presses
- //
-
- public void actionPerformed(ActionEvent evt) {
- if (evt.getSource() == browseButton) {
- if (browseFile() && recording)
- startRecording();
-
- } else if (evt.getSource() == recordButton) {
- if (!recording) {
- startRecording();
- } else {
- stopRecording();
- fnameField.setText(nextNewFilename(fnameField.getText()));
- }
-
- } else if (evt.getSource() == nextButton) {
- fnameField.setText(nextNewFilename(fnameField.getText()));
- if (recording)
- startRecording();
-
- } else if (evt.getSource() == closeButton) {
- setVisible(false);
-
- }
- }
-}
diff --git a/java/src/com/tigervnc/vncviewer/ReloginPanel.java b/java/src/com/tigervnc/vncviewer/ReloginPanel.java
deleted file mode 100644
index 51d9d21..0000000
--- a/java/src/com/tigervnc/vncviewer/ReloginPanel.java
+++ /dev/null
@@ -1,66 +0,0 @@
-//
-// Copyright (C) 2002 Cendio Systems. All Rights Reserved.
-// Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
-
-//
-// ReloginPanel class implements panel with a button for logging in again,
-// after fatal errors or disconnect
-//
-
-package com.tigervnc.vncviewer;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.applet.*;
-
-//
-// The panel which implements the Relogin button
-//
-
-class ReloginPanel extends Panel implements ActionListener {
- Button reloginButton;
- Button closeButton;
- VncViewer viewer;
-
- //
- // Constructor.
- //
- public ReloginPanel(VncViewer v) {
- viewer = v;
- setLayout(new FlowLayout(FlowLayout.CENTER));
- reloginButton = new Button("Login again");
- add(reloginButton);
- reloginButton.addActionListener(this);
- if (viewer.inSeparateFrame) {
- closeButton = new Button("Close window");
- add(closeButton);
- closeButton.addActionListener(this);
- }
- }
-
- //
- // This method is called when a button is pressed.
- //
- public synchronized void actionPerformed(ActionEvent evt) {
- if (viewer.inSeparateFrame)
- viewer.vncFrame.dispose();
- if (evt.getSource() == reloginButton)
- viewer.getAppletContext().showDocument(viewer.getDocumentBase());
- }
-}
diff --git a/java/src/com/tigervnc/vncviewer/RfbInputStream.java b/java/src/com/tigervnc/vncviewer/RfbInputStream.java
deleted file mode 100644
index cac3ec7..0000000
--- a/java/src/com/tigervnc/vncviewer/RfbInputStream.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.tigervnc.vncviewer;
-
-import java.io.IOException;
-
-//
-// This class is layer between data of private RfbProto class
-// and classes in other packages.
-//
-// For now this class is used by com.tigervnc.decoder.RawDecoder
-//
-public class RfbInputStream {
- RfbInputStream(RfbProto rfbProto) {
- rfb = rfbProto;
- }
-
- //
- // Read data methods
- //
-
- public void readFully(byte b[]) throws IOException {
- readFully(b, 0, b.length);
- }
-
- public void readFully(byte b[], int off, int len) throws IOException {
- rfb.readFully(b, off, len);
- }
-
- public int readU32() throws IOException {
- return rfb.readU32();
- }
-
- public int readU8() throws IOException {
- return rfb.readU8();
- }
-
- public int readCompactLen() throws IOException {
- return rfb.readCompactLen();
- }
-
- public int readU16() throws IOException {
- return rfb.readU16();
- }
-
- private RfbProto rfb = null;
-}
diff --git a/java/src/com/tigervnc/vncviewer/RfbProto.java b/java/src/com/tigervnc/vncviewer/RfbProto.java
deleted file mode 100644
index da54c56..0000000
--- a/java/src/com/tigervnc/vncviewer/RfbProto.java
+++ /dev/null
@@ -1,1202 +0,0 @@
-//
-// Copyright (C) 2001-2004 HorizonLive.com, Inc. All Rights Reserved.
-// Copyright (C) 2001-2006 Constantin Kaplinsky. All Rights Reserved.
-// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
-// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
-
-//
-// RfbProto.java
-//
-
-package com.tigervnc.vncviewer;
-
-import java.io.*;
-import java.awt.*;
-import java.awt.event.*;
-import java.net.Socket;
-import java.util.zip.*;
-
-class RfbProto {
-
- final static String
- versionMsg_3_3 = "RFB 003.003\n",
- versionMsg_3_7 = "RFB 003.007\n",
- versionMsg_3_8 = "RFB 003.008\n";
-
- // Security types
- final static int
- SecTypeInvalid = 0,
- SecTypeNone = 1,
- SecTypeVncAuth = 2,
- SecTypeTight = 16,
- SecTypeVeNCrypt = 19,
- SecTypePlain = 256,
- SecTypeTLSNone = 257,
- SecTypeTLSVnc = 258,
- SecTypeTLSPlain = 259,
- SecTypeX509None = 260,
- SecTypeX509Vnc = 261,
- SecTypeX509Plain = 262;
-
- // VNC authentication results
- final static int
- VncAuthOK = 0,
- VncAuthFailed = 1,
- VncAuthTooMany = 2;
-
- // Standard server-to-client messages
- final static int
- FramebufferUpdate = 0,
- SetColourMapEntries = 1,
- Bell = 2,
- ServerCutText = 3;
-
- // Standard client-to-server messages
- final static int
- SetPixelFormat = 0,
- FixColourMapEntries = 1,
- SetEncodings = 2,
- FramebufferUpdateRequest = 3,
- KeyboardEvent = 4,
- PointerEvent = 5,
- ClientCutText = 6;
-
- // Supported encodings and pseudo-encodings
- final static int
- EncodingRaw = 0,
- EncodingCopyRect = 1,
- EncodingRRE = 2,
- EncodingCoRRE = 4,
- EncodingHextile = 5,
- EncodingZlib = 6,
- EncodingTight = 7,
- EncodingZRLE = 16,
- EncodingCompressLevel0 = 0xFFFFFF00,
- EncodingQualityLevel0 = 0xFFFFFFE0,
- EncodingXCursor = 0xFFFFFF10,
- EncodingRichCursor = 0xFFFFFF11,
- EncodingPointerPos = 0xFFFFFF18,
- EncodingLastRect = 0xFFFFFF20,
- EncodingNewFBSize = 0xFFFFFF21;
-
- final static int MaxNormalEncoding = 255;
-
- // Contstants used in the Hextile decoder
- final static int
- HextileRaw = 1,
- HextileBackgroundSpecified = 2,
- HextileForegroundSpecified = 4,
- HextileAnySubrects = 8,
- HextileSubrectsColoured = 16;
-
- // Contstants used in the Tight decoder
- final static int TightMinToCompress = 12;
- final static int
- TightExplicitFilter = 0x04,
- TightFill = 0x08,
- TightJpeg = 0x09,
- TightMaxSubencoding = 0x09,
- TightFilterCopy = 0x00,
- TightFilterPalette = 0x01,
- TightFilterGradient = 0x02;
-
-
- String host;
- int port;
- Socket sock;
- OutputStream os;
- SessionRecorder rec;
- boolean inNormalProtocol = false;
- VncViewer viewer;
-
- // Input stream is declared private to make sure it can be accessed
- // only via RfbProto methods. We have to do this because we want to
- // count how many bytes were read.
- private DataInputStream is;
- private long numBytesRead = 0;
- public long getNumBytesRead() { return numBytesRead; }
-
- // Java on UNIX does not call keyPressed() on some keys, for example
- // swedish keys To prevent our workaround to produce duplicate
- // keypresses on JVMs that actually works, keep track of if
- // keyPressed() for a "broken" key was called or not.
- boolean brokenKeyPressed = false;
-
- // This will be set to true on the first framebuffer update
- // containing Zlib-, ZRLE- or Tight-encoded data.
- boolean wereZlibUpdates = false;
-
- // This fields are needed to show warnings about inefficiently saved
- // sessions only once per each saved session file.
- boolean zlibWarningShown;
- boolean tightWarningShown;
-
- // Before starting to record each saved session, we set this field
- // to 0, and increment on each framebuffer update. We don't flush
- // the SessionRecorder data into the file before the second update.
- // This allows us to write initial framebuffer update with zero
- // timestamp, to let the player show initial desktop before
- // playback.
- int numUpdatesInSession;
-
- // Measuring network throughput.
- boolean timing;
- long timeWaitedIn100us;
- long timedKbits;
-
- // Protocol version and TightVNC-specific protocol options.
- int serverMajor, serverMinor;
- int clientMajor, clientMinor;
-
- // If true, informs that the RFB socket was closed.
- private boolean closed;
-
- //
- // Constructor. Make TCP connection to RFB server.
- //
-
- RfbProto(String h, int p, VncViewer v) throws IOException {
- viewer = v;
- host = h;
- port = p;
-
- if (viewer.socketFactory == null) {
- sock = new Socket(host, port);
- sock.setTcpNoDelay(true);
- } else {
- try {
- Class factoryClass = Class.forName(viewer.socketFactory);
- SocketFactory factory = (SocketFactory)factoryClass.newInstance();
- if (viewer.inAnApplet)
- sock = factory.createSocket(host, port, viewer);
- else
- sock = factory.createSocket(host, port, viewer.mainArgs);
- } catch(Exception e) {
- e.printStackTrace();
- throw new IOException(e.getMessage());
- }
- }
- is = new DataInputStream(new BufferedInputStream(sock.getInputStream(),
- 16384));
- os = sock.getOutputStream();
-
- timing = false;
- timeWaitedIn100us = 5;
- timedKbits = 0;
- }
-
-
- synchronized void close() {
- try {
- sock.close();
- closed = true;
- System.out.println("RFB socket closed");
- if (rec != null) {
- rec.close();
- rec = null;
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- synchronized boolean closed() {
- return closed;
- }
-
- //
- // Read server's protocol version message
- //
-
- void readVersionMsg() throws Exception {
-
- byte[] b = new byte[12];
-
- readFully(b);
-
- if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ')
- || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9')
- || (b[6] < '0') || (b[6] > '9') || (b[7] != '.')
- || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9')
- || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n'))
- {
- throw new Exception("Host " + host + " port " + port +
- " is not an RFB server");
- }
-
- serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0');
- serverMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0');
-
- if (serverMajor < 3) {
- throw new Exception("RFB server does not support protocol version 3");
- }
- }
-
-
- //
- // Write our protocol version message
- //
-
- void writeVersionMsg() throws IOException {
- clientMajor = 3;
- if (serverMajor > 3 || serverMinor >= 8) {
- clientMinor = 8;
- os.write(versionMsg_3_8.getBytes());
- } else if (serverMinor >= 7) {
- clientMinor = 7;
- os.write(versionMsg_3_7.getBytes());
- } else {
- clientMinor = 3;
- os.write(versionMsg_3_3.getBytes());
- }
- }
-
-
- //
- // Negotiate the authentication scheme.
- //
-
- int negotiateSecurity() throws Exception {
- return (clientMinor >= 7) ?
- selectSecurityType() : readSecurityType();
- }
-
- //
- // Read security type from the server (protocol version 3.3).
- //
-
- int readSecurityType() throws Exception {
- int secType = readU32();
-
- switch (secType) {
- case SecTypeInvalid:
- readConnFailedReason();
- return SecTypeInvalid; // should never be executed
- case SecTypeNone:
- case SecTypeVncAuth:
- return secType;
- default:
- throw new Exception("Unknown security type from RFB server: " + secType);
- }
- }
-
- //
- // Select security type from the server's list (protocol versions 3.7/3.8).
- //
-
- int selectSecurityType() throws Exception {
- int secType = SecTypeInvalid;
-
- // Read the list of secutiry types.
- int nSecTypes = readU8();
- if (nSecTypes == 0) {
- readConnFailedReason();
- return SecTypeInvalid; // should never be executed
- }
- byte[] secTypes = new byte[nSecTypes];
- readFully(secTypes);
-
- // Find first supported security type.
- for (int i = 0; i < nSecTypes; i++) {
- if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth
- || secTypes[i] == SecTypeVeNCrypt) {
- secType = secTypes[i];
- break;
- }
- }
-
- if (secType == SecTypeInvalid) {
- throw new Exception("Server did not offer supported security type");
- } else {
- os.write(secType);
- }
-
- return secType;
- }
-
- int authenticateVeNCrypt() throws Exception {
- int majorVersion = readU8();
- int minorVersion = readU8();
- int Version = (majorVersion << 8) | minorVersion;
- if (Version < 0x0002) {
- os.write(0);
- os.write(0);
- throw new Exception("Server reported an unsupported VeNCrypt version");
- }
- os.write(0);
- os.write(2);
- if (readU8() != 0)
- throw new Exception("Server reported it could not support the VeNCrypt version");
- int nSecTypes = readU8();
- int[] secTypes = new int[nSecTypes];
- for(int i = 0; i < nSecTypes; i++)
- secTypes[i] = readU32();
-
- for(int i = 0; i < nSecTypes; i++)
- switch(secTypes[i])
- {
- case SecTypeNone:
- case SecTypeVncAuth:
- case SecTypePlain:
- case SecTypeTLSNone:
- case SecTypeTLSVnc:
- case SecTypeTLSPlain:
- case SecTypeX509None:
- case SecTypeX509Vnc:
- case SecTypeX509Plain:
- writeInt(secTypes[i]);
- return secTypes[i];
- }
-
- throw new Exception("No valid VeNCrypt sub-type");
- }
-
- //
- // Perform "no authentication".
- //
-
- void authenticateNone() throws Exception {
- if (clientMinor >= 8)
- readSecurityResult("No authentication");
- }
-
- //
- // Perform standard VNC Authentication.
- //
-
- void authenticateVNC(String pw) throws Exception {
- byte[] challenge = new byte[16];
- readFully(challenge);
-
- if (pw.length() > 8)
- pw = pw.substring(0, 8); // Truncate to 8 chars
-
- // Truncate password on the first zero byte.
- int firstZero = pw.indexOf(0);
- if (firstZero != -1)
- pw = pw.substring(0, firstZero);
-
- byte[] key = {0, 0, 0, 0, 0, 0, 0, 0};
- System.arraycopy(pw.getBytes(), 0, key, 0, pw.length());
-
- DesCipher des = new DesCipher(key);
-
- des.encrypt(challenge, 0, challenge, 0);
- des.encrypt(challenge, 8, challenge, 8);
-
- os.write(challenge);
-
- readSecurityResult("VNC authentication");
- }
-
- void authenticateTLS() throws Exception {
- TLSTunnel tunnel = new TLSTunnel(sock);
- tunnel.setup (this);
- }
-
- void authenticateX509() throws Exception {
- X509Tunnel tunnel = new X509Tunnel(sock);
- tunnel.setup (this);
- }
-
- void authenticatePlain(String User, String Password) throws Exception {
- byte[] user=User.getBytes();
- byte[] password=Password.getBytes();
- writeInt(user.length);
- writeInt(password.length);
- os.write(user);
- os.write(password);
-
- readSecurityResult("Plain authentication");
- }
-
- //
- // Read security result.
- // Throws an exception on authentication failure.
- //
-
- void readSecurityResult(String authType) throws Exception {
- int securityResult = readU32();
-
- switch (securityResult) {
- case VncAuthOK:
- System.out.println(authType + ": success");
- break;
- case VncAuthFailed:
- if (clientMinor >= 8)
- readConnFailedReason();
- throw new Exception(authType + ": failed");
- case VncAuthTooMany:
- throw new Exception(authType + ": failed, too many tries");
- default:
- throw new Exception(authType + ": unknown result " + securityResult);
- }
- }
-
- //
- // Read the string describing the reason for a connection failure,
- // and throw an exception.
- //
-
- void readConnFailedReason() throws Exception {
- int reasonLen = readU32();
- byte[] reason = new byte[reasonLen];
- readFully(reason);
- throw new Exception(new String(reason));
- }
-
- //
- // Write a 32-bit integer into the output stream.
- //
-
- void writeInt(int value) throws IOException {
- byte[] b = new byte[4];
- b[0] = (byte) ((value >> 24) & 0xff);
- b[1] = (byte) ((value >> 16) & 0xff);
- b[2] = (byte) ((value >> 8) & 0xff);
- b[3] = (byte) (value & 0xff);
- os.write(b);
- }
-
- //
- // Write the client initialisation message
- //
-
- void writeClientInit() throws IOException {
- if (viewer.options.shareDesktop) {
- os.write(1);
- } else {
- os.write(0);
- }
- viewer.options.disableShareDesktop();
- }
-
-
- //
- // Read the server initialisation message
- //
-
- String desktopName;
- int framebufferWidth, framebufferHeight;
- int bitsPerPixel, depth;
- boolean bigEndian, trueColour;
- int redMax, greenMax, blueMax, redShift, greenShift, blueShift;
-
- void readServerInit() throws IOException {
- framebufferWidth = readU16();
- framebufferHeight = readU16();
- bitsPerPixel = readU8();
- depth = readU8();
- bigEndian = (readU8() != 0);
- trueColour = (readU8() != 0);
- redMax = readU16();
- greenMax = readU16();
- blueMax = readU16();
- redShift = readU8();
- greenShift = readU8();
- blueShift = readU8();
- byte[] pad = new byte[3];
- readFully(pad);
- int nameLength = readU32();
- byte[] name = new byte[nameLength];
- readFully(name);
- desktopName = new String(name);
- inNormalProtocol = true;
- }
-
-
- //
- // Create session file and write initial protocol messages into it.
- //
-
- void startSession(String fname) throws IOException {
- rec = new SessionRecorder(fname);
- rec.writeHeader();
- rec.write(versionMsg_3_3.getBytes());
- rec.writeIntBE(SecTypeNone);
- rec.writeShortBE(framebufferWidth);
- rec.writeShortBE(framebufferHeight);
- byte[] fbsServerInitMsg = {
- 32, 24, 0, 1, 0,
- (byte)0xFF, 0, (byte)0xFF, 0, (byte)0xFF,
- 16, 8, 0, 0, 0, 0
- };
- rec.write(fbsServerInitMsg);
- rec.writeIntBE(desktopName.length());
- rec.write(desktopName.getBytes());
- numUpdatesInSession = 0;
-
- // FIXME: If there were e.g. ZRLE updates only, that should not
- // affect recording of Zlib and Tight updates. So, actually
- // we should maintain separate flags for Zlib, ZRLE and
- // Tight, instead of one ``wereZlibUpdates'' variable.
- //
-
- zlibWarningShown = false;
- tightWarningShown = false;
- }
-
- //
- // Close session file.
- //
-
- void closeSession() throws IOException {
- if (rec != null) {
- rec.close();
- rec = null;
- }
- }
-
-
- //
- // Set new framebuffer size
- //
-
- void setFramebufferSize(int width, int height) {
- framebufferWidth = width;
- framebufferHeight = height;
- }
-
-
- //
- // Read the server message type
- //
-
- int readServerMessageType() throws IOException {
- int msgType = readU8();
-
- // If the session is being recorded:
- if (rec != null) {
- if (msgType == Bell) { // Save Bell messages in session files.
- rec.writeByte(msgType);
- if (numUpdatesInSession > 0)
- rec.flush();
- }
- }
-
- return msgType;
- }
-
-
- //
- // Read a FramebufferUpdate message
- //
-
- int updateNRects;
-
- void readFramebufferUpdate() throws IOException {
- skipBytes(1);
- updateNRects = readU16();
-
- // If the session is being recorded:
- if (rec != null) {
- rec.writeByte(FramebufferUpdate);
- rec.writeByte(0);
- rec.writeShortBE(updateNRects);
- }
-
- numUpdatesInSession++;
- }
-
- //
- // Returns true if encoding is not pseudo
- //
- // FIXME: Find better way to differ pseudo and real encodings
- //
-
- boolean isRealDecoderEncoding(int encoding) {
- if ((encoding >= 1) && (encoding <= 16)) {
- return true;
- }
- return false;
- }
-
- // Read a FramebufferUpdate rectangle header
-
- int updateRectX, updateRectY, updateRectW, updateRectH, updateRectEncoding;
-
- void readFramebufferUpdateRectHdr() throws Exception {
- updateRectX = readU16();
- updateRectY = readU16();
- updateRectW = readU16();
- updateRectH = readU16();
- updateRectEncoding = readU32();
-
- if (updateRectEncoding == EncodingZlib ||
- updateRectEncoding == EncodingZRLE ||
- updateRectEncoding == EncodingTight)
- wereZlibUpdates = true;
-
- // If the session is being recorded:
- if (rec != null) {
- if (numUpdatesInSession > 1)
- rec.flush(); // Flush the output on each rectangle.
- rec.writeShortBE(updateRectX);
- rec.writeShortBE(updateRectY);
- rec.writeShortBE(updateRectW);
- rec.writeShortBE(updateRectH);
-
- //
- // If this is pseudo encoding or CopyRect that write encoding ID
- // in this place. All real encoding ID will be written to record stream
- // in decoder classes.
-
- if (((!isRealDecoderEncoding(updateRectEncoding))) && (rec != null)) {
- rec.writeIntBE(updateRectEncoding);
- }
- }
-
- if (updateRectEncoding < 0 || updateRectEncoding > MaxNormalEncoding)
- return;
-
- if (updateRectX + updateRectW > framebufferWidth ||
- updateRectY + updateRectH > framebufferHeight) {
- throw new Exception("Framebuffer update rectangle too large: " +
- updateRectW + "x" + updateRectH + " at (" +
- updateRectX + "," + updateRectY + ")");
- }
- }
-
- //
- // Read a ServerCutText message
- //
-
- String readServerCutText() throws IOException {
- skipBytes(3);
- int len = readU32();
- byte[] text = new byte[len];
- readFully(text);
- return new String(text);
- }
-
-
- //
- // Read an integer in compact representation (1..3 bytes).
- // Such format is used as a part of the Tight encoding.
- // Also, this method records data if session recording is active and
- // the viewer's recordFromBeginning variable is set to true.
- //
-
- int readCompactLen() throws IOException {
- int[] portion = new int[3];
- portion[0] = readU8();
- int byteCount = 1;
- int len = portion[0] & 0x7F;
- if ((portion[0] & 0x80) != 0) {
- portion[1] = readU8();
- byteCount++;
- len |= (portion[1] & 0x7F) << 7;
- if ((portion[1] & 0x80) != 0) {
- portion[2] = readU8();
- byteCount++;
- len |= (portion[2] & 0xFF) << 14;
- }
- }
-
- return len;
- }
-
-
- //
- // Write a FramebufferUpdateRequest message
- //
-
- void writeFramebufferUpdateRequest(int x, int y, int w, int h,
- boolean incremental)
- throws IOException
- {
- byte[] b = new byte[10];
-
- b[0] = (byte) FramebufferUpdateRequest;
- b[1] = (byte) (incremental ? 1 : 0);
- b[2] = (byte) ((x >> 8) & 0xff);
- b[3] = (byte) (x & 0xff);
- b[4] = (byte) ((y >> 8) & 0xff);
- b[5] = (byte) (y & 0xff);
- b[6] = (byte) ((w >> 8) & 0xff);
- b[7] = (byte) (w & 0xff);
- b[8] = (byte) ((h >> 8) & 0xff);
- b[9] = (byte) (h & 0xff);
-
- os.write(b);
- }
-
-
- //
- // Write a SetPixelFormat message
- //
-
- void writeSetPixelFormat(int bitsPerPixel, int depth, boolean bigEndian,
- boolean trueColour,
- int redMax, int greenMax, int blueMax,
- int redShift, int greenShift, int blueShift)
- throws IOException
- {
- byte[] b = new byte[20];
-
- b[0] = (byte) SetPixelFormat;
- b[4] = (byte) bitsPerPixel;
- b[5] = (byte) depth;
- b[6] = (byte) (bigEndian ? 1 : 0);
- b[7] = (byte) (trueColour ? 1 : 0);
- b[8] = (byte) ((redMax >> 8) & 0xff);
- b[9] = (byte) (redMax & 0xff);
- b[10] = (byte) ((greenMax >> 8) & 0xff);
- b[11] = (byte) (greenMax & 0xff);
- b[12] = (byte) ((blueMax >> 8) & 0xff);
- b[13] = (byte) (blueMax & 0xff);
- b[14] = (byte) redShift;
- b[15] = (byte) greenShift;
- b[16] = (byte) blueShift;
-
- os.write(b);
- }
-
-
- //
- // Write a FixColourMapEntries message. The values in the red, green and
- // blue arrays are from 0 to 65535.
- //
-
- void writeFixColourMapEntries(int firstColour, int nColours,
- int[] red, int[] green, int[] blue)
- throws IOException
- {
- byte[] b = new byte[6 + nColours * 6];
-
- b[0] = (byte) FixColourMapEntries;
- b[2] = (byte) ((firstColour >> 8) & 0xff);
- b[3] = (byte) (firstColour & 0xff);
- b[4] = (byte) ((nColours >> 8) & 0xff);
- b[5] = (byte) (nColours & 0xff);
-
- for (int i = 0; i < nColours; i++) {
- b[6 + i * 6] = (byte) ((red[i] >> 8) & 0xff);
- b[6 + i * 6 + 1] = (byte) (red[i] & 0xff);
- b[6 + i * 6 + 2] = (byte) ((green[i] >> 8) & 0xff);
- b[6 + i * 6 + 3] = (byte) (green[i] & 0xff);
- b[6 + i * 6 + 4] = (byte) ((blue[i] >> 8) & 0xff);
- b[6 + i * 6 + 5] = (byte) (blue[i] & 0xff);
- }
-
- os.write(b);
- }
-
-
- //
- // Write a SetEncodings message
- //
-
- void writeSetEncodings(int[] encs, int len) throws IOException {
- byte[] b = new byte[4 + 4 * len];
-
- b[0] = (byte) SetEncodings;
- b[2] = (byte) ((len >> 8) & 0xff);
- b[3] = (byte) (len & 0xff);
-
- for (int i = 0; i < len; i++) {
- b[4 + 4 * i] = (byte) ((encs[i] >> 24) & 0xff);
- b[5 + 4 * i] = (byte) ((encs[i] >> 16) & 0xff);
- b[6 + 4 * i] = (byte) ((encs[i] >> 8) & 0xff);
- b[7 + 4 * i] = (byte) (encs[i] & 0xff);
- }
-
- os.write(b);
- }
-
-
- //
- // Write a ClientCutText message
- //
-
- void writeClientCutText(String text) throws IOException {
- byte[] b = new byte[8 + text.length()];
-
- b[0] = (byte) ClientCutText;
- b[4] = (byte) ((text.length() >> 24) & 0xff);
- b[5] = (byte) ((text.length() >> 16) & 0xff);
- b[6] = (byte) ((text.length() >> 8) & 0xff);
- b[7] = (byte) (text.length() & 0xff);
-
- System.arraycopy(text.getBytes(), 0, b, 8, text.length());
-
- os.write(b);
- }
-
-
- //
- // A buffer for putting pointer and keyboard events before being sent. This
- // is to ensure that multiple RFB events generated from a single Java Event
- // will all be sent in a single network packet. The maximum possible
- // length is 4 modifier down events, a single key event followed by 4
- // modifier up events i.e. 9 key events or 72 bytes.
- //
-
- byte[] eventBuf = new byte[72];
- int eventBufLen;
-
-
- // Useful shortcuts for modifier masks.
-
- final static int CTRL_MASK = InputEvent.CTRL_MASK;
- final static int SHIFT_MASK = InputEvent.SHIFT_MASK;
- final static int META_MASK = InputEvent.META_MASK;
- final static int ALT_MASK = InputEvent.ALT_MASK;
-
-
- //
- // Write a pointer event message. We may need to send modifier key events
- // around it to set the correct modifier state.
- //
-
- int pointerMask = 0;
-
- void writePointerEvent(MouseEvent evt) throws IOException {
- int modifiers = evt.getModifiers();
-
- int mask2 = 2;
- int mask3 = 4;
- if (viewer.options.reverseMouseButtons2And3) {
- mask2 = 4;
- mask3 = 2;
- }
-
- // Note: For some reason, AWT does not set BUTTON1_MASK on left
- // button presses. Here we think that it was the left button if
- // modifiers do not include BUTTON2_MASK or BUTTON3_MASK.
-
- if (evt.getID() == MouseEvent.MOUSE_PRESSED) {
- if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
- pointerMask = mask2;
- modifiers &= ~ALT_MASK;
- } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
- pointerMask = mask3;
- modifiers &= ~META_MASK;
- } else {
- pointerMask = 1;
- }
- } else if (evt.getID() == MouseEvent.MOUSE_RELEASED) {
- pointerMask = 0;
- if ((modifiers & InputEvent.BUTTON2_MASK) != 0) {
- modifiers &= ~ALT_MASK;
- } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) {
- modifiers &= ~META_MASK;
- }
- }
-
- eventBufLen = 0;
- writeModifierKeyEvents(modifiers);
-
- int x = evt.getX();
- int y = evt.getY();
-
- if (x < 0) x = 0;
- if (y < 0) y = 0;
-
- eventBuf[eventBufLen++] = (byte) PointerEvent;
- eventBuf[eventBufLen++] = (byte) pointerMask;
- eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff);
- eventBuf[eventBufLen++] = (byte) (x & 0xff);
- eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff);
- eventBuf[eventBufLen++] = (byte) (y & 0xff);
-
- //
- // Always release all modifiers after an "up" event
- //
-
- if (pointerMask == 0) {
- writeModifierKeyEvents(0);
- }
-
- os.write(eventBuf, 0, eventBufLen);
- }
-
-
- //
- // Write a key event message. We may need to send modifier key events
- // around it to set the correct modifier state. Also we need to translate
- // from the Java key values to the X keysym values used by the RFB protocol.
- //
-
- void writeKeyEvent(KeyEvent evt) throws IOException {
-
- int keyChar = evt.getKeyChar();
-
- //
- // Ignore event if only modifiers were pressed.
- //
-
- // Some JVMs return 0 instead of CHAR_UNDEFINED in getKeyChar().
- if (keyChar == 0)
- keyChar = KeyEvent.CHAR_UNDEFINED;
-
- if (keyChar == KeyEvent.CHAR_UNDEFINED) {
- int code = evt.getKeyCode();
- if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_SHIFT ||
- code == KeyEvent.VK_META || code == KeyEvent.VK_ALT)
- return;
- }
-
- //
- // Key press or key release?
- //
-
- boolean down = (evt.getID() == KeyEvent.KEY_PRESSED);
-
- int key;
- if (evt.isActionKey()) {
-
- //
- // An action key should be one of the following.
- // If not then just ignore the event.
- //
-
- switch(evt.getKeyCode()) {
- case KeyEvent.VK_HOME: key = 0xff50; break;
- case KeyEvent.VK_LEFT: key = 0xff51; break;
- case KeyEvent.VK_UP: key = 0xff52; break;
- case KeyEvent.VK_RIGHT: key = 0xff53; break;
- case KeyEvent.VK_DOWN: key = 0xff54; break;
- case KeyEvent.VK_PAGE_UP: key = 0xff55; break;
- case KeyEvent.VK_PAGE_DOWN: key = 0xff56; break;
- case KeyEvent.VK_END: key = 0xff57; break;
- case KeyEvent.VK_INSERT: key = 0xff63; break;
- case KeyEvent.VK_F1: key = 0xffbe; break;
- case KeyEvent.VK_F2: key = 0xffbf; break;
- case KeyEvent.VK_F3: key = 0xffc0; break;
- case KeyEvent.VK_F4: key = 0xffc1; break;
- case KeyEvent.VK_F5: key = 0xffc2; break;
- case KeyEvent.VK_F6: key = 0xffc3; break;
- case KeyEvent.VK_F7: key = 0xffc4; break;
- case KeyEvent.VK_F8: key = 0xffc5; break;
- case KeyEvent.VK_F9: key = 0xffc6; break;
- case KeyEvent.VK_F10: key = 0xffc7; break;
- case KeyEvent.VK_F11: key = 0xffc8; break;
- case KeyEvent.VK_F12: key = 0xffc9; break;
- default:
- return;
- }
-
- } else {
-
- //
- // A "normal" key press. Ordinary ASCII characters go straight through.
- // For CTRL-<letter>, CTRL is sent separately so just send <letter>.
- // Backspace, tab, return, escape and delete have special keysyms.
- // Anything else we ignore.
- //
-
- key = keyChar;
-
- if (key < 0x20) {
- if (evt.isControlDown()) {
- key += 0x60;
- } else {
- switch(key) {
- case KeyEvent.VK_BACK_SPACE: key = 0xff08; break;
- case KeyEvent.VK_TAB: key = 0xff09; break;
- case KeyEvent.VK_ENTER: key = 0xff0d; break;
- case KeyEvent.VK_ESCAPE: key = 0xff1b; break;
- }
- }
- } else if (key == 0x7f) {
- // Delete
- key = 0xffff;
- } else if (key > 0xff) {
- // JDK1.1 on X incorrectly passes some keysyms straight through,
- // so we do too. JDK1.1.4 seems to have fixed this.
- // The keysyms passed are 0xff00 .. XK_BackSpace .. XK_Delete
- // Also, we pass through foreign currency keysyms (0x20a0..0x20af).
- if ((key < 0xff00 || key > 0xffff) &&
- !(key >= 0x20a0 && key <= 0x20af))
- return;
- }
- }
-
- // Fake keyPresses for keys that only generates keyRelease events
- if ((key == 0xe5) || (key == 0xc5) || // XK_aring / XK_Aring
- (key == 0xe4) || (key == 0xc4) || // XK_adiaeresis / XK_Adiaeresis
- (key == 0xf6) || (key == 0xd6) || // XK_odiaeresis / XK_Odiaeresis
- (key == 0xa7) || (key == 0xbd) || // XK_section / XK_onehalf
- (key == 0xa3)) { // XK_sterling
- // Make sure we do not send keypress events twice on platforms
- // with correct JVMs (those that actually report KeyPress for all
- // keys)
- if (down)
- brokenKeyPressed = true;
-
- if (!down && !brokenKeyPressed) {
- // We've got a release event for this key, but haven't received
- // a press. Fake it.
- eventBufLen = 0;
- writeModifierKeyEvents(evt.getModifiers());
- writeKeyEvent(key, true);
- os.write(eventBuf, 0, eventBufLen);
- }
-
- if (!down)
- brokenKeyPressed = false;
- }
-
- eventBufLen = 0;
- writeModifierKeyEvents(evt.getModifiers());
- writeKeyEvent(key, down);
-
- // Always release all modifiers after an "up" event
- if (!down)
- writeModifierKeyEvents(0);
-
- os.write(eventBuf, 0, eventBufLen);
- }
-
-
- //
- // Add a raw key event with the given X keysym to eventBuf.
- //
-
- void writeKeyEvent(int keysym, boolean down) {
- eventBuf[eventBufLen++] = (byte) KeyboardEvent;
- eventBuf[eventBufLen++] = (byte) (down ? 1 : 0);
- eventBuf[eventBufLen++] = (byte) 0;
- eventBuf[eventBufLen++] = (byte) 0;
- eventBuf[eventBufLen++] = (byte) ((keysym >> 24) & 0xff);
- eventBuf[eventBufLen++] = (byte) ((keysym >> 16) & 0xff);
- eventBuf[eventBufLen++] = (byte) ((keysym >> 8) & 0xff);
- eventBuf[eventBufLen++] = (byte) (keysym & 0xff);
- }
-
-
- //
- // Write key events to set the correct modifier state.
- //
-
- int oldModifiers = 0;
-
- void writeModifierKeyEvents(int newModifiers) {
- if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK))
- writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0);
-
- if ((newModifiers & SHIFT_MASK) != (oldModifiers & SHIFT_MASK))
- writeKeyEvent(0xffe1, (newModifiers & SHIFT_MASK) != 0);
-
- if ((newModifiers & META_MASK) != (oldModifiers & META_MASK))
- writeKeyEvent(0xffe7, (newModifiers & META_MASK) != 0);
-
- if ((newModifiers & ALT_MASK) != (oldModifiers & ALT_MASK))
- writeKeyEvent(0xffe9, (newModifiers & ALT_MASK) != 0);
-
- oldModifiers = newModifiers;
- }
-
-
- public void startTiming() {
- timing = true;
-
- // Carry over up to 1s worth of previous rate for smoothing.
-
- if (timeWaitedIn100us > 10000) {
- timedKbits = timedKbits * 10000 / timeWaitedIn100us;
- timeWaitedIn100us = 10000;
- }
- }
-
- public void stopTiming() {
- timing = false;
- if (timeWaitedIn100us < timedKbits/2)
- timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
- }
-
- public long kbitsPerSecond() {
- return timedKbits * 10000 / timeWaitedIn100us;
- }
-
- public long timeWaited() {
- return timeWaitedIn100us;
- }
-
- //
- // Methods for reading data via our DataInputStream member variable (is).
- //
- // In addition to reading data, the readFully() methods updates variables
- // used to estimate data throughput.
- //
-
- public void readFully(byte b[]) throws IOException {
- readFully(b, 0, b.length);
- }
-
- public void readFully(byte b[], int off, int len) throws IOException {
- long before = 0;
- if (timing)
- before = System.currentTimeMillis();
-
- is.readFully(b, off, len);
-
- if (timing) {
- long after = System.currentTimeMillis();
- long newTimeWaited = (after - before) * 10;
- int newKbits = len * 8 / 1000;
-
- // limit rate to between 10kbit/s and 40Mbit/s
-
- if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
- if (newTimeWaited < newKbits/4) newTimeWaited = newKbits/4;
-
- timeWaitedIn100us += newTimeWaited;
- timedKbits += newKbits;
- }
-
- numBytesRead += len;
- }
-
- final int available() throws IOException {
- return is.available();
- }
-
- // FIXME: DataInputStream::skipBytes() is not guaranteed to skip
- // exactly n bytes. Probably we don't want to use this method.
- final int skipBytes(int n) throws IOException {
- int r = is.skipBytes(n);
- numBytesRead += r;
- return r;
- }
-
- final int readU8() throws IOException {
- int r = is.readUnsignedByte();
- numBytesRead++;
- return r;
- }
-
- final int readU16() throws IOException {
- int r = is.readUnsignedShort();
- numBytesRead += 2;
- return r;
- }
-
- final int readU32() throws IOException {
- int r = is.readInt();
- numBytesRead += 4;
- return r;
- }
-
- public void setStreams(InputStream is_, OutputStream os_) {
- is = new DataInputStream(is_);
- os = os_;
- }
-}
diff --git a/java/src/com/tigervnc/vncviewer/ServerDialog.java b/java/src/com/tigervnc/vncviewer/ServerDialog.java
new file mode 100644
index 0000000..86160f8
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/ServerDialog.java
@@ -0,0 +1,178 @@
+/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.vncviewer;
+
+import java.awt.*;
+import java.awt.image.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import java.net.URL;
+import java.io.File;
+import java.util.*;
+
+import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Exception;
+
+class ServerDialog extends Dialog implements
+ ActionListener,
+ ItemListener
+{
+
+ public ServerDialog(OptionsDialog options_,
+ String defaultServerName, CConn cc_) {
+
+ super(true);
+ cc = cc_;
+ setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+ setResizable(false);
+ setSize(new Dimension(340, 135));
+ setTitle("VNC Viewer : Connection Details");
+
+ options = options_;
+ getContentPane().setLayout(new GridBagLayout());
+
+ JLabel serverLabel = new JLabel("Server:", JLabel.RIGHT);
+ if (options.defaults.getString("server") != null) {
+ server = new JComboBox(options.defaults.getString("server").split(","));
+ } else {
+ server = new JComboBox();
+ }
+
+ // Hack to set the left inset on editable JComboBox
+ if (UIManager.getLookAndFeel().getID() == "Windows") {
+ server.setBorder(BorderFactory.createCompoundBorder(server.getBorder(),
+ BorderFactory.createEmptyBorder(0,2,0,0)));
+ } else {
+ ComboBoxEditor editor = server.getEditor();
+ JTextField jtf = (JTextField)editor.getEditorComponent();
+ jtf.setBorder(new CompoundBorder(jtf.getBorder(), new EmptyBorder(0,2,0,0)));
+ }
+
+ server.setEditable(true);
+ editor = server.getEditor();
+ JLabel encryptionLabel = new JLabel("Encryption:");
+ encryption = new JComboBox();
+ serverLabel.setPreferredSize(encryptionLabel.getPreferredSize());
+
+ JPanel topPanel = new JPanel(new GridBagLayout());
+
+ addGBComponent(new JLabel(cc.logo),topPanel, 0, 0, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(5,5,5,15));
+ addGBComponent(serverLabel,topPanel, 1, 0, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_END, new Insets(10,0,5,5));
+ addGBComponent(server,topPanel, 2, 0, 1, 1, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(10,0,5,40));
+
+ optionsButton = new JButton("Options...");
+ aboutButton = new JButton("About...");
+ okButton = new JButton("OK");
+ cancelButton = new JButton("Cancel");
+ JPanel buttonPanel = new JPanel(new GridBagLayout());
+ buttonPanel.setPreferredSize(new Dimension(340, 40));
+ addGBComponent(aboutButton,buttonPanel, 0, 3, 1, 1, 0, 0, 0.2, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5));
+ addGBComponent(optionsButton,buttonPanel, 1, 3, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5));
+ addGBComponent(okButton,buttonPanel, 2, 3, 1, 1, 0, 0, 0.8, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5));
+ addGBComponent(cancelButton,buttonPanel, 3, 3, 1, 1, 0, 0, 0.5, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5));
+
+ GridBagConstraints gbc = new GridBagConstraints();
+ gbc.anchor = GridBagConstraints.LINE_START;
+ gbc.fill = GridBagConstraints.BOTH;
+ gbc.gridwidth = GridBagConstraints.REMAINDER;
+ gbc.gridheight = 1;
+ gbc.insets = new Insets(0,0,0,0);
+ gbc.ipadx = 0;
+ gbc.ipady = 0;
+ gbc.weightx = 1;
+ gbc.weighty = 1;
+ getContentPane().add(topPanel,gbc);
+ getContentPane().add(buttonPanel);
+
+ server.addActionListener(this);
+ optionsButton.addActionListener(this);
+ aboutButton.addActionListener(this);
+ okButton.addActionListener(this);
+ cancelButton.addActionListener(this);
+
+ pack();
+ }
+
+ public void itemStateChanged(ItemEvent e) {
+ Object s = e.getSource();
+ if (s instanceof JComboBox && (JComboBox)s == encryption) {
+ options.encryption=(encryption.getSelectedIndex()==1) ? false : true;
+ }
+ }
+
+ public void actionPerformed(ActionEvent e) {
+ Object s = e.getSource();
+ if (s instanceof JButton && (JButton)s == okButton) {
+ ok = true;
+ endDialog();
+ } else if (s instanceof JButton && (JButton)s == cancelButton) {
+ ok = false;
+ endDialog();
+ } else if (s instanceof JButton && (JButton)s == optionsButton) {
+ options.showDialog();
+ } 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);
+ ok = true;
+ endDialog();
+ }
+ }
+ }
+
+ public void endDialog() {
+ if (ok) {
+ options.defaults.setPref("encryption",(encryption.getSelectedIndex()==1) ? "off" : "on");
+ if (!server.getSelectedItem().toString().equals("")) {
+ String t = (options.defaults.getString("server")==null) ? "" : options.defaults.getString("server");
+ StringTokenizer st = new StringTokenizer(t, ",");
+ StringBuffer sb = new StringBuffer().append((String)server.getSelectedItem());
+ while (st.hasMoreTokens()) {
+ String s = st.nextToken();
+ if (!s.equals((String)server.getSelectedItem()) && !s.equals("")) {
+ sb.append(',');
+ sb.append(s);
+ }
+ }
+ options.defaults.setPref("server", sb.toString());
+ }
+ try {
+ options.defaults.Save();
+ } catch (java.lang.Exception x) { }
+ }
+ done = true;
+ if (modal) {
+ synchronized (this) {
+ notify();
+ }
+ }
+ this.dispose();
+ }
+
+ CConn cc;
+ JComboBox encryption, server;
+ ComboBoxEditor editor;
+ JButton aboutButton, optionsButton, okButton, cancelButton;
+ OptionsDialog options;
+ static LogWriter vlog = new LogWriter("ServerDialog");
+
+}
diff --git a/java/src/com/tigervnc/vncviewer/SessionRecorder.java b/java/src/com/tigervnc/vncviewer/SessionRecorder.java
deleted file mode 100644
index 2ae7079..0000000
--- a/java/src/com/tigervnc/vncviewer/SessionRecorder.java
+++ /dev/null
@@ -1,195 +0,0 @@
-//
-// Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
-
-//
-// SessionRecorder is a class to write FBS (FrameBuffer Stream) files.
-// FBS files are used to save RFB sessions for later playback.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.io.*;
-
-class SessionRecorder {
-
- protected FileOutputStream f;
- protected DataOutputStream df;
- protected long startTime, lastTimeOffset;
-
- protected byte[] buffer;
- protected int bufferSize;
- protected int bufferBytes;
-
- public SessionRecorder(String name, int bufsize) throws IOException {
- f = new FileOutputStream(name);
- df = new DataOutputStream(f);
- startTime = System.currentTimeMillis();
- lastTimeOffset = 0;
-
- bufferSize = bufsize;
- bufferBytes = 0;
- buffer = new byte[bufferSize];
- }
-
- public SessionRecorder(String name) throws IOException {
- this(name, 65536);
- }
-
- //
- // Close the file, free resources.
- //
-
- public void close() throws IOException {
- try {
- flush();
- } catch (IOException e) {
- }
-
- df = null;
- f.close();
- f = null;
- buffer = null;
- }
-
- //
- // Write the FBS file header as defined in the rfbproxy utility.
- //
-
- public void writeHeader() throws IOException {
- df.write("FBS 001.000\n".getBytes());
- }
-
- //
- // Write one byte.
- //
-
- public void writeByte(int b) throws IOException {
- prepareWriting();
- buffer[bufferBytes++] = (byte)b;
- }
-
- //
- // Write 16-bit value, big-endian.
- //
-
- public void writeShortBE(int v) throws IOException {
- prepareWriting();
- buffer[bufferBytes++] = (byte)(v >> 8);
- buffer[bufferBytes++] = (byte)v;
- }
-
- //
- // Write 32-bit value, big-endian.
- //
-
- public void writeIntBE(int v) throws IOException {
- prepareWriting();
- buffer[bufferBytes] = (byte)(v >> 24);
- buffer[bufferBytes + 1] = (byte)(v >> 16);
- buffer[bufferBytes + 2] = (byte)(v >> 8);
- buffer[bufferBytes + 3] = (byte)v;
- bufferBytes += 4;
- }
-
- //
- // Write 16-bit value, little-endian.
- //
-
- public void writeShortLE(int v) throws IOException {
- prepareWriting();
- buffer[bufferBytes++] = (byte)v;
- buffer[bufferBytes++] = (byte)(v >> 8);
- }
-
- //
- // Write 32-bit value, little-endian.
- //
-
- public void writeIntLE(int v) throws IOException {
- prepareWriting();
- buffer[bufferBytes] = (byte)v;
- buffer[bufferBytes + 1] = (byte)(v >> 8);
- buffer[bufferBytes + 2] = (byte)(v >> 16);
- buffer[bufferBytes + 3] = (byte)(v >> 24);
- bufferBytes += 4;
- }
-
- //
- // Write byte arrays.
- //
-
- public void write(byte b[], int off, int len) throws IOException {
- prepareWriting();
- while (len > 0) {
- if (bufferBytes > bufferSize - 4)
- flush(false);
-
- int partLen;
- if (bufferBytes + len > bufferSize) {
- partLen = bufferSize - bufferBytes;
- } else {
- partLen = len;
- }
- System.arraycopy(b, off, buffer, bufferBytes, partLen);
- bufferBytes += partLen;
- off += partLen;
- len -= partLen;
- }
- }
-
- public void write(byte b[]) throws IOException {
- write(b, 0, b.length);
- }
-
- //
- // Flush the output. This method saves buffered data in the
- // underlying file object adding data sizes and timestamps. If the
- // updateTimeOffset is set to false, then the current time offset
- // will not be changed for next write operation.
- //
-
- public void flush(boolean updateTimeOffset) throws IOException {
- if (bufferBytes > 0) {
- df.writeInt(bufferBytes);
- df.write(buffer, 0, (bufferBytes + 3) & 0x7FFFFFFC);
- df.writeInt((int)lastTimeOffset);
- bufferBytes = 0;
- if (updateTimeOffset)
- lastTimeOffset = -1;
- }
- }
-
- public void flush() throws IOException {
- flush(true);
- }
-
- //
- // Before writing any data, remember time offset and flush the
- // buffer before it becomes full.
- //
-
- protected void prepareWriting() throws IOException {
- if (lastTimeOffset == -1)
- lastTimeOffset = System.currentTimeMillis() - startTime;
- if (bufferBytes > bufferSize - 4)
- flush(false);
- }
-
-}
-
diff --git a/java/src/com/tigervnc/vncviewer/SocketFactory.java b/java/src/com/tigervnc/vncviewer/SocketFactory.java
deleted file mode 100644
index 30460dc..0000000
--- a/java/src/com/tigervnc/vncviewer/SocketFactory.java
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// Copyright (C) 2002 HorizonLive.com, Inc. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
-
-//
-// SocketFactory.java describes an interface used to substitute the
-// standard Socket class by its alternative implementations.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.applet.*;
-import java.net.*;
-import java.io.*;
-
-public interface SocketFactory {
-
- public Socket createSocket(String host, int port, Applet applet)
- throws IOException;
-
- public Socket createSocket(String host, int port, String[] args)
- throws IOException;
-}
diff --git a/java/src/com/tigervnc/vncviewer/TLSTunnel.java b/java/src/com/tigervnc/vncviewer/TLSTunnel.java
deleted file mode 100644
index 00cfb4a..0000000
--- a/java/src/com/tigervnc/vncviewer/TLSTunnel.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2003 Sun Microsystems, Inc.
- * Copyright (C) 2003-2010 Martin Koegler
- * Copyright (C) 2006 OCCAM Financial Technology
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-package com.tigervnc.vncviewer;
-
-import java.util.*;
-import java.net.*;
-import javax.net.ssl.*;
-
-public class TLSTunnel extends TLSTunnelBase
-{
-
- public TLSTunnel (Socket sock_)
- {
- super (sock_);
- }
-
-
- protected void setParam (SSLSocket sock)
- {
- String[]supported;
- ArrayList enabled = new ArrayList ();
-
- supported = sock.getSupportedCipherSuites ();
-
- for (int i = 0; i < supported.length; i++)
- if (supported[i].matches (".*DH_anon.*"))
- enabled.add (supported[i]);
-
- sock.setEnabledCipherSuites ((String[])enabled.toArray (new String[0]));
- }
-
-}
diff --git a/java/src/com/tigervnc/vncviewer/TLSTunnelBase.java b/java/src/com/tigervnc/vncviewer/TLSTunnelBase.java
deleted file mode 100644
index 922e837..0000000
--- a/java/src/com/tigervnc/vncviewer/TLSTunnelBase.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2003 Sun Microsystems, Inc.
- * Copyright (C) 2003-2010 Martin Koegler
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-package com.tigervnc.vncviewer;
-
-import java.util.ArrayList;
-import java.net.*;
-import javax.net.ssl.*;
-
-public abstract class TLSTunnelBase
-{
-
- public TLSTunnelBase (Socket sock_)
- {
- sock = sock_;
- }
-
- protected void initContext (SSLContext sc) throws java.security.
- GeneralSecurityException
- {
- sc.init (null, null, null);
- }
-
- public void setup (RfbProto cc) throws Exception
- {
- if (cc.readU8 () == 0)
- throw new Exception("Setup on the server failed");
- try
- {
- SSLSocketFactory sslfactory;
- SSLSocket sslsock;
- SSLContext sc = SSLContext.getInstance ("TLS");
- System.out.println("Generating TLS context");
- initContext (sc);
- System.out.println("Doing TLS handshake");
- sslfactory = sc.getSocketFactory ();
- sslsock = (SSLSocket) sslfactory.createSocket (sock,
- sock.getInetAddress ().
- getHostName (),
- sock.getPort (), true);
-
- setParam (sslsock);
-
- /* Not neccessary - just ensures that we know what cipher
- * suite we are using for the output of toString()
- */
- sslsock.startHandshake ();
-
- System.out.println("TLS done");
-
- cc.setStreams (sslsock.getInputStream (),
- sslsock.getOutputStream ());
- }
- catch (java.io.IOException e)
- {
- throw new Exception("TLS handshake failed " + e.toString ());
- }
- catch (java.security.GeneralSecurityException e)
- {
- throw new Exception("TLS handshake failed " + e.toString ());
- }
- }
-
-
- protected abstract void setParam (SSLSocket sock);
-
- Socket sock;
-
-}
diff --git a/java/src/com/tigervnc/vncviewer/UserPrefs.java b/java/src/com/tigervnc/vncviewer/UserPrefs.java
new file mode 100644
index 0000000..51b8b90
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/UserPrefs.java
@@ -0,0 +1,258 @@
+/* Copyright (C) 2011 TigerVNC Team. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+package com.tigervnc.vncviewer;
+
+import java.util.Properties;
+import java.io.FileOutputStream;
+import javax.swing.filechooser.*;
+
+/**
+ * Simple, generic class to load up or save properties for an application.
+ * (eg user preferences)
+ *
+ * While reading in values, it will automatically convert from string format,
+ * to your choice of int or boolean, if desired.
+ *
+ * We assume that you want properties stored in a file named
+ * $HOME/.yourappname, where $HOME represents the users "home directory"
+ *
+ * Feel free to email comments or suggestions to the author
+ *
+ * @version 1.0, 09/16/1997
+ * @author Philip Brown phil@bolthole.com
+ */
+
+public class UserPrefs extends Properties {
+ String userhome=null; //This will have fileseperator on end if it
+ String prefFile;
+ String appName;
+
+ Properties systemprops;
+
+
+ /**
+ * We try to read in a preferences file, from the user's "HOME"
+ * directory. We base the name of the file, on the name of the
+ * application we are in.
+ * Use the getHomeDir() call if you want to know what directory
+ * this is in.
+ *
+ * @param appName_ name of application calling this class
+ */
+ public UserPrefs(String appName_) {
+ appName = appName_;
+
+ systemprops=System.getProperties();
+ // This is guaranteed as always being some valid directory,
+ // according to spec.
+ prefFile= getHomeDir()+getFileSeperator()+
+ "."+appName;
+ try {
+ load(new java.io.FileInputStream(prefFile));
+ } catch (Exception err) {
+ if(err instanceof java.io.FileNotFoundException) {
+ try {
+ store(new FileOutputStream(prefFile), appName+" preferences");
+ } catch (Exception e) { /* FIXME */ }
+ } else {
+ // FIXME - should be a dialog
+ System.out.println("Error opening prefs file:"+err.getMessage());
+ }
+ }
+
+ }
+
+ // Strip off any comments
+ String trimValue(String prop) {
+ if(prop==null)
+ return null;
+
+ int lastpos;
+ lastpos=prop.indexOf('#');
+ if(lastpos==-1)
+ lastpos=prop.length()-1;
+ while((prop.charAt(lastpos)==' ') ||
+ (prop.charAt(lastpos)=='\t')) {
+ lastpos--;
+ if(lastpos==0)
+ break;
+ }
+
+ return prop.substring(0, lastpos+1);
+ }
+
+ /**
+ * The java spec guarantees that a "home" directory be
+ * specified. We look it up for our own uses at initialization
+ * If you want to do other things in the user's home dir,
+ * this routine is an easy way to find out where it is.
+ *
+ * This returns string that will have trailing fileseparator, eg "/")
+ * so you can slap together a filename directly after it, and
+ * not worry about that sort of junk.
+ */
+ final public static String getHomeDir() {
+ String homeDir = null;
+ String os = System.getProperty("os.name");
+ try {
+ if (os.startsWith("Windows")) {
+ // JRE prior to 1.5 cannot reliably determine USERPROFILE
+ // return user.home and hope it's right...
+ if (Integer.parseInt(System.getProperty("java.version").split("\\.")[1]) < 5) {
+ homeDir = System.getProperty("user.home");
+ } else {
+ homeDir = System.getenv("USERPROFILE");
+ }
+ } else {
+ homeDir = FileSystemView.getFileSystemView().
+ getDefaultDirectory().getCanonicalPath();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return homeDir;
+ }
+
+ final private String getUserName() {
+ String userName = (String) System.getProperties().get("user.name");
+ return userName;
+ }
+
+ final public static String getFileSeperator() {
+ String seperator = System.getProperties().get("file.separator").toString();
+ return seperator;
+ }
+
+ /**
+ * way to directly set a preference. You'll have to
+ * do your own type conversion.
+ * @param prefname name of property
+ * @param value string value of property
+ */
+ public void setPref(String prefname, String value) {
+ setProperty(prefname, value);
+ }
+
+ public void setPref(String prefname, int value) {
+ systemprops.put(prefname, java.lang.Integer.toString(value));
+ }
+
+ public void setPref(String prefname, boolean value) {
+ put(prefname, (value ? "true" : "false"));
+ }
+
+ /**
+ * Gets named resource, as a string value.
+ * returns null if no such resource defined.
+ * @param name name of property
+ */
+ public String getString(String name) {
+ return trimValue(getProperty(name));
+ }
+ /**
+ * Gets named resource, as a string value.
+ *
+ * @param name name of property
+ * @param defval default value to remember and return , if
+ * no existing property name found.
+ */
+ public String getString(String name, String defstr) {
+ String val = trimValue(getProperty(name));
+ if(val==null) {
+ setPref(name, defstr);
+ return defstr;
+ }
+ return val;
+ }
+
+ /**
+ * look up property that is an int value
+ * @param name name of property
+ */
+ public int getInt(String name) {
+ String strint = trimValue(getProperty(name));
+ int val=0;
+ try {
+ val = Integer.parseInt(strint);
+ } catch (NumberFormatException err) {
+ //we dont care
+ }
+ return val;
+ }
+
+ /**
+ * look up property that is an int value
+ * @param name name of property
+ * @param defval default value to remember and return , if
+ * no existing property name found.
+ */
+ public int getInt(String name, int defval) {
+ String strint = trimValue(getProperty(name));
+ if(strint==null) {
+ setPref(name, String.valueOf(defval));
+ return defval;
+ }
+ int val=0;
+ try {
+ val = Integer.parseInt(strint);
+ } catch (NumberFormatException err) {
+ //we dont care
+ }
+ return val;
+ }
+
+ /**
+ * look up property that is a boolean value
+ * @param name name of property
+ * @param defval default value to remember and return , if
+ * no existing property name found.
+ */
+ public boolean getBool(String name) {
+ String strval = trimValue(getProperty(name));
+ if(strval.equals("true"))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * @param name name of property
+ * @param defval default value to remember and return , if
+ * no existing property name found.
+ */
+ public boolean getBool(String name, boolean defval) {
+ String strval = trimValue(getProperty(name));
+ if(strval==null) {
+ setPref(name, String.valueOf(defval));
+ return defval;
+ }
+
+ if(strval.equals("true"))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * save user preferences to default file. Duh.
+ */
+ public void Save() throws java.io.IOException {
+ store(new FileOutputStream(prefFile), appName+" preferences");
+ }
+}
diff --git a/java/src/com/tigervnc/vncviewer/VncCanvas.java b/java/src/com/tigervnc/vncviewer/VncCanvas.java
deleted file mode 100644
index b776481..0000000
--- a/java/src/com/tigervnc/vncviewer/VncCanvas.java
+++ /dev/null
@@ -1,1092 +0,0 @@
-//
-// Copyright (C) 2004 Horizon Wimba. All Rights Reserved.
-// Copyright (C) 2001-2003 HorizonLive.com, Inc. All Rights Reserved.
-// Copyright (C) 2001,2002 Constantin Kaplinsky. All Rights Reserved.
-// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
-// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
-
-package com.tigervnc.vncviewer;
-
-import com.tigervnc.decoder.CoRREDecoder;
-import com.tigervnc.decoder.CopyRectDecoder;
-import com.tigervnc.decoder.HextileDecoder;
-import com.tigervnc.decoder.RREDecoder;
-import com.tigervnc.decoder.RawDecoder;
-import com.tigervnc.decoder.TightDecoder;
-import com.tigervnc.decoder.ZRLEDecoder;
-import com.tigervnc.decoder.ZlibDecoder;
-import com.tigervnc.decoder.common.Repaintable;
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.image.*;
-import java.io.*;
-import java.lang.*;
-import java.util.zip.*;
-
-
-//
-// VncCanvas is a subclass of Canvas which draws a VNC desktop on it.
-//
-
-class VncCanvas extends Canvas
- implements KeyListener, MouseListener, MouseMotionListener, Repaintable, Runnable {
-
- VncViewer viewer;
- RfbProto rfb;
- ColorModel cm8, cm24;
- int bytesPixel;
-
- int maxWidth = 0, maxHeight = 0;
- int scalingFactor;
- int scaledWidth, scaledHeight;
-
- Image memImage;
- Graphics memGraphics;
-
- //
- // Decoders
- //
-
- RawDecoder rawDecoder;
- RREDecoder rreDecoder;
- CoRREDecoder correDecoder;
- ZlibDecoder zlibDecoder;
- HextileDecoder hextileDecoder;
- ZRLEDecoder zrleDecoder;
- TightDecoder tightDecoder;
- CopyRectDecoder copyRectDecoder;
-
- // Base decoder decoders array
- RawDecoder []decoders = null;
-
- // Update statistics.
- long statStartTime; // time on first framebufferUpdateRequest
- long statNumUpdates; // counter for FramebufferUpdate messages
- long statNumTotalRects; // rectangles in FramebufferUpdate messages
- long statNumPixelRects; // the same, but excluding pseudo-rectangles
- long statNumRectsTight; // Tight-encoded rectangles (including JPEG)
- long statNumRectsTightJPEG; // JPEG-compressed Tight-encoded rectangles
- long statNumRectsZRLE; // ZRLE-encoded rectangles
- long statNumRectsHextile; // Hextile-encoded rectangles
- long statNumRectsRaw; // Raw-encoded rectangles
- long statNumRectsCopy; // CopyRect rectangles
- long statNumBytesEncoded; // number of bytes in updates, as received
- long statNumBytesDecoded; // number of bytes, as if Raw encoding was used
-
- // True if we process keyboard and mouse events.
- boolean inputEnabled;
-
- // True if was no one auto resize of canvas
- boolean isFirstSizeAutoUpdate = true;
-
- // Members for limiting sending mouse events to server
- long lastMouseEventSendTime = System.currentTimeMillis();
- long mouseMaxFreq = 20;
-
- //
- // The constructors.
- //
-
- public VncCanvas(VncViewer v, int maxWidth_, int maxHeight_)
- throws IOException {
-
- viewer = v;
- maxWidth = maxWidth_;
- maxHeight = maxHeight_;
-
- rfb = viewer.rfb;
- scalingFactor = viewer.options.scalingFactor;
-
- cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
- cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
-
- //
- // Create decoders
- //
-
- // Input stream for decoders
- RfbInputStream rfbis = new RfbInputStream(rfb);
- // Create output stream for session recording
- RecordOutputStream ros = new RecordOutputStream(rfb);
-
- rawDecoder = new RawDecoder(memGraphics, rfbis);
- rreDecoder = new RREDecoder(memGraphics, rfbis);
- correDecoder = new CoRREDecoder(memGraphics, rfbis);
- hextileDecoder = new HextileDecoder(memGraphics, rfbis);
- tightDecoder = new TightDecoder(memGraphics, rfbis);
- zlibDecoder = new ZlibDecoder(memGraphics, rfbis);
- zrleDecoder = new ZRLEDecoder(memGraphics, rfbis);
- copyRectDecoder = new CopyRectDecoder(memGraphics, rfbis);
-
- //
- // Set data for decoders that needs extra parameters
- //
-
- hextileDecoder.setRepainableControl(this);
- tightDecoder.setRepainableControl(this);
-
- //
- // Create array that contains our decoders
- //
-
- decoders = new RawDecoder[8];
- decoders[0] = rawDecoder;
- decoders[1] = rreDecoder;
- decoders[2] = correDecoder;
- decoders[3] = hextileDecoder;
- decoders[4] = zlibDecoder;
- decoders[5] = tightDecoder;
- decoders[6] = zrleDecoder;
- decoders[7] = copyRectDecoder;
-
- //
- // Set session recorder for decoders
- //
-
- for (int i = 0; i < decoders.length; i++) {
- decoders[i].setDataOutputStream(ros);
- }
-
- setPixelFormat();
-
- inputEnabled = false;
- if (!viewer.options.viewOnly)
- enableInput(true);
-
- // Enable mouse and keyboard event listeners.
- addKeyListener(this);
- addMouseListener(this);
- addMouseMotionListener(this);
-
- // Create thread, that will send mouse movement events
- // to VNC server.
- Thread mouseThread = new Thread(this);
- mouseThread.start();
- }
-
- public VncCanvas(VncViewer v) throws IOException {
- this(v, 0, 0);
- }
-
- //
- // Callback methods to determine geometry of our Component.
- //
-
- public Dimension getPreferredSize() {
- return new Dimension(scaledWidth, scaledHeight);
- }
-
- public Dimension getMinimumSize() {
- return new Dimension(scaledWidth, scaledHeight);
- }
-
- public Dimension getMaximumSize() {
- return new Dimension(scaledWidth, scaledHeight);
- }
-
- //
- // All painting is performed here.
- //
-
- public void update(Graphics g) {
- paint(g);
- }
-
- public void paint(Graphics g) {
- synchronized(memImage) {
- if (rfb.framebufferWidth == scaledWidth) {
- g.drawImage(memImage, 0, 0, null);
- } else {
- paintScaledFrameBuffer(g);
- }
- }
- if (showSoftCursor) {
- int x0 = cursorX - hotX, y0 = cursorY - hotY;
- Rectangle r = new Rectangle(x0, y0, cursorWidth, cursorHeight);
- if (r.intersects(g.getClipBounds())) {
- g.drawImage(softCursor, x0, y0, null);
- }
- }
- }
-
- public void paintScaledFrameBuffer(Graphics g) {
- g.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null);
- }
-
- //
- // Start/stop receiving mouse events. Keyboard events are received
- // even in view-only mode, because we want to map the 'r' key to the
- // screen refreshing function.
- //
-
- public synchronized void enableInput(boolean enable) {
- if (enable && !inputEnabled) {
- inputEnabled = true;
- if (viewer.showControls) {
- viewer.buttonPanel.enableRemoteAccessControls(true);
- }
- createSoftCursor(); // scaled cursor
- } else if (!enable && inputEnabled) {
- inputEnabled = false;
- if (viewer.showControls) {
- viewer.buttonPanel.enableRemoteAccessControls(false);
- }
- createSoftCursor(); // non-scaled cursor
- }
- }
-
- public void setPixelFormat() throws IOException {
- if (viewer.options.eightBitColors) {
- rfb.writeSetPixelFormat(8, 8, false, true, 7, 7, 3, 0, 3, 6);
- bytesPixel = 1;
- } else {
- rfb.writeSetPixelFormat(32, 24, false, true, 255, 255, 255, 16, 8, 0);
- bytesPixel = 4;
- }
- updateFramebufferSize();
- }
-
- void setScalingFactor(int sf) {
- scalingFactor = sf;
- updateFramebufferSize();
- invalidate();
- }
-
- void updateFramebufferSize() {
-
- // Useful shortcuts.
- int fbWidth = rfb.framebufferWidth;
- int fbHeight = rfb.framebufferHeight;
-
- // FIXME: This part of code must be in VncViewer i think
- if (viewer.options.autoScale) {
- if (viewer.inAnApplet) {
- maxWidth = viewer.getWidth();
- maxHeight = viewer.getHeight();
- } else {
- if (viewer.vncFrame != null) {
- if (isFirstSizeAutoUpdate) {
- isFirstSizeAutoUpdate = false;
- Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize();
- maxWidth = (int)screenSize.getWidth() - 100;
- maxHeight = (int)screenSize.getHeight() - 100;
- viewer.vncFrame.setSize(maxWidth, maxHeight);
- } else {
- viewer.desktopScrollPane.doLayout();
- maxWidth = viewer.desktopScrollPane.getWidth();
- maxHeight = viewer.desktopScrollPane.getHeight();
- }
- } else {
- maxWidth = fbWidth;
- maxHeight = fbHeight;
- }
- }
- int f1 = maxWidth * 100 / fbWidth;
- int f2 = maxHeight * 100 / fbHeight;
- scalingFactor = Math.min(f1, f2);
- if (scalingFactor > 100)
- scalingFactor = 100;
- System.out.println("Scaling desktop at " + scalingFactor + "%");
- }
-
- // Update scaled framebuffer geometry.
- scaledWidth = (fbWidth * scalingFactor + 50) / 100;
- scaledHeight = (fbHeight * scalingFactor + 50) / 100;
-
- // Create new off-screen image either if it does not exist, or if
- // its geometry should be changed. It's not necessary to replace
- // existing image if only pixel format should be changed.
- if (memImage == null) {
- memImage = viewer.vncContainer.createImage(fbWidth, fbHeight);
- memGraphics = memImage.getGraphics();
- } else if (memImage.getWidth(null) != fbWidth ||
- memImage.getHeight(null) != fbHeight) {
- synchronized(memImage) {
- memImage = viewer.vncContainer.createImage(fbWidth, fbHeight);
- memGraphics = memImage.getGraphics();
- }
- }
-
- //
- // Update decoders
- //
-
- //
- // FIXME: Why decoders can be null here?
- //
-
- if (decoders != null) {
- for (int i = 0; i < decoders.length; i++) {
- //
- // Set changes to every decoder that we can use
- //
-
- decoders[i].setBPP(bytesPixel);
- decoders[i].setFrameBufferSize(fbWidth, fbHeight);
- decoders[i].setGraphics(memGraphics);
-
- //
- // Update decoder
- //
-
- decoders[i].update();
- }
- }
-
- // FIXME: This part of code must be in VncViewer i think
- // Update the size of desktop containers.
- if (viewer.inSeparateFrame) {
- if (viewer.desktopScrollPane != null) {
- if (!viewer.options.autoScale) {
- resizeDesktopFrame();
- } else {
- setSize(scaledWidth, scaledHeight);
- viewer.desktopScrollPane.setSize(maxWidth + 200,
- maxHeight + 200);
- }
- }
- } else {
- setSize(scaledWidth, scaledHeight);
- }
- viewer.moveFocusToDesktop();
- }
-
- void resizeDesktopFrame() {
- setSize(scaledWidth, scaledHeight);
-
- // FIXME: Find a better way to determine correct size of a
- // ScrollPane. -- const
- Insets insets = viewer.desktopScrollPane.getInsets();
- viewer.desktopScrollPane.setSize(scaledWidth +
- 2 * Math.min(insets.left, insets.right),
- scaledHeight +
- 2 * Math.min(insets.top, insets.bottom));
-
- viewer.vncFrame.pack();
-
- // Try to limit the frame size to the screen size.
-
- Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize();
- Dimension frameSize = viewer.vncFrame.getSize();
- Dimension newSize = frameSize;
-
- // Reduce Screen Size by 30 pixels in each direction;
- // This is a (poor) attempt to account for
- // 1) Menu bar on Macintosh (should really also account for
- // Dock on OSX). Usually 22px on top of screen.
- // 2) Taxkbar on Windows (usually about 28 px on bottom)
- // 3) Other obstructions.
-
- screenSize.height -= 30;
- screenSize.width -= 30;
-
- boolean needToResizeFrame = false;
- if (frameSize.height > screenSize.height) {
- newSize.height = screenSize.height;
- needToResizeFrame = true;
- }
- if (frameSize.width > screenSize.width) {
- newSize.width = screenSize.width;
- needToResizeFrame = true;
- }
- if (needToResizeFrame) {
- viewer.vncFrame.setSize(newSize);
- }
-
- viewer.desktopScrollPane.doLayout();
- }
-
- //
- // processNormalProtocol() - executed by the rfbThread to deal with the
- // RFB socket.
- //
-
- public void processNormalProtocol() throws Exception {
-
- // Start/stop session recording if necessary.
- viewer.checkRecordingStatus();
-
- rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
- rfb.framebufferHeight, false);
-
- resetStats();
- boolean statsRestarted = false;
-
- //
- // main dispatch loop
- //
-
- while (true) {
-
- // Read message type from the server.
- int msgType = rfb.readServerMessageType();
-
- // Process the message depending on its type.
- switch (msgType) {
- case RfbProto.FramebufferUpdate:
-
- if (statNumUpdates == viewer.debugStatsExcludeUpdates &&
- !statsRestarted) {
- resetStats();
- statsRestarted = true;
- } else if (statNumUpdates == viewer.debugStatsMeasureUpdates &&
- statsRestarted) {
- viewer.disconnect();
- }
-
- rfb.readFramebufferUpdate();
- statNumUpdates++;
-
- boolean cursorPosReceived = false;
-
- for (int i = 0; i < rfb.updateNRects; i++) {
-
- rfb.readFramebufferUpdateRectHdr();
- statNumTotalRects++;
- int rx = rfb.updateRectX, ry = rfb.updateRectY;
- int rw = rfb.updateRectW, rh = rfb.updateRectH;
-
- if (rfb.updateRectEncoding == rfb.EncodingLastRect)
- break;
-
- if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) {
- rfb.setFramebufferSize(rw, rh);
- updateFramebufferSize();
- break;
- }
-
- if (rfb.updateRectEncoding == rfb.EncodingXCursor ||
- rfb.updateRectEncoding == rfb.EncodingRichCursor) {
- handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry, rw, rh);
- continue;
- }
-
- if (rfb.updateRectEncoding == rfb.EncodingPointerPos) {
- softCursorMove(rx, ry);
- cursorPosReceived = true;
- continue;
- }
-
- long numBytesReadBefore = rfb.getNumBytesRead();
-
- rfb.startTiming();
-
- switch (rfb.updateRectEncoding) {
- case RfbProto.EncodingRaw:
- statNumRectsRaw++;
- handleRawRect(rx, ry, rw, rh);
- break;
- case RfbProto.EncodingCopyRect:
- statNumRectsCopy++;
- handleCopyRect(rx, ry, rw, rh);
- break;
- case RfbProto.EncodingRRE:
- handleRRERect(rx, ry, rw, rh);
- break;
- case RfbProto.EncodingCoRRE:
- handleCoRRERect(rx, ry, rw, rh);
- break;
- case RfbProto.EncodingHextile:
- statNumRectsHextile++;
- handleHextileRect(rx, ry, rw, rh);
- break;
- case RfbProto.EncodingZRLE:
- statNumRectsZRLE++;
- handleZRLERect(rx, ry, rw, rh);
- break;
- case RfbProto.EncodingZlib:
- handleZlibRect(rx, ry, rw, rh);
- break;
- case RfbProto.EncodingTight:
- if (tightDecoder != null) {
- statNumRectsTightJPEG = tightDecoder.getNumJPEGRects();
- //statNumRectsTight = tightDecoder.getNumTightRects();
- }
- statNumRectsTight++;
- handleTightRect(rx, ry, rw, rh);
- break;
- default:
- throw new Exception("Unknown RFB rectangle encoding " +
- rfb.updateRectEncoding);
- }
-
- rfb.stopTiming();
-
- statNumPixelRects++;
- statNumBytesDecoded += rw * rh * bytesPixel;
- statNumBytesEncoded +=
- (int)(rfb.getNumBytesRead() - numBytesReadBefore);
- }
-
- boolean fullUpdateNeeded = false;
-
- // Start/stop session recording if necessary. Request full
- // update if a new session file was opened.
- if (viewer.checkRecordingStatus())
- fullUpdateNeeded = true;
-
- // Defer framebuffer update request if necessary. But wake up
- // immediately on keyboard or mouse event. Also, don't sleep
- // if there is some data to receive, or if the last update
- // included a PointerPos message.
- if (viewer.deferUpdateRequests > 0 &&
- rfb.available() == 0 && !cursorPosReceived) {
- synchronized(rfb) {
- try {
- rfb.wait(viewer.deferUpdateRequests);
- } catch (InterruptedException e) {
- }
- }
- }
-
- viewer.autoSelectEncodings();
-
- // Before requesting framebuffer update, check if the pixel
- // format should be changed.
- if (viewer.options.eightBitColors != (bytesPixel == 1)) {
- // Pixel format should be changed.
- setPixelFormat();
- fullUpdateNeeded = true;
- }
-
- // Finally, request framebuffer update if needed.
- if (fullUpdateNeeded) {
- rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
- rfb.framebufferHeight, false);
- } else {
- rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
- rfb.framebufferHeight, true);
- }
-
- break;
-
- case RfbProto.SetColourMapEntries:
- throw new Exception("Can't handle SetColourMapEntries message");
-
- case RfbProto.Bell:
- Toolkit.getDefaultToolkit().beep();
- break;
-
- case RfbProto.ServerCutText:
- String s = rfb.readServerCutText();
- viewer.clipboard.setCutText(s);
- break;
-
- default:
- throw new Exception("Unknown RFB message type " + msgType);
- }
- }
- }
-
- //
- // Handle a raw rectangle. The second form with paint==false is used
- // by the Hextile decoder for raw-encoded tiles.
- //
-
- void handleRawRect(int x, int y, int w, int h) throws IOException, Exception {
- handleRawRect(x, y, w, h, true);
- }
-
- void handleRawRect(int x, int y, int w, int h, boolean paint)
- throws IOException , Exception{
- rawDecoder.handleRect(x, y, w, h);
- if (paint)
- scheduleRepaint(x, y, w, h);
- }
-
- //
- // Handle a CopyRect rectangle.
- //
-
- void handleCopyRect(int x, int y, int w, int h) throws IOException {
- copyRectDecoder.handleRect(x, y, w, h);
- scheduleRepaint(x, y, w, h);
- }
-
- //
- // Handle an RRE-encoded rectangle.
- //
-
- void handleRRERect(int x, int y, int w, int h) throws IOException {
- rreDecoder.handleRect(x, y, w, h);
- scheduleRepaint(x, y, w, h);
- }
-
- //
- // Handle a CoRRE-encoded rectangle.
- //
-
- void handleCoRRERect(int x, int y, int w, int h) throws IOException {
- correDecoder.handleRect(x, y, w, h);
- scheduleRepaint(x, y, w, h);
- }
-
- //
- // Handle a Hextile-encoded rectangle.
- //
-
- void handleHextileRect(int x, int y, int w, int h) throws IOException,
- Exception {
- hextileDecoder.handleRect(x, y, w, h);
- }
-
- //
- // Handle a ZRLE-encoded rectangle.
- //
- // FIXME: Currently, session recording is not fully supported for ZRLE.
- //
-
- void handleZRLERect(int x, int y, int w, int h) throws Exception {
- zrleDecoder.handleRect(x, y, w, h);
- scheduleRepaint(x, y, w, h);
- }
-
- //
- // Handle a Zlib-encoded rectangle.
- //
-
- void handleZlibRect(int x, int y, int w, int h) throws Exception {
- zlibDecoder.handleRect(x, y, w, h);
- scheduleRepaint(x, y, w, h);
- }
-
- //
- // Handle a Tight-encoded rectangle.
- //
-
- void handleTightRect(int x, int y, int w, int h) throws Exception {
- tightDecoder.handleRect(x, y, w, h);
- scheduleRepaint(x, y, w, h);
- }
-
- //
- // Tell JVM to repaint specified desktop area.
- //
-
- public void scheduleRepaint(int x, int y, int w, int h) {
- // Request repaint, deferred if necessary.
- if (rfb.framebufferWidth == scaledWidth) {
- repaint(viewer.deferScreenUpdates, x, y, w, h);
- } else {
- int sx = x * scalingFactor / 100;
- int sy = y * scalingFactor / 100;
- int sw = ((x + w) * scalingFactor + 49) / 100 - sx + 1;
- int sh = ((y + h) * scalingFactor + 49) / 100 - sy + 1;
- repaint(viewer.deferScreenUpdates, sx, sy, sw, sh);
- }
- }
-
- //
- // Handle events.
- //
-
- public void keyPressed(KeyEvent evt) {
- processLocalKeyEvent(evt);
- }
- public void keyReleased(KeyEvent evt) {
- processLocalKeyEvent(evt);
- }
- public void keyTyped(KeyEvent evt) {
- evt.consume();
- }
-
- public void mousePressed(MouseEvent evt) {
- processLocalMouseEvent(evt, false);
- }
- public void mouseReleased(MouseEvent evt) {
- processLocalMouseEvent(evt, false);
- }
- public void mouseMoved(MouseEvent evt) {
- processLocalMouseEvent(evt, true);
- }
- public void mouseDragged(MouseEvent evt) {
- processLocalMouseEvent(evt, true);
- }
-
- private synchronized void trySendPointerEvent() {
- if ((needToSendMouseEvent) && (mouseEvent!=null)) {
- sendMouseEvent(mouseEvent, false);
- needToSendMouseEvent = false;
- lastMouseEventSendTime = System.currentTimeMillis();
- }
- }
-
- public void run() {
- while (true) {
- // Send mouse movement if we have it
- trySendPointerEvent();
- // Sleep for some time
- try {
- Thread.sleep(1000 / mouseMaxFreq);
- } catch (InterruptedException ex) {
- }
- }
- }
-
- //
- // Ignored events.
- //
-
- public void mouseClicked(MouseEvent evt) {}
- public void mouseEntered(MouseEvent evt) {}
- public void mouseExited(MouseEvent evt) {}
-
- //
- // Actual event processing.
- //
-
- private void processLocalKeyEvent(KeyEvent evt) {
- if (viewer.rfb != null && rfb.inNormalProtocol) {
- if (!inputEnabled) {
- if ((evt.getKeyChar() == 'r' || evt.getKeyChar() == 'R') &&
- evt.getID() == KeyEvent.KEY_PRESSED ) {
- // Request screen update.
- try {
- rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
- rfb.framebufferHeight, false);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- } else {
- // Input enabled.
- synchronized(rfb) {
- try {
- rfb.writeKeyEvent(evt);
- } catch (Exception e) {
- e.printStackTrace();
- }
- rfb.notify();
- }
- }
- }
- // Don't ever pass keyboard events to AWT for default processing.
- // Otherwise, pressing Tab would switch focus to ButtonPanel etc.
- evt.consume();
- }
-
- private void processLocalMouseEvent(MouseEvent evt, boolean moved) {
- if (viewer.rfb != null && rfb.inNormalProtocol) {
- if (inputEnabled) {
- // If mouse not moved, but it's click event then
- // send it to server immideanlty.
- // Else, it's mouse movement - we can send it in
- // our thread later.
- if (!moved) {
- sendMouseEvent(evt, moved);
- } else {
- mouseEvent = evt;
- needToSendMouseEvent = true;
- }
- }
- }
- }
-
- private void sendMouseEvent(MouseEvent evt, boolean moved) {
- if (moved) {
- softCursorMove(evt.getX(), evt.getY());
- }
- if (rfb.framebufferWidth != scaledWidth) {
- int sx = (evt.getX() * 100 + scalingFactor/2) / scalingFactor;
- int sy = (evt.getY() * 100 + scalingFactor/2) / scalingFactor;
- evt.translatePoint(sx - evt.getX(), sy - evt.getY());
- }
- synchronized(rfb) {
- try {
- rfb.writePointerEvent(evt);
- } catch (Exception e) {
- e.printStackTrace();
- }
- rfb.notify();
- lastMouseEventSendTime = System.currentTimeMillis();
- }
- }
-
- //
- // Reset update statistics.
- //
-
- void resetStats() {
- statStartTime = System.currentTimeMillis();
- statNumUpdates = 0;
- statNumTotalRects = 0;
- statNumPixelRects = 0;
- statNumRectsTight = 0;
- statNumRectsTightJPEG = 0;
- statNumRectsZRLE = 0;
- statNumRectsHextile = 0;
- statNumRectsRaw = 0;
- statNumRectsCopy = 0;
- statNumBytesEncoded = 0;
- statNumBytesDecoded = 0;
- if (tightDecoder != null) {
- tightDecoder.setNumJPEGRects(0);
- tightDecoder.setNumTightRects(0);
- }
- }
-
- //////////////////////////////////////////////////////////////////
- //
- // Handle cursor shape updates (XCursor and RichCursor encodings).
- //
-
- boolean showSoftCursor = false;
-
- MemoryImageSource softCursorSource;
- Image softCursor;
- MouseEvent mouseEvent = null;
- boolean needToSendMouseEvent = false;
- int cursorX = 0, cursorY = 0;
- int cursorWidth, cursorHeight;
- int origCursorWidth, origCursorHeight;
- int hotX, hotY;
- int origHotX, origHotY;
-
- //
- // Handle cursor shape update (XCursor and RichCursor encodings).
- //
-
- synchronized void
- handleCursorShapeUpdate(int encodingType,
- int xhot, int yhot, int width, int height)
- throws IOException {
-
- softCursorFree();
-
- if (width * height == 0)
- return;
-
- // Ignore cursor shape data if requested by user.
- if (viewer.options.ignoreCursorUpdates) {
- int bytesPerRow = (width + 7) / 8;
- int bytesMaskData = bytesPerRow * height;
-
- if (encodingType == rfb.EncodingXCursor) {
- rfb.skipBytes(6 + bytesMaskData * 2);
- } else {
- // rfb.EncodingRichCursor
- rfb.skipBytes(width * height + bytesMaskData);
- }
- return;
- }
-
- // Decode cursor pixel data.
- softCursorSource = decodeCursorShape(encodingType, width, height);
-
- // Set original (non-scaled) cursor dimensions.
- origCursorWidth = width;
- origCursorHeight = height;
- origHotX = xhot;
- origHotY = yhot;
-
- // Create off-screen cursor image.
- createSoftCursor();
-
- // Show the cursor.
- showSoftCursor = true;
- repaint(viewer.deferCursorUpdates,
- cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight);
- }
-
- //
- // decodeCursorShape(). Decode cursor pixel data and return
- // corresponding MemoryImageSource instance.
- //
-
- synchronized MemoryImageSource
- decodeCursorShape(int encodingType, int width, int height)
- throws IOException {
-
- int bytesPerRow = (width + 7) / 8;
- int bytesMaskData = bytesPerRow * height;
-
- int[] softCursorPixels = new int[width * height];
-
- if (encodingType == rfb.EncodingXCursor) {
-
- // Read foreground and background colors of the cursor.
- byte[] rgb = new byte[6];
- rfb.readFully(rgb);
- int[] colors = { (0xFF000000 | (rgb[3] & 0xFF) << 16 |
- (rgb[4] & 0xFF) << 8 | (rgb[5] & 0xFF)),
- (0xFF000000 | (rgb[0] & 0xFF) << 16 |
- (rgb[1] & 0xFF) << 8 | (rgb[2] & 0xFF)) };
-
- // Read pixel and mask data.
- byte[] pixBuf = new byte[bytesMaskData];
- rfb.readFully(pixBuf);
- byte[] maskBuf = new byte[bytesMaskData];
- rfb.readFully(maskBuf);
-
- // Decode pixel data into softCursorPixels[].
- byte pixByte, maskByte;
- int x, y, n, result;
- int i = 0;
- for (y = 0; y < height; y++) {
- for (x = 0; x < width / 8; x++) {
- pixByte = pixBuf[y * bytesPerRow + x];
- maskByte = maskBuf[y * bytesPerRow + x];
- for (n = 7; n >= 0; n--) {
- if ((maskByte >> n & 1) != 0) {
- result = colors[pixByte >> n & 1];
- } else {
- result = 0; // Transparent pixel
- }
- softCursorPixels[i++] = result;
- }
- }
- for (n = 7; n >= 8 - width % 8; n--) {
- if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) {
- result = colors[pixBuf[y * bytesPerRow + x] >> n & 1];
- } else {
- result = 0; // Transparent pixel
- }
- softCursorPixels[i++] = result;
- }
- }
-
- } else {
- // encodingType == rfb.EncodingRichCursor
-
- // Read pixel and mask data.
- byte[] pixBuf = new byte[width * height * bytesPixel];
- rfb.readFully(pixBuf);
- byte[] maskBuf = new byte[bytesMaskData];
- rfb.readFully(maskBuf);
-
- // Decode pixel data into softCursorPixels[].
- byte maskByte;
- int x, y, n, result;
- int i = 0;
- for (y = 0; y < height; y++) {
- for (x = 0; x < width / 8; x++) {
- maskByte = maskBuf[y * bytesPerRow + x];
- for (n = 7; n >= 0; n--) {
- if ((maskByte >> n & 1) != 0) {
- if (bytesPixel == 1) {
- result = cm8.getRGB(pixBuf[i]);
- } else {
- result = 0xFF000000 |
- (pixBuf[i * 4 + 2] & 0xFF) << 16 |
- (pixBuf[i * 4 + 1] & 0xFF) << 8 |
- (pixBuf[i * 4] & 0xFF);
- }
- } else {
- result = 0; // Transparent pixel
- }
- softCursorPixels[i++] = result;
- }
- }
- for (n = 7; n >= 8 - width % 8; n--) {
- if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) {
- if (bytesPixel == 1) {
- result = cm8.getRGB(pixBuf[i]);
- } else {
- result = 0xFF000000 |
- (pixBuf[i * 4 + 2] & 0xFF) << 16 |
- (pixBuf[i * 4 + 1] & 0xFF) << 8 |
- (pixBuf[i * 4] & 0xFF);
- }
- } else {
- result = 0; // Transparent pixel
- }
- softCursorPixels[i++] = result;
- }
- }
-
- }
-
- return new MemoryImageSource(width, height, softCursorPixels, 0, width);
- }
-
- //
- // createSoftCursor(). Assign softCursor new Image (scaled if necessary).
- // Uses softCursorSource as a source for new cursor image.
- //
-
- synchronized void
- createSoftCursor() {
-
- if (softCursorSource == null)
- return;
-
- int scaleCursor = viewer.options.scaleCursor;
- if (scaleCursor == 0 || !inputEnabled)
- scaleCursor = 100;
-
- // Save original cursor coordinates.
- int x = cursorX - hotX;
- int y = cursorY - hotY;
- int w = cursorWidth;
- int h = cursorHeight;
-
- cursorWidth = (origCursorWidth * scaleCursor + 50) / 100;
- cursorHeight = (origCursorHeight * scaleCursor + 50) / 100;
- hotX = (origHotX * scaleCursor + 50) / 100;
- hotY = (origHotY * scaleCursor + 50) / 100;
- softCursor = Toolkit.getDefaultToolkit().createImage(softCursorSource);
-
- if (scaleCursor != 100) {
- softCursor = softCursor.getScaledInstance(cursorWidth, cursorHeight,
- Image.SCALE_SMOOTH);
- }
-
- if (showSoftCursor) {
- // Compute screen area to update.
- x = Math.min(x, cursorX - hotX);
- y = Math.min(y, cursorY - hotY);
- w = Math.max(w, cursorWidth);
- h = Math.max(h, cursorHeight);
-
- repaint(viewer.deferCursorUpdates, x, y, w, h);
- }
- }
-
- //
- // softCursorMove(). Moves soft cursor into a particular location.
- //
-
- synchronized void softCursorMove(int x, int y) {
- int oldX = cursorX;
- int oldY = cursorY;
- cursorX = x;
- cursorY = y;
- if (showSoftCursor) {
- repaint(viewer.deferCursorUpdates,
- oldX - hotX, oldY - hotY, cursorWidth, cursorHeight);
- repaint(viewer.deferCursorUpdates,
- cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight);
- }
- }
-
- //
- // softCursorFree(). Remove soft cursor, dispose resources.
- //
-
- synchronized void softCursorFree() {
- if (showSoftCursor) {
- showSoftCursor = false;
- softCursor = null;
- softCursorSource = null;
-
- repaint(viewer.deferCursorUpdates,
- cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight);
- }
- }
-}
diff --git a/java/src/com/tigervnc/vncviewer/VncCanvas2.java b/java/src/com/tigervnc/vncviewer/VncCanvas2.java
deleted file mode 100644
index c39d4a5..0000000
--- a/java/src/com/tigervnc/vncviewer/VncCanvas2.java
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-// Copyright (C) 2006 Constantin Kaplinsky. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
-
-package com.tigervnc.vncviewer;
-
-import java.awt.*;
-import java.io.*;
-
-//
-// VncCanvas2 is a special version of VncCanvas which may use Java 2 API.
-//
-
-class VncCanvas2 extends VncCanvas {
-
- public VncCanvas2(VncViewer v) throws IOException {
- super(v);
- disableFocusTraversalKeys();
- }
-
- public VncCanvas2(VncViewer v, int maxWidth_, int maxHeight_)
- throws IOException {
-
- super(v, maxWidth_, maxHeight_);
- disableFocusTraversalKeys();
- }
-
- public void paintScaledFrameBuffer(Graphics g) {
- Graphics2D g2d = (Graphics2D)g;
- g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
- RenderingHints.VALUE_RENDER_QUALITY);
- g2d.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null);
- }
-
- //
- // Try to disable focus traversal keys (JVMs 1.4 and higher).
- //
-
- private void disableFocusTraversalKeys() {
- try {
- Class[] argClasses = { Boolean.TYPE };
- java.lang.reflect.Method method =
- getClass().getMethod("setFocusTraversalKeysEnabled", argClasses);
- Object[] argObjects = { new Boolean(false) };
- method.invoke(this, argObjects);
- } catch (Exception e) {}
- }
-
-}
-
diff --git a/java/src/com/tigervnc/vncviewer/VncViewer.java b/java/src/com/tigervnc/vncviewer/VncViewer.java
index 3a9f4c6..ba54918 100644
--- a/java/src/com/tigervnc/vncviewer/VncViewer.java
+++ b/java/src/com/tigervnc/vncviewer/VncViewer.java
@@ -1,1106 +1,268 @@
-//
-// Copyright (C) 2001-2004 HorizonLive.com, Inc. All Rights Reserved.
-// Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved.
-// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
-//
-// This is free software; you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation; either version 2 of the License, or
-// (at your option) any later version.
-//
-// This software is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this software; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
-// USA.
-//
+/* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
//
-// VncViewer.java - the VNC viewer applet. This class mainly just sets up the
-// user interface, leaving it to the VncCanvas to do the actual rendering of
-// a VNC desktop.
+// VncViewer - the VNC viewer applet. It can also be run from the
+// command-line, when it behaves as much as possibly like the windows and unix
+// viewers.
//
+// Unfortunately, because of the way Java classes are loaded on demand, only
+// configuration parameters defined in this file can be set from the command
+// line or in applet parameters.
package com.tigervnc.vncviewer;
import java.awt.*;
-import java.awt.event.*;
-import java.io.*;
-import java.net.*;
+import java.awt.Color;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.image.*;
+import java.awt.Label;
+import javax.swing.*;
+import java.net.URL;
-public class VncViewer extends java.applet.Applet
- implements java.lang.Runnable, WindowListener, ComponentListener {
+import com.tigervnc.rdr.*;
+import com.tigervnc.rfb.*;
+import com.tigervnc.rfb.Exception;
- boolean inAnApplet = true;
- boolean inSeparateFrame = false;
-
- //
- // main() is called when run as a java program from the command line.
- // It simply runs the applet inside a newly-created frame.
- //
+public class VncViewer extends java.applet.Applet implements Runnable
+{
+ public static final String version = "1.0.90";
+ public static final String about1 = "TigerVNC Viewer for Java "+version;
+ public static final String about2 = "Copyright (C) 1998-2010 "+
+ "[many holders]";
+ public static final String about3 = "Visit www.tigervnc.org "+
+ "for information on TigerVNC.";
public static void main(String[] argv) {
- VncViewer v = new VncViewer();
- v.mainArgs = argv;
- v.inAnApplet = false;
- v.inSeparateFrame = true;
-
- v.init();
- v.start();
- }
-
- String[] mainArgs;
-
- RfbProto rfb;
- Thread rfbThread;
-
- Frame vncFrame;
- Container vncContainer;
- ScrollPane desktopScrollPane;
- GridBagLayout gridbag;
- ButtonPanel buttonPanel;
- Label connStatusLabel;
- VncCanvas vc;
- OptionsFrame options;
- ClipboardFrame clipboard;
- RecordingFrame rec;
-
- // Control session recording.
- Object recordingSync;
- String sessionFileName;
- boolean recordingActive;
- boolean recordingStatusChanged;
- String cursorUpdatesDef;
- String eightBitColorsDef;
-
- // Variables read from parameter values.
- String socketFactory;
- String host;
- int port;
- String passwordParam;
- boolean showControls;
- boolean offerRelogin;
- boolean showOfflineDesktop;
- int deferScreenUpdates;
- int deferCursorUpdates;
- int deferUpdateRequests;
- int debugStatsExcludeUpdates;
- int debugStatsMeasureUpdates;
-
- // Reference to this applet for inter-applet communication.
- public static java.applet.Applet refApplet;
-
- //
- // init()
- //
-
- public void init() {
-
- readParameters();
-
- refApplet = this;
-
- if (inSeparateFrame) {
- vncFrame = new Frame("TigerVNC");
- if (!inAnApplet) {
- vncFrame.add("Center", this);
- }
- vncContainer = vncFrame;
- } else {
- vncContainer = this;
- }
-
- recordingSync = new Object();
-
- options = new OptionsFrame(this);
- clipboard = new ClipboardFrame(this);
- if (RecordingFrame.checkSecurity())
- rec = new RecordingFrame(this);
-
- sessionFileName = null;
- recordingActive = false;
- recordingStatusChanged = false;
- cursorUpdatesDef = null;
- eightBitColorsDef = null;
-
- if (inSeparateFrame) {
- vncFrame.addWindowListener(this);
- vncFrame.addComponentListener(this);
- }
-
- rfbThread = new Thread(this);
- rfbThread.start();
- }
-
- public void update(Graphics g) {
- }
-
- //
- // run() - executed by the rfbThread to deal with the RFB socket.
- //
-
- public void run() {
-
- gridbag = new GridBagLayout();
- vncContainer.setLayout(gridbag);
-
- GridBagConstraints gbc = new GridBagConstraints();
- gbc.gridwidth = GridBagConstraints.REMAINDER;
- gbc.anchor = GridBagConstraints.NORTHWEST;
-
- if (showControls) {
- buttonPanel = new ButtonPanel(this);
- gridbag.setConstraints(buttonPanel, gbc);
- vncContainer.add(buttonPanel);
- }
-
try {
- connectAndAuthenticate();
- doProtocolInitialisation();
-
- // FIXME: Use auto-scaling not only in a separate frame.
- if (options.autoScale && inSeparateFrame) {
- Dimension screenSize;
- try {
- screenSize = vncContainer.getToolkit().getScreenSize();
- } catch (Exception e) {
- screenSize = new Dimension(0, 0);
- }
- createCanvas(screenSize.width - 32, screenSize.height - 32);
+ String os = System.getProperty("os.name");
+ if (os.startsWith("Windows")) {
+ String laf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel";
+ UIManager.setLookAndFeel(laf);
} else {
- createCanvas(0, 0);
- }
-
- gbc.weightx = 1.0;
- gbc.weighty = 1.0;
-
- if (inSeparateFrame) {
-
- // Create a panel which itself is resizeable and can hold
- // non-resizeable VncCanvas component at the top left corner.
- Panel canvasPanel = new Panel();
- canvasPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
- canvasPanel.add(vc);
-
- // Create a ScrollPane which will hold a panel with VncCanvas
- // inside.
- desktopScrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
- gbc.fill = GridBagConstraints.BOTH;
- gridbag.setConstraints(desktopScrollPane, gbc);
- desktopScrollPane.add(canvasPanel);
- // If auto scale is not enabled we don't need to set first frame
- // size to fullscreen
- if (!options.autoScale) {
- vc.isFirstSizeAutoUpdate = false;
+ UIManager.put("swing.boldMetal", Boolean.FALSE);
+ javax.swing.plaf.FontUIResource f = new
+ javax.swing.plaf.FontUIResource("SansSerif", Font.PLAIN, 11);
+ java.util.Enumeration keys = UIManager.getDefaults().keys();
+ while (keys.hasMoreElements()) {
+ Object key = keys.nextElement();
+ Object value = UIManager.get (key);
+ if (value instanceof javax.swing.plaf.FontUIResource)
+ UIManager.put(key, f);
}
-
- // Finally, add our ScrollPane to the Frame window.
- vncFrame.add(desktopScrollPane);
- vncFrame.setTitle(rfb.desktopName);
- vncFrame.pack();
- vc.resizeDesktopFrame();
-
- } else {
- // Just add the VncCanvas component to the Applet.
- gridbag.setConstraints(vc, gbc);
- add(vc);
- validate();
}
+ UIManager.put("TitledBorder.titleColor",Color.blue);
+ } catch (java.lang.Exception exc) { }
+ VncViewer viewer = new VncViewer(argv);
+ viewer.start();
+ }
- if (showControls) {
- buttonPanel.enableButtons();
- }
-
- moveFocusToDesktop();
- processNormalProtocol();
-
- } catch (NoRouteToHostException e) {
- fatalError("Network error: no route to server: " + host, e);
- } catch (UnknownHostException e) {
- fatalError("Network error: server name unknown: " + host, e);
- } catch (ConnectException e) {
- fatalError("Network error: could not connect to server: " +
- host + ":" + port, e);
- } catch (EOFException e) {
- if (showOfflineDesktop) {
- e.printStackTrace();
- System.out.println("Network error: remote side closed connection");
- if (vc != null) {
- vc.enableInput(false);
- }
- if (inSeparateFrame) {
- vncFrame.setTitle(rfb.desktopName + " [disconnected]");
- }
- if (rfb != null && !rfb.closed())
- rfb.close();
- if (showControls && buttonPanel != null) {
- buttonPanel.disableButtonsOnDisconnect();
- if (inSeparateFrame) {
- vncFrame.pack();
- } else {
- validate();
- }
- }
- } else {
- fatalError("Network error: remote side closed connection", e);
- }
- } catch (IOException e) {
- String str = e.getMessage();
- if (str != null && str.length() != 0) {
- fatalError("Network Error: " + str, e);
- } else {
- fatalError(e.toString(), e);
- }
- } catch (Exception e) {
- String str = e.getMessage();
- if (str != null && str.length() != 0) {
- fatalError("Error: " + str, e);
- } else {
- fatalError(e.toString(), e);
- }
- }
+
+ public VncViewer(String[] argv) {
+ applet = false;
- }
-
- //
- // Create a VncCanvas instance.
- //
-
- void createCanvas(int maxWidth, int maxHeight) throws IOException {
- // Determine if Java 2D API is available and use a special
- // version of VncCanvas if it is present.
- vc = null;
- try {
- // This throws ClassNotFoundException if there is no Java 2D API.
- Class cl = Class.forName("java.awt.Graphics2D");
- // If we could load Graphics2D class, then we can use VncCanvas2D.
- cl = Class.forName("com.tigervnc.vncviewer.VncCanvas2");
- Class[] argClasses = { this.getClass(), Integer.TYPE, Integer.TYPE };
- java.lang.reflect.Constructor cstr = cl.getConstructor(argClasses);
- Object[] argObjects =
- { this, new Integer(maxWidth), new Integer(maxHeight) };
- vc = (VncCanvas)cstr.newInstance(argObjects);
- } catch (Exception e) {
- System.out.println("Warning: Java 2D API is not available");
- }
-
- // If we failed to create VncCanvas2D, use old VncCanvas.
- if (vc == null)
- vc = new VncCanvas(this, maxWidth, maxHeight);
- }
-
-
- //
- // Process RFB socket messages.
- // If the rfbThread is being stopped, ignore any exceptions,
- // otherwise rethrow the exception so it can be handled.
- //
-
- void processNormalProtocol() throws Exception {
- try {
- vc.processNormalProtocol();
- } catch (Exception e) {
- if (rfbThread == null) {
- System.out.println("Ignoring RFB socket exceptions" +
- " because applet is stopping");
- } else {
- throw e;
- }
- }
- }
-
-
- //
- // Connect to the RFB server and authenticate the user.
- //
-
- void connectAndAuthenticate() throws Exception
- {
- showConnectionStatus("Initializing...");
- if (inSeparateFrame) {
- vncFrame.pack();
- vncFrame.show();
- } else {
- validate();
- }
-
- showConnectionStatus("Connecting to " + host + ", port " + port + "...");
-
- rfb = new RfbProto(host, port, this);
- showConnectionStatus("Connected to server");
-
- rfb.readVersionMsg();
- showConnectionStatus("RFB server supports protocol version " +
- rfb.serverMajor + "." + rfb.serverMinor);
-
- rfb.writeVersionMsg();
- showConnectionStatus("Using RFB protocol version " +
- rfb.clientMajor + "." + rfb.clientMinor);
-
- int secType = rfb.negotiateSecurity();
- doAuthentification(secType);
- }
-
- void doAuthentification(int secType) throws Exception {
- switch (secType) {
- case RfbProto.SecTypeNone:
- showConnectionStatus("No authentication needed");
- rfb.authenticateNone();
- break;
- case RfbProto.SecTypeVncAuth:
- showConnectionStatus("Performing standard VNC authentication");
- if (passwordParam != null) {
- rfb.authenticateVNC(passwordParam);
- } else {
- String pw = askPassword();
- rfb.authenticateVNC(pw);
- }
- break;
- case RfbProto.SecTypeVeNCrypt:
- showConnectionStatus("VeNCrypt chooser");
- secType = rfb.authenticateVeNCrypt();
- doAuthentification(secType);
- break;
- case RfbProto.SecTypePlain:
- showConnectionStatus("Plain authentication");
- {
- String user = askUser();
- String pw = askPassword();
- rfb.authenticatePlain(user,pw);
- }
- break;
- case RfbProto.SecTypeTLSNone:
- showConnectionStatus("TLSNone");
- rfb.authenticateTLS();
- rfb.authenticateNone();
- break;
- case RfbProto.SecTypeTLSVnc:
- showConnectionStatus("TLSVnc");
- rfb.authenticateTLS();
- doAuthentification(RfbProto.SecTypeVncAuth);
- break;
- case RfbProto.SecTypeTLSPlain:
- showConnectionStatus("TLSPlain");
- rfb.authenticateTLS();
- doAuthentification(RfbProto.SecTypePlain);
- break;
- case RfbProto.SecTypeX509None:
- showConnectionStatus("X509None");
- rfb.authenticateX509();
- rfb.authenticateNone();
- break;
- case RfbProto.SecTypeX509Vnc:
- showConnectionStatus("X509Vnc");
- rfb.authenticateX509();
- doAuthentification(RfbProto.SecTypeVncAuth);
- break;
- case RfbProto.SecTypeX509Plain:
- showConnectionStatus("X509Plain");
- rfb.authenticateX509();
- doAuthentification(RfbProto.SecTypePlain);
- break;
- default:
- throw new Exception("Unknown authentication scheme " + secType);
- }
- }
-
-
- //
- // Show a message describing the connection status.
- // To hide the connection status label, use (msg == null).
- //
-
- void showConnectionStatus(String msg)
- {
- if (msg == null) {
- if (vncContainer.isAncestorOf(connStatusLabel)) {
- vncContainer.remove(connStatusLabel);
- }
- return;
- }
-
- System.out.println(msg);
-
- if (connStatusLabel == null) {
- connStatusLabel = new Label("Status: " + msg);
- connStatusLabel.setFont(new Font("Helvetica", Font.PLAIN, 12));
- } else {
- connStatusLabel.setText("Status: " + msg);
- }
-
- if (!vncContainer.isAncestorOf(connStatusLabel)) {
- GridBagConstraints gbc = new GridBagConstraints();
- gbc.gridwidth = GridBagConstraints.REMAINDER;
- gbc.fill = GridBagConstraints.HORIZONTAL;
- gbc.anchor = GridBagConstraints.NORTHWEST;
- gbc.weightx = 1.0;
- gbc.weighty = 1.0;
- gbc.insets = new Insets(20, 30, 20, 30);
- gridbag.setConstraints(connStatusLabel, gbc);
- vncContainer.add(connStatusLabel);
- }
-
- if (inSeparateFrame) {
- vncFrame.pack();
- } else {
- validate();
- }
- }
-
-
- //
- // Show an authentication panel.
- //
-
- String askUser() throws Exception
- {
- showConnectionStatus(null);
-
- AuthPanel authPanel = new AuthPanel(this, false);
-
- GridBagConstraints gbc = new GridBagConstraints();
- gbc.gridwidth = GridBagConstraints.REMAINDER;
- gbc.anchor = GridBagConstraints.NORTHWEST;
- gbc.weightx = 1.0;
- gbc.weighty = 1.0;
- gbc.ipadx = 100;
- gbc.ipady = 50;
- gridbag.setConstraints(authPanel, gbc);
- vncContainer.add(authPanel);
-
- if (inSeparateFrame) {
- vncFrame.pack();
- } else {
- validate();
- }
-
- authPanel.moveFocusToDefaultField();
- String pw = authPanel.getPassword();
- vncContainer.remove(authPanel);
-
- return pw;
- }
-
- String askPassword() throws Exception
- {
- showConnectionStatus(null);
-
- AuthPanel authPanel = new AuthPanel(this, true);
-
- GridBagConstraints gbc = new GridBagConstraints();
- gbc.gridwidth = GridBagConstraints.REMAINDER;
- gbc.anchor = GridBagConstraints.NORTHWEST;
- gbc.weightx = 1.0;
- gbc.weighty = 1.0;
- gbc.ipadx = 100;
- gbc.ipady = 50;
- gridbag.setConstraints(authPanel, gbc);
- vncContainer.add(authPanel);
-
- if (inSeparateFrame) {
- vncFrame.pack();
- } else {
- validate();
- }
-
- authPanel.moveFocusToDefaultField();
- String pw = authPanel.getPassword();
- vncContainer.remove(authPanel);
-
- return pw;
- }
-
-
- //
- // Do the rest of the protocol initialisation.
- //
-
- void doProtocolInitialisation() throws IOException
- {
- rfb.writeClientInit();
- rfb.readServerInit();
-
- System.out.println("Desktop name is " + rfb.desktopName);
- System.out.println("Desktop size is " + rfb.framebufferWidth + " x " +
- rfb.framebufferHeight);
-
- setEncodings();
-
- showConnectionStatus(null);
- }
-
-
- //
- // Send current encoding list to the RFB server.
- //
-
- int[] encodingsSaved;
- int nEncodingsSaved;
-
- void setEncodings() { setEncodings(false); }
- void autoSelectEncodings() { setEncodings(true); }
-
- void setEncodings(boolean autoSelectOnly) {
- if (options == null || rfb == null || !rfb.inNormalProtocol)
- return;
-
- int preferredEncoding = options.preferredEncoding;
- if (preferredEncoding == -1) {
- long kbitsPerSecond = rfb.kbitsPerSecond();
- if (nEncodingsSaved < 1) {
- // Choose Tight or ZRLE encoding for the very first update.
- System.out.println("Using Tight/ZRLE encodings");
- preferredEncoding = RfbProto.EncodingTight;
- } else if (kbitsPerSecond > 2000 &&
- encodingsSaved[0] != RfbProto.EncodingHextile) {
- // Switch to Hextile if the connection speed is above 2Mbps.
- System.out.println("Throughput " + kbitsPerSecond +
- " kbit/s - changing to Hextile encoding");
- preferredEncoding = RfbProto.EncodingHextile;
- } else if (kbitsPerSecond < 1000 &&
- encodingsSaved[0] != RfbProto.EncodingTight) {
- // Switch to Tight/ZRLE if the connection speed is below 1Mbps.
- System.out.println("Throughput " + kbitsPerSecond +
- " kbit/s - changing to Tight/ZRLE encodings");
- preferredEncoding = RfbProto.EncodingTight;
- } else {
- // Don't change the encoder.
- if (autoSelectOnly)
- return;
- preferredEncoding = encodingsSaved[0];
- }
- } else {
- // Auto encoder selection is not enabled.
- if (autoSelectOnly)
- return;
- }
-
- int[] encodings = new int[20];
- int nEncodings = 0;
-
- encodings[nEncodings++] = preferredEncoding;
- if (options.useCopyRect) {
- encodings[nEncodings++] = RfbProto.EncodingCopyRect;
- }
-
- if (preferredEncoding != RfbProto.EncodingTight) {
- encodings[nEncodings++] = RfbProto.EncodingTight;
- }
- if (preferredEncoding != RfbProto.EncodingZRLE) {
- encodings[nEncodings++] = RfbProto.EncodingZRLE;
- }
- if (preferredEncoding != RfbProto.EncodingHextile) {
- encodings[nEncodings++] = RfbProto.EncodingHextile;
- }
- if (preferredEncoding != RfbProto.EncodingZlib) {
- encodings[nEncodings++] = RfbProto.EncodingZlib;
- }
- if (preferredEncoding != RfbProto.EncodingCoRRE) {
- encodings[nEncodings++] = RfbProto.EncodingCoRRE;
- }
- if (preferredEncoding != RfbProto.EncodingRRE) {
- encodings[nEncodings++] = RfbProto.EncodingRRE;
- }
-
- if (options.compressLevel >= 0 && options.compressLevel <= 9) {
- encodings[nEncodings++] =
- RfbProto.EncodingCompressLevel0 + options.compressLevel;
- }
- if (options.jpegQuality >= 0 && options.jpegQuality <= 9) {
- encodings[nEncodings++] =
- RfbProto.EncodingQualityLevel0 + options.jpegQuality;
- }
-
- if (options.requestCursorUpdates) {
- encodings[nEncodings++] = RfbProto.EncodingXCursor;
- encodings[nEncodings++] = RfbProto.EncodingRichCursor;
- if (!options.ignoreCursorUpdates)
- encodings[nEncodings++] = RfbProto.EncodingPointerPos;
- }
-
- encodings[nEncodings++] = RfbProto.EncodingLastRect;
- encodings[nEncodings++] = RfbProto.EncodingNewFBSize;
-
- boolean encodingsWereChanged = false;
- if (nEncodings != nEncodingsSaved) {
- encodingsWereChanged = true;
- } else {
- for (int i = 0; i < nEncodings; i++) {
- if (encodings[i] != encodingsSaved[i]) {
- encodingsWereChanged = true;
- break;
- }
- }
- }
-
- if (encodingsWereChanged) {
- try {
- rfb.writeSetEncodings(encodings, nEncodings);
- if (vc != null) {
- vc.softCursorFree();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- encodingsSaved = encodings;
- nEncodingsSaved = nEncodings;
- }
- }
-
-
- //
- // setCutText() - send the given cut text to the RFB server.
- //
-
- void setCutText(String text) {
- try {
- if (rfb != null && rfb.inNormalProtocol) {
- rfb.writeClientCutText(text);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
-
- //
- // Order change in session recording status. To stop recording, pass
- // null in place of the fname argument.
- //
-
- void setRecordingStatus(String fname) {
- synchronized(recordingSync) {
- sessionFileName = fname;
- recordingStatusChanged = true;
- }
- }
-
- //
- // Start or stop session recording. Returns true if this method call
- // causes recording of a new session.
- //
-
- boolean checkRecordingStatus() throws IOException {
- synchronized(recordingSync) {
- if (recordingStatusChanged) {
- recordingStatusChanged = false;
- if (sessionFileName != null) {
- startRecording();
- return true;
- } else {
- stopRecording();
- }
- }
- }
- return false;
- }
-
- //
- // Start session recording.
- //
-
- protected void startRecording() throws IOException {
- synchronized(recordingSync) {
- if (!recordingActive) {
- // Save settings to restore them after recording the session.
- cursorUpdatesDef =
- options.choices[options.cursorUpdatesIndex].getSelectedItem();
- eightBitColorsDef =
- options.choices[options.eightBitColorsIndex].getSelectedItem();
- // Set options to values suitable for recording.
- options.choices[options.cursorUpdatesIndex].select("Disable");
- options.choices[options.cursorUpdatesIndex].setEnabled(false);
- options.setEncodings();
- options.choices[options.eightBitColorsIndex].select("No");
- options.choices[options.eightBitColorsIndex].setEnabled(false);
- options.setColorFormat();
- } else {
- rfb.closeSession();
+ // Override defaults with command-line options
+ for (int i = 0; i < argv.length; i++) {
+ if (argv[i].equalsIgnoreCase("-log")) {
+ if (++i >= argv.length) usage();
+ System.err.println("Log setting: "+argv[i]);
+ LogWriter.setLogParams(argv[i]);
+ continue;
}
- System.out.println("Recording the session in " + sessionFileName);
- rfb.startSession(sessionFileName);
- recordingActive = true;
- }
- }
+ if (Configuration.setParam(argv[i]))
+ continue;
- //
- // Stop session recording.
- //
-
- protected void stopRecording() throws IOException {
- synchronized(recordingSync) {
- if (recordingActive) {
- // Restore options.
- options.choices[options.cursorUpdatesIndex].select(cursorUpdatesDef);
- options.choices[options.cursorUpdatesIndex].setEnabled(true);
- options.setEncodings();
- options.choices[options.eightBitColorsIndex].select(eightBitColorsDef);
- options.choices[options.eightBitColorsIndex].setEnabled(true);
- options.setColorFormat();
-
- rfb.closeSession();
- System.out.println("Session recording stopped.");
- }
- sessionFileName = null;
- recordingActive = false;
- }
- }
-
-
- //
- // readParameters() - read parameters from the html source or from the
- // command line. On the command line, the arguments are just a sequence of
- // param_name/param_value pairs where the names and values correspond to
- // those expected in the html applet tag source.
- //
-
- void readParameters() {
- host = readParameter("HOST", !inAnApplet);
- if (host == null) {
- host = getCodeBase().getHost();
- if (host.equals("")) {
- fatalError("HOST parameter not specified");
- }
- }
-
- port = readIntParameter("PORT", 5900);
-
- // Read "ENCPASSWORD" or "PASSWORD" parameter if specified.
- readPasswordParameters();
-
- String str;
- if (inAnApplet) {
- str = readParameter("Open New Window", false);
- if (str != null && str.equalsIgnoreCase("Yes"))
- inSeparateFrame = true;
- }
-
- // "Show Controls" set to "No" disables button panel.
- showControls = true;
- str = readParameter("Show Controls", false);
- if (str != null && str.equalsIgnoreCase("No"))
- showControls = false;
-
- // "Offer Relogin" set to "No" disables "Login again" and "Close
- // window" buttons under error messages in applet mode.
- offerRelogin = true;
- str = readParameter("Offer Relogin", false);
- if (str != null && str.equalsIgnoreCase("No"))
- offerRelogin = false;
-
- // Do we continue showing desktop on remote disconnect?
- showOfflineDesktop = false;
- str = readParameter("Show Offline Desktop", false);
- if (str != null && str.equalsIgnoreCase("Yes"))
- showOfflineDesktop = true;
-
- // Fine tuning options.
- deferScreenUpdates = readIntParameter("Defer screen updates", 20);
- deferCursorUpdates = readIntParameter("Defer cursor updates", 10);
- deferUpdateRequests = readIntParameter("Defer update requests", 0);
-
- // Debugging options.
- debugStatsExcludeUpdates = readIntParameter("DEBUG_XU", 0);
- debugStatsMeasureUpdates = readIntParameter("DEBUG_CU", 0);
-
- // SocketFactory.
- socketFactory = readParameter("SocketFactory", false);
- }
-
- //
- // Read password parameters. If an "ENCPASSWORD" parameter is set,
- // then decrypt the password into the passwordParam string. Otherwise,
- // try to read the "PASSWORD" parameter directly to passwordParam.
- //
-
- private void readPasswordParameters() {
- String encPasswordParam = readParameter("ENCPASSWORD", false);
- if (encPasswordParam == null) {
- passwordParam = readParameter("PASSWORD", false);
- } else {
- // ENCPASSWORD is hexascii-encoded. Decode.
- byte[] pw = {0, 0, 0, 0, 0, 0, 0, 0};
- int len = encPasswordParam.length() / 2;
- if (len > 8)
- len = 8;
- for (int i = 0; i < len; i++) {
- String hex = encPasswordParam.substring(i*2, i*2+2);
- Integer x = new Integer(Integer.parseInt(hex, 16));
- pw[i] = x.byteValue();
- }
- // Decrypt the password.
- byte[] key = {23, 82, 107, 6, 35, 78, 88, 7};
- DesCipher des = new DesCipher(key);
- des.decrypt(pw, 0, pw, 0);
- passwordParam = new String(pw);
- }
- }
-
- public String readParameter(String name, boolean required) {
- if (inAnApplet) {
- String s = getParameter(name);
- if ((s == null) && required) {
- fatalError(name + " parameter not specified");
- }
- return s;
- }
-
- for (int i = 0; i < mainArgs.length; i += 2) {
- if (mainArgs[i].equalsIgnoreCase(name)) {
- try {
- return mainArgs[i+1];
- } catch (Exception e) {
- if (required) {
- fatalError(name + " parameter not specified");
- }
- return null;
- }
- }
- }
- if (required) {
- fatalError(name + " parameter not specified");
- }
- return null;
- }
-
- int readIntParameter(String name, int defaultValue) {
- String str = readParameter(name, false);
- int result = defaultValue;
- if (str != null) {
- try {
- result = Integer.parseInt(str);
- } catch (NumberFormatException e) { }
- }
- return result;
- }
-
- //
- // moveFocusToDesktop() - move keyboard focus either to VncCanvas.
- //
-
- void moveFocusToDesktop() {
- if (vncContainer != null) {
- if (vc != null && vncContainer.isAncestorOf(vc))
- vc.requestFocus();
- }
- }
-
- //
- // disconnect() - close connection to server.
- //
-
- synchronized public void disconnect() {
- System.out.println("Disconnecting");
-
- if (vc != null) {
- double sec = (System.currentTimeMillis() - vc.statStartTime) / 1000.0;
- double rate = Math.round(vc.statNumUpdates / sec * 100) / 100.0;
- long nRealRects = vc.statNumPixelRects;
- long nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects;
- System.out.println("Updates received: " + vc.statNumUpdates + " (" +
- nRealRects + " rectangles + " + nPseudoRects +
- " pseudo), " + rate + " updates/sec");
- long numRectsOther = nRealRects - vc.statNumRectsTight
- - vc.statNumRectsZRLE - vc.statNumRectsHextile
- - vc.statNumRectsRaw - vc.statNumRectsCopy;
- System.out.println("Rectangles:" +
- " Tight=" + vc.statNumRectsTight +
- "(JPEG=" + vc.statNumRectsTightJPEG +
- ") ZRLE=" + vc.statNumRectsZRLE +
- " Hextile=" + vc.statNumRectsHextile +
- " Raw=" + vc.statNumRectsRaw +
- " CopyRect=" + vc.statNumRectsCopy +
- " other=" + numRectsOther);
-
- long raw = vc.statNumBytesDecoded;
- long compressed = vc.statNumBytesEncoded;
- if (compressed > 0) {
- double ratio = Math.round((double)raw / compressed * 1000) / 1000.0;
- System.out.println("Pixel data: " + vc.statNumBytesDecoded +
- " bytes, " + vc.statNumBytesEncoded +
- " compressed, ratio " + ratio);
- }
- }
-
- if (rfb != null && !rfb.closed())
- rfb.close();
- options.dispose();
- clipboard.dispose();
- if (rec != null)
- rec.dispose();
-
- if (inAnApplet) {
- showMessage("Disconnected");
- } else {
- System.exit(0);
- }
- }
-
- //
- // fatalError() - print out a fatal error message.
- // FIXME: Do we really need two versions of the fatalError() method?
- //
-
- synchronized public void fatalError(String str) {
- System.out.println(str);
-
- if (inAnApplet) {
- // vncContainer null, applet not inited,
- // can not present the error to the user.
- Thread.currentThread().stop();
- } else {
- System.exit(1);
- }
- }
-
- synchronized public void fatalError(String str, Exception e) {
-
- if (rfb != null && rfb.closed()) {
- // Not necessary to show error message if the error was caused
- // by I/O problems after the rfb.close() method call.
- System.out.println("RFB thread finished");
- return;
- }
-
- System.out.println(str);
- e.printStackTrace();
-
- if (rfb != null)
- rfb.close();
-
- if (inAnApplet) {
- showMessage(str);
- } else {
- System.exit(1);
- }
- }
-
- //
- // Show message text and optionally "Relogin" and "Close" buttons.
- //
-
- void showMessage(String msg) {
- vncContainer.removeAll();
-
- Label errLabel = new Label(msg, Label.CENTER);
- errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12));
-
- if (offerRelogin) {
-
- Panel gridPanel = new Panel(new GridLayout(0, 1));
- Panel outerPanel = new Panel(new FlowLayout(FlowLayout.LEFT));
- outerPanel.add(gridPanel);
- vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 16));
- vncContainer.add(outerPanel);
- Panel textPanel = new Panel(new FlowLayout(FlowLayout.CENTER));
- textPanel.add(errLabel);
- gridPanel.add(textPanel);
- gridPanel.add(new ReloginPanel(this));
-
- } else {
-
- vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30));
- vncContainer.add(errLabel);
-
- }
-
- if (inSeparateFrame) {
- vncFrame.pack();
- } else {
- validate();
- }
- }
-
- //
- // Stop the applet.
- // Main applet thread will terminate on first exception
- // after seeing that rfbThread has been set to null.
- //
-
- public void stop() {
- System.out.println("Stopping applet");
- rfbThread = null;
- }
-
- //
- // This method is called before the applet is destroyed.
- //
-
- public void destroy() {
- System.out.println("Destroying applet");
-
- vncContainer.removeAll();
- options.dispose();
- clipboard.dispose();
- if (rec != null)
- rec.dispose();
- if (rfb != null && !rfb.closed())
- rfb.close();
- if (inSeparateFrame)
- vncFrame.dispose();
- }
-
- //
- // Start/stop receiving mouse events.
- //
-
- public void enableInput(boolean enable) {
- vc.enableInput(enable);
- }
-
- //
- // Resize framebuffer if autoScale is enabled.
- //
-
- public void componentResized(ComponentEvent e) {
- if (e.getComponent() == vncFrame) {
- if (options.autoScale) {
- if (vc != null) {
- if (!vc.isFirstSizeAutoUpdate) {
- vc.updateFramebufferSize();
+ if (argv[i].charAt(0) == '-') {
+ if (i+1 < argv.length) {
+ if (Configuration.setParam(argv[i].substring(1), argv[i+1])) {
+ i++;
+ continue;
}
}
+ usage();
}
+
+ if (vncServerName.getValue() != null)
+ usage();
+ vncServerName.setParam(argv[i]);
}
}
-
- //
- // Ignore component events we're not interested in.
- //
-
- public void componentShown(ComponentEvent e) { }
- public void componentMoved(ComponentEvent e) { }
- public void componentHidden(ComponentEvent e) { }
- //
- // Close application properly on window close event.
- //
+ public static void usage() {
+ String usage = ("\nusage: vncviewer [options/parameters] "+
+ "[host:displayNum] [options/parameters]\n"+
+ //" vncviewer [options/parameters] -listen [port] "+
+ //"[options/parameters]\n"+
+ "\n"+
+ "Options:\n"+
+ " -log <level> configure logging level\n"+
+ "\n"+
+ "Parameters can be turned on with -<param> or off with "+
+ "-<param>=0\n"+
+ "Parameters which take a value can be specified as "+
+ "-<param> <value>\n"+
+ "Other valid forms are <param>=<value> -<param>=<value> "+
+ "--<param>=<value>\n"+
+ "Parameter names are case-insensitive. The parameters "+
+ "are:\n\n"+
+ Configuration.listParams());
+ System.err.print(usage);
+ System.exit(1);
+ }
- public void windowClosing(WindowEvent evt) {
- System.out.println("Closing window");
- if (rfb != null)
- disconnect();
+ public VncViewer() {
+ applet = true;
+ firstApplet = true;
+ }
- vncContainer.hide();
+ public static void newViewer(VncViewer oldViewer) {
+ VncViewer viewer = new VncViewer();
+ viewer.applet = oldViewer.applet;
+ viewer.firstApplet = false;
+ viewer.start();
+ }
- if (!inAnApplet) {
+
+ public void init() {
+ vlog.debug("init called");
+ setBackground(Color.white);
+ logo = getImage(getDocumentBase(), "logo150x150.gif");
+ }
+
+ public void start() {
+ vlog.debug("start called");
+ nViewers++;
+ if (firstApplet) {
+ alwaysShowServerDialog.setParam(true);
+ Configuration.readAppletParams(this);
+ 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)));
+ }
+ }
+ thread = new Thread(this);
+ thread.start();
+ }
+
+ public void run() {
+ CConn cc = null;
+ try {
+ cc = new CConn(this, null, vncServerName.getValue(), false);
+ while (true)
+ cc.processMsg();
+ } catch (EndOfStream e) {
+ vlog.info(e.toString());
+ } catch (java.lang.Exception e) {
+ if (cc != null) cc.deleteWindow();
+ if (cc == null || !cc.shuttingDown) {
+ e.printStackTrace();
+ JOptionPane.showMessageDialog(null,
+ e.toString(),
+ "VNC Viewer : Error",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ if (cc != null) cc.deleteWindow();
+ nViewers--;
+ if (!applet && nViewers == 0) {
System.exit(0);
}
}
- //
- // Ignore window events we're not interested in.
- //
+ BoolParameter fastCopyRect
+ = new BoolParameter("FastCopyRect",
+ "Use fast CopyRect - turn this off if you get "+
+ "screen corruption when copying from off-screen",
+ true);
+ 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);
+ 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 fullColor
+ = new AliasParameter("FullColor", "Alias for FullColour", fullColour);
+ 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 acceptClipboard
+ = new BoolParameter("AcceptClipboard",
+ "Accept clipboard changes from the server", true);
+ BoolParameter sendClipboard
+ = new BoolParameter("SendClipboard",
+ "Send clipboard changes to the server", true);
+ 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 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, 9 = High",
+ 6);
+ 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);
- public void windowActivated(WindowEvent evt) {}
- public void windowDeactivated (WindowEvent evt) {}
- public void windowOpened(WindowEvent evt) {}
- public void windowClosed(WindowEvent evt) {}
- public void windowIconified(WindowEvent evt) {}
- public void windowDeiconified(WindowEvent evt) {}
+ Thread thread;
+ boolean applet, firstApplet;
+ Image logo;
+ static int nViewers;
+ static LogWriter vlog = new LogWriter("main");
}
diff --git a/java/src/com/tigervnc/vncviewer/X509Tunnel.java b/java/src/com/tigervnc/vncviewer/X509Tunnel.java
deleted file mode 100644
index ddc3f82..0000000
--- a/java/src/com/tigervnc/vncviewer/X509Tunnel.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2003 Sun Microsystems, Inc.
- * Copyright (C) 2003-2010 Martin Koegler
- * Copyright (C) 2006 OCCAM Financial Technology
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-package com.tigervnc.vncviewer;
-
-import java.util.*;
-import java.net.*;
-import javax.net.ssl.*;
-import java.security.*;
-import java.security.cert.*;
-
-public class X509Tunnel extends TLSTunnelBase
-{
-
- public X509Tunnel (Socket sock_)
- {
- super (sock_);
- }
-
- protected void setParam (SSLSocket sock)
- {
- String[]supported;
- ArrayList enabled = new ArrayList ();
-
- supported = sock.getSupportedCipherSuites ();
-
- for (int i = 0; i < supported.length; i++)
- if (!supported[i].matches (".*DH_anon.*"))
- enabled.add (supported[i]);
-
- sock.setEnabledCipherSuites ((String[])enabled.toArray (new String[0]));
- }
-
- protected void initContext (SSLContext sc) throws java.security.
- GeneralSecurityException
- {
- TrustManager[] myTM = new TrustManager[]
- {
- new MyX509TrustManager ()};
- sc.init (null, myTM, null);
- }
-
-
- class MyX509TrustManager implements X509TrustManager
- {
-
- X509TrustManager tm;
-
- MyX509TrustManager () throws java.security.GeneralSecurityException
- {
- TrustManagerFactory tmf =
- TrustManagerFactory.getInstance ("SunX509", "SunJSSE");
- KeyStore ks = KeyStore.getInstance ("JKS");
- tmf.init (ks);
- tm = (X509TrustManager) tmf.getTrustManagers ()[0];
- }
- public void checkClientTrusted (X509Certificate[]chain,
- String authType) throws
- CertificateException
- {
- tm.checkClientTrusted (chain, authType);
- }
-
- public void checkServerTrusted (X509Certificate[]chain,
- String authType)
- throws CertificateException
- {
- try
- {
- tm.checkServerTrusted (chain, authType);
- } catch (CertificateException e)
- {
- MessageBox m =
- new MessageBox (e.toString (), MessageBox.MB_OKAYCANCEL);
- if (!m.result ())
- throw e;
- }
- }
-
- public X509Certificate[] getAcceptedIssuers ()
- {
- return tm.getAcceptedIssuers ();
- }
- }
-}
diff --git a/java/src/com/tigervnc/vncviewer/ZlibInStream.java b/java/src/com/tigervnc/vncviewer/ZlibInStream.java
deleted file mode 100644
index 2f2a3c0..0000000
--- a/java/src/com/tigervnc/vncviewer/ZlibInStream.java
+++ /dev/null
@@ -1,113 +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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- * USA.
- */
-
-//
-// A ZlibInStream reads from a zlib.io.InputStream
-//
-
-package com.tigervnc.vncviewer;
-
-public class ZlibInStream extends InStream {
-
- static final int defaultBufSize = 16384;
-
- public ZlibInStream(int bufSize_) {
- bufSize = bufSize_;
- b = new byte[bufSize];
- ptr = end = ptrOffset = 0;
- inflater = new java.util.zip.Inflater();
- }
-
- public ZlibInStream() { this(defaultBufSize); }
-
- public void setUnderlying(InStream is, int bytesIn_) {
- underlying = is;
- bytesIn = bytesIn_;
- ptr = end = 0;
- }
-
- public void reset() throws Exception {
- ptr = end = 0;
- if (underlying == null) return;
-
- while (bytesIn > 0) {
- decompress();
- end = 0; // throw away any data
- }
- underlying = null;
- }
-
- public int pos() { return ptrOffset + ptr; }
-
- protected int overrun(int itemSize, int nItems) throws Exception {
- if (itemSize > bufSize)
- throw new Exception("ZlibInStream overrun: max itemSize exceeded");
- if (underlying == null)
- throw new Exception("ZlibInStream overrun: no underlying stream");
-
- if (end - ptr != 0)
- System.arraycopy(b, ptr, b, 0, end - ptr);
-
- ptrOffset += ptr;
- end -= ptr;
- ptr = 0;
-
- while (end < itemSize) {
- decompress();
- }
-
- if (itemSize * nItems > end)
- nItems = end / itemSize;
-
- return nItems;
- }
-
- // decompress() calls the decompressor once. Note that this won't
- // necessarily generate any output data - it may just consume some input
- // data. Returns false if wait is false and we would block on the underlying
- // stream.
-
- private void decompress() throws Exception {
- try {
- underlying.check(1);
- int avail_in = underlying.getend() - underlying.getptr();
- if (avail_in > bytesIn)
- avail_in = bytesIn;
-
- if (inflater.needsInput()) {
- inflater.setInput(underlying.getbuf(), underlying.getptr(), avail_in);
- }
-
- int n = inflater.inflate(b, end, bufSize - end);
-
- end += n;
- if (inflater.needsInput()) {
- bytesIn -= avail_in;
- underlying.setptr(underlying.getptr() + avail_in);
- }
- } catch (java.util.zip.DataFormatException e) {
- throw new Exception("ZlibInStream: inflate failed");
- }
- }
-
- private InStream underlying;
- private int bufSize;
- private int ptrOffset;
- private java.util.zip.Inflater inflater;
- private int bytesIn;
-}
diff --git a/java/src/com/tigervnc/vncviewer/tigervnc.ico b/java/src/com/tigervnc/vncviewer/tigervnc.ico
new file mode 100644
index 0000000..50b90f0
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/tigervnc.ico
Binary files differ
diff --git a/java/src/com/tigervnc/vncviewer/tigervnc.png b/java/src/com/tigervnc/vncviewer/tigervnc.png
new file mode 100644
index 0000000..8ac883e
--- /dev/null
+++ b/java/src/com/tigervnc/vncviewer/tigervnc.png
Binary files differ