From: Constantin Kaplinsky Date: Fri, 18 Apr 2008 09:51:44 +0000 (+0000) Subject: Copying old Java Viewer source where RfbPlayer development had originated. X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=1215b99daaebafdc97caa5fbb15157faeb8d9f6b;p=tigervnc.git Copying old Java Viewer source where RfbPlayer development had originated. git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@2495 3789f03b-4d11-0410-bbf8-ca57d06f2519 --- diff --git a/java/src/com/tightvnc/rfbplayer/AuthPanel.java b/java/src/com/tightvnc/rfbplayer/AuthPanel.java new file mode 100644 index 00000000..cdc4fe8c --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/AuthPanel.java @@ -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 index 00000000..de3ddfee --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/ButtonPanel.java @@ -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 index 00000000..f0a5b8df --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/ClipboardFrame.java @@ -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 index 00000000..2b7b5f22 --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/DesCipher.java @@ -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 , 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 . 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. +//

+// 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. +//

+// Most of this code is by Dave Zimmerman , and is +// Copyright (c) 1996 Widget Workshop, Inc. See the source file for details. +//

+// Fetch the software.
+// Fetch the entire Acme package. +//

+// @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 index 00000000..ae3b5319 --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/LICENCE.TXT @@ -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. + + 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.) + +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. + + 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. + + 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 + + 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. + + + Copyright (C) 19yy + + 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. + + , 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 index 00000000..76dbca43 --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/Makefile @@ -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 index 00000000..45e5ab6e --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/OptionsFrame.java @@ -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 index 00000000..b9f24c0f --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/README @@ -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 + and Jef Poskanzer . 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 . 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 index 00000000..a86aac53 --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/RfbProto.java @@ -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-, CTRL is sent separately so just send . + // 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 index 00000000..6d12aa25 --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/VncCanvas.java @@ -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 index 00000000..2524a50b --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/VncViewer.java @@ -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 index 00000000..21744056 --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/dir.mk @@ -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 index 00000000..d758ccdf --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/hextile.vnc @@ -0,0 +1,19 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + + +
+www.TightVNC.com + diff --git a/java/src/com/tightvnc/rfbplayer/index.vnc b/java/src/com/tightvnc/rfbplayer/index.vnc new file mode 100644 index 00000000..d9bb30ef --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/index.vnc @@ -0,0 +1,17 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + +
+www.TightVNC.com + diff --git a/java/src/com/tightvnc/rfbplayer/noshared.vnc b/java/src/com/tightvnc/rfbplayer/noshared.vnc new file mode 100644 index 00000000..b0dc3afc --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/noshared.vnc @@ -0,0 +1,17 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) [not shared] + + + + + +
+www.TightVNC.com + diff --git a/java/src/com/tightvnc/rfbplayer/shared.vnc b/java/src/com/tightvnc/rfbplayer/shared.vnc new file mode 100644 index 00000000..2f8ea1c5 --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/shared.vnc @@ -0,0 +1,17 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) [shared] + + + + + +
+www.TightVNC.com + diff --git a/java/src/com/tightvnc/rfbplayer/tight.vnc b/java/src/com/tightvnc/rfbplayer/tight.vnc new file mode 100644 index 00000000..3ca7a69f --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/tight.vnc @@ -0,0 +1,18 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + + +
+www.TightVNC.com + diff --git a/java/src/com/tightvnc/rfbplayer/zlib.vnc b/java/src/com/tightvnc/rfbplayer/zlib.vnc new file mode 100644 index 00000000..877ec5d1 --- /dev/null +++ b/java/src/com/tightvnc/rfbplayer/zlib.vnc @@ -0,0 +1,18 @@ + + + + +$USER's $DESKTOP desktop ($DISPLAY) + + + + + +
+www.TightVNC.com +