]> source.dussan.org Git - tigervnc.git/commitdiff
Copying old Java Viewer source where RfbPlayer development had originated.
authorConstantin Kaplinsky <const@tightvnc.com>
Fri, 18 Apr 2008 09:51:44 +0000 (09:51 +0000)
committerConstantin Kaplinsky <const@tightvnc.com>
Fri, 18 Apr 2008 09:51:44 +0000 (09:51 +0000)
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2495 3789f03b-4d11-0410-bbf8-ca57d06f2519

18 files changed:
java/src/com/tightvnc/rfbplayer/AuthPanel.java [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/ButtonPanel.java [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/ClipboardFrame.java [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/DesCipher.java [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/LICENCE.TXT [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/Makefile [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/OptionsFrame.java [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/README [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/RfbProto.java [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/VncCanvas.java [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/VncViewer.java [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/dir.mk [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/hextile.vnc [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/index.vnc [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/noshared.vnc [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/shared.vnc [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/tight.vnc [new file with mode: 0644]
java/src/com/tightvnc/rfbplayer/zlib.vnc [new file with mode: 0644]

diff --git a/java/src/com/tightvnc/rfbplayer/AuthPanel.java b/java/src/com/tightvnc/rfbplayer/AuthPanel.java
new file mode 100644 (file)
index 0000000..cdc4fe8
--- /dev/null
@@ -0,0 +1,116 @@
+//
+//  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.
+//
+
+import java.awt.*;
+import java.awt.event.*;
+
+//
+// The panel which implements the user authentication scheme
+//
+
+class AuthPanel extends Panel implements ActionListener {
+
+  Label title, retry, prompt;
+  TextField password;
+  Button ok;
+
+  //
+  // Constructor.
+  //
+
+  public AuthPanel() {
+
+    title = new Label("VNC Authentication",Label.CENTER);
+    title.setFont(new Font("Helvetica", Font.BOLD, 18));
+
+    prompt = new Label("Password:",Label.CENTER);
+
+    password = new TextField(10);
+    password.setForeground(Color.black);
+    password.setBackground(Color.white);
+    password.setEchoChar('*');
+
+    ok = new Button("OK");
+
+    retry = new Label("",Label.CENTER);
+    retry.setFont(new Font("Courier", Font.BOLD, 16));
+
+
+    GridBagLayout gridbag = new GridBagLayout();
+    GridBagConstraints gbc = new GridBagConstraints();
+
+    setLayout(gridbag);
+
+    gbc.gridwidth = GridBagConstraints.REMAINDER;
+    gridbag.setConstraints(title,gbc);
+    add(title);
+
+    gbc.fill = GridBagConstraints.HORIZONTAL;
+    gridbag.setConstraints(retry,gbc);
+    add(retry);
+
+    gbc.fill = GridBagConstraints.NONE;
+    gbc.gridwidth = 1;
+    gridbag.setConstraints(prompt,gbc);
+    add(prompt);
+
+    gridbag.setConstraints(password,gbc);
+    add(password);
+    password.addActionListener(this);
+
+    gbc.ipady = 10;
+    gbc.gridwidth = GridBagConstraints.REMAINDER;
+    gbc.fill = GridBagConstraints.BOTH;
+    gbc.insets = new Insets(0,20,0,0);
+    gbc.ipadx = 40;
+    gridbag.setConstraints(ok,gbc);
+    add(ok);
+    ok.addActionListener(this);
+  }
+
+  //
+  // Move keyboard focus to the password text field object.
+  //
+
+  public void moveFocusToPasswordField() {
+    password.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() == password || evt.getSource() == ok) {
+      notify();
+    }
+  }
+
+  //
+  // retry().
+  //
+
+  public void retry() {
+    retry.setText("Sorry. Try again.");
+    password.setText("");
+    moveFocusToPasswordField();
+  }
+
+}
diff --git a/java/src/com/tightvnc/rfbplayer/ButtonPanel.java b/java/src/com/tightvnc/rfbplayer/ButtonPanel.java
new file mode 100644 (file)
index 0000000..de3ddfe
--- /dev/null
@@ -0,0 +1,120 @@
+//
+//  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.
+//
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+
+class ButtonPanel extends Panel implements ActionListener {
+
+  VncViewer viewer;
+  Button disconnectButton;
+  Button optionsButton;
+  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);
+    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);
+  }
+
+  public void enableButtons() {
+    disconnectButton.setEnabled(true);
+    clipboardButton.setEnabled(true);
+    refreshButton.setEnabled(true);
+  }
+
+  //
+  // Enable/disable controls that should not be available in view-only
+  // mode.
+  //
+
+  void enableRemoteAccessControls(boolean enable) {
+    ctrlAltDelButton.setEnabled(enable);
+  }
+
+  //
+  // Event processing.
+  //
+
+  public void actionPerformed(ActionEvent evt) {
+    if (evt.getSource() == disconnectButton) {
+      viewer.disconnect();
+
+    } else if (evt.getSource() == optionsButton) {
+      viewer.options.setVisible(!viewer.options.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();
+      }
+    }
+    viewer.moveFocusToDesktop();
+  }
+}
+
diff --git a/java/src/com/tightvnc/rfbplayer/ClipboardFrame.java b/java/src/com/tightvnc/rfbplayer/ClipboardFrame.java
new file mode 100644 (file)
index 0000000..f0a5b8d
--- /dev/null
@@ -0,0 +1,133 @@
+//
+//  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.
+//
+
+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("TightVNC 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/tightvnc/rfbplayer/DesCipher.java b/java/src/com/tightvnc/rfbplayer/DesCipher.java
new file mode 100644 (file)
index 0000000..2b7b5f2
--- /dev/null
@@ -0,0 +1,496 @@
+//
+// 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/
+
+
+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/tightvnc/rfbplayer/LICENCE.TXT b/java/src/com/tightvnc/rfbplayer/LICENCE.TXT
new file mode 100644 (file)
index 0000000..ae3b531
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+         Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+        59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program 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 program 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 program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+    USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/java/src/com/tightvnc/rfbplayer/Makefile b/java/src/com/tightvnc/rfbplayer/Makefile
new file mode 100644 (file)
index 0000000..76dbca4
--- /dev/null
@@ -0,0 +1,35 @@
+#
+# Making the VNC applet.
+#
+
+CP = cp
+JC = javac
+JAR = jar
+ARCHIVE = VncViewer.jar
+PAGES = index.vnc shared.vnc noshared.vnc hextile.vnc zlib.vnc tight.vnc
+INSTALL_DIR = /usr/local/vnc/classes
+
+CLASSES = VncViewer.class RfbProto.class AuthPanel.class VncCanvas.class \
+         OptionsFrame.class ClipboardFrame.class ButtonPanel.class \
+         DesCipher.class
+
+SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \
+         OptionsFrame.java ClipboardFrame.java ButtonPanel.java \
+         DesCipher.java
+
+all: $(CLASSES) $(ARCHIVE)
+
+$(CLASSES): $(SOURCES)
+       $(JC) -O $(SOURCES)
+
+$(ARCHIVE): $(CLASSES)
+       $(JAR) cf $(ARCHIVE) $(CLASSES)
+
+install: $(CLASSES) $(ARCHIVE)
+       $(CP) $(CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR)
+
+export:: $(CLASSES) $(ARCHIVE) $(PAGES)
+       @$(ExportJavaClasses)
+
+clean::
+       $(RM) *.class *.jar
diff --git a/java/src/com/tightvnc/rfbplayer/OptionsFrame.java b/java/src/com/tightvnc/rfbplayer/OptionsFrame.java
new file mode 100644 (file)
index 0000000..45e5ab6
--- /dev/null
@@ -0,0 +1,387 @@
+//
+//  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.
+//
+
+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",
+    "Share desktop",
+  };
+
+  static String[][] values = {
+    { "Raw", "RRE", "CoRRE", "Hextile", "Zlib", "Tight" },
+    { "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" },
+    { "Yes", "No" },
+  };
+
+  final int
+    encodingIndex        = 0,
+    compressLevelIndex   = 1,
+    jpegQualityIndex     = 2,
+    cursorUpdatesIndex   = 3,
+    useCopyRectIndex     = 4,
+    eightBitColorsIndex  = 5,
+    mouseButtonIndex     = 6,
+    viewOnlyIndex        = 7,
+    shareDesktopIndex    = 8;
+
+  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[] encodings = new int[20];
+  int nEncodings;
+
+  int compressLevel;
+  int jpegQuality;
+
+  boolean eightBitColors;
+
+  boolean requestCursorUpdates;
+  boolean ignoreCursorUpdates;
+
+  boolean reverseMouseButtons2And3;
+  boolean shareDesktop;
+  boolean viewOnly;
+  boolean showControls;
+
+  //
+  // Constructor.  Set up the labels and choices from the names and values
+  // arrays.
+  //
+
+  OptionsFrame(VncViewer v) {
+    super("TightVNC 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("Tight");
+    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[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);
+         }
+       }
+      }
+    }
+
+    // "Show Controls" setting does not have associated GUI option
+
+    showControls = true;
+    String s = viewer.readParameter("Show Controls", false);
+    if (s != null && s.equalsIgnoreCase("No"))
+      showControls = false;
+
+    // Make the booleans and encodings array correspond to the state of the GUI
+
+    setEncodings();
+    setColorFormat();
+    setOtherOptions();
+  }
+
+
+  //
+  // Disable the shareDesktop option
+  //
+
+  void disableShareDesktop() {
+    labels[shareDesktopIndex].setEnabled(false);
+    choices[shareDesktopIndex].setEnabled(false);
+  }
+
+
+  //
+  // setEncodings looks at the encoding, compression level, JPEG
+  // quality level, cursor shape updates and copyRect choices and sets
+  // the encodings array appropriately. It also calls the VncViewer's
+  // setEncodings method to send a message to the RFB server if
+  // necessary.
+  //
+
+  void setEncodings() {
+    nEncodings = 0;
+    if (choices[useCopyRectIndex].getSelectedItem().equals("Yes")) {
+      encodings[nEncodings++] = RfbProto.EncodingCopyRect;
+    }
+
+    int preferredEncoding = RfbProto.EncodingRaw;
+    boolean enableCompressLevel = false;
+
+    if (choices[encodingIndex].getSelectedItem().equals("RRE")) {
+      preferredEncoding = RfbProto.EncodingRRE;
+    } else if (choices[encodingIndex].getSelectedItem().equals("CoRRE")) {
+      preferredEncoding = RfbProto.EncodingCoRRE;
+    } else if (choices[encodingIndex].getSelectedItem().equals("Hextile")) {
+      preferredEncoding = RfbProto.EncodingHextile;
+    } else if (choices[encodingIndex].getSelectedItem().equals("Zlib")) {
+      preferredEncoding = RfbProto.EncodingZlib;
+      enableCompressLevel = true;
+    } else if (choices[encodingIndex].getSelectedItem().equals("Tight")) {
+      preferredEncoding = RfbProto.EncodingTight;
+      enableCompressLevel = true;
+    }
+
+    encodings[nEncodings++] = preferredEncoding;
+    if (preferredEncoding != RfbProto.EncodingHextile) {
+      encodings[nEncodings++] = RfbProto.EncodingHextile;
+    }
+    if (preferredEncoding != RfbProto.EncodingTight) {
+      encodings[nEncodings++] = RfbProto.EncodingTight;
+    }
+    if (preferredEncoding != RfbProto.EncodingZlib) {
+      encodings[nEncodings++] = RfbProto.EncodingZlib;
+    }
+    if (preferredEncoding != RfbProto.EncodingCoRRE) {
+      encodings[nEncodings++] = RfbProto.EncodingCoRRE;
+    }
+    if (preferredEncoding != RfbProto.EncodingRRE) {
+      encodings[nEncodings++] = RfbProto.EncodingRRE;
+    }
+
+    // Handle compression level setting.
+
+    if (enableCompressLevel) {
+      labels[compressLevelIndex].setEnabled(true);
+      choices[compressLevelIndex].setEnabled(true);
+      try {
+       compressLevel =
+         Integer.parseInt(choices[compressLevelIndex].getSelectedItem());
+      }
+      catch (NumberFormatException e) {
+       compressLevel = -1;
+      }
+      if (compressLevel >= 1 && compressLevel <= 9) {
+       encodings[nEncodings++] =
+         RfbProto.EncodingCompressLevel0 + compressLevel;
+      } else {
+       compressLevel = -1;
+      }
+    } else {
+      labels[compressLevelIndex].setEnabled(false);
+      choices[compressLevelIndex].setEnabled(false);
+    }
+
+    // Handle JPEG quality setting.
+
+    if (preferredEncoding == RfbProto.EncodingTight) {
+      labels[jpegQualityIndex].setEnabled(true);
+      choices[jpegQualityIndex].setEnabled(true);
+      try {
+       jpegQuality =
+         Integer.parseInt(choices[jpegQualityIndex].getSelectedItem());
+      }
+      catch (NumberFormatException e) {
+       jpegQuality = -1;
+      }
+      if (jpegQuality >= 0 && jpegQuality <= 9) {
+       encodings[nEncodings++] =
+         RfbProto.EncodingQualityLevel0 + jpegQuality;
+      } else {
+       jpegQuality = -1;
+      }
+    } else {
+      labels[jpegQualityIndex].setEnabled(false);
+      choices[jpegQualityIndex].setEnabled(false);
+    }
+
+    // Request cursor shape updates if necessary.
+
+    requestCursorUpdates =
+      !choices[cursorUpdatesIndex].getSelectedItem().equals("Disable");
+
+    if (requestCursorUpdates) {
+      encodings[nEncodings++] = RfbProto.EncodingXCursor;
+      encodings[nEncodings++] = RfbProto.EncodingRichCursor;
+      ignoreCursorUpdates =
+       choices[cursorUpdatesIndex].getSelectedItem().equals("Ignore");
+    }
+
+    encodings[nEncodings++] = RfbProto.EncodingLastRect;
+    encodings[nEncodings++] = RfbProto.EncodingNewFBSize;
+
+    viewer.setEncodings();
+  }
+
+  //
+  // setColorFormat sets eightBitColors variable depending on the GUI
+  // setting, and switches between 8-bit and 24-bit colors mode, if
+  // necessary.
+  //
+
+  void setColorFormat() {
+
+    eightBitColors
+      = choices[eightBitColorsIndex].getSelectedItem().equals("Yes");
+
+    // FIXME: implement dynamic changing of the color mode.
+
+  }
+
+  //
+  // setOtherOptions looks at the "other" choices (ones which don't set the
+  // encoding or the color format) 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");
+  }
+
+
+  //
+  // 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();
+
+    } else if (source == choices[eightBitColorsIndex]) {
+
+      setColorFormat();
+
+    } else if (source == choices[mouseButtonIndex] ||
+              source == choices[shareDesktopIndex] ||
+              source == choices[viewOnlyIndex]) {
+
+      setOtherOptions();
+    }
+  }
+
+  //
+  // 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/tightvnc/rfbplayer/README b/java/src/com/tightvnc/rfbplayer/README
new file mode 100644 (file)
index 0000000..b9f24c0
--- /dev/null
@@ -0,0 +1,274 @@
+
+TightVNC 1.2.3 Java Viewer
+==========================
+
+Copyright (C) 2001,2002 HorizonLive.com, Inc.  All Rights Reserved.
+Copyright (C) 2001 Constantin Kaplinsky.  All Rights Reserved.
+Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+
+This software is distributed under the GNU General Public Licence as
+published by the Free Software Foundation. See the file LICENCE.TXT for the
+conditions under which this software is made available. VNC also contains
+code from other sources. See the Acknowledgements section below, and the
+individual files for details of the conditions under which they are made
+available.
+
+
+Compiling from the sources
+==========================
+
+To compile all the .java files to .class files, simply do:
+
+       % make all
+
+This will also generate a JAR (Java archive) file containing all the classes. 
+Copy all the .class files, the .jar file and the .vnc files to an
+installation directory (e.g. /usr/local/vnc/classes):
+
+       % cp *.class *.jar *.vnc /usr/local/vnc/classes
+
+Make sure that the vncserver script is configured to point to the
+installation directory.
+
+
+Configuration
+=============
+
+TightVNC Java viewer supports a number of parameters allowing you to
+customize its behaviour. Most parameter names copy settings available from
+the Options frame in the Java viewer. Both parameter names and their values
+are case-insensitive, with one exception for the "PASSWORD" parameter. Here
+is the full list of parameters supported in TightVNC Java viewer:
+
+--> "HOST" (no GUI equivalent)
+
+    Value: host name or IP address of the VNC server.
+    Default: in applet mode, the host from which the applet was loaded.
+
+    This parameter tells the viewer which server to connect to. Normally,
+    it's not needed, because default Java security policy allow connections
+    from applets to the only one host anyway, and that is the host from which
+    the applet was loaded.
+
+--> "PORT" (no GUI equivalent)
+
+    Value: TCP port number on the VNC server.
+    Default: none.
+
+    This parameter is required in all cases. Note that this port is not the
+    one used for HTTP connection from the browser, it is the port used for
+    RFB connection. Usually, VNC servers use ports 58xx for HTTP connections,
+    and ports 59xx for RFB connections. Thus, most likely, this parameter
+    should be set to something like 5900, 5901 etc.
+
+--> "PASSWORD"
+
+    Value: session password in plan text.
+    Default: none, ask user.
+
+    DO NOT EVER USE THIS PARAMETER, unless you really know what you are
+    doing. It's extremely dangerous from the security point of view. When
+    this parameter is set, the viewer won't ever ask for a password.
+
+--> "Encoding"
+
+    Values: "Raw", "RRE", "CoRRE", "Hextile", "Zlib", "Tight".
+    Default: "Tight".
+
+    The preferred encoding. "Hextile" is a good choice for fast networks,
+    while "Tight" is better suited for low-bandwidth connections. From the
+    other side, the "Tight" decoder in TightVNC Java viewer seems to be more
+    efficient than "Hextile" decoder so it's possible that this default
+    setting can be ok for fast networks too.
+
+--> "Compression level"
+
+    Values: "Default", "1", "2", "3", "4", "5", "6", "7", "8", "9".
+    Default: "Default". ;-)
+
+    Use specified compression level for "Tight" and "Zlib" encodings. Level 1
+    uses minimum of CPU time on the server but achieves weak compression
+    ratios. Level 9 offers best compression but may be slow in terms of CPU
+    time consumption on the server side. Use high levels with very slow
+    network connections, and low levels when working over higher-speed
+    networks. The "Default" value means that the server's default compression
+    level should be used.
+
+--> "JPEG image quality"
+
+    Values: "JPEG off", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9".
+    Default: "6".
+
+    Use the specified image quality level in "Tight" encoding. Quality level
+    0 denotes bad image quality but very impressive compression ratios, while
+    level 9 offers very good image quality at lower compression ratios. If
+    the value is "JPEG off", the server will not use lossy JPEG compression
+    in "Tight" encoding.
+
+--> "Cursor shape updates"
+
+    Values: "Enable", "Ignore", "Disable".
+    Default: "Enable".
+
+    Cursor shape updates is a protocol extension used to handle remote cursor
+    movements locally on the client side, saving bandwidth and eliminating
+    delays in mouse pointer movement. Note that current implementation of
+    cursor shape updates does not allow a client to track mouse cursor
+    position at the server side. This means that clients would not see mouse
+    cursor movements if mouse was moved either locally on the server, or by
+    another remote VNC client. Set this parameter to "Disable" if you always
+    want to see real cursor position on the remote side. Setting this option
+    to "Ignore" is similar to "Enable" but the remote cursor will not be
+    visible at all. This can be a reasonable setting if you don't care about
+    cursor shape and don't want to see two mouse cursors, one above another.
+
+--> "Use CopyRect"
+
+    Values: "Yes", "No".
+    Default: "Yes".
+
+    The "CopyRect" encoding saves bandwidth and drawing time when parts of
+    the remote screen are moving around. Most likely, you don't want to
+    change this setting.
+
+--> "Restricted colors"
+
+    Values: "Yes", "No".
+    Default: "No".
+
+    If set to "No", then 24-bit color format is used to represent pixel data. 
+    If set to "Yes", then only 8 bits are used to represent each pixel. 8-bit
+    color format can save bandwidth, but colors may look very inaccurate.
+
+--> "Mouse buttons 2 and 3"
+
+    Values: "Normal", "Reversed".
+    Default: "Normal".
+
+    If set to "Reversed", then right mouse button (button 2) will act as it
+    was middle mouse button (button 3), and vice versa.
+
+--> "View only"
+
+    Values: "Yes", "No".
+    Default: "No".
+
+    If set to "Yes", then all keyboard and mouse events in the desktop window
+    will be silently ignored and will not be passed to the remote side.
+
+--> "Share desktop"
+
+    Values: "Yes", "No".
+    Default: "Yes".
+
+    Share the connection with other clients on the same VNC server. The exact
+    behaviour in each case depends on the server configuration.
+
+--> "Open new window" (no GUI equivalent, applicable only in the applet mode)
+
+    Values: "Yes", "No".
+    Default: "No".
+
+    Operate in a separate window. This makes possible resizing the desktop,
+    and adds scroll bars when necessary. If the server supports variable
+    desktop size, the window will resize automatically when remote desktop
+    size changes.
+
+--> "Show controls" (no GUI equivalent)
+
+    Values: "Yes", "No".
+    Default: "Yes".
+
+    Set to "No" if you want to get rid of that button panel at the top.
+
+--> "Defer screen updates" (no GUI equivalent)
+
+    Value: time in milliseconds.
+    Default: "20".
+
+    When updating the desktop contents after receiving an update from server,
+    schedule repaint within the specified number of milliseconds. Small delay
+    helps to coalesce several small updates into one drawing operation,
+    improving CPU usage. Set this parameter to 0 to disable deferred updates.
+
+--> "Defer cursor updates" (no GUI equivalent)
+
+    Value: time in milliseconds.
+    Default: "10".
+
+    When updating the desktop after moving the mouse, schedule repaint within
+    the specified number of milliseconds. This setting makes sense only when
+    "Cursor shape updates" parameter is set to "Enable". Small delay helps to
+    coalesce several small updates into one drawing operation, improving CPU
+    usage. Set this parameter to 0 to disable deferred cursor updates.
+
+--> "Defer update requests" (no GUI equivalent)
+
+    Value: time in milliseconds.
+    Default: "50".
+
+    After processing an update received from server, wait for the specified
+    number of milliseconds before requesting next screen update. Such delay
+    will end immediately on every mouse or keyboard event if not in the "view
+    only" mode. Small delay helps the server to coalesce several small
+    updates into one framebuffer update, improving both bandwidth and CPU
+    usage. Increasing the parameter value does not affect responsiveness on
+    mouse and keyboard events, but causes delays in updating the screen when
+    there is no mouse and keyboard activity on the client side.
+
+
+ACKNOWLEDGEMENTS
+================
+
+This distribution contains Java DES software by Dave Zimmerman
+<dzimm@widget.com> and Jef Poskanzer <jef@acme.com>.  This 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.
+
+    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/
diff --git a/java/src/com/tightvnc/rfbplayer/RfbProto.java b/java/src/com/tightvnc/rfbplayer/RfbProto.java
new file mode 100644 (file)
index 0000000..a86aac5
--- /dev/null
@@ -0,0 +1,678 @@
+//
+//  Copyright (C) 2001,2002 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.
+//
+
+//
+// RfbProto.java
+//
+
+import java.io.*;
+import java.awt.*;
+import java.awt.event.*;
+import java.net.Socket;
+
+class RfbProto {
+
+  final String versionMsg = "RFB 003.003\n";
+  final static int ConnFailed = 0, NoAuth = 1, VncAuth = 2;
+  final static int VncAuthOK = 0, VncAuthFailed = 1, VncAuthTooMany = 2;
+
+  final static int FramebufferUpdate = 0, SetColourMapEntries = 1, Bell = 2,
+    ServerCutText = 3;
+
+  final int SetPixelFormat = 0, FixColourMapEntries = 1, SetEncodings = 2,
+    FramebufferUpdateRequest = 3, KeyboardEvent = 4, PointerEvent = 5,
+    ClientCutText = 6;
+
+  final static int
+    EncodingRaw            = 0,
+    EncodingCopyRect       = 1,
+    EncodingRRE            = 2,
+    EncodingCoRRE          = 4,
+    EncodingHextile        = 5,
+    EncodingZlib           = 6,
+    EncodingTight          = 7,
+    EncodingCompressLevel0 = 0xFFFFFF00,
+    EncodingQualityLevel0  = 0xFFFFFFE0,
+    EncodingXCursor        = 0xFFFFFF10,
+    EncodingRichCursor     = 0xFFFFFF11,
+    EncodingLastRect       = 0xFFFFFF20,
+    EncodingNewFBSize      = 0xFFFFFF21;
+
+  final int HextileRaw                 = (1 << 0);
+  final int HextileBackgroundSpecified = (1 << 1);
+  final int HextileForegroundSpecified = (1 << 2);
+  final int HextileAnySubrects         = (1 << 3);
+  final int HextileSubrectsColoured    = (1 << 4);
+
+  final static int TightExplicitFilter  = 0x04;
+  final static int TightFill            = 0x08;
+  final static int TightJpeg            = 0x09;
+  final static int TightMaxSubencoding  = 0x09;
+  final static int TightFilterCopy      = 0x00;
+  final static int TightFilterPalette   = 0x01;
+  final static int TightFilterGradient  = 0x02;
+
+  final static int TightMinToCompress   = 12;
+
+  String host;
+  int port;
+  Socket sock;
+  DataInputStream is;
+  OutputStream os;
+  boolean inNormalProtocol = false;
+  VncViewer viewer;
+
+
+  //
+  // Constructor.  Just make TCP connection to RFB server.
+  //
+
+  RfbProto(String h, int p, VncViewer v) throws IOException {
+    viewer = v;
+    host = h;
+    port = p;
+    sock = new Socket(host, port);
+    is = new DataInputStream(new BufferedInputStream(sock.getInputStream(),
+                                                    16384));
+    os = sock.getOutputStream();
+  }
+
+
+  void close() {
+    try {
+      sock.close();
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  //
+  // Read server's protocol version message
+  //
+
+  int serverMajor, serverMinor;
+
+  void readVersionMsg() throws IOException {
+
+    byte[] b = new byte[12];
+
+    is.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 IOException("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');
+  }
+
+
+  //
+  // Write our protocol version message
+  //
+
+  void writeVersionMsg() throws IOException {
+    os.write(versionMsg.getBytes());
+  }
+
+
+  //
+  // Find out the authentication scheme.
+  //
+
+  int readAuthScheme() throws IOException {
+    int authScheme = is.readInt();
+
+    switch (authScheme) {
+
+    case ConnFailed:
+      int reasonLen = is.readInt();
+      byte[] reason = new byte[reasonLen];
+      is.readFully(reason);
+      throw new IOException(new String(reason));
+
+    case NoAuth:
+    case VncAuth:
+      return authScheme;
+
+    default:
+      throw new IOException("Unknown authentication scheme from RFB " +
+                           "server " + authScheme);
+
+    }
+  }
+
+
+  //
+  // 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 = is.readUnsignedShort();
+    framebufferHeight = is.readUnsignedShort();
+    bitsPerPixel = is.readUnsignedByte();
+    depth = is.readUnsignedByte();
+    bigEndian = (is.readUnsignedByte() != 0);
+    trueColour = (is.readUnsignedByte() != 0);
+    redMax = is.readUnsignedShort();
+    greenMax = is.readUnsignedShort();
+    blueMax = is.readUnsignedShort();
+    redShift = is.readUnsignedByte();
+    greenShift = is.readUnsignedByte();
+    blueShift = is.readUnsignedByte();
+    byte[] pad = new byte[3];
+    is.readFully(pad);
+    int nameLength = is.readInt();
+    byte[] name = new byte[nameLength];
+    is.readFully(name);
+    desktopName = new String(name);
+
+    inNormalProtocol = true;
+  }
+
+
+  //
+  // Set new framebuffer size
+  //
+
+  void setFramebufferSize(int width, int height) {
+    framebufferWidth = width;
+    framebufferHeight = height;
+  }
+
+
+  //
+  // Read the server message type
+  //
+
+  int readServerMessageType() throws IOException {
+    return is.readUnsignedByte();
+  }
+
+
+  //
+  // Read a FramebufferUpdate message
+  //
+
+  int updateNRects;
+
+  void readFramebufferUpdate() throws IOException {
+    is.readByte();
+    updateNRects = is.readUnsignedShort();
+  }
+
+  // Read a FramebufferUpdate rectangle header
+
+  int updateRectX, updateRectY, updateRectW, updateRectH, updateRectEncoding;
+
+  void readFramebufferUpdateRectHdr() throws IOException {
+    updateRectX = is.readUnsignedShort();
+    updateRectY = is.readUnsignedShort();
+    updateRectW = is.readUnsignedShort();
+    updateRectH = is.readUnsignedShort();
+    updateRectEncoding = is.readInt();
+
+    if ((updateRectEncoding == EncodingLastRect) ||
+       (updateRectEncoding == EncodingNewFBSize))
+      return;
+
+    if ((updateRectX + updateRectW > framebufferWidth) ||
+       (updateRectY + updateRectH > framebufferHeight)) {
+      throw new IOException("Framebuffer update rectangle too large: " +
+                           updateRectW + "x" + updateRectH + " at (" +
+                           updateRectX + "," + updateRectY + ")");
+    }
+  }
+
+  // Read CopyRect source X and Y.
+
+  int copyRectSrcX, copyRectSrcY;
+
+  void readCopyRect() throws IOException {
+    copyRectSrcX = is.readUnsignedShort();
+    copyRectSrcY = is.readUnsignedShort();
+  }
+
+
+  //
+  // Read a ServerCutText message
+  //
+
+  String readServerCutText() throws IOException {
+    byte[] pad = new byte[3];
+    is.readFully(pad);
+    int len = is.readInt();
+    byte[] text = new byte[len];
+    is.readFully(text);
+    return new String(text);
+  }
+
+
+  //
+  // Read integer in compact representation
+  //
+
+  int readCompactLen() throws IOException {
+    int portion = is.readUnsignedByte();
+    int len = portion & 0x7F;
+    if ((portion & 0x80) != 0) {
+      portion = is.readUnsignedByte();
+      len |= (portion & 0x7F) << 7;
+      if ((portion & 0x80) != 0) {
+       portion = is.readUnsignedByte();
+       len |= (portion & 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 < 32) {
+        if (evt.isControlDown()) {
+          key += 96;
+        } 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 >= 127) {
+        if (key == 127) {
+          key = 0xffff;
+        } else {
+          // JDK1.1 on X incorrectly passes some keysyms straight through,
+          // so we do too.  JDK1.1.4 seems to have fixed this.
+          if ((key < 0xff00) || (key > 0xffff))
+            return;
+        }
+      }
+    }
+
+    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;
+  }
+}
diff --git a/java/src/com/tightvnc/rfbplayer/VncCanvas.java b/java/src/com/tightvnc/rfbplayer/VncCanvas.java
new file mode 100644 (file)
index 0000000..6d12aa2
--- /dev/null
@@ -0,0 +1,1303 @@
+//
+//  Copyright (C) 2001,2002 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.
+//
+
+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 {
+
+  VncViewer viewer;
+  RfbProto rfb;
+  ColorModel cm8, cm24;
+  Color[] colors;
+  int bytesPixel;
+
+  Image memImage;
+  Graphics memGraphics;
+
+  Image rawPixelsImage;
+  MemoryImageSource pixelsSource;
+  byte[] pixels8;
+  int[] pixels24;
+
+  // Zlib encoder's data.
+  byte[] zlibBuf;
+  int zlibBufLen = 0;
+  Inflater zlibInflater;
+
+  // Tight encoder's data.
+  final static int tightZlibBufferSize = 512;
+  Inflater[] tightInflaters;
+
+  // Since JPEG images are loaded asynchronously, we have to remember
+  // their position in the framebuffer. Also, this jpegRect object is
+  // used for synchronization between the rfbThread and a JVM's thread
+  // which decodes and loads JPEG images.
+  Rectangle jpegRect;
+
+  // True if we process keyboard and mouse events.
+  boolean listenersInstalled;
+
+  //
+  // The constructor.
+  //
+
+  VncCanvas(VncViewer v) throws IOException {
+    viewer = v;
+    rfb = viewer.rfb;
+
+    tightInflaters = new Inflater[4];
+
+    cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6));
+    cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF);
+
+    colors = new Color[256];
+    for (int i = 0; i < 256; i++)
+      colors[i] = new Color(cm8.getRGB(i));
+
+    setPixelFormat();
+
+    listenersInstalled = false;
+    if (!viewer.options.viewOnly)
+      enableInput(true);
+  }
+
+  //
+  // Callback methods to determine geometry of our Component.
+  //
+
+  public Dimension getPreferredSize() {
+    return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
+  }
+
+  public Dimension getMinimumSize() {
+    return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
+  }
+
+  public Dimension getMaximumSize() {
+    return new Dimension(rfb.framebufferWidth, rfb.framebufferHeight);
+  }
+
+  //
+  // All painting is performed here.
+  //
+
+  public void update(Graphics g) {
+    paint(g);
+  }
+
+  public void paint(Graphics g) {
+    synchronized(memImage) {
+      g.drawImage(memImage, 0, 0, null);
+    }
+    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);
+      }
+    }
+  }
+
+  //
+  // Override the ImageObserver interface method to handle drawing of
+  // JPEG-encoded data.
+  //
+
+  public boolean imageUpdate(Image img, int infoflags,
+                             int x, int y, int width, int height) {
+    if ((infoflags & (ALLBITS | ABORT)) == 0) {
+      return true;             // We need more image data.
+    } else {
+      // If the whole image is available, draw it now.
+      if ((infoflags & ALLBITS) != 0) {
+       if (jpegRect != null) {
+         synchronized(jpegRect) {
+           memGraphics.drawImage(img, jpegRect.x, jpegRect.y, null);
+           scheduleRepaint(jpegRect.x, jpegRect.y,
+                           jpegRect.width, jpegRect.height);
+           jpegRect.notify();
+         }
+       }
+      }
+      return false;            // All image data was processed.
+    }
+  }
+
+  //
+  // Start/stop receiving keyboard and mouse events.
+  //
+
+  public synchronized void enableInput(boolean enable) {
+    if (enable && !listenersInstalled) {
+      listenersInstalled = true;
+      addKeyListener(this);
+      addMouseListener(this);
+      addMouseMotionListener(this);
+      viewer.buttonPanel.enableRemoteAccessControls(true);
+    } else if (!enable && listenersInstalled) {
+      listenersInstalled = false;
+      removeKeyListener(this);
+      removeMouseListener(this);
+      removeMouseMotionListener(this);
+      viewer.buttonPanel.enableRemoteAccessControls(false);
+    }
+  }
+
+  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, true, true, 255, 255, 255, 16, 8, 0);
+      bytesPixel = 4;
+    }
+    updateFramebufferSize();
+  }
+
+  void updateFramebufferSize() {
+
+    // Useful shortcuts.
+    int fbWidth = rfb.framebufferWidth;
+    int fbHeight = rfb.framebufferHeight;
+
+    // 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.createImage(fbWidth, fbHeight);
+      memGraphics = memImage.getGraphics();
+    } else if (memImage.getWidth(null) != fbWidth ||
+              memImage.getHeight(null) != fbHeight) {
+      synchronized(memImage) {
+       memImage = viewer.createImage(fbWidth, fbHeight);
+       memGraphics = memImage.getGraphics();
+      }
+    }
+
+    // Images with raw pixels should be re-allocated on every change
+    // of geometry or pixel format.
+    if (bytesPixel == 1) {
+      pixels24 = null;
+      pixels8 = new byte[fbWidth * fbHeight];
+
+      pixelsSource =
+       new MemoryImageSource(fbWidth, fbHeight, cm8, pixels8, 0, fbWidth);
+    } else {
+      pixels8 = null;
+      pixels24 = new int[fbWidth * fbHeight];
+
+      pixelsSource =
+       new MemoryImageSource(fbWidth, fbHeight, cm24, pixels24, 0, fbWidth);
+    }
+    pixelsSource.setAnimated(true);
+    rawPixelsImage = createImage(pixelsSource);
+
+    // Update the size of desktop containers.
+    if (viewer.inSeparateFrame) {
+      if (viewer.desktopScrollPane != null)
+       resizeDesktopFrame();
+    } else {
+      setSize(fbWidth, fbHeight);
+    }
+  }
+
+  void resizeDesktopFrame() {
+    setSize(rfb.framebufferWidth, rfb.framebufferHeight);
+
+    // FIXME: Find a better way to determine correct size of a
+    // ScrollPane.  -- const
+    Insets insets = viewer.desktopScrollPane.getInsets();
+    viewer.desktopScrollPane.setSize(rfb.framebufferWidth +
+                                    2 * Math.min(insets.left, insets.right),
+                                    rfb.framebufferHeight +
+                                    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;
+    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 IOException {
+
+    rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth,
+                                     rfb.framebufferHeight, false);
+
+    //
+    // main dispatch loop
+    //
+
+    while (true) {
+      int msgType = rfb.readServerMessageType();
+
+      switch (msgType) {
+      case RfbProto.FramebufferUpdate:
+       rfb.readFramebufferUpdate();
+
+       for (int i = 0; i < rfb.updateNRects; i++) {
+         rfb.readFramebufferUpdateRectHdr();
+
+         if (rfb.updateRectEncoding == rfb.EncodingLastRect)
+           break;
+
+         if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) {
+           rfb.setFramebufferSize(rfb.updateRectW, rfb.updateRectH);
+           updateFramebufferSize();
+           break;
+         }
+
+         if (rfb.updateRectEncoding == rfb.EncodingXCursor ||
+             rfb.updateRectEncoding == rfb.EncodingRichCursor) {
+           handleCursorShapeUpdate(rfb.updateRectEncoding,
+                                   rfb.updateRectX, rfb.updateRectY,
+                                   rfb.updateRectW, rfb.updateRectH);
+           continue;
+         }
+
+         switch (rfb.updateRectEncoding) {
+
+         case RfbProto.EncodingRaw:
+         {
+           handleRawRect(rfb.updateRectX, rfb.updateRectY,
+                         rfb.updateRectW, rfb.updateRectH);
+           break;
+         }
+
+         case RfbProto.EncodingCopyRect:
+         {
+           rfb.readCopyRect();
+
+           int sx = rfb.copyRectSrcX, sy = rfb.copyRectSrcY;
+           int rx = rfb.updateRectX, ry = rfb.updateRectY;
+           int rw = rfb.updateRectW, rh = rfb.updateRectH;
+
+           memGraphics.copyArea(sx, sy, rw, rh, rx - sx, ry - sy);
+
+           scheduleRepaint(rx, ry, rw, rh);
+           break;
+         }
+
+         case RfbProto.EncodingRRE:
+         {
+           int rx = rfb.updateRectX, ry = rfb.updateRectY;
+           int rw = rfb.updateRectW, rh = rfb.updateRectH;
+           int nSubrects = rfb.is.readInt();
+           int x, y, w, h;
+           Color pixel;
+
+           if (bytesPixel == 1) {
+             memGraphics.setColor(colors[rfb.is.readUnsignedByte()]);
+             memGraphics.fillRect(rx, ry, rw, rh);
+
+             for (int j = 0; j < nSubrects; j++) {
+               pixel = colors[rfb.is.readUnsignedByte()];
+               x = rx + rfb.is.readUnsignedShort();
+               y = ry + rfb.is.readUnsignedShort();
+               w = rfb.is.readUnsignedShort();
+               h = rfb.is.readUnsignedShort();
+
+               memGraphics.setColor(pixel);
+               memGraphics.fillRect(x, y, w, h);
+             }
+           } else {            // 24-bit color
+             pixel = new Color(0xFF000000 | rfb.is.readInt());
+             memGraphics.setColor(pixel);
+             memGraphics.fillRect(rx, ry, rw, rh);
+
+             for (int j = 0; j < nSubrects; j++) {
+               pixel = new Color(0xFF000000 | rfb.is.readInt());
+               x = rx + rfb.is.readUnsignedShort();
+               y = ry + rfb.is.readUnsignedShort();
+               w = rfb.is.readUnsignedShort();
+               h = rfb.is.readUnsignedShort();
+
+               memGraphics.setColor(pixel);
+               memGraphics.fillRect(x, y, w, h);
+             }
+           }
+
+           scheduleRepaint(rx, ry, rw, rh);
+           break;
+         }
+
+         case RfbProto.EncodingCoRRE:
+         {
+           int rx = rfb.updateRectX, ry = rfb.updateRectY;
+           int rw = rfb.updateRectW, rh = rfb.updateRectH;
+           int nSubrects = rfb.is.readInt();
+           int x, y, w, h;
+           Color pixel;
+
+           if (bytesPixel == 1) {
+             memGraphics.setColor(colors[rfb.is.readUnsignedByte()]);
+             memGraphics.fillRect(rx, ry, rw, rh);
+
+             for (int j = 0; j < nSubrects; j++) {
+               pixel = colors[rfb.is.readUnsignedByte()];
+               x = rx + rfb.is.readUnsignedByte();
+               y = ry + rfb.is.readUnsignedByte();
+               w = rfb.is.readUnsignedByte();
+               h = rfb.is.readUnsignedByte();
+
+               memGraphics.setColor(pixel);
+               memGraphics.fillRect(x, y, w, h);
+             }
+           } else {            // 24-bit color
+             pixel = new Color(0xFF000000 | rfb.is.readInt());
+             memGraphics.setColor(pixel);
+             memGraphics.fillRect(rx, ry, rw, rh);
+
+             for (int j = 0; j < nSubrects; j++) {
+               pixel = new Color(0xFF000000 | rfb.is.readInt());
+               x = rx + rfb.is.readUnsignedByte();
+               y = ry + rfb.is.readUnsignedByte();
+               w = rfb.is.readUnsignedByte();
+               h = rfb.is.readUnsignedByte();
+
+               memGraphics.setColor(pixel);
+               memGraphics.fillRect(x, y, w, h);
+             }
+           }
+
+           scheduleRepaint(rx, ry, rw, rh);
+           break;
+         }
+
+         case RfbProto.EncodingHextile:
+         {
+           int rx = rfb.updateRectX, ry = rfb.updateRectY;
+           int rw = rfb.updateRectW, rh = rfb.updateRectH;
+           Color bg = new Color(0), fg = new Color(0);
+
+           for (int ty = ry; ty < ry + rh; ty += 16) {
+
+             int th = 16;
+             if (ry + rh - ty < 16)
+               th = ry + rh - ty;
+
+             for (int tx = rx; tx < rx + rw; tx += 16) {
+
+               int tw = 16;
+               if (rx + rw - tx < 16)
+                 tw = rx + rw - tx;
+
+               int subencoding = rfb.is.readUnsignedByte();
+
+               // Is it a raw-encoded sub-rectangle?
+               if ((subencoding & rfb.HextileRaw) != 0) {
+                 if (bytesPixel == 1) {
+                   for (int j = ty; j < ty + th; j++) {
+                     rfb.is.readFully(pixels8, j*rfb.framebufferWidth+tx, tw);
+                   }
+                 } else {
+                   byte[] buf = new byte[tw * 4];
+                   int count, offset;
+                   for (int j = ty; j < ty + th; j++) {
+                     rfb.is.readFully(buf);
+                     offset = j * rfb.framebufferWidth + tx;
+                     for (count = 0; count < tw; count++) {
+                       pixels24[offset + count] =
+                         (buf[count * 4 + 1] & 0xFF) << 16 |
+                         (buf[count * 4 + 2] & 0xFF) << 8 |
+                         (buf[count * 4 + 3] & 0xFF);
+                     }
+                   }
+                 }
+                 handleUpdatedPixels(tx, ty, tw, th);
+                 continue;
+               }
+
+               // Read and draw the background if specified.
+               if ((subencoding & rfb.HextileBackgroundSpecified) != 0) {
+                 if (bytesPixel == 1) {
+                   bg = colors[rfb.is.readUnsignedByte()];
+                 } else {
+                   bg = new Color(0xFF000000 | rfb.is.readInt());
+                 }
+               }
+               memGraphics.setColor(bg);
+               memGraphics.fillRect(tx, ty, tw, th);
+
+               // Read the foreground color if specified.
+               if ((subencoding & rfb.HextileForegroundSpecified) != 0) {
+                 if (bytesPixel == 1) {
+                   fg = colors[rfb.is.readUnsignedByte()];
+                 } else {
+                   fg = new Color(0xFF000000 | rfb.is.readInt());
+                 }
+               }
+
+               // Done with this tile if there is no sub-rectangles.
+               if ((subencoding & rfb.HextileAnySubrects) == 0)
+                 continue;
+
+               int nSubrects = rfb.is.readUnsignedByte();
+
+               int b1, b2, sx, sy, sw, sh;
+               if (bytesPixel == 1) {
+
+                 // BGR233 (8-bit color) version.
+                 if ((subencoding & rfb.HextileSubrectsColoured) != 0) {
+                   for (int j = 0; j < nSubrects; j++) {
+                     fg = colors[rfb.is.readUnsignedByte()];
+                     b1 = rfb.is.readUnsignedByte();
+                     b2 = rfb.is.readUnsignedByte();
+                     sx = tx + (b1 >> 4);
+                     sy = ty + (b1 & 0xf);
+                     sw = (b2 >> 4) + 1;
+                     sh = (b2 & 0xf) + 1;
+                     memGraphics.setColor(fg);
+                     memGraphics.fillRect(sx, sy, sw, sh);
+                   }
+                 } else {
+                   memGraphics.setColor(fg);
+                   for (int j = 0; j < nSubrects; j++) {
+                     b1 = rfb.is.readUnsignedByte();
+                     b2 = rfb.is.readUnsignedByte();
+                     sx = tx + (b1 >> 4);
+                     sy = ty + (b1 & 0xf);
+                     sw = (b2 >> 4) + 1;
+                     sh = (b2 & 0xf) + 1;
+                     memGraphics.fillRect(sx, sy, sw, sh);
+                   }
+                 }
+
+               } else {
+
+                 // Full-color (24-bit) version.
+                 if ((subencoding & rfb.HextileSubrectsColoured) != 0) {
+                   for (int j = 0; j < nSubrects; j++) {
+                     fg = new Color(0xFF000000 | rfb.is.readInt());
+                     b1 = rfb.is.readUnsignedByte();
+                     b2 = rfb.is.readUnsignedByte();
+                     sx = tx + (b1 >> 4);
+                     sy = ty + (b1 & 0xf);
+                     sw = (b2 >> 4) + 1;
+                     sh = (b2 & 0xf) + 1;
+                     memGraphics.setColor(fg);
+                     memGraphics.fillRect(sx, sy, sw, sh);
+                   }
+                 } else {
+                   memGraphics.setColor(fg);
+                   for (int j = 0; j < nSubrects; j++) {
+                     b1 = rfb.is.readUnsignedByte();
+                     b2 = rfb.is.readUnsignedByte();
+                     sx = tx + (b1 >> 4);
+                     sy = ty + (b1 & 0xf);
+                     sw = (b2 >> 4) + 1;
+                     sh = (b2 & 0xf) + 1;
+                     memGraphics.fillRect(sx, sy, sw, sh);
+                   }
+                 }
+
+               }
+
+             }
+             // Finished with a row of tiles, now let's show it.
+             scheduleRepaint(rx, ty, rw, th);
+           }
+           break;
+         }
+
+         case RfbProto.EncodingZlib:
+         {
+           int nBytes = rfb.is.readInt();
+
+            if (zlibBuf == null || zlibBufLen < nBytes) {
+              zlibBufLen = nBytes * 2;
+              zlibBuf = new byte[zlibBufLen];
+            }
+
+            rfb.is.readFully(zlibBuf, 0, nBytes);
+
+            if (zlibInflater == null) {
+              zlibInflater = new Inflater();
+            }
+            zlibInflater.setInput(zlibBuf, 0, nBytes);
+
+            handleZlibRect(rfb.updateRectX, rfb.updateRectY,
+                          rfb.updateRectW, rfb.updateRectH);
+
+           break;
+         }
+
+         case RfbProto.EncodingTight:
+         {
+           handleTightRect(rfb.updateRectX, rfb.updateRectY,
+                           rfb.updateRectW, rfb.updateRectH);
+
+           break;
+         }
+
+         default:
+           throw new IOException("Unknown RFB rectangle encoding " +
+                                 rfb.updateRectEncoding);
+         }
+
+       }
+
+       // Defer framebuffer update request if necessary. But wake up
+       // immediately on keyboard or mouse event.
+       if (viewer.deferUpdateRequests > 0) {
+         synchronized(rfb) {
+           try {
+             rfb.wait(viewer.deferUpdateRequests);
+           } catch (InterruptedException e) {
+           }
+         }
+       }
+
+       // Before requesting framebuffer update, check if the pixel
+       // format should be changed. If it should, request full update
+       // instead of incremental one.
+       if (viewer.options.eightBitColors != (bytesPixel == 1)) {
+         setPixelFormat();
+         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 IOException("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 IOException("Unknown RFB message type " + msgType);
+      }
+    }
+  }
+
+
+  //
+  // Handle a raw rectangle.
+  //
+
+  void handleRawRect(int x, int y, int w, int h) throws IOException {
+
+    if (bytesPixel == 1) {
+      for (int dy = y; dy < y + h; dy++) {
+       rfb.is.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
+      }
+    } else {
+      byte[] buf = new byte[w * 4];
+      int i, offset;
+      for (int dy = y; dy < y + h; dy++) {
+       rfb.is.readFully(buf);
+       offset = dy * rfb.framebufferWidth + x;
+       for (i = 0; i < w; i++) {
+         pixels24[offset + i] =
+           (buf[i * 4 + 1] & 0xFF) << 16 |
+           (buf[i * 4 + 2] & 0xFF) << 8 |
+           (buf[i * 4 + 3] & 0xFF);
+       }
+      }
+    }
+
+    handleUpdatedPixels(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 IOException {
+
+    try {
+      if (bytesPixel == 1) {
+       for (int dy = y; dy < y + h; dy++) {
+         zlibInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w);
+       }
+      } else {
+       byte[] buf = new byte[w * 4];
+       int i, offset;
+       for (int dy = y; dy < y + h; dy++) {
+         zlibInflater.inflate(buf);
+         offset = dy * rfb.framebufferWidth + x;
+         for (i = 0; i < w; i++) {
+           pixels24[offset + i] =
+             (buf[i * 4 + 1] & 0xFF) << 16 |
+             (buf[i * 4 + 2] & 0xFF) << 8 |
+             (buf[i * 4 + 3] & 0xFF);
+         }
+       }
+      }
+    }
+    catch (DataFormatException dfe) {
+      throw new IOException(dfe.toString());
+    }
+
+    handleUpdatedPixels(x, y, w, h);
+    scheduleRepaint(x, y, w, h);
+  }
+
+
+  //
+  // Handle a tight rectangle.
+  //
+
+  void handleTightRect(int x, int y, int w, int h) throws IOException {
+
+    int comp_ctl = rfb.is.readUnsignedByte();
+
+    // Flush zlib streams if we are told by the server to do so.
+    for (int stream_id = 0; stream_id < 4; stream_id++) {
+      if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) {
+       tightInflaters[stream_id] = null;
+      }
+      comp_ctl >>= 1;
+    }
+
+    // Check correctness of subencoding value.
+    if (comp_ctl > rfb.TightMaxSubencoding) {
+      throw new IOException("Incorrect tight subencoding: " + comp_ctl);
+    }
+
+    // Handle solid-color rectangles.
+    if (comp_ctl == rfb.TightFill) {
+      if (bytesPixel == 1) {
+       memGraphics.setColor(colors[rfb.is.readUnsignedByte()]);
+      } else {
+       byte[] buf = new byte[3];
+       rfb.is.readFully(buf);
+       Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16 |
+                            (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF));
+       memGraphics.setColor(bg);
+      }
+      memGraphics.fillRect(x, y, w, h);
+      scheduleRepaint(x, y, w, h);
+      return;
+    }
+
+    if (comp_ctl == rfb.TightJpeg) {
+
+      // Read JPEG data.
+      byte[] jpegData = new byte[rfb.readCompactLen()];
+      rfb.is.readFully(jpegData);
+
+      // Create an Image object from the JPEG data.
+      Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData);
+
+      // Remember the rectangle where the image should be drawn.
+      jpegRect = new Rectangle(x, y, w, h);
+
+      // Let the imageUpdate() method do the actual drawing, here just
+      // wait until the image is fully loaded and drawn.
+      synchronized(jpegRect) {
+       Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1, this);
+       try {
+         // Wait no longer than three seconds.
+         jpegRect.wait(3000);
+       } catch (InterruptedException e) {
+         throw new IOException("Interrupted while decoding JPEG image");
+       }
+      }
+
+      // Done, jpegRect is not needed any more.
+      jpegRect = null;
+      return;
+
+    }
+
+    // Read filter id and parameters.
+    int numColors = 0, rowSize = w;
+    byte[] palette8 = new byte[2];
+    int[] palette24 = new int[256];
+    boolean useGradient = false;
+    if ((comp_ctl & rfb.TightExplicitFilter) != 0) {
+      int filter_id = rfb.is.readUnsignedByte();
+      if (filter_id == rfb.TightFilterPalette) {
+       numColors = rfb.is.readUnsignedByte() + 1;
+        if (bytesPixel == 1) {
+         if (numColors != 2) {
+           throw new IOException("Incorrect tight palette size: " +
+                                 numColors);
+         }
+         palette8[0] = rfb.is.readByte();
+         palette8[1] = rfb.is.readByte();
+       } else {
+         byte[] buf = new byte[numColors * 3];
+         rfb.is.readFully(buf);
+         for (int i = 0; i < numColors; i++) {
+           palette24[i] = ((buf[i * 3] & 0xFF) << 16 |
+                           (buf[i * 3 + 1] & 0xFF) << 8 |
+                           (buf[i * 3 + 2] & 0xFF));
+         }
+       }
+       if (numColors == 2)
+         rowSize = (w + 7) / 8;
+      } else if (filter_id == rfb.TightFilterGradient) {
+       useGradient = true;
+      } else if (filter_id != rfb.TightFilterCopy) {
+       throw new IOException("Incorrect tight filter id: " + filter_id);
+      }
+    }
+    if (numColors == 0 && bytesPixel == 4)
+      rowSize *= 3;
+
+    // Read, optionally uncompress and decode data.
+    int dataSize = h * rowSize;
+    if (dataSize < rfb.TightMinToCompress) {
+      // Data size is small - not compressed with zlib.
+      if (numColors != 0) {
+       // Indexed colors.
+       byte[] indexedData = new byte[dataSize];
+       rfb.is.readFully(indexedData);
+       if (numColors == 2) {
+         // Two colors.
+         if (bytesPixel == 1) {
+           decodeMonoData(x, y, w, h, indexedData, palette8);
+         } else {
+           decodeMonoData(x, y, w, h, indexedData, palette24);
+         }
+       } else {
+         // 3..255 colors (assuming bytesPixel == 4).
+         int i = 0;
+         for (int dy = y; dy < y + h; dy++) {
+           for (int dx = x; dx < x + w; dx++) {
+             pixels24[dy * rfb.framebufferWidth + dx] =
+               palette24[indexedData[i++] & 0xFF];
+           }
+         }
+       }
+      } else if (useGradient) {
+       // "Gradient"-processed data
+       byte[] buf = new byte[w * h * 3];
+       rfb.is.readFully(buf);
+       decodeGradientData(x, y, w, h, buf);
+      } else {
+       // Raw truecolor data.
+       if (bytesPixel == 1) {
+         for (int dy = y; dy < y + h; dy++) {
+           rfb.is.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
+         }
+       } else {
+         byte[] buf = new byte[w * 3];
+         int i, offset;
+         for (int dy = y; dy < y + h; dy++) {
+           rfb.is.readFully(buf);
+           offset = dy * rfb.framebufferWidth + x;
+           for (i = 0; i < w; i++) {
+             pixels24[offset + i] =
+               (buf[i * 3] & 0xFF) << 16 |
+               (buf[i * 3 + 1] & 0xFF) << 8 |
+               (buf[i * 3 + 2] & 0xFF);
+           }
+         }
+       }
+      }
+    } else {
+      // Data was compressed with zlib.
+      int zlibDataLen = rfb.readCompactLen();
+      byte[] zlibData = new byte[zlibDataLen];
+      rfb.is.readFully(zlibData);
+      int stream_id = comp_ctl & 0x03;
+      if (tightInflaters[stream_id] == null) {
+       tightInflaters[stream_id] = new Inflater();
+      }
+      Inflater myInflater = tightInflaters[stream_id];
+      myInflater.setInput(zlibData);
+      try {
+       if (numColors != 0) {
+         // Indexed colors.
+         byte[] indexedData = new byte[dataSize];
+         myInflater.inflate(indexedData);
+         if (numColors == 2) {
+           // Two colors.
+           if (bytesPixel == 1) {
+             decodeMonoData(x, y, w, h, indexedData, palette8);
+           } else {
+             decodeMonoData(x, y, w, h, indexedData, palette24);
+           }
+         } else {
+           // More than two colors (assuming bytesPixel == 4).
+           int i = 0;
+           for (int dy = y; dy < y + h; dy++) {
+             for (int dx = x; dx < x + w; dx++) {
+               pixels24[dy * rfb.framebufferWidth + dx] =
+                 palette24[indexedData[i++] & 0xFF];
+             }
+           }
+         }
+       } else if (useGradient) {
+         // Compressed "Gradient"-filtered data (assuming bytesPixel == 4).
+         byte[] buf = new byte[w * h * 3];
+         myInflater.inflate(buf);
+         decodeGradientData(x, y, w, h, buf);
+       } else {
+         // Compressed truecolor data.
+         if (bytesPixel == 1) {
+           for (int dy = y; dy < y + h; dy++) {
+             myInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w);
+           }
+         } else {
+           byte[] buf = new byte[w * 3];
+           int i, offset;
+           for (int dy = y; dy < y + h; dy++) {
+             myInflater.inflate(buf);
+             offset = dy * rfb.framebufferWidth + x;
+             for (i = 0; i < w; i++) {
+               pixels24[offset + i] =
+                 (buf[i * 3] & 0xFF) << 16 |
+                 (buf[i * 3 + 1] & 0xFF) << 8 |
+                 (buf[i * 3 + 2] & 0xFF);
+             }
+           }
+         }
+       }
+      }
+      catch(DataFormatException dfe) {
+       throw new IOException(dfe.toString());
+      }
+    }
+
+    handleUpdatedPixels(x, y, w, h);
+    scheduleRepaint(x, y, w, h);
+  }
+
+  //
+  // Decode 1bpp-encoded bi-color rectangle (8-bit and 24-bit versions).
+  //
+
+  void decodeMonoData(int x, int y, int w, int h, byte[] src, byte[] palette)
+    throws IOException {
+
+    int dx, dy, n;
+    int i = y * rfb.framebufferWidth + x;
+    int rowBytes = (w + 7) / 8;
+    byte b;
+
+    for (dy = 0; dy < h; dy++) {
+      for (dx = 0; dx < w / 8; dx++) {
+       b = src[dy*rowBytes+dx];
+       for (n = 7; n >= 0; n--)
+         pixels8[i++] = palette[b >> n & 1];
+      }
+      for (n = 7; n >= 8 - w % 8; n--) {
+       pixels8[i++] = palette[src[dy*rowBytes+dx] >> n & 1];
+      }
+      i += (rfb.framebufferWidth - w);
+    }
+  }
+
+  void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette)
+    throws IOException {
+
+    int dx, dy, n;
+    int i = y * rfb.framebufferWidth + x;
+    int rowBytes = (w + 7) / 8;
+    byte b;
+
+    for (dy = 0; dy < h; dy++) {
+      for (dx = 0; dx < w / 8; dx++) {
+       b = src[dy*rowBytes+dx];
+       for (n = 7; n >= 0; n--)
+         pixels24[i++] = palette[b >> n & 1];
+      }
+      for (n = 7; n >= 8 - w % 8; n--) {
+       pixels24[i++] = palette[src[dy*rowBytes+dx] >> n & 1];
+      }
+      i += (rfb.framebufferWidth - w);
+    }
+  }
+
+  //
+  // Decode data processed with the "Gradient" filter.
+  //
+
+  void decodeGradientData (int x, int y, int w, int h, byte[] buf)
+    throws IOException {
+
+    int dx, dy, c;
+    byte[] prevRow = new byte[w * 3];
+    byte[] thisRow = new byte[w * 3];
+    byte[] pix = new byte[3];
+    int[] est = new int[3];
+
+    int offset = y * rfb.framebufferWidth + x;
+
+    for (dy = 0; dy < h; dy++) {
+
+      /* First pixel in a row */
+      for (c = 0; c < 3; c++) {
+       pix[c] = (byte)(prevRow[c] + buf[dy * w * 3 + c]);
+       thisRow[c] = pix[c];
+      }
+      pixels24[offset++] =
+       (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
+
+      /* Remaining pixels of a row */
+      for (dx = 1; dx < w; dx++) {
+       for (c = 0; c < 3; c++) {
+         est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) -
+                   (prevRow[(dx-1) * 3 + c] & 0xFF));
+         if (est[c] > 0xFF) {
+           est[c] = 0xFF;
+         } else if (est[c] < 0x00) {
+           est[c] = 0x00;
+         }
+         pix[c] = (byte)(est[c] + buf[(dy * w + dx) * 3 + c]);
+         thisRow[dx * 3 + c] = pix[c];
+       }
+       pixels24[offset++] =
+         (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF);
+      }
+
+      System.arraycopy(thisRow, 0, prevRow, 0, w * 3);
+      offset += (rfb.framebufferWidth - w);
+    }
+  }
+
+
+  //
+  // Display newly updated area of pixels.
+  //
+
+  void handleUpdatedPixels(int x, int y, int w, int h) {
+
+    // Draw updated pixels of the off-screen image.
+    pixelsSource.newPixels(x, y, w, h);
+    memGraphics.setClip(x, y, w, h);
+    memGraphics.drawImage(rawPixelsImage, 0, 0, null);
+    memGraphics.setClip(0, 0, rfb.framebufferWidth, rfb.framebufferHeight);
+  }
+
+  //
+  // Tell JVM to repaint specified desktop area.
+  //
+
+  void scheduleRepaint(int x, int y, int w, int h) {
+    // Request repaint, deferred if necessary.
+    repaint(viewer.deferScreenUpdates, x, y, w, h);
+  }
+
+
+  //
+  // Handle events.
+  //
+
+  public void keyPressed(KeyEvent evt) {
+    processLocalKeyEvent(evt);
+  }
+  public void keyReleased(KeyEvent evt) {
+    processLocalKeyEvent(evt);
+  }
+  public void keyTyped(KeyEvent evt) {
+    evt.consume();
+  }
+
+  public void mousePressed(MouseEvent evt) {
+    processLocalMouseEvent(evt, false);
+  }
+  public void mouseReleased(MouseEvent evt) {
+    processLocalMouseEvent(evt, false);
+  }
+  public void mouseMoved(MouseEvent evt) {
+    processLocalMouseEvent(evt, true);
+  }
+  public void mouseDragged(MouseEvent evt) {
+    processLocalMouseEvent(evt, true);
+  }
+
+  public void processLocalKeyEvent(KeyEvent evt) {
+    if (rfb != null && rfb.inNormalProtocol) {
+      synchronized(rfb) {
+       try {
+         rfb.writeKeyEvent(evt);
+       } catch (Exception e) {
+         e.printStackTrace();
+       }
+       rfb.notify();
+      }
+    }
+    // Don't ever pass keyboard events to AWT for default processing. 
+    // Otherwise, pressing Tab would switch focus to ButtonPanel etc.
+    evt.consume();
+  }
+
+  public void processLocalMouseEvent(MouseEvent evt, boolean moved) {
+    if (rfb != null && rfb.inNormalProtocol) {
+      if (moved) {
+       softCursorMove(evt.getX(), evt.getY());
+      }
+      synchronized(rfb) {
+       try {
+         rfb.writePointerEvent(evt);
+       } catch (Exception e) {
+         e.printStackTrace();
+       }
+       rfb.notify();
+      }
+    }
+  }
+
+
+  //
+  // Ignored events.
+  //
+
+  public void mouseClicked(MouseEvent evt) {}
+  public void mouseEntered(MouseEvent evt) {}
+  public void mouseExited(MouseEvent evt) {}
+
+
+  //////////////////////////////////////////////////////////////////
+  //
+  // Handle cursor shape updates (XCursor and RichCursor encodings).
+  //
+
+  boolean showSoftCursor = false;
+
+  int[] softCursorPixels;
+  MemoryImageSource softCursorSource;
+  Image softCursor;
+
+  int cursorX = 0, cursorY = 0;
+  int cursorWidth, cursorHeight;
+  int hotX, hotY;
+
+  //
+  // Handle cursor shape update (XCursor and RichCursor encodings).
+  //
+
+  synchronized void
+    handleCursorShapeUpdate(int encodingType,
+                           int xhot, int yhot, int width, int height)
+    throws IOException {
+
+    int bytesPerRow = (width + 7) / 8;
+    int bytesMaskData = bytesPerRow * height;
+
+    softCursorFree();
+
+    if (width * height == 0)
+      return;
+
+    // Ignore cursor shape data if requested by user.
+
+    if (viewer.options.ignoreCursorUpdates) {
+      if (encodingType == rfb.EncodingXCursor) {
+       rfb.is.skipBytes(6 + bytesMaskData * 2);
+      } else {
+       // rfb.EncodingRichCursor
+       rfb.is.skipBytes(width * height + bytesMaskData);
+      }
+      return;
+    }
+
+    // Decode cursor pixel data.
+
+    softCursorPixels = new int[width * height];
+
+    if (encodingType == rfb.EncodingXCursor) {
+
+      // Read foreground and background colors of the cursor.
+      byte[] rgb = new byte[6];
+      rfb.is.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.is.readFully(pixBuf);
+      byte[] maskBuf = new byte[bytesMaskData];
+      rfb.is.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.is.readFully(pixBuf);
+      byte[] maskBuf = new byte[bytesMaskData];
+      rfb.is.readFully(maskBuf);
+
+      // Decode pixel data into softCursorPixels[].
+      byte pixByte, maskByte;
+      int x, y, n, result;
+      int i = 0;
+      for (y = 0; y < height; y++) {
+       for (x = 0; x < width / 8; x++) {
+         maskByte = maskBuf[y * bytesPerRow + x];
+         for (n = 7; n >= 0; n--) {
+           if ((maskByte >> n & 1) != 0) {
+             if (bytesPixel == 1) {
+               result = cm8.getRGB(pixBuf[i]);
+             } else {
+               result = 0xFF000000 |
+                 (pixBuf[i * 4 + 1] & 0xFF) << 16 |
+                 (pixBuf[i * 4 + 2] & 0xFF) << 8 |
+                 (pixBuf[i * 4 + 3] & 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 + 1] & 0xFF) << 16 |
+               (pixBuf[i * 4 + 2] & 0xFF) << 8 |
+               (pixBuf[i * 4 + 3] & 0xFF);
+           }
+         } else {
+           result = 0;         // Transparent pixel
+         }
+         softCursorPixels[i++] = result;
+       }
+      }
+
+    }
+
+    // Draw the cursor on an off-screen image.
+
+    softCursorSource =
+      new MemoryImageSource(width, height, softCursorPixels, 0, width);
+    softCursor = createImage(softCursorSource);
+
+    // Set remaining data associated with cursor.
+
+    cursorWidth = width;
+    cursorHeight = height;
+    hotX = xhot;
+    hotY = yhot;
+
+    showSoftCursor = true;
+
+    // Show the cursor.
+
+    repaint(viewer.deferCursorUpdates,
+           cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight);
+  }
+
+  //
+  // softCursorMove(). Moves soft cursor into a particular location.
+  //
+
+  synchronized void softCursorMove(int x, int y) {
+    if (showSoftCursor) {
+      repaint(viewer.deferCursorUpdates,
+             cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight);
+      repaint(viewer.deferCursorUpdates,
+             x - hotX, y - hotY, cursorWidth, cursorHeight);
+    }
+
+    cursorX = x;
+    cursorY = y;
+  }
+
+  //
+  // softCursorFree(). Remove soft cursor, dispose resources.
+  //
+
+  synchronized void softCursorFree() {
+    if (showSoftCursor) {
+      showSoftCursor = false;
+      softCursor = null;
+      softCursorSource = null;
+      softCursorPixels = null;
+
+      repaint(viewer.deferCursorUpdates,
+             cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight);
+    }
+  }
+}
diff --git a/java/src/com/tightvnc/rfbplayer/VncViewer.java b/java/src/com/tightvnc/rfbplayer/VncViewer.java
new file mode 100644 (file)
index 0000000..2524a50
--- /dev/null
@@ -0,0 +1,564 @@
+//
+//  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.
+//
+
+//
+// VncViewer.java - the VNC viewer applet.  This class mainly just sets up the
+// user interface, leaving it to the VncCanvas to do the actual rendering of
+// a VNC desktop.
+//
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+
+public class VncViewer extends java.applet.Applet
+  implements java.lang.Runnable, WindowListener {
+
+  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 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;
+  AuthPanel authenticator;
+  VncCanvas vc;
+  OptionsFrame options;
+  ClipboardFrame clipboard;
+
+  // Variables read from parameter values.
+  String host;
+  int port;
+  String passwordParam;
+  int deferScreenUpdates;
+  int deferCursorUpdates;
+  int deferUpdateRequests;
+
+
+  //
+  // init()
+  //
+
+  public void init() {
+
+    readParameters();
+
+    if (inSeparateFrame) {
+      vncFrame = new Frame("TightVNC");
+      if (!inAnApplet) {
+       vncFrame.add("Center", this);
+      }
+      vncContainer = vncFrame;
+    } else {
+      vncContainer = this;
+    }
+
+    options = new OptionsFrame(this);
+    clipboard = new ClipboardFrame(this);
+    authenticator = new AuthPanel();
+
+    if (inSeparateFrame)
+      vncFrame.addWindowListener(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 (options.showControls) {
+      buttonPanel = new ButtonPanel(this);
+      buttonPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
+      gridbag.setConstraints(buttonPanel, gbc);
+      vncContainer.add(buttonPanel);
+    }
+
+    try {
+      connectAndAuthenticate();
+
+      doProtocolInitialisation();
+
+      vc = new VncCanvas(this);
+      gbc.weightx = 1.0;
+      gbc.weighty = 1.0;
+
+      if (inSeparateFrame) {
+
+       // Create a panel which itself is resizeable and can hold
+       // non-resizeable VncCanvas component at the top left corner.
+       Panel canvasPanel = new Panel();
+       canvasPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
+       canvasPanel.add(vc);
+
+       // Create a ScrollPane which will hold a panel with VncCanvas
+       // inside.
+       desktopScrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED);
+       gbc.fill = GridBagConstraints.BOTH;
+       gridbag.setConstraints(desktopScrollPane, gbc);
+       desktopScrollPane.add(canvasPanel);
+
+       // Finally, add our ScrollPane to the Frame window.
+       vncFrame.add(desktopScrollPane);
+       vncFrame.setTitle(rfb.desktopName);
+       vncFrame.pack();
+       vc.resizeDesktopFrame();
+
+      } else {
+
+       // Just add the VncCanvas component to the Applet.
+       gridbag.setConstraints(vc, gbc);
+       add(vc);
+       validate();
+
+      }
+
+      if (options.showControls)
+        buttonPanel.enableButtons();
+
+      moveFocusToDesktop();
+      vc.processNormalProtocol();
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      fatalError(e.toString());
+    }
+    
+  }
+
+
+  //
+  // Connect to the RFB server and authenticate the user.
+  //
+
+  void connectAndAuthenticate() throws IOException {
+
+    // The simplest case -- don't ask user a password, get it from the
+    // "PASSWORD" parameter instead. Authentication failures would be
+    // fatal.
+
+    if (passwordParam != null) {
+      if (inSeparateFrame) {
+       vncFrame.pack();
+       vncFrame.show();
+      } else {
+       validate();
+      }
+      if (!tryAuthenticate(passwordParam)) {
+       throw new IOException("VNC authentication failed");
+      }
+      return;
+    }
+
+    // There is no "PASSWORD" parameter -- ask user for a password,
+    // try to authenticate, retry on authentication failures.
+
+    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(authenticator, gbc);
+    vncContainer.add(authenticator);
+
+    if (inSeparateFrame) {
+      vncFrame.pack();
+      vncFrame.show();
+    } else {
+      validate();
+      // FIXME: here moveFocusToPasswordField() does not always work
+      // under Netscape 4.7x/Java 1.1.5/Linux. It seems like this call
+      // is being executed before the password field of the
+      // authenticator is fully drawn and activated, therefore
+      // requestFocus() does not work. Currently, I don't know how to
+      // solve this problem.
+      //   -- const
+      authenticator.moveFocusToPasswordField();
+    }
+
+    while (true) {
+      // Wait for user entering a password.
+      synchronized(authenticator) {
+       try {
+         authenticator.wait();
+       } catch (InterruptedException e) {
+       }
+      }
+
+      // Try to authenticate with a given password.
+      if (tryAuthenticate(authenticator.password.getText()))
+       break;
+
+      // Retry on authentication failure.
+      authenticator.retry();
+    }
+
+    vncContainer.remove(authenticator);
+  }
+
+
+  //
+  // Try to authenticate with a given password.
+  //
+
+  boolean tryAuthenticate(String pw) throws IOException {
+
+    rfb = new RfbProto(host, port, this);
+
+    rfb.readVersionMsg();
+
+    System.out.println("RFB server supports protocol version " +
+                      rfb.serverMajor + "." + rfb.serverMinor);
+
+    rfb.writeVersionMsg();
+
+    int authScheme = rfb.readAuthScheme();
+
+    switch (authScheme) {
+
+    case RfbProto.NoAuth:
+      System.out.println("No authentication needed");
+      return true;
+
+    case RfbProto.VncAuth:
+      byte[] challenge = new byte[16];
+      rfb.is.readFully(challenge);
+
+      if (pw.length() > 8)
+       pw = pw.substring(0, 8); // Truncate to 8 chars
+
+      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);
+
+      rfb.os.write(challenge);
+
+      int authResult = rfb.is.readInt();
+
+      switch (authResult) {
+      case RfbProto.VncAuthOK:
+       System.out.println("VNC authentication succeeded");
+       return true;
+      case RfbProto.VncAuthFailed:
+       System.out.println("VNC authentication failed");
+       break;
+      case RfbProto.VncAuthTooMany:
+       throw new IOException("VNC authentication failed - " +
+                             "too many tries");
+      default:
+       throw new IOException("Unknown VNC authentication result " +
+                             authResult);
+      }
+      break;
+
+    default:
+      throw new IOException("Unknown VNC authentication scheme " +
+                           authScheme);
+    }
+    return false;
+  }
+
+
+  //
+  // 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();
+  }
+
+
+  //
+  // Send current encoding list to the RFB server.
+  //
+
+  void setEncodings() {
+    try {
+      if (rfb != null && rfb.inNormalProtocol) {
+       rfb.writeSetEncodings(options.encodings, options.nEncodings);
+       if (vc != null) {
+         vc.softCursorFree();
+       }
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+
+  //
+  // 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();
+    }
+  }
+
+
+  //
+  // 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.
+  //
+
+  public void readParameters() {
+    host = readParameter("HOST", !inAnApplet);
+    if (host == null) {
+      host = getCodeBase().getHost();
+      if (host.equals("")) {
+       fatalError("HOST parameter not specified");
+      }
+    }
+
+    String str = readParameter("PORT", true);
+    port = Integer.parseInt(str);
+
+    if (inAnApplet) {
+      str = readParameter("Open New Window", false);
+      if (str != null && str.equalsIgnoreCase("Yes"))
+       inSeparateFrame = true;
+    }
+
+    passwordParam = readParameter("PASSWORD", false);
+
+    // Fine tuning options.
+    deferScreenUpdates = readIntParameter("Defer screen updates", 20);
+    deferCursorUpdates = readIntParameter("Defer cursor updates", 10);
+    deferUpdateRequests = readIntParameter("Defer update requests", 50);
+  }
+
+  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 the
+  // VncCanvas or to the AuthPanel.
+  //
+
+  void moveFocusToDesktop() {
+    if (vncContainer != null) {
+      if (vc != null && vncContainer.isAncestorOf(vc)) {
+       vc.requestFocus();
+      } else if (vncContainer.isAncestorOf(authenticator)) {
+       authenticator.moveFocusToPasswordField();
+      }
+    }
+  }
+
+  //
+  // disconnect() - close connection to server.
+  //
+
+  public void disconnect() {
+    System.out.println("Disconnect");
+    options.dispose();
+    clipboard.dispose();
+
+    if (inAnApplet) {
+      vncContainer.removeAll();
+      if (rfb != null) {
+       rfb.close();
+       rfb = null;
+      }
+      Label errLabel = new Label("Disconnected");
+      errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12));
+      vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30));
+      vncContainer.add(errLabel);
+      if (inSeparateFrame) {
+       vncFrame.pack();
+      } else {
+       validate();
+      }
+      rfbThread.stop();
+    } else {
+      System.exit(0);
+    }
+  }
+
+  //
+  // fatalError() - print out a fatal error message.
+  //
+
+  public void fatalError(String str) {
+    System.out.println(str);
+
+    if (inAnApplet) {
+      vncContainer.removeAll();
+      if (rfb != null) {
+       rfb.close();
+       rfb = null;
+      }
+      Label errLabel = new Label(str);
+      errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12));
+      vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30));
+      vncContainer.add(errLabel);
+      if (inSeparateFrame) {
+       vncFrame.pack();
+      } else {
+       validate();
+      }
+      Thread.currentThread().stop();
+    } else {
+      System.exit(1);
+    }
+  }
+
+
+  //
+  // This method is called before the applet is destroyed.
+  //
+
+  public void destroy() {
+    vncContainer.removeAll();
+    options.dispose();
+    clipboard.dispose();
+    if (rfb != null) {
+      rfb.close();
+    }
+    if (inSeparateFrame) {
+      vncFrame.dispose();
+    }
+  }
+
+
+  //
+  // Close application properly on window close event.
+  //
+
+  public void windowClosing(WindowEvent evt) {
+    if (rfb != null)
+      disconnect();
+
+    vncFrame.dispose();
+    if (!inAnApplet) {
+      System.exit(0);
+    }
+  }
+
+  //
+  // Move the keyboard focus to the password field on window activation.
+  //
+
+  public void windowActivated(WindowEvent evt) {
+    if (vncFrame.isAncestorOf(authenticator))
+      authenticator.moveFocusToPasswordField();
+  }
+
+  //
+  // Ignore window events we're not interested in.
+  //
+
+  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/tightvnc/rfbplayer/dir.mk b/java/src/com/tightvnc/rfbplayer/dir.mk
new file mode 100644 (file)
index 0000000..2174405
--- /dev/null
@@ -0,0 +1,20 @@
+#
+# Making the VNC applet.
+#
+
+CLASSES = VncViewer.class RfbProto.class AuthPanel.class VncCanvas.class \
+         OptionsFrame.class ClipboardFrame.class ButtonPanel.class \
+         DesCipher.class
+
+PAGES = index.vnc shared.vnc noshared.vnc hextile.vnc zlib.vnc tight.vnc
+
+all: $(CLASSES) VncViewer.jar
+
+VncViewer.jar: $(CLASSES)
+       @$(JavaArchive)
+
+export:: $(CLASSES) VncViewer.jar $(PAGES)
+       @$(ExportJavaClasses)
+
+clean::
+       $(RM) *.class *.jar
diff --git a/java/src/com/tightvnc/rfbplayer/hextile.vnc b/java/src/com/tightvnc/rfbplayer/hextile.vnc
new file mode 100644 (file)
index 0000000..d758ccd
--- /dev/null
@@ -0,0 +1,19 @@
+<!-- hextile.vnc - hextile html page for Java VNC viewer applet.  On
+     any file ending in .vnc, the HTTP server embedded in Xvnc will
+     substitute the following variables when preceded by a dollar:
+     USER, DESKTOP, DISPLAY, APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT,
+     PORT.  Use two dollar signs ($$) to get a dollar sign in the
+     generated html. -->
+
+<HTML>
+<TITLE>
+$USER's $DESKTOP desktop ($DISPLAY)
+</TITLE>
+<APPLET CODE=VncViewer.class ARCHIVE=VncViewer.jar
+        WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
+<param name=PORT value=$PORT>
+<param name=ENCODING value=Hextile>
+</APPLET>
+<BR>
+<A href="http://www.tightvnc.com/">www.TightVNC.com</A>
+</HTML>
diff --git a/java/src/com/tightvnc/rfbplayer/index.vnc b/java/src/com/tightvnc/rfbplayer/index.vnc
new file mode 100644 (file)
index 0000000..d9bb30e
--- /dev/null
@@ -0,0 +1,17 @@
+<!-- index.vnc - default html page for Java VNC viewer applet.  On any file
+     ending in .vnc, the HTTP server embedded in Xvnc will substitute the
+     following variables when preceded by a dollar: USER, DESKTOP, DISPLAY,
+     APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT.  Use two dollar signs
+     ($$) to get a dollar sign in the generated html. -->
+
+<HTML>
+<TITLE>
+$USER's $DESKTOP desktop ($DISPLAY)
+</TITLE>
+<APPLET CODE=VncViewer.class ARCHIVE=VncViewer.jar
+        WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
+<param name=PORT value=$PORT>
+</APPLET>
+<BR>
+<A href="http://www.tightvnc.com/">www.TightVNC.com</A>
+</HTML>
diff --git a/java/src/com/tightvnc/rfbplayer/noshared.vnc b/java/src/com/tightvnc/rfbplayer/noshared.vnc
new file mode 100644 (file)
index 0000000..b0dc3af
--- /dev/null
@@ -0,0 +1,17 @@
+<!-- This is an example of how to set default options in the Java VNC viewer
+     applet - in this case the "Share Desktop" option is set to "No".  Any
+     option can be set by giving a parameter with the same name as the
+     option (spaces are important, but case isn't) -->
+
+<HTML>
+<TITLE>
+$USER's $DESKTOP desktop ($DISPLAY) [not shared]
+</TITLE>
+<APPLET CODE=VncViewer.class ARCHIVE=VncViewer.jar
+        WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
+<param name=PORT value=$PORT>
+<param name="share desktop" value=no>
+</APPLET>
+<BR>
+<A href="http://www.tightvnc.com/">www.TightVNC.com</A>
+</HTML>
diff --git a/java/src/com/tightvnc/rfbplayer/shared.vnc b/java/src/com/tightvnc/rfbplayer/shared.vnc
new file mode 100644 (file)
index 0000000..2f8ea1c
--- /dev/null
@@ -0,0 +1,17 @@
+<!-- This is an example of how to set default options in the Java VNC viewer
+     applet - in this case the "Share Desktop" option is set to "Yes".  Any
+     option can be set by giving a parameter with the same name as the
+     option (spaces are important, but case isn't) -->
+
+<HTML>
+<TITLE>
+$USER's $DESKTOP desktop ($DISPLAY) [shared]
+</TITLE>
+<APPLET CODE=VncViewer.class ARCHIVE=VncViewer.jar
+        WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
+<param name=PORT value=$PORT>
+<param name="share desktop" value=yes>
+</APPLET>
+<BR>
+<A href="http://www.tightvnc.com/">www.TightVNC.com</A>
+</HTML>
diff --git a/java/src/com/tightvnc/rfbplayer/tight.vnc b/java/src/com/tightvnc/rfbplayer/tight.vnc
new file mode 100644 (file)
index 0000000..3ca7a69
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- tight.vnc - tight html page for Java VNC viewer applet.  On any file
+     ending in .vnc, the HTTP server embedded in Xvnc will substitute the
+     following variables when preceded by a dollar: USER, DESKTOP, DISPLAY,
+     APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT.  Use two dollar signs
+     ($$) to get a dollar sign in the generated html. -->
+
+<HTML>
+<TITLE>
+$USER's $DESKTOP desktop ($DISPLAY)
+</TITLE>
+<APPLET CODE=VncViewer.class ARCHIVE=VncViewer.jar
+        WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
+<param name=PORT value=$PORT>
+<param name=ENCODING value=Tight>
+</APPLET>
+<BR>
+<A href="http://www.tightvnc.com/">www.TightVNC.com</A>
+</HTML>
diff --git a/java/src/com/tightvnc/rfbplayer/zlib.vnc b/java/src/com/tightvnc/rfbplayer/zlib.vnc
new file mode 100644 (file)
index 0000000..877ec5d
--- /dev/null
@@ -0,0 +1,18 @@
+<!-- zlib.vnc - zlib html page for Java VNC viewer applet.  On any file
+     ending in .vnc, the HTTP server embedded in Xvnc will substitute the
+     following variables when preceded by a dollar: USER, DESKTOP, DISPLAY,
+     APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT.  Use two dollar signs
+     ($$) to get a dollar sign in the generated html. -->
+
+<HTML>
+<TITLE>
+$USER's $DESKTOP desktop ($DISPLAY)
+</TITLE>
+<APPLET CODE=VncViewer.class ARCHIVE=VncViewer.jar
+        WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT>
+<param name=PORT value=$PORT>
+<param name=ENCODING value=Zlib>
+</APPLET>
+<BR>
+<A href="http://www.tightvnc.com/">www.TightVNC.com</A>
+</HTML>