# HG changeset patch # User Yu Taninari # Date 1329765012 -32400 # Node ID 756bfaf731f35b7433431315832ae69693bbefe2 create new repository diff -r 000000000000 -r 756bfaf731f3 README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,44 @@ +README + +【ソフト名】TreeVnc +【種 別】画面共有ソフト +【圧縮形式】jar , tar.gz +【動作環境】Mac OS X おそらくwindowsでも動作すると思います +【開発環境】Mac OS X 10.6.8 + このソフトウェアを使用したことによって生じたすべての障害・損害・不具 合等に関しては、私と私の関係者および私の所属するいかなる団体・組織とも、 一切の責任を負いません。各自の責任においてご使用ください。 +  +・はじめに +このプログラムはTightVncをもとに作られたものです。多人数で画面共有することを目的に作成されました。 + +・ファイル構成 +bin/ +src/ +build.xml/ +Makefile/ +TreeVnc.jar +README +conf.txt + +・インストール方法 +TreeVNCをdownloadする。 + +・アンインストール方法 +TreeVNCを削除する。 + +・使い方 +Macで使用する際、共有したい画面の「システム環境設定->共有->画面共有」にチェックを入れ、コンピュータ設定から「他のユーザが画面操作の権限を要求することを許可」似チェックを入れる。 + +プロキシを以下のコマンドにより起動させる。 +% java -jar TreeVnc.jar [HostAddress] [5900] または +%java -jar TreeVnc.jar -p +一人がこのプロキシを立てたら後の人はこのプロキシに対して接続を行います。 + + +以下のコマンドでプロキシに接続する。 +% java -jar TreeVnc.jar または +TreeVnc.jar をダブルクリック + +直接クライアントを指定する際は +% java -jar TreeVnc.jar -c + +クライアントを起動するとローカル内にあるプロキシを自動で検出するので共有したい画面を選択して画面共有を開始する。 diff -r 000000000000 -r 756bfaf731f3 build.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/build.xml Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r 000000000000 -r 756bfaf731f3 conf.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf.txt Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,4 @@ +● conf.txtの書き方 +行の先頭にユーザタグ()を付けて Ipアドレス Port番号 の順番で書きこんで下さい。 +以下の形式が正しい書き方です。 + urasoe.ie.u-ryukyu.ac.jp 5999 diff -r 000000000000 -r 756bfaf731f3 src/CuiMyVncClient.MF --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/CuiMyVncClient.MF Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Ant-Version: Apache Ant 1.8.2 +Created-By: 1.6.0_24-b07-334-10M3326 (Apple Inc.) +Main-Class: treeVnc.CuiMyVncClient diff -r 000000000000 -r 756bfaf731f3 src/CuiMyVncClient.MF~ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/CuiMyVncClient.MF~ Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Ant-Version: Apache Ant 1.8.2 +Created-By: 1.6.0_24-b07-334-10M3326 (Apple Inc.) +Main-Class: treeVnc.CuiMyVncClient diff -r 000000000000 -r 756bfaf731f3 src/TreeVnc.MF --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/TreeVnc.MF Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,4 @@ +Manifest-Version: 1.0 +Ant-Version: Apache Ant 1.8.2 +Created-By: 1.6.0_24-b07-334-10M3326 (Apple Inc.) +Main-Class: treeVnc.TreeVnc diff -r 000000000000 -r 756bfaf731f3 src/test/getLocalAddress.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/test/getLocalAddress.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,16 @@ +package test; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +public class getLocalAddress { + public static void main(String[] argv) { + try { + InetAddress addr = InetAddress.getLocalHost(); + System.out.println(addr); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/#GetBroadCastClient.java# --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/#GetBroadCastClient.java# Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,82 @@ +package treeVnc; + +import java.io.BufferedReader; +//import java.io.ByteArrayInputStream; +//import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.ServerSocket; +import java.net.Socket; + +public class GetBroadCastClient implements Runnable { + private ServerSocket server = null; + BufferedReader is; + private int port = 8182; + private boolean stopFlag = false; + TextBoxClient text = new TextBoxClient(); + + private void getData() { +// try { +// server = new ServerSocket(port); +// while(true) { +// Socket socket = server.accept(); +// is = new BufferedReader(new InputStreamReader( +// socket.getInputStream())); +// String line = is.readLine(); +// if(line!=null){ +// text.checkBox(line); +// } +// text.setButton(); +// text.visible(); +// } +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } + +// void socketClose() { +// try { +// text.unVisible(); +// //server.close(); after the modify +// is.close(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } + +// /* +// private int castInt(byte[] a) { +// int value = 0; +// ByteArrayInputStream bais = new ByteArrayInputStream(a); + DataInputStream dis = new DataInputStream(bais); + try { + value = dis.readInt(); + } catch (IOException e) { + } + System.out.println(value); + return value; + } + + private String castString(byte[] a) { + String recover = new String(a); + recover = recover.replace("n", ""); + recover = recover.trim(); + System.out.println(recover); + return recover; + } + */ + + @Override + public void run() { + getData(); + } + + public void setStopFlag(boolean stopFlag) { + this.stopFlag = stopFlag; + } + + public boolean isStopFlag() { + return stopFlag; + } + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/#TreeVnc.java# --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/#TreeVnc.java# Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,37 @@ +package treeVnc; +/* +import myVncClient.MyVncClient; +import myVncProxy.VncProxyService; +*/ + +public class TreeVnc { + public static void main(String[] argv) { + /* + * SelectType select = new SelectType(); select.selectType(); + * + * while (select.wait) { try { Thread.sleep(1000); } catch + * (InterruptedException e) { e.printStackTrace(); } } + * System.out.println("test"); + */ + if (argv.length == 0) { + System.out.println("client"); + MyVncClient client = new MyVncClient(); + client.treeVncClient(argv); + } else if(argv.length == 1) { + if ("-p".equals(argv[0])) { + System.out.println("proxy"); + String[] temp = new String[0]; + VncProxyService proxy = new VncProxyService(); + proxy.treeVncProxy(temp); + } else { + System.out.println("usage : java -jar TreeVnc.jar (IpAddress) 5900"); + } + } else if(argv.length==2) { + System.out.println("proxy"); + VncProxyService proxy = new VncProxyService(); + proxy.treeVncProxy(argv); + } else{ + System.out.println("usage : java -jar TreeVnc.jar (IpAddress) 5900"); + } + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/AcceptClient.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/AcceptClient.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,303 @@ +package treeVnc; + +import java.net.Socket; +import java.net.UnknownHostException; +import java.io.*; +import java.net.*; +import java.util.*; + +public class AcceptClient { + private int counter = 0, parentnum = 0 /* 落ちたときの親の番号をカウントするためのもの */; + private LinkedList ls = new LinkedList(); + private boolean addrRegistor = true; + private int passNumber=0,numberZone; + boolean runflag = false; + //private String name; + private int passCheck = 0; + private final int treebranch = 2; // treeの子ノードの数 + private String newparent,request,myAddress; + private String leaderflag = "0", sendleaderflag = "0"; + private final int intv_time = 100; + + + + public AcceptClient(String name) { + //this.name = name; + } + + + /* + public AcceptClient() { + new CreateThread(this); + } + */ + + // public synchronized void transferParentAddrerss(BufferedReader + // is,PrintStream os) { + public void transferParentAddrerss(BufferedReader is,PrintStream os) { + // クライアントからのメッセージを待ち、受け取ったメッセージをそのまま返す + try { + while (true) { + String line = is.readLine(); + String port = is.readLine(); + myAddress = getMyAddress(); + if ("1".equals(line)||"3".equals(line)) { + String treeNumber = is.readLine(); + // reply to Parents lost node + checkWait(os,is,port,Integer.parseInt(treeNumber)); + + } else if ("2".equals(line)) { + // reply to not Found Parents + replyNodeInformation(port); + listupdate(port, newparent); + outputStream(os, newparent, String.valueOf(parentnum), + port, leaderflag); + os.close(); + is.close(); + } else if (line!=null){ + //connection First time + if(checkAddress(line)){ + outputStream(os, myAddress,"0","0","0"); + break; + }else { + if(replyCreateTree(os, port, line)) { + break; + } else { + break; + } + } + } + } + } catch (IOException e) { + System.out.println(e); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private boolean checkAddress(String line){ + String test[] = line.split("\\."); + int a = Integer.parseInt(test[0]); + int b = Integer.parseInt(test[1]); + if((192==a&&b==168)||(172==a&&(b>15||b<32))||10==a) { + return true; + } else{ + return false; + } + } + + /** + * @param port + * parent value + */ + private synchronized void listupdate(String port) { + ls.remove(Integer.parseInt(port)); + ls.add(Integer.parseInt(port), ls.getLast()); + ls.removeLast(); + } + + private synchronized void listupdate(String port,String myaddr) { + ls.remove(Integer.parseInt(port)); + ls.add(Integer.parseInt(port), myaddr); + ls.removeLast(); + } + + private void outputStream(PrintStream os, String request, String parentnum, + String treenum, String leaderflag) { + os.println(request); + os.println(parentnum); + os.println(treenum); + os.println(leaderflag); + } + + private void checkParameter(int parent, int counter, String leaderflag) { + System.out.println("number p =" + parentnum); + System.out.println("number i =" + counter); + System.out.println("leaderflag=" + leaderflag + "\n"); + } + + private synchronized void addClientAdress(String line, LinkedList ls) { + if (line != null) { + ls.add(line); + } + displyLinkedList(ls); + } + + private void displyLinkedList( LinkedList ls) { + int g = 0; + for (String bs : ls) { + System.out.println(g + "number" + bs); + g++; + } + } + + private String decisionLeader(int counter, int treebranch) { + if ((counter - 1) % treebranch == 1) { // children in most young treenum + // have leaderflag 1 other 0 + return "0"; + } else { + return "1"; + } + } + + String getMyAddress () { + InetAddress addr = null; + try { + addr = InetAddress.getLocalHost(); + } catch (UnknownHostException e) { + e.printStackTrace(); + } + return new String(addr.getHostAddress()); + } + + private void replyNodeInformation (String port) { + parentnum = (Integer.parseInt(port) - 1) / treebranch; + newparent = ls.get(parentnum); + sendleaderflag = decisionLeader(Integer.parseInt(port), treebranch); + //leaderflag = decisionLeader(Integer.parseInt(port),treebranch); + } + + private void replyLeaderNode(PrintStream os,BufferedReader is,String port,String treeNumber) throws IOException, InterruptedException { + os.println(ls.getLast()); + replyNodeInformation(port); + counter--; + passCheck = 1; + reportLastNode(ls.getLast(), newparent,port, String.valueOf(parentnum),sendleaderflag); + listupdate(port); + waitThread(); + /* + if (Integer.parseInt(treeNumber)==ls.size()-1) + return; + */ + displyLinkedList(ls); + os.println(port); + leaderflag = decisionLeader(Integer.parseInt(treeNumber),treebranch); + os.println(treeNumber); + Thread.sleep(intv_time); + is.close(); + os.close(); + } + + private void replyNormalChildren(PrintStream os,BufferedReader is,String port,String treeNumber,boolean flag) throws IOException, InterruptedException { + if(flag) + notifyThread(); + else + waitThread(); + if (Integer.parseInt(treeNumber)==ls.size()) + return; + os.println(ls.get(Integer.parseInt(port))); + os.println(port); + if(ls.size()-1+passCheck == Integer.parseInt(treeNumber)) + treeNumber = "skip"; + passCheck = 0; + os.println(treeNumber); + System.out.println("num4="+ ls.get(Integer.parseInt(port))); + runflag = false; + is.close(); + os.close(); + } + + private synchronized boolean replyCreateTree(PrintStream os,String port,String line) throws InterruptedException { + if (addrRegistor == true) { + ls.add(myAddress); + addrRegistor = false; + } + + if (line != null) { + addClientAdress(line, ls); + counter++; + } else { + return true; + } + + if (counter >= treebranch + 1) { + leaderflag = decisionLeader(counter, treebranch); + parentnum = (counter - 1) / treebranch; + request = ls.get(parentnum); + System.out.println(parentnum); + outputStream(os, request,String.valueOf(parentnum), + String.valueOf(counter), leaderflag); + checkParameter(parentnum, counter, leaderflag); + } else { + // treeの親ノードに接続する人に接続する人を教える + outputStream(os, myAddress, "0", + String.valueOf(counter), leaderflag); + } + Thread.sleep(intv_time); + return false; + } + + void reportLastNode(String newchild, String newparent, String newtreenum, + String newpnum, String newleaderflag) throws IOException { + try { + Socket echoSocket; + System.out.println(newchild + "connect"); + // echoSocket = new Socket(newchild, 10001 + (i + 1));// + // i+1は実験中に同じマシーンを使っていたのでportを変えて対応、本番時には取り除く予定。 + echoSocket = new Socket(newchild, 10001); + DataOutputStream os = new DataOutputStream(echoSocket.getOutputStream()); + os.writeBytes(newparent + "\n"); + os.writeBytes(newpnum + "\n"); + os.writeBytes(newtreenum + "\n"); + os.writeBytes(newleaderflag + "\n"); + os.close(); + } catch (UnknownHostException e) { + System.err.println("Don't know about host: localhost"); + } catch (IOException e) { + System.err + .println("Couldn't get I/O for the connection to: localhost"); + } + } + + void reportFinishFlag(String nextLastNode) { + Socket echoSocket; + try { + echoSocket = new Socket(nextLastNode, 10001); + DataOutputStream os = new DataOutputStream( + echoSocket.getOutputStream()); + os.writeBytes("lastnode" + "\n"); + } catch (UnknownHostException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + synchronized void checkWait(PrintStream os,BufferedReader is,String port,int treeNum) throws InterruptedException, IOException { + final int TIMEOUT = 3000; + if (passNumber == 0) { + passNumber++; + numberZone = ((treeNum - 1) / treebranch); + System.out.println(treeNum + "--wait--"); + replyLeaderNode(os,is,port,String.valueOf(treeNum)); + notifyAll(); + passNumber = 0; + } else if (numberZone == ((treeNum - 1) / treebranch)) { + if (++passNumber == treebranch) { + System.out.println(treeNum + "--notify--"); + passNumber = 0; + replyNormalChildren(os,is,port,String.valueOf(treeNum),true); + } else { + System.out.println(treeNum + "--waityobi--"); + replyNormalChildren(os,is,port,String.valueOf(treeNum),false); + wait(TIMEOUT); + } + } else { + wait(); + checkWait(os,is,port,treeNum); + } + } + + private void waitThread() { + final int TIMEOUT = 3000; + try { + wait(TIMEOUT); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + private void notifyThread() { + notifyAll(); + } + } \ No newline at end of file diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/AcceptThread.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/AcceptThread.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,42 @@ +package treeVnc; +import java.net.Socket; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class AcceptThread implements Runnable { + MyRfbProto rfb = null; + byte[] imageBytes; + int port; + + AcceptThread(MyRfbProto _rfb) { + rfb = _rfb; + } + + + AcceptThread(MyRfbProto _rfb, int p) { + rfb = _rfb; + port = p; + } + + public void changeRfb(MyRfbProto _rfb) { + rfb = _rfb; + } + + public void run() { + rfb.selectPort(port); + + while (true) { + try { + Socket newCli = rfb.accept(); + + OutputStream os = newCli.getOutputStream(); + InputStream is = newCli.getInputStream(); + rfb.newClient(this, newCli, os, is); + } catch (IOException e) { + e.printStackTrace(); + System.out.println(e); + } + } + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/AuthPanel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/AuthPanel.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,121 @@ +package treeVnc; +// +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// Copyright (C) 2002-2006 Constantin Kaplinsky. All Rights Reserved. +// +// This is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +import java.awt.*; +import java.awt.event.*; + +// +// The panel which implements the user authentication scheme +// + +class AuthPanel extends Panel implements ActionListener { + + /** + * + */ + private static final long serialVersionUID = 1L; +TextField passwordField; + Button okButton; + + // + // Constructor. + // + + public AuthPanel(VncViewer viewer) + { + Label titleLabel = new Label("VNC Authentication", Label.CENTER); + titleLabel.setFont(new Font("Helvetica", Font.BOLD, 18)); + + Label promptLabel = new Label("Password:", Label.CENTER); + + passwordField = new TextField(10); + passwordField.setForeground(Color.black); + passwordField.setBackground(Color.white); + passwordField.setEchoChar('*'); + + okButton = new Button("OK"); + + GridBagLayout gridbag = new GridBagLayout(); + GridBagConstraints gbc = new GridBagConstraints(); + + setLayout(gridbag); + + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.insets = new Insets(0,0,20,0); + gridbag.setConstraints(titleLabel,gbc); + add(titleLabel); + + gbc.fill = GridBagConstraints.NONE; + gbc.gridwidth = 1; + gbc.insets = new Insets(0,0,0,0); + gridbag.setConstraints(promptLabel,gbc); + add(promptLabel); + + gridbag.setConstraints(passwordField,gbc); + add(passwordField); + passwordField.addActionListener(this); + + // gbc.ipady = 10; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(0,20,0,0); + gbc.ipadx = 30; + gridbag.setConstraints(okButton,gbc); + add(okButton); + okButton.addActionListener(this); + } + + // + // Move keyboard focus to the default object, that is, the password + // text field. + // + + public void moveFocusToDefaultField() + { + passwordField.requestFocus(); + } + + // + // This method is called when a button is pressed or return is + // pressed in the password text field. + // + + public synchronized void actionPerformed(ActionEvent evt) + { + if (evt.getSource() == passwordField || evt.getSource() == okButton) { + passwordField.setEnabled(false); + notify(); + } + } + + // + // Wait for user entering a password, and return it as String. + // + + public synchronized String getPassword() throws Exception + { + try { + wait(); + } catch (InterruptedException e) { } + return passwordField.getText(); + } + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/BroadCastClient.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/BroadCastClient.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,57 @@ +package treeVnc; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.MulticastSocket; + +public class BroadCastClient { + final int BUFSIZE = 1024; + final String MCASTADDR = "224.0.0.1"; + final int PORT = 8183; + private byte[] buf = new byte[BUFSIZE]; + private InetAddress mAddr; + private MulticastSocket soc; + private String str; + + public BroadCastClient(String _str) { + str = _str; + } + + void createSocket() { + try { + mAddr = InetAddress.getByName(MCASTADDR); + soc = new MulticastSocket(); + soc.setTimeToLive(1); + } catch (IOException e) { + e.printStackTrace(); + } + } + + void sendData() { + buf = str.getBytes(); + // System.out.println("strlen"+str.length()); + // System.out.println("str"+str); + DatagramPacket sendPacket = new DatagramPacket(buf, str.length(), + mAddr, PORT); + try { + soc.send(sendPacket); + } catch (IOException e) { + e.printStackTrace(); + } + } +/* + @Override + public void run() { + createSocket(); + while (true) { + sendData(); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + System.out.println(e); + } + } + } + */ +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/BroadCastProxy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/BroadCastProxy.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,40 @@ +package treeVnc; + +import java.io.IOException; +import java.io.PrintStream; +import java.net.Socket; + +public class BroadCastProxy { + private String str; + private Socket socket = null; + private PrintStream os = null; + private int port = 8182; + + public BroadCastProxy(String _str) { + str = _str; + } + + void createSocket(String addr) { + while (true) { + try { + Thread.sleep(1000); + socket = new Socket(addr, port); + os = new PrintStream(socket.getOutputStream()); + sendData(); + os.close(); + socket.close(); + break; + } catch (IOException e) { + System.out.println("Connection faild"); + continue; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + void sendData() { + os.println(str); + } + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/ButtonPanel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/ButtonPanel.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,166 @@ +package treeVnc; +// +// 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 { + + /** + * + */ + private static final long serialVersionUID = 1L; +VncViewer viewer; + Button disconnectButton; + Button optionsButton; + Button recordButton; + Button clipboardButton; + Button ctrlAltDelButton; + Button refreshButton; + Button changeHost; + + ButtonPanel(VncViewer v) { + viewer = v; + + setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); + disconnectButton = new Button("Disconnect"); + disconnectButton.setEnabled(false); + add(disconnectButton); + disconnectButton.addActionListener(this); + optionsButton = new Button("Options"); + add(optionsButton); + optionsButton.addActionListener(this); + clipboardButton = new Button("Clipboard"); + clipboardButton.setEnabled(false); + add(clipboardButton); + clipboardButton.addActionListener(this); + if (viewer.rec != null) { + recordButton = new Button("Record"); + add(recordButton); + recordButton.addActionListener(this); + } + ctrlAltDelButton = new Button("Send Ctrl-Alt-Del"); + ctrlAltDelButton.setEnabled(false); + add(ctrlAltDelButton); + ctrlAltDelButton.addActionListener(this); + refreshButton = new Button("Refresh"); + refreshButton.setEnabled(false); + add(refreshButton); + refreshButton.addActionListener(this); + changeHost = new Button("Change Host"); + add(changeHost); + changeHost.addActionListener(this); + } + + // + // Enable buttons on successful connection. + // + + public void enableButtons() { + disconnectButton.setEnabled(true); + clipboardButton.setEnabled(true); + refreshButton.setEnabled(true); + } + + // + // Disable all buttons on disconnect. + // + + public void disableButtonsOnDisconnect() { + remove(disconnectButton); + disconnectButton = new Button("Hide desktop"); + disconnectButton.setEnabled(true); + add(disconnectButton, 0); + disconnectButton.addActionListener(this); + + optionsButton.setEnabled(false); + clipboardButton.setEnabled(false); + ctrlAltDelButton.setEnabled(false); + refreshButton.setEnabled(false); + + validate(); + } + + // + // Enable/disable controls that should not be available in view-only + // mode. + // + + public void enableRemoteAccessControls(boolean enable) { + ctrlAltDelButton.setEnabled(enable); + } + + // + // Event processing. + // + + public void actionPerformed(ActionEvent evt) { + + viewer.moveFocusToDesktop(); + + if (evt.getSource() == disconnectButton) { + viewer.disconnect(); + + } else if (evt.getSource() == optionsButton) { + viewer.options.setVisible(!viewer.options.isVisible()); + + } else if (evt.getSource() == recordButton) { + viewer.rec.setVisible(!viewer.rec.isVisible()); + + } else if (evt.getSource() == clipboardButton) { + viewer.clipboard.setVisible(!viewer.clipboard.isVisible()); + + }else if (evt.getSource() == changeHost) { + //write when click to "Change Host" + + } 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,KeyEvent.CHAR_UNDEFINED); + viewer.rfb.writeKeyEvent(ctrlAltDelEvent); + + ctrlAltDelEvent = + new KeyEvent(this, KeyEvent.KEY_RELEASED, 0, modifiers, 127,KeyEvent.CHAR_UNDEFINED); + viewer.rfb.writeKeyEvent(ctrlAltDelEvent); + + } catch (IOException e) { + e.printStackTrace(); + } + } else if (evt.getSource() == refreshButton) { + try { + RfbProto rfb = viewer.rfb; + rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, + rfb.framebufferHeight, false); + } catch (IOException e) { + e.printStackTrace(); + } + } + } +} + diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/CapabilityInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/CapabilityInfo.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,88 @@ +package treeVnc; +// +// Copyright (C) 2003 Constantin Kaplinsky. All Rights Reserved. +// +// This is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +// +// CapabilityInfo.java - A class to hold information about a +// particular capability as used in the RFB protocol 3.130. +// + +class CapabilityInfo { + + // Public methods + + public CapabilityInfo(int code, + String vendorSignature, + String nameSignature, + String description) { + this.code = code; + this.vendorSignature = vendorSignature; + this.nameSignature = nameSignature; + this.description = description; + enabled = false; + } + + public CapabilityInfo(int code, + byte[] vendorSignature, + byte[] nameSignature) { + this.code = code; + this.vendorSignature = new String(vendorSignature); + this.nameSignature = new String(nameSignature); + this.description = null; + enabled = false; + } + + public int getCode() { + return code; + } + + public String getDescription() { + return description; + } + + public boolean isEnabled() { + return enabled; + } + + public void enable() { + enabled = true; + } + + public boolean equals(CapabilityInfo other) { + return (other != null && this.code == other.code && + this.vendorSignature.equals(other.vendorSignature) && + this.nameSignature.equals(other.nameSignature)); + } + + public boolean enableIfEquals(CapabilityInfo other) { + if (this.equals(other)) + enable(); + + return isEnabled(); + } + + // Protected data + + protected int code; + protected String vendorSignature; + protected String nameSignature; + + protected String description; + protected boolean enabled; +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/CapsContainer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/CapsContainer.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,104 @@ +package treeVnc; +// +// Copyright (C) 2003 Constantin Kaplinsky. All Rights Reserved. +// +// This is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +// +// CapsContainer.java - A container of capabilities as used in the RFB +// protocol 3.130 +// + +import java.util.Vector; +import java.util.Hashtable; + +class CapsContainer { + + // Public methods + + public CapsContainer() { + infoMap = new Hashtable(64, (float)0.25); + orderedList = new Vector(32, 8); + } + + public void add(CapabilityInfo capinfo) { + Integer key = new Integer(capinfo.getCode()); + infoMap.put(key, capinfo); + } + + public void add(int code, String vendor, String name, String desc) { + Integer key = new Integer(code); + infoMap.put(key, new CapabilityInfo(code, vendor, name, desc)); + } + + public boolean isKnown(int code) { + return infoMap.containsKey(new Integer(code)); + } + + public CapabilityInfo getInfo(int code) { + return infoMap.get(new Integer(code)); + } + + public String getDescription(int code) { + CapabilityInfo capinfo = infoMap.get(new Integer(code)); + if (capinfo == null) + return null; + + return capinfo.getDescription(); + } + + public boolean enable(CapabilityInfo other) { + Integer key = new Integer(other.getCode()); + CapabilityInfo capinfo = infoMap.get(key); + if (capinfo == null) + return false; + + boolean enabled = capinfo.enableIfEquals(other); + if (enabled) + orderedList.addElement(key); + + return enabled; + } + + public boolean isEnabled(int code) { + CapabilityInfo capinfo = infoMap.get(new Integer(code)); + if (capinfo == null) + return false; + + return capinfo.isEnabled(); + } + + public int numEnabled() { + return orderedList.size(); + } + + public int getByOrder(int idx) { + int code; + try { + code = (orderedList.elementAt(idx)).intValue(); + } catch (ArrayIndexOutOfBoundsException e) { + code = 0; + } + return code; + } + + // Protected data + + protected Hashtable infoMap; + protected Vector orderedList; +} + diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/ClipboardFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/ClipboardFrame.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,138 @@ +package treeVnc; +// +// 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 { + + /** + * + */ + private static final long serialVersionUID = 1L; +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 -r 000000000000 -r 756bfaf731f3 src/treeVnc/ConfFileReader.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/ConfFileReader.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,45 @@ +package treeVnc; + +import java.io.*; +import java.util.LinkedList; + + +public class ConfFileReader { + private LinkedList name = new LinkedList(); + private LinkedList port = new LinkedList(); + + public void fileRead() { + try { + FileReader in = new FileReader("conf.txt"); + BufferedReader br = new BufferedReader(in); + String line; + while ((line = br.readLine()) != null) { + spilitAndWrite(line); + } + br.close(); + in.close(); + } catch (IOException e) { + System.out.println(e); + } + for(String n : name) + System.out.println("name:"+n); + for(int p : port) + System.out.println("port:"+p); + } + + private void spilitAndWrite(String line) { + String[] temp = line.split(" "); + if("".equals(temp[0])) { + name.add(temp[1]); + port.add(Integer.parseInt(temp[2])); + } + } + + public LinkedList getName() { + return name; + } + + public LinkedList getPort() { + return port; + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/CreateThread.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/CreateThread.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,90 @@ +package treeVnc; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.net.BindException; +import java.net.ServerSocket; +import java.net.Socket; + +public class CreateThread implements Runnable { + ServerSocket echoServer; + AcceptClient acceptClient; + int port; + + public CreateThread(AcceptClient _acc) { + acceptClient = _acc; + port = 9999; + } + + + public CreateThread(int port,AcceptClient _acc) { + acceptClient = _acc; + this.port = port; + } + + + void newEchoClient(final BufferedReader is,final PrintStream os) { + Runnable echoSender = new Runnable() { + public void run() { + // AcceptClient acceptClient = new AcceptClient(); + // acceptClient new + System.out.println("Threadつくります"); + acceptClient.transferParentAddrerss(is,os); + } + }; + new Thread(echoSender).start(); + } + + void selectPort(int p) { + int port = p; + while (true) { + try { + initServSock(port); + break; + } catch (BindException e) { + port++; + continue; + } catch (IOException e) { + + } + } + System.out.println("accept Echo port = " + port); + } + + void initServSock(int port) throws IOException { + echoServer = new ServerSocket(port); + this.port = port; + } + + + public void run() { + selectPort(port); + + while (true) { + try { +// echoServer = new ServerSocket(9999); + Socket clientSocket = echoServer.accept(); + BufferedReader is = new BufferedReader(new InputStreamReader( + clientSocket.getInputStream())); + PrintStream os = new PrintStream(clientSocket.getOutputStream()); + System.out.println("accしたよ"); + newEchoClient(is,os); +// acceptClient.transferParentAddrerss(is, os); + } catch (IOException e) { + System.out.println(e); + } +/* + try { + echoServer.close(); + } catch (IOException e) { + System.out.println(e); + } +*/ + + } + + } + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/CuiMyVncClient.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/CuiMyVncClient.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,1074 @@ +package treeVnc; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.net.*; +import java.nio.ByteBuffer; +import java.util.Random; +/* +import AcceptThread; +import OptionsNoFrame; +*/ + +public class CuiMyVncClient implements InterfaceForViewer { + + + String[] mainArgs; + String username; + + // RfbProto rfb; + MyRfbProtoClient rfb; + Thread rfbThread; + Thread accThread; + Thread clientThread; + + Frame vncFrame; + Container vncContainer; + ScrollPane desktopScrollPane; + GridBagLayout gridbag; + ButtonPanel buttonPanel; + Label connStatusLabel; + CuiVncCanvas vc; + // OptionsFrame options; + OptionsNoFrame options; + ClipboardFrame clipboard; + RecordingFrame rec; + + // Control session recording. + Object recordingSync; + String sessionFileName; + boolean recordingActive; + boolean recordingStatusChanged; + String cursorUpdatesDef; + String eightBitColorsDef; + + // Variables read from parameter values. + String socketFactory; + String host; + int port; + String passwordParam; + boolean showControls; + boolean offerRelogin; + boolean showOfflineDesktop; + int deferScreenUpdates; + int deferCursorUpdates; + int deferUpdateRequests; + int debugStatsExcludeUpdates; + int debugStatsMeasureUpdates; + + private static final long serialVersionUID = 1L; + boolean inAnApplet = true; + boolean inSeparateFrame = false; + Socket clientSocket = null; + String parent, treenum; + private String leaderflag; + boolean runflag = false; + boolean first = true; + + EchoClient echoValue; + int echoPort; + String pHost; + + public static void main(String[] argv) { + CuiMyVncClient v = new CuiMyVncClient(); + v.echoValue = null; + v.runClient(argv, v); + + } + + private void runClient(String[] argv, CuiMyVncClient v) { + mainArgs = argv; + + if (mainArgs.length > 0) + pHost = mainArgs[0]; + else + pHost = "cls080.ie.u-ryukyu.ac.jp"; + if (mainArgs.length > 1) + port = Integer.parseInt(mainArgs[1]); + else + port = 5999; + + v.init(); + v.start_threads(); + + } + + + void checkArgs(String[] argv) { + if (argv.length > 3) { + username = argv[3]; + } else if (argv.length < 2) { + System.out.println("Please enter argv"); + System.out.println("hostname(IPaddress) port password"); + System.exit(0); + } else { + username = argv[0]; + } + } + + // + // init() + // + + public void init() { + + Random rnd = new Random(); + long ran = rnd.nextInt(5000); + try { + Thread.sleep(ran); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + readParameters(); + //readParameters(value); + + options = new OptionsNoFrame(this); + recordingSync = new Object(); + + sessionFileName = null; + recordingActive = false; + recordingStatusChanged = false; + cursorUpdatesDef = null; + eightBitColorsDef = null; + + /* + * try { connectAndAuthenticate(); } catch (NoRouteToHostException e) { + * fatalError("Network error: no route to server: " + host, e); } catch + * (UnknownHostException e) { + * fatalError("Network error: server name unknown: " + host, e); } catch + * (ConnectException e) { + * fatalError("Network error: could not connect to server: " + host + + * ":" + port, e); } catch (Exception e) { } + */ + rfbThread = new Thread(this); + + } + + // + // run() - executed by the rfbThread to deal with the RFB socket. + // + + public void start_threads() { + rfbThread.start(); + } + + public void run() { + + try { + if (first) { + connectAndAuthenticate(); + accThread = new Thread(new AcceptThread(rfb, 5999)); + accThread.start(); + first = false; + }else { + reConnectAndAuthenticate(); + } + doProtocolInitialisation(); + + vc = new CuiVncCanvas(this, 0, 0); + vc.updateFramebufferSize(); + + } catch (IOException e) { + + System.out.println("Socket error"); + // parent no find + Random rnd = new Random(); + long ran = rnd.nextInt(5000) + 5000; + System.out.println(ran); + // 親がいない場合の処理はここに書く!!!! + /** + * this while reconnection + */ + + int counter = 0; + + /* + vncFrame.setVisible(false); + vncFrame.dispose(); +*/ + while (true) { + /** + * if my last node case reconnectoion stop + */ + + echoValue = new EchoClient(echoValue, this); + try { + Thread.sleep(ran); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + + if (counter >= 3) { + echoValue.openport(); + echoValue.notfoundParent(); + } + + echoValue.openport(); + // runflag = echo.losthost(); + if (echoValue.lostHost()) { + break; + } + counter++; + } + // System.exit(0); + }catch (Exception e) { + System.out.println(e); + System.exit(0); + } + try{ + + processNormalProtocol();// main loop + + } catch (NoRouteToHostException e) { + fatalError("Network error: no route to server: " + host, e); + } catch (UnknownHostException e) { + fatalError("Network error: server name unknown: " + host, e); + } catch (ConnectException e) { + fatalError("Network error: could not connect to server: " + host + + ":" + port, e); + } catch (EOFException e) { + + if (leaderflag != null) { + while (true) { + echoValue = new EchoClient(echoValue, this); + echoValue.openport(); + // runflag = echo.losthost(); + if (echoValue.lostHost()) { + break; + } + } + } else { + if (showOfflineDesktop) { + e.printStackTrace(); + System.out + .println("Network error: remote side closed connection"); + if (vc != null) { + vc.enableInput(false); + } + if (rfb != null && !rfb.closed()) + rfb.close(); + if (showControls && buttonPanel != null) { + buttonPanel.disableButtonsOnDisconnect(); + } + } else { + fatalError("Network error: remote side closed connection", + e); + } + } + } catch (IOException e) { + String str = e.getMessage(); + if (str != null && str.length() != 0) { + fatalError("Network Error: " + str, e); + } else { + fatalError(e.toString(), e); + } + } catch (Exception e) { + String str = e.getMessage(); + if (str != null && str.length() != 0) { + fatalError("Error: " + str, e); + } else { + fatalError(e.toString(), e); + } + } + + } + + // + // Process RFB socket messages. + // If the rfbThread is being stopped, ignore any exceptions, + // otherwise rethrow the exception so it can be handled. + // + + void processNormalProtocol() throws Exception { + try { + vc.processNormalProtocol();// main loop + } catch (Exception e) { + if (rfbThread == null) { + System.out.println("Ignoring RFB socket exceptions" + + " because applet is stopping"); + } else { + throw e; + } + } + } + + // + // Connect to the RFB server and authenticate the user. + // + + void connectAndAuthenticate() throws Exception { + showConnectionStatus("Initializing..."); + + showConnectionStatus("Connecting to " + host + ", port " + port + "..."); + + // rfb = new RfbProto(host, port, this); + rfb = new MyRfbProtoClient(pHost, port, this); + + showConnectionStatus("Connected to server"); + + rfb.readVersionMsg(); + showConnectionStatus("RFB server supports protocol version " + + rfb.serverMajor + "." + rfb.serverMinor); + + rfb.writeVersionMsg(); + showConnectionStatus("Using RFB protocol version " + rfb.clientMajor + + "." + rfb.clientMinor); + + + if (rfb.serverMinor == 855) { + /* + * if connect to proxy, userEchoPortFlag is true. + * if connect to client, userEchoPortFlag is false. + */ + boolean useEchoPortFlag = rfb.readProxyFlag(); + + if (useEchoPortFlag) { + byte[] b = new byte[4]; + b = rfb.readEchoPort(); + echoPort = castByteInt(b); + + InetAddress addr = InetAddress.getByName(pHost); + String h = new String(addr.getHostAddress()); + + getParentName(); + if (!(h.equals(host))) { + rfb.changeParent(host, port); + rfb.readVersionMsg(); + rfb.writeVersionMsg(); + //boolean flag = rfb.readProxyFlag(); + } + } + } + + int secType = rfb.negotiateSecurity(); + int authType; + if (secType == RfbProto.SecTypeTight) { + showConnectionStatus("Enabling TightVNC protocol extensions"); + rfb.setupTunneling(); + authType = rfb.negotiateAuthenticationTight(); + } else { + authType = secType; + } + + switch (authType) { + case RfbProto.AuthNone: + showConnectionStatus("No authentication needed"); + rfb.authenticateNone(); + break; + case RfbProto.AuthVNC: + showConnectionStatus("Performing standard VNC authentication"); + if (passwordParam != null) { + rfb.authenticateVNC(passwordParam); + } else { + String pw = askPassword(); + rfb.authenticateVNC(pw); + } + break; + default: + throw new Exception("Unknown authentication scheme " + authType); + } + } + + + + void reConnectAndAuthenticate() throws Exception { + showConnectionStatus("Initializing..."); + + showConnectionStatus("Connecting to " + host + ", port " + port + "..."); + + rfb.changeParent(host, port); + + showConnectionStatus("Connected to server"); + + rfb.readVersionMsg(); + showConnectionStatus("RFB server supports protocol version " + + rfb.serverMajor + "." + rfb.serverMinor); + + rfb.writeVersionMsg(); + showConnectionStatus("Using RFB protocol version " + rfb.clientMajor + + "." + rfb.clientMinor); + + if (rfb.serverMinor == 855) { + boolean useEchoPortFlag = rfb.readProxyFlag(); + if (useEchoPortFlag) { + byte[] b = new byte[4]; + b = rfb.readEchoPort(); + echoPort = castByteInt(b); + + InetAddress addr = InetAddress.getByName(pHost); + String h = new String(addr.getHostAddress()); + + getParentName(); + if (!(h.equals(host))) { + rfb.changeParent(host, port); + rfb.readVersionMsg(); + rfb.writeVersionMsg(); + //boolean flag = rfb.readProxyFlag(); + } + } + } + + int secType = rfb.negotiateSecurity(); + int authType; + if (secType == RfbProto.SecTypeTight) { + showConnectionStatus("Enabling TightVNC protocol extensions"); + rfb.setupTunneling(); + authType = rfb.negotiateAuthenticationTight(); + } else { + authType = secType; + } + + switch (authType) { + case RfbProto.AuthNone: + showConnectionStatus("No authentication needed"); + rfb.authenticateNone(); + break; + case RfbProto.AuthVNC: + showConnectionStatus("Performing standard VNC authentication"); + if (passwordParam != null) { + rfb.authenticateVNC(passwordParam); + } else { + String pw = askPassword(); + rfb.authenticateVNC(pw); + } + break; + default: + throw new Exception("Unknown authentication scheme " + authType); + } + } + + // + // Show a message describing the connection status. + // To hide the connection status label, use (msg == null). + // + + void showConnectionStatus(String msg) { + System.out.println(msg); + } + + // + // Show an authentication panel. + // + + String askPassword() throws Exception { + /* + * showConnectionStatus(null); AuthPanel authPanel = new + * AuthPanel(this); + * + * GridBagConstraints gbc = new GridBagConstraints(); gbc.gridwidth = + * GridBagConstraints.REMAINDER; gbc.anchor = + * GridBagConstraints.NORTHWEST; gbc.weightx = 1.0; gbc.weighty = 1.0; + * gbc.ipadx = 100; gbc.ipady = 50; gridbag.setConstraints(authPanel, + * gbc); vncContainer.add(authPanel); + * + * + * authPanel.moveFocusToDefaultField(); vncContainer.remove(authPanel); + */ + showConnectionStatus("ask password..."); + String pw = mainArgs[2]; + return pw; + } + + // + // Do the rest of the protocol initialisation. + // + + void doProtocolInitialisation() throws IOException { + rfb.writeClientInit(); + rfb.readServerInit(); + + System.out.println("Desktop name is " + rfb.desktopName); + System.out.println("Desktop size is " + rfb.framebufferWidth + " x " + + rfb.framebufferHeight); + + setEncodings(); + + // showConnectionStatus(null); + } + + // + // Send current encoding list to the RFB server. + // + + int[] encodingsSaved; + int nEncodingsSaved; + + void setEncodings() { + setEncodings(false); + } + + void autoSelectEncodings() { + setEncodings(true); + } + + void setEncodings(boolean autoSelectOnly) { + if (options == null || rfb == null || !rfb.inNormalProtocol) + return; + + int preferredEncoding = options.preferredEncoding; + if (preferredEncoding == -1) { + long kbitsPerSecond = rfb.kbitsPerSecond(); + if (nEncodingsSaved < 1) { + // Choose Tight or ZRLE encoding for the very first update. + System.out.println("Using Tight/ZRLE encodings"); + preferredEncoding = RfbProto.EncodingTight; + } else if (kbitsPerSecond > 2000 + && encodingsSaved[0] != RfbProto.EncodingHextile) { + // Switch to Hextile if the connection speed is above 2Mbps. + System.out.println("Throughput " + kbitsPerSecond + + " kbit/s - changing to Hextile encoding"); + preferredEncoding = RfbProto.EncodingHextile; + } else if (kbitsPerSecond < 1000 + && encodingsSaved[0] != RfbProto.EncodingTight) { + // Switch to Tight/ZRLE if the connection speed is below 1Mbps. + System.out.println("Throughput " + kbitsPerSecond + + " kbit/s - changing to Tight/ZRLE encodings"); + preferredEncoding = RfbProto.EncodingTight; + } else { + // Don't change the encoder. + if (autoSelectOnly) + return; + preferredEncoding = encodingsSaved[0]; + } + } else { + // Auto encoder selection is not enabled. + if (autoSelectOnly) + return; + } + + int[] encodings = new int[20]; + int nEncodings = 0; + + encodings[nEncodings++] = preferredEncoding; + if (options.useCopyRect) { + encodings[nEncodings++] = RfbProto.EncodingCopyRect; + } + if (preferredEncoding != RfbProto.EncodingTight) { + encodings[nEncodings++] = RfbProto.EncodingTight; + } + if (preferredEncoding != RfbProto.EncodingZRLE) { + encodings[nEncodings++] = RfbProto.EncodingZRLE; + } + if (preferredEncoding != RfbProto.EncodingHextile) { + encodings[nEncodings++] = RfbProto.EncodingHextile; + } + if (preferredEncoding != RfbProto.EncodingZlib) { + encodings[nEncodings++] = RfbProto.EncodingZlib; + } + /* + * if (preferredEncoding != RfbProto.EncodingCoRRE) { + * encodings[nEncodings++] = RfbProto.EncodingCoRRE; } if + * (preferredEncoding != RfbProto.EncodingRRE) { encodings[nEncodings++] + * = RfbProto.EncodingRRE; } + * + * if (options.compressLevel >= 0 && options.compressLevel <= 9) { + * encodings[nEncodings++] = RfbProto.EncodingCompressLevel0 + + * options.compressLevel; } if (options.jpegQuality >= 0 && + * options.jpegQuality <= 9) { encodings[nEncodings++] = + * RfbProto.EncodingQualityLevel0 + options.jpegQuality; } if + * (options.requestCursorUpdates) { encodings[nEncodings++] = + * RfbProto.EncodingXCursor; encodings[nEncodings++] = + * RfbProto.EncodingRichCursor; if (!options.ignoreCursorUpdates) + * encodings[nEncodings++] = RfbProto.EncodingPointerPos; } + */ + + encodings[nEncodings++] = RfbProto.EncodingLastRect; + encodings[nEncodings++] = RfbProto.EncodingNewFBSize; + + boolean encodingsWereChanged = false; + if (nEncodings != nEncodingsSaved) { + encodingsWereChanged = true; + } else { + for (int i = 0; i < nEncodings; i++) { + if (encodings[i] != encodingsSaved[i]) { + encodingsWereChanged = true; + break; + } + } + } + + if (encodingsWereChanged) { + try { + // rfb.writeSetEncodings(encodings, nEncodings); + if (vc != null) { + vc.softCursorFree(); + } + } catch (Exception e) { + e.printStackTrace(); + } + encodingsSaved = encodings; + nEncodingsSaved = nEncodings; + } + } + + // + // setCutText() - send the given cut text to the RFB server. + // + + void setCutText(String text) { + try { + if (rfb != null && rfb.inNormalProtocol) { + rfb.writeClientCutText(text); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // + // Order change in session recording status. To stop recording, pass + // null in place of the fname argument. + // + + void setRecordingStatus(String fname) { + synchronized (recordingSync) { + sessionFileName = fname; + recordingStatusChanged = true; + } + } + + // + // Start or stop session recording. Returns true if this method call + // causes recording of a new session. + // + + boolean checkRecordingStatus() throws IOException { + synchronized (recordingSync) { + if (recordingStatusChanged) { + recordingStatusChanged = false; + if (sessionFileName != null) { + startRecording(); + return true; + } else { + stopRecording(); + } + } + } + return false; + } + + // + // Start session recording. + // + + protected void startRecording() throws IOException { + synchronized (recordingSync) { + if (!recordingActive) { + // Save settings to restore them after recording the session. + cursorUpdatesDef = options.choices[options.cursorUpdatesIndex] + .getSelectedItem(); + eightBitColorsDef = options.choices[options.eightBitColorsIndex] + .getSelectedItem(); + // Set options to values suitable for recording. + options.choices[options.cursorUpdatesIndex].select("Disable"); + options.choices[options.cursorUpdatesIndex].setEnabled(false); + options.setEncodings(); + options.choices[options.eightBitColorsIndex].select("No"); + options.choices[options.eightBitColorsIndex].setEnabled(false); + options.setColorFormat(); + } else { + rfb.closeSession(); + } + + System.out.println("Recording the session in " + sessionFileName); + rfb.startSession(sessionFileName); + recordingActive = true; + } + } + + // + // Stop session recording. + // + + protected void stopRecording() throws IOException { + synchronized (recordingSync) { + if (recordingActive) { + // Restore options. + options.choices[options.cursorUpdatesIndex] + .select(cursorUpdatesDef); + options.choices[options.cursorUpdatesIndex].setEnabled(true); + options.setEncodings(); + options.choices[options.eightBitColorsIndex] + .select(eightBitColorsDef); + options.choices[options.eightBitColorsIndex].setEnabled(true); + options.setColorFormat(); + + rfb.closeSession(); + System.out.println("Session recording stopped."); + } + sessionFileName = null; + recordingActive = false; + } + } + + // + // readParameters() - read parameters from the html source or from the + // command line. On the command line, the arguments are just a sequence of + // param_name/param_value pairs where the names and values correspond to + // those expected in the html applet tag source. + // + + void readParameters() { + /* + * host = readParameter("HOST", !inAnApplet); + * + * if (host == null) { host = getCodeBase().getHost(); if + * (host.equals("")) { fatalError("HOST parameter not specified"); } } + * + * port = readIntParameter("PORT", 5550); + */ + + // Read "ENCPASSWORD" or "PASSWORD" parameter if specified. + // readPasswordParameters(); + + String str; + if (inAnApplet) { + str = readParameter("Open New Window", false); + if (str != null && str.equalsIgnoreCase("Yes")) + inSeparateFrame = true; + } + + // "Show Controls" set to "No" disables button panel. + showControls = true; + str = readParameter("Show Controls", false); + if (str != null && str.equalsIgnoreCase("No")) + showControls = false; + + // "Offer Relogin" set to "No" disables "Login again" and "Close + // window" buttons under error messages in applet mode. + offerRelogin = true; + str = readParameter("Offer Relogin", false); + if (str != null && str.equalsIgnoreCase("No")) + offerRelogin = false; + + // Do we continue showing desktop on remote disconnect? + showOfflineDesktop = false; + str = readParameter("Show Offline Desktop", false); + if (str != null && str.equalsIgnoreCase("Yes")) + showOfflineDesktop = true; + + // Fine tuning options. + deferScreenUpdates = readIntParameter("Defer screen updates", 20); + deferCursorUpdates = readIntParameter("Defer cursor updates", 10); + deferUpdateRequests = readIntParameter("Defer update requests", 0); + + // Debugging options. + debugStatsExcludeUpdates = readIntParameter("DEBUG_XU", 0); + debugStatsMeasureUpdates = readIntParameter("DEBUG_CU", 0); + + // SocketFactory. + socketFactory = readParameter("SocketFactory", false); + } + + // + // Read password parameters. If an "ENCPASSWORD" parameter is set, + // then decrypt the password into the passwordParam string. Otherwise, + // try to read the "PASSWORD" parameter directly to passwordParam. + // +/* + private void readPasswordParameters() { + String encPasswordParam = readParameter("ENCPASSWORD", false); + + if (encPasswordParam == null) { + passwordParam = readParameter("PASSWORD", false); + } else { + // ENCPASSWORD is hexascii-encoded. Decode. + byte[] pw = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int len = encPasswordParam.length() / 2; + if (len > 8) + len = 8; + for (int i = 0; i < len; i++) { + String hex = encPasswordParam.substring(i * 2, i * 2 + 2); + Integer x = new Integer(Integer.parseInt(hex, 16)); + pw[i] = x.byteValue(); + } + // Decrypt the password. + byte[] key = { 23, 82, 107, 6, 35, 78, 88, 7 }; + DesCipher des = new DesCipher(key); + des.decrypt(pw, 0, pw, 0); + passwordParam = new String(pw); + + } + } +*/ + public String readParameter(String name, boolean required) { + 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; + } + + // + // disconnect() - close connection to server. + // + + synchronized public void disconnect() { + System.out.println("Disconnecting"); + + if (vc != null) { + double sec = (System.currentTimeMillis() - vc.statStartTime) / 1000.0; + double rate = Math.round(vc.statNumUpdates / sec * 100) / 100.0; + int nRealRects = vc.statNumPixelRects; + int nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects; + System.out.println("Updates received: " + vc.statNumUpdates + " (" + + nRealRects + " rectangles + " + nPseudoRects + + " pseudo), " + rate + " updates/sec"); + int numRectsOther = nRealRects - vc.statNumRectsTight + - vc.statNumRectsZRLE - vc.statNumRectsHextile + - vc.statNumRectsRaw - vc.statNumRectsCopy; + System.out.println("Rectangles:" + " Tight=" + vc.statNumRectsTight + + "(JPEG=" + vc.statNumRectsTightJPEG + ") ZRLE=" + + vc.statNumRectsZRLE + " Hextile=" + + vc.statNumRectsHextile + " Raw=" + vc.statNumRectsRaw + + " CopyRect=" + vc.statNumRectsCopy + " other=" + + numRectsOther); + + int raw = vc.statNumBytesDecoded; + int compressed = vc.statNumBytesEncoded; + if (compressed > 0) { + double ratio = Math.round((double) raw / compressed * 1000) / 1000.0; + System.out.println("Pixel data: " + vc.statNumBytesDecoded + + " bytes, " + vc.statNumBytesEncoded + + " compressed, ratio " + ratio); + } + } + + if (rfb != null && !rfb.closed()) + rfb.close(); + // options.dispose(); + clipboard.dispose(); + if (rec != null) + rec.dispose(); + + System.exit(0); + + } + + // + // fatalError() - print out a fatal error message. + // FIXME: Do we really need two versions of the fatalError() method? + // + + synchronized public void fatalError(String str) { + System.out.println(str); + System.exit(1); + } + + synchronized public void fatalError(String str, Exception e) { + + if (rfb != null && rfb.closed()) { + // Not necessary to show error message if the error was caused + // by I/O problems after the rfb.close() method call. + System.out.println("RFB thread finished"); + return; + } + + System.out.println(str); + e.printStackTrace(); + + if (rfb != null) + rfb.close(); + + System.exit(1); + + } + + // + // Show message text and optionally "Relogin" and "Close" buttons. + // + + void showMessage(String msg) { + vncContainer.removeAll(); + + Label errLabel = new Label(msg, Label.CENTER); + errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); + + if (offerRelogin) { + /* + * Panel gridPanel = new Panel(new GridLayout(0, 1)); Panel + * outerPanel = new Panel(new FlowLayout(FlowLayout.LEFT)); + * outerPanel.add(gridPanel); vncContainer.setLayout(new + * FlowLayout(FlowLayout.LEFT, 30, 16)); + * vncContainer.add(outerPanel); Panel textPanel = new Panel(new + * FlowLayout(FlowLayout.CENTER)); textPanel.add(errLabel); + * gridPanel.add(textPanel); gridPanel.add(new ReloginPanel(this)); + */ + } else { + /* + * vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30)); + * vncContainer.add(errLabel); + */ + } + + } + + // + // Stop the applet. + // Main applet thread will terminate on first exception + // after seeing that rfbThread has been set to null. + // + + public void stop() { + System.out.println("Stopping applet"); + rfbThread = null; + } + + // + // This method is called before the applet is destroyed. + // + + public void destroy() { + System.out.println("Destroying applet"); + + vncContainer.removeAll(); + // options.dispose(); + clipboard.dispose(); + if (rec != null) + rec.dispose(); + if (rfb != null && !rfb.closed()) + rfb.close(); + } + + // + // Start/stop receiving mouse events. + // + + public void enableInput(boolean enable) { + vc.enableInput(enable); + } + + // + // Close application properly on window close event. + // + + public void windowClosing(WindowEvent evt) { + System.out.println("Closing window"); + if (rfb != null) + disconnect(); + + vncContainer.setVisible(false); + + } + + // + // Ignore window events we're not interested in. + // + + public void windowActivated(WindowEvent evt) { + } + + public void windowDeactivated(WindowEvent evt) { + } + + public void windowOpened(WindowEvent evt) { + } + + public void windowClosed(WindowEvent evt) { + } + + public void windowIconified(WindowEvent evt) { + } + + public void windowDeiconified(WindowEvent evt) { + } + + public void setClientSocket(Socket sock) { + clientSocket = sock; + } + + public void start() { + + } + public void close() { + rfb.close(); + } + public void getParentName() { + if (echoValue == null) { + + if (clientSocket == null) { + String pHost; + if (mainArgs.length > 0) + pHost = mainArgs[0]; + else + pHost = "localhost"; +// echo = new EchoClient(pHost, this); + echoValue = new EchoClient(pHost, this, echoPort); + echoValue.openport(); + + echoValue = echoValue.requestHostName("1"); + } else { + echoValue = new EchoClient(); + echoValue = echoValue.Interruption(clientSocket); + } + } + + // proxyからの返信で接続先を決定する + host = echoValue.responseLine; + parent = echoValue.parent; + if (echoValue.treenum != null) { + treenum = echoValue.treenum; + } else { + treenum = echoValue.treenum; + } + if (echoValue.leaderflag != null) { + leaderflag = echoValue.leaderflag; + } else { + leaderflag = echoValue.leaderflag; + } + System.out.println("Parent =" + parent); + System.out.println("mynumber =" + treenum); + System.out.println("connect host =" + host); + System.out.println("leaderflag(boolean) = " + leaderflag); + +// port = 5999; + + } + public void setEchoValue(EchoClient value) { + this.echoValue = value; + } + + + int castByteInt(byte[] b) { + ByteBuffer bb = ByteBuffer.wrap(b); + int echoValue = bb.getInt(); + return echoValue; + } + public Image getScreenImage() { + return vc.rawPixelsImage; + } + + public void writeScreenData(byte[] b, String imageFormat) { + try{ + vc.drawBufferedImage(b); + }catch(IOException e){ + e.printStackTrace(); + } + } + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/CuiVncCanvas.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/CuiVncCanvas.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,1957 @@ +package treeVnc; +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import java.io.*; +import java.util.zip.*; + + +import javax.imageio.ImageIO; + +// +//VncCanvas is a subclass of Canvas which draws a VNC desktop on it. +// + +class CuiVncCanvas extends Canvas implements KeyListener, MouseListener, + MouseMotionListener { + + /** + * + */ + private static final long serialVersionUID = 1L; + CuiMyVncClient viewer; + MyRfbProtoClient rfb; + ColorModel cm8, cm24; + Color[] colors; + int bytesPixel; + + int maxWidth = 0, maxHeight = 0; + int scalingFactor; + int scaledWidth, scaledHeight; + +// Image memImage; + BufferedImage memImage; + Graphics memGraphics; + + Image rawPixelsImage; +// BufferedImage rawPixelsImage; + BufferedImage bimg; + + MemoryImageSource pixelsSource; + byte[] pixels8; + int[] pixels24; + + // Update statistics. + long statStartTime; // time on first framebufferUpdateRequest + int statNumUpdates; // counter for FramebufferUpdate messages + int statNumTotalRects; // rectangles in FramebufferUpdate messages + int statNumPixelRects; // the same, but excluding pseudo-rectangles + int statNumRectsTight; // Tight-encoded rectangles (including JPEG) + int statNumRectsTightJPEG; // JPEG-compressed Tight-encoded rectangles + int statNumRectsZRLE; // ZRLE-encoded rectangles + int statNumRectsHextile; // Hextile-encoded rectangles + int statNumRectsRaw; // Raw-encoded rectangles + int statNumRectsCopy; // CopyRect rectangles + int statNumBytesEncoded; // number of bytes in updates, as received + int statNumBytesDecoded; // number of bytes, as if Raw encoding was used + + // ZRLE encoder's data. + byte[] zrleBuf; + int zrleBufLen = 0; + byte[] zrleTilePixels8; + int[] zrleTilePixels24; + ZlibInStream zrleInStream; + boolean zrleRecWarningShown = false; + + // 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 inputEnabled; + //private int b = 0; + + + + // + // The constructors. + // + + public CuiVncCanvas(CuiMyVncClient v, int maxWidth_, int maxHeight_) + throws IOException { + + viewer = v; + maxWidth = maxWidth_; + maxHeight = maxHeight_; + + 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(); + + inputEnabled = false; + // Keyboard listener is enabled even in view-only mode, to catch + // 'r' or 'R' key presses used to request screen update. + addKeyListener(this); + } + + public CuiVncCanvas(CuiMyVncClient v) throws IOException { + this(v, 0, 0); + } + + // + // Callback methods to determine geometry of our Component. + // + + public Dimension getPreferredSize() { + return new Dimension(scaledWidth, scaledHeight); + } + + public Dimension getMinimumSize() { + return new Dimension(scaledWidth, scaledHeight); + } + + public Dimension getMaximumSize() { + return new Dimension(scaledWidth, scaledHeight); + } + + // + // All painting is performed here. + // + + public void update(Graphics g) { + paint(g); + } + + public void paint(Graphics g) { + synchronized (memImage) { + if (rfb.framebufferWidth == scaledWidth) { + g.drawImage(memImage, 0, 0, null); + } else { + paintScaledFrameBuffer(g); + } + } + if (showSoftCursor) { + int x0 = cursorX - hotX, y0 = cursorY - hotY; + Rectangle r = new Rectangle(x0, y0, cursorWidth, cursorHeight); + if (r.intersects(g.getClipBounds())) { + g.drawImage(softCursor, x0, y0, null); + } + } + } + + public void paintScaledFrameBuffer(Graphics g) { + g.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null); + } + + // + // 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 mouse events. Keyboard events are received + // even in view-only mode, because we want to map the 'r' key to the + // screen refreshing function. + // + + public synchronized void enableInput(boolean enable) { + if (enable && !inputEnabled) { + inputEnabled = true; + addMouseListener(this); + addMouseMotionListener(this); + if (viewer.showControls) { + viewer.buttonPanel.enableRemoteAccessControls(true); + } + createSoftCursor(); // scaled cursor + } else if (!enable && inputEnabled) { + inputEnabled = false; + removeMouseListener(this); + removeMouseMotionListener(this); + if (viewer.showControls) { + viewer.buttonPanel.enableRemoteAccessControls(false); + } + createSoftCursor(); // non-scaled cursor + } + } + + public void setPixelFormat() throws IOException { +/* + if (viewer.options.eightBitColors) { + rfb.writeSetPixelFormat(8, 8, false, true, 7, 7, 3, 0, 3, 6); + bytesPixel = 1; + } else { + rfb.writeSetPixelFormat(32, 24, false, true, 255, 255, 255, 16, 8, + 0); + bytesPixel = 4; + } +*/ + updateFramebufferSize(); + } + + void updateFramebufferSize() { + + // Useful shortcuts. + int fbWidth = rfb.framebufferWidth; + int fbHeight = rfb.framebufferHeight; + + // Calculate scaling factor for auto scaling. + if (maxWidth > 0 && maxHeight > 0) { + int f1 = maxWidth * 100 / fbWidth; + int f2 = maxHeight * 100 / fbHeight; + scalingFactor = Math.min(f1, f2); + if (scalingFactor > 100) + scalingFactor = 100; + System.out.println("Scaling desktop at " + scalingFactor + "%"); + } + + // Update scaled framebuffer geometry. + scaledWidth = (fbWidth * scalingFactor + 50) / 100; + scaledHeight = (fbHeight * scalingFactor + 50) / 100; + + // Create new off-screen image either if it does not exist, or if + // its geometry should be changed. It's not necessary to replace + // existing image if only pixel format should be changed. +/* + if (memImage == null) { + memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); + memGraphics = memImage.getGraphics(); + } else if (memImage.getWidth(null) != fbWidth + || memImage.getHeight(null) != fbHeight) { + synchronized (memImage) { + memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); + memGraphics = memImage.getGraphics(); + } + } +*/ + memImage = new BufferedImage(rfb.framebufferWidth, rfb.framebufferHeight, BufferedImage.TYPE_INT_RGB ); + 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); + + zrleTilePixels24 = null; + zrleTilePixels8 = new byte[64 * 64]; + + } else { + + pixels8 = null; + pixels24 = new int[fbWidth * fbHeight]; + + pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm24, + pixels24, 0, fbWidth); + + zrleTilePixels8 = null; + zrleTilePixels24 = new int[64 * 64]; + + } + pixelsSource.setAnimated(true); + rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource); +// rawPixelsImage = (BufferedImage) Toolkit.getDefaultToolkit().createImage(pixelsSource); + + } + + void resizeDesktopFrame() { + setSize(scaledWidth, scaledHeight); + + // FIXME: Find a better way to determine correct size of a + // ScrollPane. -- const + Insets insets = viewer.desktopScrollPane.getInsets(); + viewer.desktopScrollPane.setSize( + scaledWidth + 2 * Math.min(insets.left, insets.right), + scaledHeight + 2 * Math.min(insets.top, insets.bottom)); + + viewer.vncFrame.pack(); + + // Try to limit the frame size to the screen size. + + Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize(); + Dimension frameSize = viewer.vncFrame.getSize(); + Dimension newSize = frameSize; + + // Reduce Screen Size by 30 pixels in each direction; + // This is a (poor) attempt to account for + // 1) Menu bar on Macintosh (should really also account for + // Dock on OSX). Usually 22px on top of screen. + // 2) Taxkbar on Windows (usually about 28 px on bottom) + // 3) Other obstructions. + + screenSize.height -= 30; + screenSize.width -= 30; + + boolean needToResizeFrame = false; + if (frameSize.height > screenSize.height) { + newSize.height = screenSize.height; + needToResizeFrame = true; + } + if (frameSize.width > screenSize.width) { + newSize.width = screenSize.width; + needToResizeFrame = true; + } + if (needToResizeFrame) { + viewer.vncFrame.setSize(newSize); + } + + viewer.desktopScrollPane.doLayout(); + } + + // + // processNormalProtocol() - executed by the rfbThread to deal with the + // RFB socket. + // + + public void processNormalProtocol() throws Exception { + + // Start/stop session recording if necessary. + viewer.checkRecordingStatus(); + + rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, + rfb.framebufferHeight, false); + + resetStats(); + boolean statsRestarted = false; + + // + // main dispatch loop + // + + long count = 0; + while (true) { +// System.out.println("\ncount=" + count); + + count++; + + /** + * read Data from parents and send Data to Client. + */ + + rfb.sendDataToClient(); + + long numBytesRead = rfb.getNumBytesRead(); + + // Read message type from the server. + int msgType = rfb.readServerMessageType(); + + // Process the message depending on its type. + switch (msgType) { + case MyRfbProtoClient.SpeedCheckMillis: + rfb.readSpeedCheck(); + + break; + case MyRfbProtoClient.WriteJpegData: + byte[] b = rfb.readJpegData(); + drawBufferedImage(b); + break; + case RfbProto.FramebufferUpdate: + + if (statNumUpdates == viewer.debugStatsExcludeUpdates + && !statsRestarted) { + resetStats(); + statsRestarted = true; + } else if (statNumUpdates == viewer.debugStatsMeasureUpdates + && statsRestarted) { + viewer.disconnect(); + } + + rfb.readFramebufferUpdate(); + statNumUpdates++; + + boolean cursorPosReceived = false; + + for (int i = 0; i < rfb.updateNRects; i++) { + + rfb.readFramebufferUpdateRectHdr(); + statNumTotalRects++; + int rx = rfb.updateRectX, ry = rfb.updateRectY; + int rw = rfb.updateRectW, rh = rfb.updateRectH; + + if (rfb.updateRectEncoding == rfb.EncodingLastRect) + break; + + if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) { + rfb.setFramebufferSize(rw, rh); + updateFramebufferSize(); + break; + } + + if (rfb.updateRectEncoding == rfb.EncodingXCursor + || rfb.updateRectEncoding == rfb.EncodingRichCursor) { + handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry, + rw, rh); + continue; + } + + if (rfb.updateRectEncoding == rfb.EncodingPointerPos) { + softCursorMove(rx, ry); + cursorPosReceived = true; + continue; + } + + long numBytesReadBefore = rfb.getNumBytesRead(); + + rfb.startTiming(); + + switch (rfb.updateRectEncoding) { + case RfbProto.EncodingRaw: + statNumRectsRaw++; + handleRawRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingCopyRect: + statNumRectsCopy++; + handleCopyRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingRRE: + handleRRERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingCoRRE: + handleCoRRERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingHextile: + statNumRectsHextile++; + handleHextileRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingZRLE: + statNumRectsZRLE++; + handleZRLERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingZRLEE: + statNumRectsZRLE++; + handleZRLERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingZlib: + handleZlibRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingTight: + statNumRectsTight++; + handleTightRect(rx, ry, rw, rh); + break; + default: + throw new Exception("Unknown RFB rectangle encoding " + + rfb.updateRectEncoding); + } + + rfb.stopTiming(); + + long kbitsPerSecond = rfb.kbitsPerSecond(); +// System.out.println("kbitsPerSecond = " + kbitsPerSecond); + + statNumPixelRects++; + statNumBytesDecoded += rw * rh * bytesPixel; + statNumBytesEncoded += (int) (rfb.getNumBytesRead() - numBytesReadBefore); + } + + boolean fullUpdateNeeded = false; + + // Start/stop session recording if necessary. Request full + // update if a new session file was opened. + if (viewer.checkRecordingStatus()) + fullUpdateNeeded = true; + + // Defer framebuffer update request if necessary. But wake up + // immediately on keyboard or mouse event. Also, don't sleep + // if there is some data to receive, or if the last update + // included a PointerPos message. + if (viewer.deferUpdateRequests > 0 && rfb.available() == 0 + && !cursorPosReceived) { + synchronized (rfb) { + try { + rfb.wait(viewer.deferUpdateRequests); + } catch (InterruptedException e) { + } + } + } + + viewer.autoSelectEncodings(); + + // Before requesting framebuffer update, check if the pixel + // format should be changed. +/* + if (viewer.options.eightBitColors != (bytesPixel == 1)) { + // Pixel format should be changed. + setPixelFormat(); + fullUpdateNeeded = true; + } +*/ + // Request framebuffer update if needed. + int w = rfb.framebufferWidth; + int h = rfb.framebufferHeight; + rfb.writeFramebufferUpdateRequest(0, 0, w, h, !fullUpdateNeeded); + + break; + + case RfbProto.SetColourMapEntries: + throw new Exception("Can't handle SetColourMapEntries message"); + + case RfbProto.Bell: + Toolkit.getDefaultToolkit().beep(); + break; + + case RfbProto.ServerCutText: + String s = rfb.readServerCutText(); + viewer.clipboard.setCutText(s); + break; + + default: + throw new Exception("Unknown RFB message type " + msgType); + } + + int bufSize = (int)(rfb.getNumBytesRead() - numBytesRead); +// System.out.println("bufSize="+bufSize); +// rfb.bufResetSend(bufSize); + + + + if(rfb.createBimgFlag){ +// bimg = createBufferedImage(rawPixelsImage); + bimg = createBufferedImage(memImage); + //bimg(BufferedImage) -> rfb.pngBytes(byte[]) + rfb.createPngBytes(bimg); + rfb.sendPngImage(); + rfb.createBimgFlag = false; + } + + +/* + boolean result = false; + try{ + result = ImageIO.write(bimg, "png", new File("sample.png")); + }catch(Exception e){ + e.printStackTrace(); + result = false; + } +*/ + } + } + + // + // Handle a raw rectangle. The second form with paint==false is used + // by the Hextile decoder for raw-encoded tiles. + // + + void handleRawRect(int x, int y, int w, int h) throws IOException { + handleRawRect(x, y, w, h, true); + } + + void handleRawRect(int x, int y, int w, int h, boolean paint) + throws IOException { + + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w); + if (rfb.rec != null) { + rfb.rec.write(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.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(buf); + } + + offset = dy * rfb.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 | + (buf[i * 4 + 1] & 0xFF) << 8 | + (buf[i * 4] & 0xFF); + } + + } + } + + handleUpdatedPixels(x, y, w, h); +// if (paint) scheduleRepaint(x, y, w, h); + + } + + // + // Handle a CopyRect rectangle. + // + + void handleCopyRect(int x, int y, int w, int h) throws IOException { + + rfb.readCopyRect(); + memGraphics.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY, w, h, x + - rfb.copyRectSrcX, y - rfb.copyRectSrcY); + + scheduleRepaint(x, y, w, h); + } + + // + // Handle an RRE-encoded rectangle. + // + + void handleRRERect(int x, int y, int w, int h) throws IOException { + + int nSubrects = rfb.readU32(); + + byte[] bg_buf = new byte[bytesPixel]; + rfb.readFully(bg_buf); + Color pixel; + if (bytesPixel == 1) { + pixel = colors[bg_buf[0] & 0xFF]; + } else { + pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, + bg_buf[0] & 0xFF); + } + memGraphics.setColor(pixel); + memGraphics.fillRect(x, y, w, h); + + byte[] buf = new byte[nSubrects * (bytesPixel + 8)]; + rfb.readFully(buf); + DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf)); + + if (rfb.rec != null) { + rfb.rec.writeIntBE(nSubrects); + rfb.rec.write(bg_buf); + rfb.rec.write(buf); + } + + int sx, sy, sw, sh; + + for (int j = 0; j < nSubrects; j++) { + if (bytesPixel == 1) { + pixel = colors[ds.readUnsignedByte()]; + } else { + ds.skip(4); + pixel = new Color(buf[j * 12 + 2] & 0xFF, + buf[j * 12 + 1] & 0xFF, buf[j * 12] & 0xFF); + } + sx = x + ds.readUnsignedShort(); + sy = y + ds.readUnsignedShort(); + sw = ds.readUnsignedShort(); + sh = ds.readUnsignedShort(); + + memGraphics.setColor(pixel); + memGraphics.fillRect(sx, sy, sw, sh); + } + + scheduleRepaint(x, y, w, h); + } + + // + // Handle a CoRRE-encoded rectangle. + // + + void handleCoRRERect(int x, int y, int w, int h) throws IOException { + int nSubrects = rfb.readU32(); + + byte[] bg_buf = new byte[bytesPixel]; + rfb.readFully(bg_buf); + Color pixel; + if (bytesPixel == 1) { + pixel = colors[bg_buf[0] & 0xFF]; + } else { + pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, + bg_buf[0] & 0xFF); + } + memGraphics.setColor(pixel); + memGraphics.fillRect(x, y, w, h); + + byte[] buf = new byte[nSubrects * (bytesPixel + 4)]; + rfb.readFully(buf); + + if (rfb.rec != null) { + rfb.rec.writeIntBE(nSubrects); + rfb.rec.write(bg_buf); + rfb.rec.write(buf); + } + + int sx, sy, sw, sh; + int i = 0; + + for (int j = 0; j < nSubrects; j++) { + if (bytesPixel == 1) { + pixel = colors[buf[i++] & 0xFF]; + } else { + pixel = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF, + buf[i] & 0xFF); + i += 4; + } + sx = x + (buf[i++] & 0xFF); + sy = y + (buf[i++] & 0xFF); + sw = buf[i++] & 0xFF; + sh = buf[i++] & 0xFF; + + memGraphics.setColor(pixel); + memGraphics.fillRect(sx, sy, sw, sh); + } + + scheduleRepaint(x, y, w, h); + } + + // + // Handle a Hextile-encoded rectangle. + // + + // These colors should be kept between handleHextileSubrect() calls. + private Color hextile_bg, hextile_fg; + + void handleHextileRect(int x, int y, int w, int h) throws IOException { + + hextile_bg = new Color(0); + hextile_fg = new Color(0); + + for (int ty = y; ty < y + h; ty += 16) { + int th = 16; + if (y + h - ty < 16) + th = y + h - ty; + + for (int tx = x; tx < x + w; tx += 16) { + int tw = 16; + if (x + w - tx < 16) + tw = x + w - tx; + + handleHextileSubrect(tx, ty, tw, th); + } + + // Finished with a row of tiles, now let's show it. + scheduleRepaint(x, y, w, h); + } + } + + // + // Handle one tile in the Hextile-encoded data. + // + + void handleHextileSubrect(int tx, int ty, int tw, int th) + throws IOException { + + int subencoding = rfb.readU8(); + if (rfb.rec != null) { + rfb.rec.writeByte(subencoding); + } + + // Is it a raw-encoded sub-rectangle? + if ((subencoding & rfb.HextileRaw) != 0) { + handleRawRect(tx, ty, tw, th, false); + return; + } + + // Read and draw the background if specified. + byte[] cbuf = new byte[bytesPixel]; + if ((subencoding & rfb.HextileBackgroundSpecified) != 0) { + rfb.readFully(cbuf); + if (bytesPixel == 1) { + hextile_bg = colors[cbuf[0] & 0xFF]; + } else { + hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, + cbuf[0] & 0xFF); + } + if (rfb.rec != null) { + rfb.rec.write(cbuf); + } + } + memGraphics.setColor(hextile_bg); + memGraphics.fillRect(tx, ty, tw, th); + + // Read the foreground color if specified. + if ((subencoding & rfb.HextileForegroundSpecified) != 0) { + rfb.readFully(cbuf); + if (bytesPixel == 1) { + hextile_fg = colors[cbuf[0] & 0xFF]; + } else { + hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, + cbuf[0] & 0xFF); + } + if (rfb.rec != null) { + rfb.rec.write(cbuf); + } + } + + // Done with this tile if there is no sub-rectangles. + if ((subencoding & rfb.HextileAnySubrects) == 0) + return; + + int nSubrects = rfb.readU8(); + int bufsize = nSubrects * 2; + if ((subencoding & rfb.HextileSubrectsColoured) != 0) { + bufsize += nSubrects * bytesPixel; + } + byte[] buf = new byte[bufsize]; + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.writeByte(nSubrects); + rfb.rec.write(buf); + } + + int b1, b2, sx, sy, sw, sh; + int i = 0; + + if ((subencoding & rfb.HextileSubrectsColoured) == 0) { + + // Sub-rectangles are all of the same color. + memGraphics.setColor(hextile_fg); + for (int j = 0; j < nSubrects; j++) { + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.fillRect(sx, sy, sw, sh); + } + } else if (bytesPixel == 1) { + + // BGR233 (8-bit color) version for colored sub-rectangles. + for (int j = 0; j < nSubrects; j++) { + hextile_fg = colors[buf[i++] & 0xFF]; + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.setColor(hextile_fg); + memGraphics.fillRect(sx, sy, sw, sh); + } + + } else { + + // Full-color (24-bit) version for colored sub-rectangles. + for (int j = 0; j < nSubrects; j++) { + hextile_fg = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF, + buf[i] & 0xFF); + i += 4; + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.setColor(hextile_fg); + memGraphics.fillRect(sx, sy, sw, sh); + } + + } + } + + // + // Handle a ZRLE-encoded rectangle. + // + // FIXME: Currently, session recording is not fully supported for ZRLE. + // + + void handleZRLERect(int x, int y, int w, int h) throws Exception { + if (rfb.updateRectEncoding==RfbProto.EncodingZRLEE) zrleInStream = null; + + if (zrleInStream == null) + zrleInStream = new ZlibInStream(); + + int nBytes = rfb.readU32(); + if (nBytes > 64 * 1024 * 1024) + throw new Exception("ZRLE decoder: illegal compressed data size"); + + if (zrleBuf == null || zrleBufLen < nBytes) { + zrleBufLen = nBytes + 4096; + zrleBuf = new byte[zrleBufLen]; + } + + // FIXME: Do not wait for all the data before decompression. + rfb.readFully(zrleBuf, 0, nBytes); + + if (rfb.rec != null) { + if (rfb.recordFromBeginning) { + rfb.rec.writeIntBE(nBytes); + rfb.rec.write(zrleBuf, 0, nBytes); + } else if (!zrleRecWarningShown) { + System.out.println("Warning: ZRLE session can be recorded" + + " only from the beginning"); + System.out.println("Warning: Recorded file may be corrupted"); + zrleRecWarningShown = true; + } + + } + + zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes); + + for (int ty = y; ty < y + h; ty += 64) { + + int th = Math.min(y + h - ty, 64); + + for (int tx = x; tx < x + w; tx += 64) { + + int tw = Math.min(x + w - tx, 64); + + int mode = zrleInStream.readU8(); + boolean rle = (mode & 128) != 0; + int palSize = mode & 127; + int[] palette = new int[128]; + + readZrlePalette(palette, palSize); + + if (palSize == 1) { + int pix = palette[0]; + Color c = (bytesPixel == 1) ? colors[pix] : new Color( + 0xFF000000 | pix); + memGraphics.setColor(c); + memGraphics.fillRect(tx, ty, tw, th); + continue; + } + + if (!rle) { + if (palSize == 0) { + readZrleRawPixels(tw, th); + } else { + readZrlePackedPixels(tw, th, palette, palSize); + } + } else { + if (palSize == 0) { + readZrlePlainRLEPixels(tw, th); + } else { + readZrlePackedRLEPixels(tw, th, palette); + } + } + handleUpdatedZrleTile(tx, ty, tw, th); + } + } + + zrleInStream.reset(); + + scheduleRepaint(x, y, w, h); + } + + int readPixel(InStream is) throws Exception { + int pix; + + if (bytesPixel == 1) { + + pix = is.readU8(); + } else { + int p1 = is.readU8(); + int p2 = is.readU8(); + int p3 = is.readU8(); + pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF); + } + return pix; + } + + void readPixels(InStream is, int[] dst, int count) throws Exception { + int pix; + if (bytesPixel == 1) { + byte[] buf = new byte[count]; + is.readBytes(buf, 0, count); + for (int i = 0; i < count; i++) { + dst[i] = (int) buf[i] & 0xFF; + } + } else { + byte[] buf = new byte[count * 3]; + is.readBytes(buf, 0, count * 3); + for (int i = 0; i < count; i++) { + dst[i] = ((buf[i * 3 + 2] & 0xFF) << 16 + | (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3] & 0xFF)); + } + } + } + + void readZrlePalette(int[] palette, int palSize) throws Exception { + readPixels(zrleInStream, palette, palSize); + } + + void readZrleRawPixels(int tw, int th) throws Exception { + if (bytesPixel == 1) { + zrleInStream.readBytes(zrleTilePixels8, 0, tw * th); + } else { + readPixels(zrleInStream, zrleTilePixels24, tw * th); // / + } + } + + void readZrlePackedPixels(int tw, int th, int[] palette, int palSize) + throws Exception { + + int bppp = ((palSize > 16) ? 8 : ((palSize > 4) ? 4 + : ((palSize > 2) ? 2 : 1))); + int ptr = 0; + + for (int i = 0; i < th; i++) { + int eol = ptr + tw; + int b = 0; + int nbits = 0; + + while (ptr < eol) { + if (nbits == 0) { + b = zrleInStream.readU8(); + nbits = 8; + } + nbits -= bppp; + int index = (b >> nbits) & ((1 << bppp) - 1) & 127; + if (bytesPixel == 1) { + zrleTilePixels8[ptr++] = (byte) palette[index]; + } else { + zrleTilePixels24[ptr++] = palette[index]; + } + } + } + } + + void readZrlePlainRLEPixels(int tw, int th) throws Exception { + int ptr = 0; + int end = ptr + tw * th; + while (ptr < end) { + int pix = readPixel(zrleInStream); + int len = 1; + int b; + do { + b = zrleInStream.readU8(); + len += b; + } while (b == 255); + + if (!(len <= end - ptr)) + throw new Exception("ZRLE decoder: assertion failed" + + " (len <= end-ptr)"); + + if (bytesPixel == 1) { + while (len-- > 0) + zrleTilePixels8[ptr++] = (byte) pix; + } else { + while (len-- > 0) + zrleTilePixels24[ptr++] = pix; + } + } + } + + void readZrlePackedRLEPixels(int tw, int th, int[] palette) + throws Exception { + + int ptr = 0; + int end = ptr + tw * th; + while (ptr < end) { + int index = zrleInStream.readU8(); + int len = 1; + if ((index & 128) != 0) { + int b; + do { + b = zrleInStream.readU8(); + len += b; + } while (b == 255); + + if (!(len <= end - ptr)) + throw new Exception("ZRLE decoder: assertion failed" + + " (len <= end - ptr)"); + } + + index &= 127; + int pix = palette[index]; + + if (bytesPixel == 1) { + while (len-- > 0) + zrleTilePixels8[ptr++] = (byte) pix; + } else { + while (len-- > 0) + zrleTilePixels24[ptr++] = pix; + } + } + } + + // + // Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update. + // + + void handleUpdatedZrleTile(int x, int y, int w, int h) { + Object src, dst; + if (bytesPixel == 1) { + src = zrleTilePixels8; + dst = pixels8; + } else { + src = zrleTilePixels24; + dst = pixels24; + } + int offsetSrc = 0; + int offsetDst = (y * rfb.framebufferWidth + x); + for (int j = 0; j < h; j++) { + System.arraycopy(src, offsetSrc, dst, offsetDst, w); + offsetSrc += w; + offsetDst += rfb.framebufferWidth; + } + handleUpdatedPixels(x, y, w, h); + } + + // + // Handle a Zlib-encoded rectangle. + // + + void handleZlibRect(int x, int y, int w, int h) throws Exception { + + int nBytes = rfb.readU32(); + + if (zlibBuf == null || zlibBufLen < nBytes) { + zlibBufLen = nBytes * 2; + zlibBuf = new byte[zlibBufLen]; + } + + rfb.readFully(zlibBuf, 0, nBytes); + + if (rfb.rec != null && rfb.recordFromBeginning) { + rfb.rec.writeIntBE(nBytes); + rfb.rec.write(zlibBuf, 0, nBytes); + } + + if (zlibInflater == null) { + zlibInflater = new Inflater(); + } + zlibInflater.setInput(zlibBuf, 0, nBytes); + + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + zlibInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w); + if (rfb.rec != null && !rfb.recordFromBeginning) + rfb.rec.write(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 + 2] & 0xFF) << 16 + | (buf[i * 4 + 1] & 0xFF) << 8 + | (buf[i * 4] & 0xFF); + } + if (rfb.rec != null && !rfb.recordFromBeginning) + rfb.rec.write(buf); + } + } + + handleUpdatedPixels(x, y, w, h); + scheduleRepaint(x, y, w, h); + } + + // + // Handle a Tight-encoded rectangle. + // + + void handleTightRect(int x, int y, int w, int h) throws Exception { + + int comp_ctl = rfb.readU8(); + if (rfb.rec != null) { + if (rfb.recordFromBeginning || comp_ctl == (rfb.TightFill << 4) + || comp_ctl == (rfb.TightJpeg << 4)) { + // Send data exactly as received. + rfb.rec.writeByte(comp_ctl); + } else { + // Tell the decoder to flush each of the four zlib streams. + rfb.rec.writeByte(comp_ctl | 0x0F); + } + } + + // 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 Exception("Incorrect tight subencoding: " + comp_ctl); + } + + // Handle solid-color rectangles. + if (comp_ctl == rfb.TightFill) { + + if (bytesPixel == 1) { + int idx = rfb.readU8(); + memGraphics.setColor(colors[idx]); + if (rfb.rec != null) { + rfb.rec.writeByte(idx); + } + } else { + byte[] buf = new byte[3]; + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(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) { + + statNumRectsTightJPEG++; + + // Read JPEG data. + byte[] jpegData = new byte[rfb.readCompactLen()]; + rfb.readFully(jpegData); + if (rfb.rec != null) { + if (!rfb.recordFromBeginning) { + rfb.recordCompactLen(jpegData.length); + } + rfb.rec.write(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 Exception("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.readU8(); + if (rfb.rec != null) { + rfb.rec.writeByte(filter_id); + } + if (filter_id == rfb.TightFilterPalette) { + numColors = rfb.readU8() + 1; + if (rfb.rec != null) { + rfb.rec.writeByte(numColors - 1); + } + if (bytesPixel == 1) { + if (numColors != 2) { + throw new Exception("Incorrect tight palette size: " + + numColors); + } + rfb.readFully(palette8); + if (rfb.rec != null) { + rfb.rec.write(palette8); + } + } else { + byte[] buf = new byte[numColors * 3]; + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(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 Exception("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.readFully(indexedData); + if (rfb.rec != null) { + rfb.rec.write(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.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(buf); + } + decodeGradientData(x, y, w, h, buf); + } else { + // Raw truecolor data. + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w); + if (rfb.rec != null) { + rfb.rec.write(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.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(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.readFully(zlibData); + if (rfb.rec != null && rfb.recordFromBeginning) { + rfb.rec.write(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); + byte[] buf = new byte[dataSize]; + myInflater.inflate(buf); + if (rfb.rec != null && !rfb.recordFromBeginning) { + rfb.recordCompressedData(buf); + } + + if (numColors != 0) { + // Indexed colors. + if (numColors == 2) { + // Two colors. + if (bytesPixel == 1) { + decodeMonoData(x, y, w, h, buf, palette8); + } else { + decodeMonoData(x, y, w, h, buf, 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[buf[i++] & 0xFF]; + } + } + } + } else if (useGradient) { + // Compressed "Gradient"-filtered data (assuming bytesPixel == + // 4). + decodeGradientData(x, y, w, h, buf); + } else { + // Compressed truecolor data. + if (bytesPixel == 1) { + int destOffset = y * rfb.framebufferWidth + x; + for (int dy = 0; dy < h; dy++) { + System.arraycopy(buf, dy * w, pixels8, destOffset, w); + destOffset += rfb.framebufferWidth; + } + } else { + int srcOffset = 0; + int destOffset, i; + for (int dy = 0; dy < h; dy++) { + myInflater.inflate(buf); + destOffset = (y + dy) * rfb.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16 + | (buf[srcOffset + 1] & 0xFF) << 8 + | (buf[srcOffset + 2] & 0xFF); + srcOffset += 3; + } + } + } + } + } + + 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) { + + 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) { + + 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) { + + 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. + if (rfb.framebufferWidth == scaledWidth) { + repaint(viewer.deferScreenUpdates, x, y, w, h); + } else { + int sx = x * scalingFactor / 100; + int sy = y * scalingFactor / 100; + int sw = ((x + w) * scalingFactor + 49) / 100 - sx + 1; + int sh = ((y + h) * scalingFactor + 49) / 100 - sy + 1; + repaint(viewer.deferScreenUpdates, sx, sy, sw, sh); + } + } + + // + // Handle events. + // + + public void keyPressed(KeyEvent evt) { + processLocalKeyEvent(evt); + } + + public void keyReleased(KeyEvent evt) { + processLocalKeyEvent(evt); + } + + public void keyTyped(KeyEvent evt) { + evt.consume(); + } + + public void mousePressed(MouseEvent evt) { + processLocalMouseEvent(evt, false); + } + + public void mouseReleased(MouseEvent evt) { + processLocalMouseEvent(evt, false); + } + + public void mouseMoved(MouseEvent evt) { + processLocalMouseEvent(evt, true); + } + + public void mouseDragged(MouseEvent evt) { + processLocalMouseEvent(evt, true); + } + + public void processLocalKeyEvent(KeyEvent evt) { + if (viewer.rfb != null && rfb.inNormalProtocol) { + if (!inputEnabled) { + if ((evt.getKeyChar() == 'r' || evt.getKeyChar() == 'R') + && evt.getID() == KeyEvent.KEY_PRESSED) { + // Request screen update. + try { + rfb.writeFramebufferUpdateRequest(0, 0, + rfb.framebufferWidth, rfb.framebufferHeight, + false); + } catch (IOException e) { + e.printStackTrace(); + } + } + } else { + // Input enabled. + synchronized (rfb) { + try { + rfb.writeKeyEvent(evt); + } catch (Exception e) { + e.printStackTrace(); + } + rfb.notify(); + } + } + } + // Don't ever pass keyboard events to AWT for default processing. + // Otherwise, pressing Tab would switch focus to ButtonPanel etc. + evt.consume(); + } + + public void processLocalMouseEvent(MouseEvent evt, boolean moved) { + if (viewer.rfb != null && rfb.inNormalProtocol) { + if (moved) { + softCursorMove(evt.getX(), evt.getY()); + } + if (rfb.framebufferWidth != scaledWidth) { + int sx = (evt.getX() * 100 + scalingFactor / 2) / scalingFactor; + int sy = (evt.getY() * 100 + scalingFactor / 2) / scalingFactor; + evt.translatePoint(sx - evt.getX(), sy - evt.getY()); + } + synchronized (rfb) { + try { + rfb.writePointerEvent(evt); + } catch (Exception e) { + e.printStackTrace(); + } + rfb.notify(); + } + } + } + + // + // Ignored events. + // + + public void mouseClicked(MouseEvent evt) { + } + + public void mouseEntered(MouseEvent evt) { + } + + public void mouseExited(MouseEvent evt) { + } + + // + // Reset update statistics. + // + + void resetStats() { + statStartTime = System.currentTimeMillis(); + statNumUpdates = 0; + statNumTotalRects = 0; + statNumPixelRects = 0; + statNumRectsTight = 0; + statNumRectsTightJPEG = 0; + statNumRectsZRLE = 0; + statNumRectsHextile = 0; + statNumRectsRaw = 0; + statNumRectsCopy = 0; + statNumBytesEncoded = 0; + statNumBytesDecoded = 0; + } + + // //////////////////////////////////////////////////////////////// + // + // Handle cursor shape updates (XCursor and RichCursor encodings). + // + + boolean showSoftCursor = false; + + MemoryImageSource softCursorSource; + Image softCursor; + + int cursorX = 0, cursorY = 0; + int cursorWidth, cursorHeight; + int origCursorWidth, origCursorHeight; + int hotX, hotY; + int origHotX, origHotY; + + // + // Handle cursor shape update (XCursor and RichCursor encodings). + // + + synchronized void handleCursorShapeUpdate(int encodingType, int xhot, + int yhot, int width, int height) throws IOException { + + softCursorFree(); + + if (width * height == 0) + return; + + // Ignore cursor shape data if requested by user. + if (viewer.options.ignoreCursorUpdates) { + int bytesPerRow = (width + 7) / 8; + int bytesMaskData = bytesPerRow * height; + + if (encodingType == rfb.EncodingXCursor) { + rfb.skipBytes(6 + bytesMaskData * 2); + } else { + // rfb.EncodingRichCursor + rfb.skipBytes(width * height * bytesPixel + bytesMaskData); + } + return; + } + + // Decode cursor pixel data. + softCursorSource = decodeCursorShape(encodingType, width, height); + + // Set original (non-scaled) cursor dimensions. + origCursorWidth = width; + origCursorHeight = height; + origHotX = xhot; + origHotY = yhot; + + // Create off-screen cursor image. + createSoftCursor(); + + // Show the cursor. + showSoftCursor = true; + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); + } + + // + // decodeCursorShape(). Decode cursor pixel data and return + // corresponding MemoryImageSource instance. + // + + synchronized MemoryImageSource decodeCursorShape(int encodingType, + int width, int height) throws IOException { + + int bytesPerRow = (width + 7) / 8; + int bytesMaskData = bytesPerRow * height; + + int[] softCursorPixels = new int[width * height]; + + if (encodingType == rfb.EncodingXCursor) { + + // Read foreground and background colors of the cursor. + byte[] rgb = new byte[6]; + rfb.readFully(rgb); + int[] colors = { + (0xFF000000 | (rgb[3] & 0xFF) << 16 | (rgb[4] & 0xFF) << 8 | (rgb[5] & 0xFF)), + (0xFF000000 | (rgb[0] & 0xFF) << 16 | (rgb[1] & 0xFF) << 8 | (rgb[2] & 0xFF)) }; + + // Read pixel and mask data. + byte[] pixBuf = new byte[bytesMaskData]; + rfb.readFully(pixBuf); + byte[] maskBuf = new byte[bytesMaskData]; + rfb.readFully(maskBuf); + + // Decode pixel data into softCursorPixels[]. + byte pixByte, maskByte; + int x, y, n, result; + int i = 0; + for (y = 0; y < height; y++) { + for (x = 0; x < width / 8; x++) { + pixByte = pixBuf[y * bytesPerRow + x]; + maskByte = maskBuf[y * bytesPerRow + x]; + for (n = 7; n >= 0; n--) { + if ((maskByte >> n & 1) != 0) { + result = colors[pixByte >> n & 1]; + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + for (n = 7; n >= 8 - width % 8; n--) { + if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { + result = colors[pixBuf[y * bytesPerRow + x] >> n & 1]; + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + + } else { + // encodingType == rfb.EncodingRichCursor + + // Read pixel and mask data. + byte[] pixBuf = new byte[width * height * bytesPixel]; + rfb.readFully(pixBuf); + byte[] maskBuf = new byte[bytesMaskData]; + rfb.readFully(maskBuf); + + // Decode pixel data into softCursorPixels[]. + byte 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 + 2] & 0xFF) << 16 + | (pixBuf[i * 4 + 1] & 0xFF) << 8 + | (pixBuf[i * 4] & 0xFF); + } + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + for (n = 7; n >= 8 - width % 8; n--) { + if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { + if (bytesPixel == 1) { + result = cm8.getRGB(pixBuf[i]); + } else { + result = 0xFF000000 + | (pixBuf[i * 4 + 2] & 0xFF) << 16 + | (pixBuf[i * 4 + 1] & 0xFF) << 8 + | (pixBuf[i * 4] & 0xFF); + } + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + + } + + return new MemoryImageSource(width, height, softCursorPixels, 0, width); + } + + // + // createSoftCursor(). Assign softCursor new Image (scaled if necessary). + // Uses softCursorSource as a source for new cursor image. + // + + synchronized void createSoftCursor() { + + if (softCursorSource == null) + return; + + int scaleCursor = viewer.options.scaleCursor; + if (scaleCursor == 0 || !inputEnabled) + scaleCursor = 100; + + // Save original cursor coordinates. + int x = cursorX - hotX; + int y = cursorY - hotY; + int w = cursorWidth; + int h = cursorHeight; + + cursorWidth = (origCursorWidth * scaleCursor + 50) / 100; + cursorHeight = (origCursorHeight * scaleCursor + 50) / 100; + hotX = (origHotX * scaleCursor + 50) / 100; + hotY = (origHotY * scaleCursor + 50) / 100; + softCursor = Toolkit.getDefaultToolkit().createImage(softCursorSource); + + if (scaleCursor != 100) { + softCursor = softCursor.getScaledInstance(cursorWidth, + cursorHeight, Image.SCALE_SMOOTH); + } + + if (showSoftCursor) { + // Compute screen area to update. + x = Math.min(x, cursorX - hotX); + y = Math.min(y, cursorY - hotY); + w = Math.max(w, cursorWidth); + h = Math.max(h, cursorHeight); + + repaint(viewer.deferCursorUpdates, x, y, w, h); + } + } + + // + // softCursorMove(). Moves soft cursor into a particular location. + // + + synchronized void softCursorMove(int x, int y) { + int oldX = cursorX; + int oldY = cursorY; + cursorX = x; + cursorY = y; + if (showSoftCursor) { + repaint(viewer.deferCursorUpdates, oldX - hotX, oldY - hotY, + cursorWidth, cursorHeight); + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); + } + } + + // + // softCursorFree(). Remove soft cursor, dispose resources. + // + + synchronized void softCursorFree() { + if (showSoftCursor) { + showSoftCursor = false; + softCursor = null; + softCursorSource = null; + + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); + } + } + + BufferedImage createBufferedImage(Image img){ + BufferedImage bimg = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB ); + + Graphics g = bimg.getGraphics(); + g.drawImage(img, 0, 0, null); + g.dispose(); + return bimg; + } + + byte[] getBytes(BufferedImage img)throws IOException { + byte[] b = getImageBytes(img, "raw"); + return b; + } + + byte[] getImageBytes(BufferedImage image, String imageFormat) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + BufferedOutputStream os = new BufferedOutputStream(bos); + image.flush(); + ImageIO.write(image, imageFormat, os); + os.flush(); + os.close(); + return bos.toByteArray(); + } + + void drawBufferedImage(byte[] b) throws IOException{ + BufferedImage bimg = ImageIO.read(new ByteArrayInputStream(b)); +// ImageIO.write(bimg, "jpeg", new File("sample.jpeg")); + memGraphics.setClip(0,0, rfb.framebufferWidth, rfb.framebufferHeight ); + memGraphics.drawImage( bimg, 0,0, null); + scheduleRepaint(0, 0, rfb.framebufferWidth, rfb.framebufferHeight ); + } + + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/DesCipher.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/DesCipher.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,497 @@ +package treeVnc; +// +// 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 -r 000000000000 -r 756bfaf731f3 src/treeVnc/EchoClient.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/EchoClient.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,284 @@ +package treeVnc; + +import java.io.*; +import java.net.*; + +//import myVncProxy.MulticastQueue.Client; + +public class EchoClient { + private String name; + private BufferedReader is = null; + private DataOutputStream os = null; + private Socket echoSocket = null; + private boolean runflag = false; + private WaitReply waitReply; + private Socket clientSocket = null; + // MyVncClient client; + private InterfaceForViewer client; + private int echoPort = 9999; + // private IpV6 ipV6; + // private MyVncClient checkMove; + // private VncViewer vncV; + String responseLine; + String parent;// 親の番号 + String treenum;// 自分の番号 + String leaderflag;// リーダフラグ + + // boolean passflag; + + // WaitReplyに自分自身を渡している + public EchoClient() { + } + + public EchoClient(EchoClient echo) { + this.name = echo.name; + this.leaderflag = echo.leaderflag; + this.parent = echo.parent; + this.treenum = echo.treenum; + this.client = echo.client; + this.waitReply = echo.waitReply; + // checkMove = (MyVncClient)echo.client; + } + + // VncViewerから引数をもらってきてproxy役を認識する + public EchoClient(String name, MyVncClient client) { + this.client = client; + this.name = name; + } + + public EchoClient(String name, MyVncClient client, int echoPort) { + this.client = client; + this.name = name; + this.echoPort = echoPort; + } + + public EchoClient(EchoClient echo, MyVncClient client) { + this.client = client; + this.name = echo.name; + leaderflag = echo.leaderflag; + parent = echo.parent; + treenum = echo.treenum; + waitReply = echo.waitReply; + } + + public EchoClient(String name, CuiMyVncClient client) { + this.client = client; + this.name = name; + } + + public EchoClient(String name, CuiMyVncClient client, int echoPort) { + this.client = client; + this.name = name; + this.echoPort = echoPort; + } + + public EchoClient(EchoClient echo, CuiMyVncClient client) { + this.client = client; + this.name = echo.name; + leaderflag = echo.leaderflag; + parent = echo.parent; + treenum = echo.treenum; + } + + // void hostn(String args){ + void openport() { + try { + if (name != null) { + echoSocket = new Socket(name, echoPort); + } else { + echoSocket = new Socket("133.13.48.18", echoPort); + } + os = new DataOutputStream(echoSocket.getOutputStream()); + is = new BufferedReader(new InputStreamReader( + echoSocket.getInputStream())); + } catch (UnknownHostException e) { + System.err.println("Don't know about host: localhost"); + } catch (IOException e) { + System.out.println(name + " Connection Faild"); + System.exit(0); + } + + } + + /** + * @param args + * select connect port + * @return + */ + EchoClient requestHostName(String args) { + // サーバーにメッセージを送る + if (echoSocket != null && os != null && is != null) { + try { + + InetAddress addr = InetAddress.getLocalHost(); + String add = new String(addr.getHostAddress()); + // add = getIpV6(); + + os.writeBytes(add + "\n"); + os.writeBytes(args + "\n"); + getProxyData(is); + + streamClose(); + } catch (UnknownHostException e) { + System.err.println("Trying to connect to unknown host: " + e); + } catch (IOException e) { + System.err.println("IOException: " + e); + } + waitReply = new WaitReply(treenum, client); + waitReply.start(); + } + return this; + } + + /** + * Call at lost host + */ + boolean lostHost() { + if (echoSocket != null && os != null && is != null) { + try { + if (runflag) { + return true; + } + sendDataProxy(); + String checkRepetition = getProxyData2(is); + if (checkRepetition.equals("stop")) { + return true; + } + // if(!(checkRepetition.equals("skip")) || + // "1".equals(leaderflag)) { + if (!(waitReply.checkPath())) { + Thread.sleep(1000); + reConnectionMain(echoSocket); + streamClose(); + } + // Thread.sleep(1000); + /* + * if(!(checkMove.vncFrame.isShowing())&&"skip".equals( + * checkRepetition)) { openport(); notfoundParent(); } + */ + } catch (UnknownHostException e) { + System.err.println("Trying to connect to unknown host: " + e); + } catch (IOException e) { + return false; + } catch (InterruptedException e) { + e.printStackTrace(); + + } catch (NullPointerException e) { + openport(); + System.out.println("notFoundParents"); + notfoundParent(); + } + } + return true; + } + + boolean notfoundParent() { + if (echoSocket != null && os != null && is != null) { + runflag = true; + try { + sendDataProxy("2", parent, null); + getProxyData(is); + reConnectionMain(echoSocket); + streamClose(); + } catch (UnknownHostException e) { + System.err.println("Trying to connect to unknown host: " + e); + } catch (IOException e) { + System.err.println("IOException: " + e); + } + } + return true; + } + + EchoClient Interruption(Socket _clientSocket) { + clientSocket = _clientSocket; + BufferedReader lostis = null;// あとで修正する + + try { + lostis = new BufferedReader(new InputStreamReader( + clientSocket.getInputStream())); + getProxyData(lostis); + clientSocket.close();// WaitReplyのacceptを終了させる + } catch (IOException e) { + System.out.println(e); + } + return this; + } + + void getProxyData(BufferedReader is) throws IOException { + if ((responseLine = is.readLine()) != null) { + System.out.println("Server: " + responseLine); + } + if ((parent = is.readLine()) != null) { + System.out.println("parent: " + parent); + } + if ((treenum = is.readLine()) != null) { + System.out.println("treenum: " + treenum); + } + if ((leaderflag = is.readLine()) != null) { + System.out.println("leaderflag: " + leaderflag); + } + } + + String getProxyData2(BufferedReader is) throws IOException { + String checkRepetition; + System.out.println("-------------------re----------------------------"); + if ((responseLine = is.readLine()) != null) { + System.out.println("Server: " + responseLine); + } + if ((parent = is.readLine()) != null) { + System.out.println("parent:test " + parent); + } + if ((checkRepetition = is.readLine()) != null) { + System.out.println("checkRepetition: " + checkRepetition); + } + return checkRepetition; + } + + void reConnectionMain(Socket echoSocket) { + client.close(); + client.setClientSocket(echoSocket); + client.init(); + client.setEchoValue(this); + client.getParentName(); + client.start_threads(); + client.start(); + } + + void streamClose() throws IOException { + os.close(); + is.close(); + echoSocket.close(); + } + + void sendDataProxy() { + if ("1".equals(leaderflag)) { + sendDataProxy("1", parent, treenum); + System.out.println("---------------------------------------------"); + } else { + sendDataProxy("3", parent, treenum); + System.out.println("---------------------------------------------"); + } + } + + void sendDataProxy(String type, String num, String treenum) { + try { + if (treenum != null) { + os.writeBytes(type + "\n"); + os.writeBytes(num + "\n"); + os.writeBytes(treenum + "\n"); + } else { + os.writeBytes(type + "\n"); + os.writeBytes(num + "\n"); + } + + } catch (UnknownHostException e) { + System.err.println("Trying to connect to unknown host: " + e); + } catch (IOException e) { + System.err.println("IOException: " + e); + } + } + /* + * String getIpV6() { ipV6 = new IpV6(); ipV6.getInterface(); return + * ipV6.getV6(); } + */ +} \ No newline at end of file diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/GetBroadCastClient.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/GetBroadCastClient.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,81 @@ +package treeVnc; + +import java.io.BufferedReader; +//import java.io.ByteArrayInputStream; +//import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.ServerSocket; +import java.net.Socket; + +public class GetBroadCastClient implements Runnable { + private ServerSocket server = null; + BufferedReader is; + private int port = 8182; + private boolean stopFlag = false; + TextBoxClient text = new TextBoxClient(); + + private void getData() { + try { + server = new ServerSocket(port); + while(true) { + Socket socket = server.accept(); + is = new BufferedReader(new InputStreamReader( + socket.getInputStream())); + String line = is.readLine(); + if(line!=null){ + text.checkBox(line); + } + text.setButton(); + text.visible(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + void socketClose() { + try { + text.unVisible(); + //server.close(); after the modify + is.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + +/* + private int castInt(byte[] a) { + int value = 0; + ByteArrayInputStream bais = new ByteArrayInputStream(a); + DataInputStream dis = new DataInputStream(bais); + try { + value = dis.readInt(); + } catch (IOException e) { + } + System.out.println(value); + return value; + } + + private String castString(byte[] a) { + String recover = new String(a); + recover = recover.replace("¥n", ""); + recover = recover.trim(); + System.out.println(recover); + return recover; + } +*/ + + public void run() { + getData(); + } + + public void setStopFlag(boolean stopFlag) { + this.stopFlag = stopFlag; + } + + public boolean isStopFlag() { + return stopFlag; + } + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/GetBroadCastProxy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/GetBroadCastProxy.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,98 @@ +package treeVnc; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.InetAddress; +import java.net.MulticastSocket; +import java.net.SocketAddress; + +//import TextBoxProxy; + +public class GetBroadCastProxy implements Runnable { + static final String McastAddr = "224.0.0.1"; + static final int Port = 8183; + static final int BufSize = 1024; + private byte[] buf = new byte[BufSize]; + private byte[] resorve = new byte[BufSize]; + private ByteArrayInputStream inputStream; + private boolean stopFlag = false; + private VncProxyService vps; + private BroadCastProxy bCast; + private String address; + + public GetBroadCastProxy(VncProxyService _vps){ + vps = _vps; + bCast = new BroadCastProxy(vps.rfb.acceptPort+":"+vps.host+":" + +vps.rfb.desktopName+":"+vps.acc.getMyAddress()+":"); + } + + private synchronized void getData() { + try { + InetAddress mAddr = InetAddress.getByName(McastAddr); + MulticastSocket soc = new MulticastSocket(Port); + DatagramPacket recvPacket = new DatagramPacket(buf, BufSize); + soc.joinGroup(mAddr); + while (true) { + soc.receive(recvPacket); + address = getAddress(recvPacket.getSocketAddress()); + inputStream = new ByteArrayInputStream(recvPacket.getData()); + inputStream.read(resorve); + if("who".equals(castString(resorve))){ + replyBroadCast(); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void replyBroadCast() { + Runnable sender = new Runnable() { + public void run() { + bCast.createSocket(address); + } + }; + new Thread(sender).start(); + } + + private String getAddress(SocketAddress addr) { + String str = addr.toString(); + str = str.substring(1,str.indexOf(":")); + return str; + } +/* + private int castBytoToInt(byte[] a) { + int value = 0; + ByteArrayInputStream bais = new ByteArrayInputStream(a); + DataInputStream dis = new DataInputStream(bais); + try { + value = dis.readInt(); + } catch (IOException e) { + } + System.out.println(value); + return value; + } +*/ + private String castString(byte[] a) { + String recover = new String(a); + recover = recover.replace("¥n", ""); + recover = recover.trim(); + System.out.println(recover); + return recover; + } + + + public void run() { + getData(); + } + + public void setStopFlag(boolean stopFlag) { + this.stopFlag = stopFlag; + } + + public boolean isStopFlag() { + return stopFlag; + } + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/HTTPConnectSocket.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/HTTPConnectSocket.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,60 @@ +package treeVnc; +// +// Copyright (C) 2002 Constantin Kaplinsky, Inc. All Rights Reserved. +// +// This is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +// +// HTTPConnectSocket.java together with HTTPConnectSocketFactory.java +// implement an alternate way to connect to VNC servers via one or two +// HTTP proxies supporting the HTTP CONNECT method. +// + +import java.net.*; +import java.io.*; + +class HTTPConnectSocket extends Socket { + + public HTTPConnectSocket(String host, int port, + String proxyHost, int proxyPort) + throws IOException { + + // Connect to the specified HTTP proxy + super(proxyHost, proxyPort); + + // Send the CONNECT request + getOutputStream().write(("CONNECT " + host + ":" + port + + " HTTP/1.0\r\n\r\n").getBytes()); + + // Read the first line of the response + DataInputStream is = new DataInputStream(getInputStream()); + String str = is.readLine(); + + // Check the HTTP error code -- it should be "200" on success + if (!str.startsWith("HTTP/1.0 200 ")) { + if (str.startsWith("HTTP/1.0 ")) + str = str.substring(9); + throw new IOException("Proxy reports \"" + str + "\""); + } + + // Success -- skip remaining HTTP headers + do { + str = is.readLine(); + } while (str.length() != 0); + } +} + diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/HTTPConnectSocketFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/HTTPConnectSocketFactory.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,87 @@ +package treeVnc; +// +// Copyright (C) 2002 Constantin Kaplinsky, Inc. All Rights Reserved. +// +// This is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +// +// HTTPConnectSocketFactory.java together with HTTPConnectSocket.java +// implement an alternate way to connect to VNC servers via one or two +// HTTP proxies supporting the HTTP CONNECT method. +// + +import java.applet.*; +import java.net.*; +import java.io.*; + +class HTTPConnectSocketFactory implements SocketFactory { + + public Socket createSocket(String host, int port, Applet applet) + throws IOException { + + return createSocket(host, port, + applet.getParameter("PROXYHOST1"), + applet.getParameter("PROXYPORT1")); + } + + public Socket createSocket(String host, int port, String[] args) + throws IOException { + + return createSocket(host, port, + readArg(args, "PROXYHOST1"), + readArg(args, "PROXYPORT1")); + } + + public Socket createSocket(String host, int port, + String proxyHost, String proxyPortStr) + throws IOException { + + int proxyPort = 0; + if (proxyPortStr != null) { + try { + proxyPort = Integer.parseInt(proxyPortStr); + } catch (NumberFormatException e) { } + } + + if (proxyHost == null || proxyPort == 0) { + System.out.println("Incomplete parameter list for HTTPConnectSocket"); + return new Socket(host, port); + } + + System.out.println("HTTP CONNECT via proxy " + proxyHost + + " port " + proxyPort); + HTTPConnectSocket s = + new HTTPConnectSocket(host, port, proxyHost, proxyPort); + + return (Socket)s; + } + + private String readArg(String[] args, String name) { + + for (int i = 0; i < args.length; i += 2) { + if (args[i].equalsIgnoreCase(name)) { + try { + return args[i+1]; + } catch (Exception e) { + return null; + } + } + } + return null; + } +} + diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/InStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/InStream.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,178 @@ +package treeVnc; +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// rdr::InStream marshalls data from a buffer stored in RDR (RFB Data +// Representation). +// + +abstract public class InStream { + + // check() ensures there is buffer data for at least one item of size + // itemSize bytes. Returns the number of items in the buffer (up to a + // maximum of nItems). + + public final int check(int itemSize, int nItems) throws Exception { + if (ptr + itemSize * nItems > end) { + if (ptr + itemSize > end) + return overrun(itemSize, nItems); + + nItems = (end - ptr) / itemSize; + } + return nItems; + } + + public final void check(int itemSize) throws Exception { + if (ptr + itemSize > end) + overrun(itemSize, 1); + } + + // readU/SN() methods read unsigned and signed N-bit integers. + + public final int readS8() throws Exception { + check(1); return b[ptr++]; + } + + public final int readS16() throws Exception { + check(2); int b0 = b[ptr++]; + int b1 = b[ptr++] & 0xff; return b0 << 8 | b1; + } + + public final int readS32() throws Exception { + check(4); int b0 = b[ptr++]; + int b1 = b[ptr++] & 0xff; + int b2 = b[ptr++] & 0xff; + int b3 = b[ptr++] & 0xff; + return b0 << 24 | b1 << 16 | b2 << 8 | b3; + } + + public final int readU8() throws Exception { + return readS8() & 0xff; + } + + public final int readU16() throws Exception { + return readS16() & 0xffff; + } + + public final int readU32() throws Exception { + return readS32() & 0xffffffff; + } + + // readString() reads a string - a U32 length followed by the data. + + public final String readString() throws Exception { + int len = readU32(); + if (len > maxStringLength) + throw new Exception("InStream max string length exceeded"); + + char[] str = new char[len]; + int i = 0; + while (i < len) { + int j = i + check(1, len - i); + while (i < j) { + str[i++] = (char)b[ptr++]; + } + } + + return new String(str); + } + + // maxStringLength protects against allocating a huge buffer. Set it + // higher if you need longer strings. + + public static int maxStringLength = 65535; + + public final void skip(int bytes) throws Exception { + while (bytes > 0) { + int n = check(1, bytes); + ptr += n; + bytes -= n; + } + } + + // readBytes() reads an exact number of bytes into an array at an offset. + + public void readBytes(byte[] data, int offset, int length) throws Exception { + int offsetEnd = offset + length; + while (offset < offsetEnd) { + int n = check(1, offsetEnd - offset); + System.arraycopy(b, ptr, data, offset, n); + ptr += n; + offset += n; + } + } + + // readOpaqueN() reads a quantity "without byte-swapping". Because java has + // no byte-ordering, we just use big-endian. + + public final int readOpaque8() throws Exception { + return readU8(); + } + + public final int readOpaque16() throws Exception { + return readU16(); + } + + public final int readOpaque32() throws Exception { + return readU32(); + } + + public final int readOpaque24A() throws Exception { + check(3); int b0 = b[ptr++]; + int b1 = b[ptr++]; int b2 = b[ptr++]; + return b0 << 24 | b1 << 16 | b2 << 8; + } + + public final int readOpaque24B() throws Exception { + check(3); int b0 = b[ptr++]; + int b1 = b[ptr++]; int b2 = b[ptr++]; + return b0 << 16 | b1 << 8 | b2; + } + + // pos() returns the position in the stream. + + abstract public int pos(); + + // bytesAvailable() returns true if at least one byte can be read from the + // stream without blocking. i.e. if false is returned then readU8() would + // block. + + public boolean bytesAvailable() { return end != ptr; } + + // getbuf(), getptr(), getend() and setptr() are "dirty" methods which allow + // you to manipulate the buffer directly. This is useful for a stream which + // is a wrapper around an underlying stream. + + public final byte[] getbuf() { return b; } + public final int getptr() { return ptr; } + public final int getend() { return end; } + public final void setptr(int p) { ptr = p; } + + // overrun() is implemented by a derived class to cope with buffer overrun. + // It ensures there are at least itemSize bytes of buffer data. Returns + // the number of items in the buffer (up to a maximum of nItems). itemSize + // is supposed to be "small" (a few bytes). + + abstract protected int overrun(int itemSize, int nItems) throws Exception; + + protected InStream() {} + protected byte[] b; + protected int ptr; + protected int end; +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/InterfaceForViewer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/InterfaceForViewer.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,32 @@ +package treeVnc; + +import java.awt.Image; +import java.net.Socket; + +public interface InterfaceForViewer extends java.lang.Runnable{ + + public void init(); + public void start_threads(); + public void start(); + + public void setEchoValue(EchoClient value); + public String readParameter(String name, boolean required); + + public void getParentName(); + // synchronized + public void disconnect(); + public void fatalError(String str); + public void fatalError(String str, Exception e); + + + public void destroy(); + + public void enableInput(boolean enable); + + + public void setClientSocket(Socket sock); + public void close(); + public Image getScreenImage(); + public void writeScreenData(byte[] b, String imageFormat); + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/MemInStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/MemInStream.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,33 @@ +package treeVnc; +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +public class MemInStream extends InStream { + + public MemInStream(byte[] data, int offset, int len) { + b = data; + ptr = offset; + end = offset + len; + } + + public int pos() { return ptr; } + + protected int overrun(int itemSize, int nItems) throws Exception { + throw new Exception("MemInStream overrun: end of stream"); + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/MostRecentMultiCast.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/MostRecentMultiCast.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,27 @@ +package treeVnc; + +import java.util.LinkedList; + + +public class MostRecentMultiCast extends MulticastQueue { + + LinkedList> alive; + int count = 0; + MostRecentMultiCast(int limit) { + count = limit; + this.alive = new LinkedList>(); + } + + @Override + public synchronized void put(T item) + { + Node next = new Node(item); + tail.set(next); + tail = next; + alive.addLast(next); + if (alive.size()>count) { + Node old = alive.getFirst(); + old.clear(); + } + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/MulticastQueue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/MulticastQueue.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,87 @@ +package treeVnc; + +import java.util.concurrent.CountDownLatch; + +public class MulticastQueue +{ + + Node tail; + + public MulticastQueue() + { + tail = new Node(null); + } + + public synchronized void put(T item) + { + Node next = new Node(item); + tail.set(next); + tail = next; + } + + public Client newClient() + { + return new Client(tail); + } + + static class Client + { + Node node; + + Client(Node tail) + { + node = tail; + } + + synchronized public T poll() + { + Node next = null; + T item = null; + do { + try { + next = node.next(); + }catch(InterruptedException _e){ + continue; + } +// item = node.getItem(); + item = next.getItem(); + node = next; + } while ( item == null); + return item; + } + } + + static class Node + { + private T item; + private Node next; + private CountDownLatch latch; + + public Node(T item) + { + this.item = item; + this.next = null; + latch = new CountDownLatch(1); + } + + synchronized public T getItem() { + return item; + } + + public void set(Node next) + { + this.next = next; + latch.countDown(); + } + + public Node next() throws InterruptedException + { + latch.await(); + return next; + } + + synchronized public void clear() { + item = null; + } + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/MyRfbProto.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/MyRfbProto.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,15 @@ +package treeVnc; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +public interface MyRfbProto { + + public void selectPort(int port); + + public Socket accept() throws IOException; + + public void newClient(AcceptThread acceptThread, Socket newCli, OutputStream os, InputStream is) throws IOException; +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/MyRfbProtoClient.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/MyRfbProtoClient.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,974 @@ +package treeVnc; + + +import java.awt.Graphics; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.BindException; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.util.LinkedList; + +import javax.imageio.ImageIO; + +//import MulticastQueue.Client; + + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.zip.DataFormatException; +import java.util.zip.Deflater; +import java.util.zip.Inflater; +import java.io.OutputStream; + +public +class MyRfbProtoClient extends RfbProto implements MyRfbProto { + final static String versionMsg_3_855 = "RFB 003.855\n"; + + /** + * CheckMillis is one of new msgType for RFB 3.855. + */ + final static byte SpeedCheckMillis = 4; + final static byte WriteJpegData = 5; + + private static final int INFLATE_BUFSIZE = 1024*100; + boolean printStatusFlag = false; + long startCheckTime; + + private int messageType; + private int rectangles; + private int rectX; + private int rectY; + private int rectW; + private int rectH; + private int encoding; + private int zLen; + //private boolean clicomp = false; + + private ServerSocket servSock; + private int acceptPort; + private byte initData[]; + private LinkedList cliListTmp; + private LinkedList cliList; + boolean createBimgFlag; + boolean proxyFlag = false; + + //override + InterfaceForViewer viewer; + + ExecutorService executor; + + byte[] pngBytes; + + // private MulticastQueue> multicastqueue = new MostRecentMultiCast>(10); + private MulticastQueue> multicastqueue = new MulticastQueue>(); + private int clients = 0; + private Inflater inflater = new Inflater(); + private Deflater deflater = new Deflater(); + + public + MyRfbProtoClient() throws IOException { + } + + MyRfbProtoClient(String h, int p, VncViewer v) throws IOException { + super(h, p, v); + cliList = new LinkedList(); + cliListTmp = new LinkedList(); + createBimgFlag = false; + // sendThreads = new LinkedList(); + // executor = Executors.newCachedThreadPool(); + // executor = Executors.newSingleThreadExecutor(); + } + + MyRfbProtoClient(String h, int p, MyVncClient v) throws IOException { + super(h, p); + this.viewer = v; + cliList = new LinkedList(); + cliListTmp = new LinkedList(); + createBimgFlag = false; + proxyFlag = false; + // sendThreads = new LinkedList(); + // executor = Executors.newCachedThreadPool(); + // executor = Executors.newSingleThreadExecutor(); + } + + MyRfbProtoClient(String h, int p, CuiMyVncClient v) throws IOException { + super(h, p); + this.viewer = v; + cliList = new LinkedList(); + cliListTmp = new LinkedList(); + createBimgFlag = false; + proxyFlag = false; + // sendThreads = new LinkedList(); + // executor = Executors.newCachedThreadPool(); + // executor = Executors.newSingleThreadExecutor(); + } + + + MyRfbProtoClient(String h, int p) throws IOException { + super(h, p); + cliList = new LinkedList(); + cliListTmp = new LinkedList(); + createBimgFlag = false; + // sendThreads = new LinkedList(); + // executor = Executors.newCachedThreadPool(); + // executor = Executors.newSingleThreadExecutor(); + } + + + void sendProxyFlag(OutputStream os) throws IOException { + if(proxyFlag) os.write(1); + else os.write(0); + } + + boolean readProxyFlag() throws IOException{ + int flag = readU8(); + if(flag == 1) + return true; + else + return false; + } + +/* + void sendPortNumber(OutputStream os) throws IOException { + byte[] b = new byte[4]; + b = castIntByte(geth.port); + os.write(b); + } +*/ + + byte[] readEchoPort() throws Exception { + byte[] b = new byte[4]; + readFully(b); + + return b; + } + + + + void changeParent(String h, int p) throws IOException { + host = h; + port = p; + + sock = new Socket(host, port); + is = new DataInputStream(new BufferedInputStream(sock.getInputStream(), + 16384)); + os = sock.getOutputStream(); + + timing = false; + timeWaitedIn100us = 5; + timedKbits = 0; + } + + // over write + void writeVersionMsg() throws IOException { + clientMajor = 3; + if (serverMinor == 855) { + clientMinor = 855; + os.write(versionMsg_3_855.getBytes()); + } else if (serverMajor > 3 || serverMinor >= 8) { + clientMinor = 8; + os.write(versionMsg_3_8.getBytes()); + } else if (serverMinor >= 7) { + clientMinor = 7; + os.write(versionMsg_3_7.getBytes()); + } else { + clientMinor = 3; + os.write(versionMsg_3_3.getBytes()); + } + protocolTightVNC = false; + initCapabilities(); + } + + void initServSock(int port) throws IOException { + servSock = new ServerSocket(port); + acceptPort = port; + } + + // 5550を開けるが、開いてないなら+1のポートを開ける。 + public void selectPort(int p) { + int port = p; + while (true) { + try { + initServSock(port); + break; + } catch (BindException e) { + port++; + continue; + } catch (IOException e) { + + } + } + System.out.println("accept port = " + port); + } + + int getAcceptPort() { + return acceptPort; + } + + void setSoTimeout(int num) throws IOException { + servSock.setSoTimeout(num); + } + + public Socket accept() throws IOException { + return servSock.accept(); + } + + void addSock(Socket sock) { + cliList.add(sock); + } + + void addSockTmp(Socket sock) { + System.out.println("connected " + sock.getInetAddress()); + cliListTmp.add(sock); + } + + boolean markSupported() { + return is.markSupported(); + } + + void readServerInit() throws IOException { + + is.mark(255); + skipBytes(20); + int nlen = readU32(); + int blen = 20 + 4 + nlen; + initData = new byte[blen]; + is.reset(); + + is.mark(blen); + readFully(initData); + is.reset(); + + framebufferWidth = readU16(); + framebufferHeight = readU16(); + bitsPerPixel = readU8(); + depth = readU8(); + bigEndian = (readU8() != 0); + trueColour = (readU8() != 0); + redMax = readU16(); + greenMax = readU16(); + blueMax = readU16(); + redShift = readU8(); + greenShift = readU8(); + blueShift = readU8(); + byte[] pad = new byte[3]; + readFully(pad); + int nameLength = readU32(); + byte[] name = new byte[nameLength]; + readFully(name); + desktopName = new String(name); + + // Read interaction capabilities (TightVNC protocol extensions) + if (protocolTightVNC) { + int nServerMessageTypes = readU16(); + int nClientMessageTypes = readU16(); + int nEncodingTypes = readU16(); + readU16(); + readCapabilityList(serverMsgCaps, nServerMessageTypes); + readCapabilityList(clientMsgCaps, nClientMessageTypes); + readCapabilityList(encodingCaps, nEncodingTypes); + } + + inNormalProtocol = true; + } + + void sendRfbVersion(OutputStream os) throws IOException { + os.write(versionMsg_3_855.getBytes()); +// os.write(versionMsg_3_8.getBytes()); + } + + int readVersionMsg(InputStream is, OutputStream os) throws IOException { + + byte[] b = new byte[12]; + + is.read(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"); + } + + int rfbMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); + int rfbMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0'); + + if (rfbMajor < 3) { + throw new IOException( + "RFB server does not support protocol version 3"); + } + + if (rfbMinor == 855) { + sendProxyFlag(os); +// if(proxyFlag)sendPortNumber(os); + } + return rfbMinor; + + } void readVersionMsg(InputStream is) throws IOException { + + byte[] b = new byte[12]; + + is.read(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'); + + if (serverMajor < 3) { + throw new IOException( + "RFB server does not support protocol version 3"); + } + + + } + + + void sendSecurityType(OutputStream os) throws IOException { + // number-of-security-types + os.write(1); + // security-types + // 1:None + os.write(1); + } + + void readSecType(InputStream is) throws IOException { + byte[] b = new byte[1]; + is.read(b); + + } + + void sendSecResult(OutputStream os) throws IOException { + byte[] b = castIntByte(0); + os.write(b); + } + + void readClientInit(InputStream in) throws IOException { + byte[] b = new byte[0]; + in.read(b); + } + + void sendInitData(OutputStream os) throws IOException { + os.write(initData); + } + + + void sendPngImage() { + try { + for (Socket cli : cliListTmp) { + try { + sendPngData(cli); + addSock(cli); + } catch (IOException e) { + // if socket closed + cliListTmp.remove(cli); + } + } + // System.out.println("cliSize="+cliSize()); + } catch (Exception e) { + } + cliListTmp.clear(); + } + + boolean ready() throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + return br.ready(); + } + + int cliSize() { + return cliList.size(); + } + + void printNumBytesRead() { + System.out.println("numBytesRead=" + numBytesRead); + } + + + + void regiFramebufferUpdate() throws IOException { + is.mark(20); + messageType = readU8(); // 0 + skipBytes(1); // 1 + rectangles = readU16(); // 2 + rectX = readU16(); // 4 + rectY = readU16(); // 6 + rectW = readU16(); // 8 + rectH = readU16(); // 10 + encoding = readU32(); // 12 +// System.out.println("encoding = "+encoding); + if (encoding == EncodingZRLE|| encoding==EncodingZRLEE||encoding==EncodingZlib) + zLen = readU32(); + else + zLen = 0; + is.reset(); + + } + + int checkAndMark() throws IOException { + int dataLen; + switch (encoding) { + case RfbProto.EncodingRaw: + dataLen = rectW * rectH * 4 + 16; + is.mark(dataLen); + break; + case RfbProto.EncodingCopyRect: + dataLen = 16 + 4; + is.mark(dataLen); + break; + case RfbProto.EncodingRRE: + case RfbProto.EncodingCoRRE: + case RfbProto.EncodingHextile: + case RfbProto.EncodingTight: + dataLen = zLen + 20; + is.mark(dataLen); + break; + case RfbProto.EncodingZlib: + case RfbProto.EncodingZRLE: + case RfbProto.EncodingZRLEE: + dataLen = zLen + 20; + is.mark(dataLen); + break; + case RfbProto.EncodingXCursor: + case RfbProto.EncodingRichCursor: + int pixArray = rectW * rectH * 4; + int u8Array = (int)Math.floor((rectW + 7)/8) * rectH; + dataLen = pixArray + u8Array; + printFramebufferUpdate(); + is.mark(dataLen); + break; + default: + dataLen = 1000000; + is.mark(dataLen); + } + return dataLen; + } + + + void sendDataToClient() throws Exception { + regiFramebufferUpdate(); + int dataLen = checkAndMark(); + readSendData(dataLen); + } + + BufferedImage createBufferedImage(Image img) { + BufferedImage bimg = new BufferedImage(img.getWidth(null), + img.getHeight(null), BufferedImage.TYPE_INT_RGB); + + Graphics g = bimg.getGraphics(); + g.drawImage(img, 0, 0, null); + g.dispose(); + return bimg; + } + + void createPngBytes(BufferedImage bimg) throws IOException { + pngBytes = getImageBytes(bimg, "png"); + } + + byte[] getBytes(BufferedImage img) throws IOException { + byte[] b = getImageBytes(img, "png"); + return b; + } + + byte[] getImageBytes(BufferedImage image, String imageFormat) + throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + BufferedOutputStream os = new BufferedOutputStream(bos); + image.flush(); + ImageIO.write(image, imageFormat, os); + os.flush(); + os.close(); + return bos.toByteArray(); + } + + void sendPngData(Socket sock) throws IOException { + byte[] dataLength = castIntByte(pngBytes.length); + sock.getOutputStream().write(dataLength); + sock.getOutputStream().write(pngBytes); + } + + byte[] castIntByte(int len) { + byte[] b = new byte[4]; + b[0] = (byte) ((len >>> 24) & 0xFF); + b[1] = (byte) ((len >>> 16) & 0xFF); + b[2] = (byte) ((len >>> 8) & 0xFF); + b[3] = (byte) ((len >>> 0) & 0xFF); + return b; + } + + BufferedImage createBimg() throws IOException { + BufferedImage bimg = ImageIO.read(new ByteArrayInputStream(pngBytes)); + return bimg; + } + + void printFramebufferUpdate() { + + System.out.println("messageType=" + messageType); + System.out.println("rectangles=" + rectangles); + System.out.println("encoding=" + encoding); + System.out.println("rectX = "+rectX+": rectY = "+rectY); + System.out.println("rectW = "+rectW+": rectH = "+rectH); + switch (encoding) { + case RfbProto.EncodingRaw: + System.out.println("rectW * rectH * 4 + 16 =" + rectW * rectH * 4 + + 16); + break; + default: + } + } + + void sendFullScreen(String imageFormat, OutputStream os) { + BufferedImage bimg = getBufferedImage(viewer.getScreenImage()); + try { + byte[] b = getImageBytes(bimg, imageFormat); +// int len = b.length; + byte[] length = castIntByte(b.length); +// System.out.println("jpeg length = " + b.length); + os.write((byte)WriteJpegData); + os.write(length); // length of jpeg data + os.write(b); // jpeg data + os.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + + // unnecessary method + void readWriteJpegData() throws IOException { + byte[] b = readJpegData(); + viewer.writeScreenData(b, "jpeg"); + } + + byte[] readJpegData() throws IOException { + int len = readU32(); + byte[] b = new byte[len]; + readFully(b); + return b; + } + + + public BufferedImage getBufferedImage(Image img) { + BufferedImage bimg = new BufferedImage(framebufferWidth, framebufferHeight, BufferedImage.TYPE_INT_RGB); + Graphics g = bimg.getGraphics(); + g.drawImage(img, 0, 0, null); + g.dispose(); + return bimg; + } + + void readSpeedCheck() throws IOException { + byte[] b = new byte[1]; + readFully(b); + } + + void startSpeedCheck() { + ByteBuffer b = ByteBuffer.allocate(10); + b.put((byte)SpeedCheckMillis); + b.flip(); + startCheckTime = System.currentTimeMillis(); + System.out.println("startChckTime = "+ startCheckTime); + LinkedListbufs = new LinkedList(); + bufs.add(b); + multicastqueue.put(bufs); + } + + void endSpeedCheck() { + long accTime = System.currentTimeMillis(); + long time = accTime - startCheckTime; + System.out.println("checkMillis: " + time); + } + + + synchronized void changeStatusFlag() { + printStatusFlag = true; + } + + void printMills() { + if(printStatusFlag) { + + changeStatusFlag(); + } else { + changeStatusFlag(); + } + } + + void speedCheckMillis() { + Runnable stdin = new Runnable() { + public void run() { + int c; + try { + while( (c = System.in.read()) != -1 ) { + switch(c) { + case 's': + break; + default: + startSpeedCheck(); + break; + } + } + }catch(IOException e){ + System.out.println(e); + } + } + }; + + new Thread(stdin).start(); + } + + /** + * gzip byte arrays + * @param deflater + * @param inputs + * byte data[] + * @param inputIndex + * @param outputs + * byte data[] + * @return byte length in last byte array + * @throws IOException + */ + public int zip(Deflater deflater,LinkedList inputs, int inputIndex, LinkedList outputs) throws IOException { + int len = 0; + ByteBuffer c1= ByteBuffer.allocate(INFLATE_BUFSIZE); + while(inputIndex < inputs.size() ) { + ByteBuffer b1 = inputs.get(inputIndex++); + deflater.setInput(b1.array(),b1.position(),b1.remaining()); + /** + * If we finish() stream and reset() it, Deflater start new gzip stream, this makes continuous zlib reader unhappy. + * if we remove finish(), Deflater.deflate() never flushes its output. The original zlib deflate has flush flag. I'm pretty + * sure this a kind of bug of Java library. + */ + if (inputIndex==inputs.size()) + deflater.finish(); + int len1 = 0; + do { + len1 = deflater.deflate(c1.array(),c1.position(),c1.remaining()); + if (len1>0) { + len += len1; + c1.position(c1.position()+len1); + if (c1.remaining()==0) { + c1.flip(); outputs.addLast(c1); + c1 = ByteBuffer.allocate(INFLATE_BUFSIZE); + } + } + } while (len1 >0 || !deflater.needsInput()); // &&!deflater.finished()); + } + if (c1.position()!=0) { + c1.flip(); outputs.addLast(c1); + } + deflater.reset(); + return len; + } + + /** + * gunzip byte arrays + * @param inflater + * @param inputs + * byte data[] + * @param outputs + * byte data[] + *@return number of total bytes + * @throws IOException + */ + public int unzip(Inflater inflater, LinkedList inputs, int inputIndex, LinkedList outputs,int bufSize) + throws DataFormatException { + int len=0; + ByteBuffer buf = ByteBuffer.allocate(bufSize); + while (inputIndex < inputs.size()) { + ByteBuffer input = inputs.get(inputIndex++); + inflater.setInput(input.array(),input.position(),input.limit()); +// if (inputIndex==inputs.size()) if inflater/deflater has symmetry, we need this +// inflater.end(); but this won't work + do { + int len0 = inflater.inflate(buf.array(),buf.position(),buf.remaining()); + if (len0>0) { + buf.position(buf.position()+len0); + len += len0; + if (buf.remaining()==0) { + buf.flip(); + outputs.addLast(buf); + buf = ByteBuffer.allocate(bufSize); + } + } + } while (!inflater.needsInput()); + } + if (buf.position()!=0) { + buf.flip(); + outputs.addLast(buf); + } + return len; + } + + /** + * send data to clients + * @param dataLen + * @throws IOException + * @throws DataFormatException + * + * Zlibed packet is compressed in context dependent way, that is, it have to send from the beginning. But this is + * impossible. So we have to compress it again for each clients. Separate deflater for each clients is necessary. + * + * Java's deflater does not support flush. This means to get the result, we have to finish the compression. Reseting + * start new compression, but it is not accepted well in zlib continuous reading. So we need new Encoding ZRLEE + * which reset decoder for each packet. ZRLEE can be invisible from user, but it have to be implemented in the clients. + * ZRLEE compression is not context dependent, so no recompression is necessary. + */ + void sendDataCheckDelay() { + LinkedListbufs = new LinkedList(); + ByteBuffer b = ByteBuffer.allocate(8); + b.put((byte)CheckDelay); + bufs.add(b); + multicastqueue.put(bufs); + } + + void readSendData(int dataLen) throws IOException, DataFormatException { + LinkedListbufs = new LinkedList(); + ByteBuffer header = ByteBuffer.allocate(16); + readFully(header.array(),0,16); + header.limit(16); + if (header.get(0)==RfbProto.FramebufferUpdate) { + int encoding = header.getInt(12); + if (encoding==RfbProto.EncodingZRLE||encoding==RfbProto.EncodingZlib) { // ZRLEE is already recompressed + ByteBuffer len = ByteBuffer.allocate(4); + readFully(len.array(),0,4); len.limit(4); + ByteBuffer inputData = ByteBuffer.allocate(dataLen-20); + readFully(inputData.array(),0,inputData.capacity()); inputData.limit(dataLen-20); + LinkedListinputs = new LinkedList(); + inputs.add(inputData); + + header.putInt(12, RfbProto.EncodingZRLEE); // means recompress every time + // using new Deflecter every time is incompatible with the protocol, clients have to be modified. + Deflater nDeflater = deflater; // new Deflater(); + LinkedList out = new LinkedList(); + unzip(inflater, inputs, 0 , out, INFLATE_BUFSIZE); + // dump32(inputs); + int len2 = zip(nDeflater, out, 0, bufs); + ByteBuffer blen = ByteBuffer.allocate(4); blen.putInt(len2); blen.flip(); + bufs.addFirst(blen); + + bufs.addFirst(header); + multicastqueue.put(bufs); + is.reset(); + return ; + } + } + bufs.add(header); + if (dataLen>16) { + ByteBuffer b = ByteBuffer.allocate(dataLen-16); + readFully(b.array(),0,dataLen-16); b.limit(dataLen-16); + bufs.add(b); + } + multicastqueue.put(bufs); + is.reset(); + + // It may be compressed. We can inflate here to avoid repeating clients decompressing here, + // but it may generate too many large data. It is better to do it in each client. + // But we have do inflation for all input data, so we have to do it here. + } + + public void newClient(AcceptThread acceptThread, final Socket newCli, + final OutputStream os, final InputStream is) throws IOException { + // createBimgFlag = true; + // rfb.addSockTmp(newCli); + // addSock(newCli); + final int myId = clients; + final MulticastQueue.Client > c = multicastqueue.newClient(); + final AtomicInteger writerRunning = new AtomicInteger(); + writerRunning.set(1); + /** + * Timeout thread. If a client is suspended, it has top of queue indefinitely, which caused memory + * overflow. After the timeout, we poll the queue and discard it. Start long wait if writer is running. + */ + final Runnable timer = new Runnable() { + public void run() { + int count = 0; + for(;;) { + long timeout = 40000/8; + try { + synchronized(this) { + int state,flag; + writerRunning.set(0); + wait(timeout); + flag = 0; + while((state=writerRunning.get())==0) { + c.poll(); // discard, should be timeout + count++; + if (flag==0) { + System.out.println("Discarding "+myId + " count="+ count); flag = 1; + } + wait(10); // if this is too short, writer cannot take the poll, if this is too long, memory will overflow... + } + if (flag==1) System.out.println("Resuming "+myId + " count="+count); + if (state!=1) { + System.out.println("Client died "+myId); + break; + } + } + } catch (InterruptedException e) { + } + } + } + }; + new Thread(timer).start(); + /** + * discard all incoming from clients + */ + final Runnable reader = new Runnable() { + public void run() { + byte b[] = new byte[4096]; + for(;;) { + try { + int c = is.read(b); + if (c<=0) throw new IOException(); + // System.out.println("client read "+c); + } catch (IOException e) { + try { + writerRunning.set(2); + os.close(); + is.close(); + } catch (IOException e1) { + } + return; + } + } + } + }; + /** + * send packets to a client + */ + Runnable sender = new Runnable() { + public void run() { + writerRunning.set(1); + try { + /** + * initial connection of RFB protocol + */ + sendRfbVersion(os); +// readVersionMsg(is); + int rfbMinor = readVersionMsg(is,os); + sendSecurityType(os); + readSecType(is); + sendSecResult(os); + readClientInit(is); + sendInitData(os); + new Thread(reader).start(); // discard incoming packet here after. + if(rfbMinor == 855){ + //checkDilay(os); + // send jpeg data of full screen. + // sendFullScreen("jpeg" ,os); + } else { + // send raw data of full screen. + + } + for (;;) { + LinkedList bufs = c.poll(); + int inputIndex = 0; + ByteBuffer header = bufs.get(inputIndex); + if (header==null) continue; + if (header.get(0)==RfbProto.CheckDelay) { + System.out.println("--------------------"); + //writeToClient(os, bufs, inputIndex); + } + if (header.get(0)==RfbProto.FramebufferUpdate) { + // System.out.println("client "+ myId); + } + writeToClient(os, bufs, inputIndex); + writerRunning.set(1); // yes my client is awaking. + } + } catch (IOException e) { + try { + writerRunning.set(2); + os.close(); + } catch (IOException e1) { + /* if socket closed cliList.remove(newCli); */ + } + } + } + + public void writeToClient(final OutputStream os, + LinkedList bufs, int inputIndex) + throws IOException { + while(inputIndex < bufs.size()) { + ByteBuffer b = bufs.get(inputIndex++); + os.write(b.array(), b.position(), b.limit()); + } + os.flush(); + } + }; + clients++; + new Thread(sender).start(); + + } + + + public void dump32(LinkedListbufs) { + int len =0; + for(ByteBuffer b: bufs) len += b.remaining(); + ByteBuffer top = bufs.getFirst(); + ByteBuffer end = bufs.getLast(); + System.err.println("length: "+len); + System.err.print("head 0: "); + for(int i = 0; i<16 && i < top.remaining(); i++) { + System.err.print(" "+ top.get(i)); + } + System.err.print("tail 0: "); + for(int i = 0; i<16 && i < end.remaining(); i++) { + System.err.print(" "+end.get(i)); + } + System.err.println(); + } +/* + private Iterable byteBufferIterator(final LinkedList in) { + return new Iterable() { + public Iterator iterator() { + return new Iterator() { + int bytes = 0; + int buffers = 0; + public boolean hasNext() { + for(;;) { + if (buffers>=in.size()) return false; + ByteBuffer b = in.get(buffers); + if (! (bytes cliListTmp; + private LinkedList cliList; + boolean createBimgFlag; + boolean proxyFlag = true; + + ExecutorService executor; + + byte[] pngBytes; + + // private MulticastQueue> multicastqueue = new + // MostRecentMultiCast>(10); + private MulticastQueue> multicastqueue = new MulticastQueue>(); + private int clients = 0; + private Inflater inflater = new Inflater(); + private Deflater deflater = new Deflater(); + private CreateThread geth; + // private Thread requestThread; + private RequestScreenThread rThread; + private Thread requestThread; + + public MyRfbProtoProxy() throws IOException { + } + + MyRfbProtoProxy(String h, int p, VncViewer v) throws IOException { + super(h, p, v); + + rThread = new RequestScreenThread(this); + requestThread = new Thread(rThread); + // requestThread = new Thread(new RequestScreenThread(this)); + } + + MyRfbProtoProxy(String h, int p, CreateThread geth) throws IOException { + super(h, p); + this.geth = geth; + proxyFlag = true; + + rThread = new RequestScreenThread(this); + requestThread = new Thread(rThread); + // requestThread = new Thread(new RequestScreenThread(this)); + } + + MyRfbProtoProxy(String h, int p) throws IOException { + super(h, p); + + rThread = new RequestScreenThread(this); + requestThread = new Thread(rThread); + // requestThread = new Thread(new RequestScreenThread(this)); + } + + // over write + void writeVersionMsg() throws IOException { + clientMajor = 3; + if (serverMinor == 855) { + clientMinor = 855; + os.write(versionMsg_3_855.getBytes()); + } else if (serverMajor > 3 || serverMinor >= 8) { + clientMinor = 8; + os.write(versionMsg_3_8.getBytes()); + } else if (serverMinor >= 7) { + clientMinor = 7; + os.write(versionMsg_3_7.getBytes()); + } else { + clientMinor = 3; + os.write(versionMsg_3_3.getBytes()); + } + protocolTightVNC = false; + initCapabilities(); + } + + void initServSock(int port) throws IOException { + servSock = new ServerSocket(port); + acceptPort = port; + } + + void authenticationRequestAccess() throws IOException { + + byte[] headBuf = new byte[2]; + is.read(headBuf); + if (headBuf[1] == 2) { + byte[] b = new byte[258]; + is.read(b); + + byte[] outBuf = new byte[256]; + os.write(outBuf); + os.flush(); + } else if (headBuf[1] == 23) { + byte[] b = new byte[130]; + is.read(b); + byte[] outBuf = new byte[192]; + os.write(outBuf); + os.flush(); + } + + int result = readU32(); + if (result != 0) { + System.out.println("faild authentication "); + throw new IOException(); + } + + } + + /* + * default port number is 5999. + */ + public void selectPort(int p) { + if (servSock != null) + return; + int port = p; + while (true) { + try { + initServSock(port); + break; + } catch (BindException e) { + port++; + continue; + } catch (IOException e) { + + } + } + System.out.println("accept port = " + port); + } + + int getAcceptPort() { + return acceptPort; + } + + void setSoTimeout(int num) throws IOException { + servSock.setSoTimeout(num); + } + + public Socket accept() throws IOException { + return servSock.accept(); + } + + void addSock(Socket sock) { + cliList.add(sock); + } + + void addSockTmp(Socket sock) { + System.out.println("connected " + sock.getInetAddress()); + cliListTmp.add(sock); + } + + boolean markSupported() { + return is.markSupported(); + } + + void readServerInit() throws IOException { + + is.mark(255); + skipBytes(20); + int nlen = readU32(); + int blen = 20 + 4 + nlen; + initData = new byte[blen]; + is.reset(); + + is.mark(blen); + readFully(initData); + is.reset(); + + framebufferWidth = readU16(); + framebufferHeight = readU16(); + bitsPerPixel = readU8(); + depth = readU8(); + bigEndian = (readU8() != 0); + trueColour = (readU8() != 0); + redMax = readU16(); + greenMax = readU16(); + blueMax = readU16(); + redShift = readU8(); + greenShift = readU8(); + blueShift = readU8(); + byte[] pad = new byte[3]; + readFully(pad); + int nameLength = readU32(); + byte[] name = new byte[nameLength]; + readFully(name); + desktopName = new String(name); + + // Read interaction capabilities (TightVNC protocol extensions) + if (protocolTightVNC) { + int nServerMessageTypes = readU16(); + int nClientMessageTypes = readU16(); + int nEncodingTypes = readU16(); + readU16(); + readCapabilityList(serverMsgCaps, nServerMessageTypes); + readCapabilityList(clientMsgCaps, nClientMessageTypes); + readCapabilityList(encodingCaps, nEncodingTypes); + } + + inNormalProtocol = true; + } + + void sendRfbVersion(OutputStream os) throws IOException { + // os.write(versionMsg_3_8.getBytes()); + os.write(versionMsg_3_855.getBytes()); + } + + int readVersionMsg(InputStream is, OutputStream os) throws IOException { + + byte[] b = new byte[12]; + + is.read(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"); + } + + int rfbMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); + int rfbMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0'); + + if (rfbMajor < 3) { + throw new IOException( + "RFB server does not support protocol version 3"); + } + + if (rfbMinor == 855) { + sendProxyFlag(os); + if (proxyFlag) + sendPortNumber(os); + } + return rfbMinor; + } + + void sendProxyFlag(OutputStream os) throws IOException { + if (proxyFlag) + os.write(1); + else + os.write(0); + } + + boolean readProxyFlag() throws IOException { + int flag = readU8(); + if (flag == 1) + return true; + else + return false; + } + + void sendPortNumber(OutputStream os) throws IOException { + byte[] b = new byte[4]; + b = castIntByte(geth.port); + os.write(b); + } + + void sendSecurityType(OutputStream os) throws IOException { + // number-of-security-types + os.write(1); + // security-types + // 1:None + os.write(1); + + /* + * os.write(4); os.write(30); os.write(31); os.write(32); os.write(35); + * os.flush(); + */ + } + + void readSecType(InputStream is) throws IOException { + byte[] b = new byte[1]; + is.read(b); + } + + void readSecType(InputStream is, OutputStream os) throws IOException { + byte[] b = new byte[1]; + is.read(b); + + int count = 260; + int[] data = { 0, 2, 0, -128, -1, -1, -1, -1, -1, -1, -1, -1, -55, 15, + -38, -94, 33, 104, -62, 52, -60, -58, 98, -117, -128, -36, 28, + -47, 41, 2, 78, 8, -118, 103, -52, 116, 2, 11, -66, -90, 59, + 19, -101, 34, 81, 74, 8, 121, -114, 52, 4, -35, -17, -107, 25, + -77, -51, 58, 67, 27, 48, 43, 10, 109, -14, 95, 20, 55, 79, + -31, 53, 109, 109, 81, -62, 69, -28, -123, -75, 118, 98, 94, + 126, -58, -12, 76, 66, -23, -90, 55, -19, 107, 11, -1, 92, -74, + -12, 6, -73, -19, -18, 56, 107, -5, 90, -119, -97, -91, -82, + -97, 36, 17, 124, 75, 31, -26, 73, 40, 102, 81, -20, -26, 83, + -127, -1, -1, -1, -1, -1, -1, -1, -1, -111, 73, -29, 30, 57, + -67, -75, -77, -49, -50, -99, -76, -80, -80, 14, 65, 57, -105, + -103, -54, -102, 3, 39, -44, 39, 35, 118, -84, -64, 37, -117, + -21, 89, -31, -68, 70, 5, 122, -92, -119, 9, 121, 63, -112, + -60, 122, -46, -69, -36, 92, -103, -92, 74, 92, -73, 87, 120, + -8, 116, -47, 111, 20, -41, 110, 122, -3, -94, 14, 42, -51, + -59, 48, -54, -125, 117, 60, 77, -52, -31, 98, 32, -2, -102, + -15, -29, 58, -14, -106, -116, -32, -86, 50, -32, -16, -3, + -123, 87, 88, -118, 10, 120, -107, -37, 125, -110, 59, 87, 93, + -24, 124, -99, 18, 78, -13, -49, -34, -24, -27, 1, 114, -67, + -98, -56, -3, 85, -67, -126, 77 }; + for (int i = 0; i < count; i++) { + os.write((byte) data[i]); + os.flush(); + } + + byte[] c = new byte[256]; + is.read(c); + + System.out.println(new String(c)); + + } + + void sendSecResult(OutputStream os) throws IOException { + byte[] b = castIntByte(0); + os.write(b); + } + + void readClientInit(InputStream in) throws IOException { + byte[] b = new byte[0]; + in.read(b); + } + + void sendInitData(OutputStream os) throws IOException { + os.write(initData); + } + + void sendPngImage() { + try { + for (Socket cli : cliListTmp) { + try { + sendPngData(cli); + addSock(cli); + } catch (IOException e) { + // if socket closed + cliListTmp.remove(cli); + } + } + // System.out.println("cliSize="+cliSize()); + } catch (Exception e) { + } + cliListTmp.clear(); + } + + boolean ready() throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + return br.ready(); + } + + int cliSize() { + return cliList.size(); + } + + void printNumBytesRead() { + System.out.println("numBytesRead=" + numBytesRead); + } + + void regiFramebufferUpdate() throws IOException { + is.mark(20); + messageType = readU8(); // 0 + skipBytes(1); // 1 + rectangles = readU16(); // 2 + rectX = readU16(); // 4 + rectY = readU16(); // 6 + rectW = readU16(); // 8 + rectH = readU16(); // 10 + encoding = readU32(); // 12 + // System.out.println("encoding = "+encoding); + if (encoding == EncodingZRLE || encoding == EncodingZRLEE + || encoding == EncodingZlib) + zLen = readU32(); + else + zLen = 0; + //System.out.println(zLen); + is.reset(); + + } + + int checkAndMark() throws IOException { + int dataLen; + switch (encoding) { + case RfbProto.EncodingRaw: + dataLen = rectW * rectH * 4 + 16; + // is.mark(dataLen); + break; + case RfbProto.EncodingCopyRect: + dataLen = 16 + 4; + // is.mark(dataLen); + break; + case RfbProto.EncodingRRE: + case RfbProto.EncodingCoRRE: + case RfbProto.EncodingHextile: + case RfbProto.EncodingTight: + dataLen = zLen + 20; + // is.mark(dataLen); + break; + case RfbProto.EncodingZlib: + case RfbProto.EncodingZRLE: + case RfbProto.EncodingZRLEE: + dataLen = zLen + 20; + // is.mark(dataLen); + break; + case RfbProto.EncodingXCursor: + case RfbProto.EncodingRichCursor: + int pixArray = rectW * rectH * 4; + int u8Array = (int) Math.floor((rectW + 7) / 8) * rectH; + dataLen = pixArray + u8Array; + printFramebufferUpdate(); + // is.mark(dataLen); + break; + default: + dataLen = 1000000; + // is.mark(dataLen); + } + return dataLen; + } + + void sendDataToClient() throws Exception { + regiFramebufferUpdate(); + printFramebufferUpdate(); + int dataLen = checkAndMark(); + readSendData(dataLen); + } + + BufferedImage createBufferedImage(Image img) { + BufferedImage bimg = new BufferedImage(img.getWidth(null), + img.getHeight(null), BufferedImage.TYPE_INT_RGB); + + Graphics g = bimg.getGraphics(); + g.drawImage(img, 0, 0, null); + g.dispose(); + return bimg; + } + + void createPngBytes(BufferedImage bimg) throws IOException { + pngBytes = getImageBytes(bimg, "png"); + } + + byte[] getBytes(BufferedImage img) throws IOException { + byte[] b = getImageBytes(img, "png"); + return b; + } + + byte[] getImageBytes(BufferedImage image, String imageFormat) + throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + BufferedOutputStream os = new BufferedOutputStream(bos); + image.flush(); + ImageIO.write(image, imageFormat, os); + os.flush(); + os.close(); + return bos.toByteArray(); + } + + void sendPngData(Socket sock) throws IOException { + byte[] dataLength = castIntByte(pngBytes.length); + sock.getOutputStream().write(dataLength); + sock.getOutputStream().write(pngBytes); + } + + byte[] castIntByte(int len) { + byte[] b = new byte[4]; + b[0] = (byte) ((len >>> 24) & 0xFF); + b[1] = (byte) ((len >>> 16) & 0xFF); + b[2] = (byte) ((len >>> 8) & 0xFF); + b[3] = (byte) ((len >>> 0) & 0xFF); + return b; + } + + BufferedImage createBimg() throws IOException { + BufferedImage bimg = ImageIO.read(new ByteArrayInputStream(pngBytes)); + return bimg; + } + + void printFramebufferUpdate() { + /* + * System.out.println("messageType=" + messageType); + * System.out.println("rectangles=" + rectangles); + * System.out.println("encoding=" + encoding); + * System.out.println("rectX = "+rectX+": rectY = "+rectY); + * System.out.println("rectW = "+rectW+": rectH = "+rectH); + */ + switch (encoding) { + case RfbProto.EncodingRaw: + System.out.println("rectW * rectH * 4 + 16 =" + rectW * rectH * 4 + + 16); + break; + default: + } + } + + int returnMsgtype() { + return messageType; + } + + void readSpeedCheck() throws IOException { + byte[] b = new byte[1]; + readFully(b); + } + + void startSpeedCheck() { + ByteBuffer b = ByteBuffer.allocate(10); + b.put((byte) SpeedCheckMillis); + b.flip(); + startCheckTime = System.currentTimeMillis(); + System.out.println("startChckTime = " + startCheckTime); + LinkedList bufs = new LinkedList(); + bufs.add(b); + multicastqueue.put(bufs); + } + + void endSpeedCheck() { + long accTime = System.currentTimeMillis(); + long time = accTime - startCheckTime; + System.out.println("checkMillis: " + time); + } + + synchronized void changeStatusFlag() { + printStatusFlag = true; + } + + void printMills() { + if (printStatusFlag) { + + changeStatusFlag(); + } else { + changeStatusFlag(); + } + } + + void speedCheckMillis() { + Runnable stdin = new Runnable() { + public void run() { + int c; + try { + while ((c = System.in.read()) != -1) { + switch (c) { + case 's': + break; + default: + startSpeedCheck(); + break; + } + } + } catch (IOException e) { + System.out.println(e); + } + } + }; + + new Thread(stdin).start(); + } + + void requestThreadStart() { + requestThread.start(); + } + + public synchronized void requestThreadNotify() { + rThread.reStart(); + } + + /** + * gzip byte arrays + * + * @param deflater + * @param inputs + * byte data[] + * @param inputIndex + * @param outputs + * byte data[] + * @return byte length in last byte array + * @throws IOException + */ + public int zip(Deflater deflater, LinkedList inputs, + int inputIndex, LinkedList outputs) throws IOException { + int len = 0; + ByteBuffer c1 = ByteBuffer.allocate(INFLATE_BUFSIZE); + while (inputIndex < inputs.size()) { + ByteBuffer b1 = inputs.get(inputIndex++); + deflater.setInput(b1.array(), b1.position(), b1.remaining()); + /** + * If we finish() stream and reset() it, Deflater start new gzip + * stream, this makes continuous zlib reader unhappy. if we remove + * finish(), Deflater.deflate() never flushes its output. The + * original zlib deflate has flush flag. I'm pretty sure this a kind + * of bug of Java library. + */ + if (inputIndex == inputs.size()) + deflater.finish(); + int len1 = 0; + do { + len1 = deflater.deflate(c1.array(), c1.position(), + c1.remaining()); + if (len1 > 0) { + len += len1; + c1.position(c1.position() + len1); + if (c1.remaining() == 0) { + c1.flip(); + outputs.addLast(c1); + c1 = ByteBuffer.allocate(INFLATE_BUFSIZE); + } + } + } while (len1 > 0 || !deflater.needsInput()); // &&!deflater.finished()); + } + if (c1.position() != 0) { + c1.flip(); + outputs.addLast(c1); + } + deflater.reset(); + return len; + } + + /** + * gunzip byte arrays + * + * @param inflater + * @param inputs + * byte data[] + * @param outputs + * byte data[] + * @return number of total bytes + * @throws IOException + */ + public int unzip(Inflater inflater, LinkedList inputs, + int inputIndex, LinkedList outputs, int bufSize) + throws DataFormatException { + int len = 0; + ByteBuffer buf = ByteBuffer.allocate(bufSize); + while (inputIndex < inputs.size()) { + ByteBuffer input = inputs.get(inputIndex++); + inflater.setInput(input.array(), input.position(), input.limit()); + // if (inputIndex==inputs.size()) if inflater/deflater has symmetry, + // we need this + // inflater.end(); but this won't work + do { + int len0 = inflater.inflate(buf.array(), buf.position(), + buf.remaining()); + if (len0 > 0) { + buf.position(buf.position() + len0); + len += len0; + if (buf.remaining() == 0) { + buf.flip(); + outputs.addLast(buf); + buf = ByteBuffer.allocate(bufSize); + } + } + } while (!inflater.needsInput()); + } + if (buf.position() != 0) { + buf.flip(); + outputs.addLast(buf); + } + return len; + } + + float maxMag = 1; + + /** + * send data to clients + * + * @param dataLen + * @throws IOException + * @throws DataFormatException + * + * Zlibed packet is compressed in context dependent way, that + * is, it have to send from the beginning. But this is + * impossible. So we have to compress it again for each clients. + * Separate deflater for each clients is necessary. + * + * Java's deflater does not support flush. This means to get the + * result, we have to finish the compression. Reseting start new + * compression, but it is not accepted well in zlib continuous + * reading. So we need new Encoding ZRLEE which reset decoder + * for each packet. ZRLEE can be invisible from user, but it + * have to be implemented in the clients. ZRLEE compression is + * not context dependent, so no recompression is necessary. + */ + void sendDataCheckDelay() { + LinkedList bufs = new LinkedList(); + ByteBuffer b = ByteBuffer.allocate(1); + b.put((byte) CheckDelay); + b.position(0); + bufs.add(b); + multicastqueue.put(bufs); + } + + void readSendData(int dataLen) throws IOException, DataFormatException { + LinkedList bufs = new LinkedList(); + ByteBuffer header = ByteBuffer.allocate(16); + readFully(header.array(), 0, 16); + header.limit(16); + if (header.get(0) == RfbProto.FramebufferUpdate) { + int encoding = header.getInt(12); + if (encoding == RfbProto.EncodingZRLE + || encoding == RfbProto.EncodingZlib) { // ZRLEE is already + // recompressed + ByteBuffer len = ByteBuffer.allocate(4); + readFully(len.array(), 0, 4); + len.limit(4); + ByteBuffer inputData = ByteBuffer.allocate(dataLen - 20); + + startTiming(); + readFully(inputData.array(), 0, inputData.capacity()); + System.out.println(dataLen); + inputData.limit(dataLen - 20); + stopTiming(); + + LinkedList inputs = new LinkedList(); + inputs.add(inputData); + + header.putInt(12, RfbProto.EncodingZRLEE); // means recompress + // every time + // using new Deflecter every time is incompatible with the + // protocol, clients have to be modified. + Deflater nDeflater = deflater; // new Deflater(); + LinkedList out = new LinkedList(); + unzip(inflater, inputs, 0, out, INFLATE_BUFSIZE); + // dump32(inputs); + int len2 = zip(nDeflater, out, 0, bufs); + ByteBuffer blen = ByteBuffer.allocate(4); + blen.putInt(len2); + blen.flip(); + bufs.addFirst(blen); + + bufs.addFirst(header); + if(dataLen<=64000) + multicastqueue.put(bufs); + // is.reset(); + + /* + * System.out.println("ZRLE = "+dataLen); + * System.out.println("ZRLEE = "+(len2+20)); float mag = + * (float)dataLen / (float)(len2 + 20); + * System.out.println("ZRLE / ZRLEE = "+ mag); if(mag > maxMag) + * maxMag = mag; System.out.println("maxMag = "+maxMag); + */ + return; + } + bufs.add(header); + if (dataLen > 16) { + ByteBuffer b = ByteBuffer.allocate(dataLen - 16); + startTiming(); + readFully(b.array(), 0, dataLen - 16); + b.limit(dataLen - 16); + stopTiming(); + bufs.add(b); + } + multicastqueue.put(bufs); + // is.reset(); + return; + } + is.reset(); + + // It may be compressed. We can inflate here to avoid repeating clients + // decompressing here, + // but it may generate too many large data. It is better to do it in + // each client. + // But we have do inflation for all input data, so we have to do it + // here. + } + + public void newClient(AcceptThread acceptThread, final Socket newCli, + final OutputStream os, final InputStream is) throws IOException { + // createBimgFlag = true; + // rfb.addSockTmp(newCli); + // addSock(newCli); + final int myId = clients; + final MulticastQueue.Client> c = multicastqueue + .newClient(); + final AtomicInteger writerRunning = new AtomicInteger(); + writerRunning.set(1); + /** + * Timeout thread. If a client is suspended, it has top of queue + * indefinitely, which caused memory overflow. After the timeout, we + * poll the queue and discard it. Start long wait if writer is running. + */ + final Runnable timer = new Runnable() { + public void run() { + int count = 0; + for (;;) { + long timeout = 50000 / 8; + try { + synchronized (this) { + int state, flag; + writerRunning.set(0); + wait(timeout); + flag = 0; + while ((state = writerRunning.get()) == 0) { + c.poll(); // discard, should be timeout + count++; + if (flag == 0) { + System.out.println("Discarding " + myId + + " count=" + count); + flag = 1; + } + wait(10); // if this is too short, writer cannot + // take the poll, if this is too + // long, memory will overflow... + } + if (flag == 1) + System.out.println("Resuming " + myId + + " count=" + count); + if (state != 1) { + System.out.println("Client died " + myId); + break; + } + } + } catch (InterruptedException e) { + } + } + } + }; + new Thread(timer).start(); + /** + * discard all incoming from clients + */ + final Runnable reader = new Runnable() { + public void run() { + byte b[] = new byte[4096]; + for (;;) { + try { + int c = is.read(b); + if (c <= 0) + throw new IOException(); + // System.out.println("client read "+c); + } catch (IOException e) { + try { + writerRunning.set(2); + os.close(); + is.close(); + } catch (IOException e1) { + } + return; + } + } + } + }; + /** + * send packets to a client + */ + Runnable sender = new Runnable() { + public void run() { + writerRunning.set(1); + try { + requestThreadNotify(); + // rThread.checkDelay(); + + /** + * initial connection of RFB protocol + */ + sendRfbVersion(os); + // readVersionMsg(is); + int rfbMinor = readVersionMsg(is, os); + sendSecurityType(os); + readSecType(is); + sendSecResult(os); + readClientInit(is); + sendInitData(os); + new Thread(reader).start(); // discard incoming packet here + // after. + // writeFramebufferUpdateRequest(0,0, framebufferWidth, + // framebufferHeight, false ); + int i = 0; + for (;;) { + LinkedList bufs = c.poll(); + int inputIndex = 0; + ByteBuffer header = bufs.get(inputIndex); + if (header == null) + continue; + else if (header.get(0) == RfbProto.CheckDelay) { + writeToClient(os, bufs, inputIndex); + continue; + } else if (header.get(0) == RfbProto.FramebufferUpdate) { + // System.out.println("client "+ myId); + } + /* + if(i%20==0){ + sendDataCheckDelay(); + } + i++; + */ + writeToClient(os, bufs, inputIndex); + writerRunning.set(1); // yes my client is awaking. + } + } catch (IOException e) { + try { + writerRunning.set(2); + os.close(); + } catch (IOException e1) { + } + /* if socket closed cliList.remove(newCli); */ + } + } + + public void writeToClient(final OutputStream os, + LinkedList bufs, int inputIndex) + throws IOException { + while (inputIndex < bufs.size()) { + ByteBuffer b = bufs.get(inputIndex++); + os.write(b.array(), b.position(), b.limit()); + } + os.flush(); + } + }; + clients++; + new Thread(sender).start(); + + } + + public void dump32(LinkedList bufs) { + int len = 0; + for (ByteBuffer b : bufs) + len += b.remaining(); + ByteBuffer top = bufs.getFirst(); + ByteBuffer end = bufs.getLast(); + System.err.println("length: " + len); + System.err.print("head 0: "); + for (int i = 0; i < 16 && i < top.remaining(); i++) { + System.err.print(" " + top.get(i)); + } + System.err.print("tail 0: "); + for (int i = 0; i < 16 && i < end.remaining(); i++) { + System.err.print(" " + end.get(i)); + } + System.err.println(); + } + + @Test + public void test1() { + try { + LinkedList in = new LinkedList(); + LinkedList out = new LinkedList(); + LinkedList out2 = new LinkedList(); + // if (false) { + // for(int i=0;i<10;i++) { + // in.add(ByteBuffer.wrap("test1".getBytes())); + // in.add(ByteBuffer.wrap("test2".getBytes())); + // in.add(ByteBuffer.wrap("test3".getBytes())); + // in.add(ByteBuffer.wrap("test44".getBytes())); + // } + // } else + { + String t = ""; + for (int i = 0; i < 10; i++) { + t += "test1"; + t += "test2"; + t += "test3"; + t += "test44"; + } + in.add(ByteBuffer.wrap(t.getBytes())); + } + + LinkedList in1 = clone(in); + + Deflater deflater = new Deflater(); + zip(deflater, in, 0, out); + // LinkedList out3 = clone(out); zipped result is depend + // on deflator's state + unzip(inflater, out, 0, out2, INFLATE_BUFSIZE); + // inflater.reset(); + equalByteBuffers(in1, out2); + LinkedList out4 = new LinkedList(); + deflater = new Deflater(); + zip(deflater, out2, 0, out4); + LinkedList out5 = new LinkedList(); + unzip(inflater, out4, 0, out5, INFLATE_BUFSIZE); + int len = equalByteBuffers(in1, out5); + + System.out.println("Test Ok. " + len); + } catch (Exception e) { + assertEquals(0, 1); + } + } + + private LinkedList clone(LinkedList in) { + LinkedList copy = new LinkedList(); + for (ByteBuffer b : in) { + ByteBuffer c = b.duplicate(); + copy.add(c); + } + return copy; + } + + public int equalByteBuffers(LinkedList in, + LinkedList out2) { + int len = 0; + Iterable i = byteBufferIterator(in); + Iterator o = byteBufferIterator(out2).iterator(); + + for (int b : i) { + len++; + if (o.hasNext()) { + int c = o.next(); + assertEquals(b, c); + } else + assertEquals(0, 1); + } + if (o.hasNext()) + assertEquals(0, 1); + // System.out.println(); + return len; + } + + private Iterable byteBufferIterator(final LinkedList in) { + return new Iterable() { + public Iterator iterator() { + return new Iterator() { + int bytes = 0; + int buffers = 0; + + public boolean hasNext() { + for (;;) { + if (buffers >= in.size()) + return false; + ByteBuffer b = in.get(buffers); + if (!(bytes < b.remaining())) { + buffers++; + bytes = 0; + } else + return true; + } + } + + public Byte next() { + ByteBuffer bf = in.get(buffers); + byte b = bf.get(bytes++); + if (bf.remaining() <= bytes) { + buffers++; + bytes = 0; + } + // System.out.print(b); + return b; + } + + public void remove() { + } + }; + } + }; + } + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/MyVncClient.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/MyVncClient.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,1285 @@ +package treeVnc; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.net.*; +import java.util.Random; + +import java.nio.ByteBuffer; + +public class MyVncClient extends VncViewer implements InterfaceForViewer, + java.lang.Runnable, WindowListener { + + /** + * + */ + private static final long serialVersionUID = 1L; + private boolean inAnApplet = true; + private boolean inSeparateFrame = false; + private Socket clientSocket = null; + private String parent, treenum; + private String leaderflag; + // private boolean runflag = false; + private boolean first = true; + + private EchoClient echoValue; + private int echoPort; + private String pHost; + private TextBoxClient getHost; + private GetBroadCastClient getBcast; + private Thread runBcast; + private BroadCastClient bCast; + boolean isClient = 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 void treeVncClient(String[] argv) { + isClient = true; + MyVncClient v = new MyVncClient(); + v.echoValue = null; + v.runClient(argv); + } + + private void runClient(String[] argv) { + mainArgs = argv; + System.out.println(mainArgs.length); + inAnApplet = false; + inSeparateFrame = true; + if (mainArgs.length != 0) { + if ("-c".equals(argv[0])) { + if(argv.length==3){ + pHost = argv[1]; + port = Integer.parseInt(argv[2]); + } else { + getHost = new TextBoxClient(); + getHost.ipRegister(); + pHost = getHost.getAddressOption(); + port = Integer.parseInt(getHost.getPortOption()); + } + } else { + bCast = new BroadCastClient("who"); + bCast.createSocket(); + bCast.sendData(); + getBcast = new GetBroadCastClient(); + if ("-reg".equals(argv[0]) && getNamePort() != null) { + getBcast.text.checkBox(getNamePort()); + getBcast.text.setButton(); + getBcast.text.visible(); + } else { + runBcast = new Thread(getBcast); + runBcast.start(); + getBcast.setStopFlag(true); + } + pHost = getBcast.text.getAddress(); + port = Integer.parseInt(getBcast.text.getPort()); + if (!("-reg".equals(argv[0]))) + getBcast.socketClose(); + + } + } else { + bCast = new BroadCastClient("who"); + bCast.createSocket(); + bCast.sendData(); + getBcast = new GetBroadCastClient(); + runBcast = new Thread(getBcast); + runBcast.start(); + getBcast.setStopFlag(true); + pHost = getBcast.text.getAddress(); + port = Integer.parseInt(getBcast.text.getPort()); + } + + // getBcast.text.checkBox(getNamePort()); + /* + * try { Thread.sleep(1000); } catch (InterruptedException e) { + * e.printStackTrace(); } + */ + + /* + * + * if(mainArgs.length == 0) getBcast.setStopFlag(true); else { getHost = + * new TextBoxClient(); getHost.ipRegister(); } + * + * if (mainArgs.length > 0) pHost = getHost.getAddressOption(); //pHost + * = mainArgs[0]; else { pHost = getBcast.text.getAddress(); } + * + * + * //pHost = "cls080.ie.u-ryukyu.ac.jp"; if (mainArgs.length > 0) port = + * Integer.parseInt(getHost.getPortOption()); //port = + * Integer.parseInt(mainArgs[1]); else { port = + * Integer.parseInt(getBcast.text.getPort()); getBcast.socketClose(); // + * port = 5999; } + */ + init(); + start_threads(); + start(); + + } + + public void init() { + + readParameters(); + + refApplet = this; + + if (inSeparateFrame) { + vncFrame = new Frame("TightVNC"); + if (!inAnApplet) { + vncFrame.add("Center", this); + } + vncContainer = vncFrame; + } else { + vncContainer = this; + } + + recordingSync = new Object(); + + options = new OptionsFrame(this); + clipboard = new ClipboardFrame(this); + if (RecordingFrame.checkSecurity()) + rec = new RecordingFrame(this); + + sessionFileName = null; + recordingActive = false; + recordingStatusChanged = false; + cursorUpdatesDef = null; + eightBitColorsDef = null; + + if (inSeparateFrame) + vncFrame.addWindowListener(this); + + rfbThread = new Thread(this); + } + + public void update(Graphics g) { + } + + // + // run() - executed by the rfbThread to deal with the RFB socket. + // + + public void start_threads() { + rfbThread.start(); + } + + public void run() { + + gridbag = new GridBagLayout(); + vncContainer.setLayout(gridbag); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.anchor = GridBagConstraints.NORTHWEST; + + if (showControls) { + buttonPanel = new ButtonPanel(this); + gridbag.setConstraints(buttonPanel, gbc); + vncContainer.add(buttonPanel); + } + + try { + + if (first) { + connectAndAuthenticate(); + accThread = new Thread(new AcceptThread(rfb, 5999)); + accThread.start(); + first = false; + } else { + System.out.println("reConnectAndAuthenticate() "); + reConnectAndAuthenticate(); + // accThread = new Thread(new AcceptThread(rfb, 5999)); + // accThread.start(); + } + + doProtocolInitialisation(); + + createCanvas(0, 0); + + } catch (IOException e) { + try { + rfb.sock.close(); + + } catch (IOException e2) { + e2.printStackTrace(); + } + System.out.println("Socket error"); + // parent no find + Random rnd = new Random(); + long ran = rnd.nextInt(3000) + 3000; + System.out.println(ran); + + /** + * this while reconnection + */ + + int counter = 0; + vncFrame.setVisible(false); + vncFrame.dispose(); + + while (true) { + /** + * if my last node case reconnectoion stop + */ + + echoValue = new EchoClient(echoValue, this); + // echoValue = new EchoClient(echoValue); + + try { + Thread.sleep(ran); + } catch (InterruptedException e1) { + e1.printStackTrace(); + } + + if (counter >= 3) { + echoValue.openport(); + echoValue.notfoundParent(); + } + + echoValue.openport(); + + if (echoValue.lostHost()) { + break; + } + + counter++; + } + } catch (Exception e) { + System.out.println(e); + System.exit(0); + } + gbc.weightx = 1.0; + gbc.weighty = 1.0; + + if (inSeparateFrame) { + // Create a panel which itself is resizeable and can hold + // non-resizeable VncCanvas component at the top left corner. + Panel canvasPanel = new Panel(); + canvasPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); + canvasPanel.add(vc); + // Create a ScrollPane which will hold a panel with VncCanvas + // inside. + desktopScrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); + gbc.fill = GridBagConstraints.BOTH; + gridbag.setConstraints(desktopScrollPane, gbc); + desktopScrollPane.add(canvasPanel); + // 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(); + + } + + try { + if (showControls) + buttonPanel.enableButtons(); + + moveFocusToDesktop(); + + processNormalProtocol();// main loop + + } catch (NoRouteToHostException e) { + fatalError("Network error: no route to server: " + host, e); + } catch (UnknownHostException e) { + fatalError("Network error: server name unknown: " + host, e); + } catch (ConnectException e) { + fatalError("Network error: could not connect to server: " + host + + ":" + port, e); + } catch (EOFException e) { + + vncFrame.setVisible(false); + vncFrame.dispose(); + // num4 + if (leaderflag != null) { + while (true) { + // echoValue = new EchoClient(echoValue, this); + echoValue = new EchoClient(echoValue); + echoValue.openport(); + // runflag = echo.losthost(); + if (echoValue.lostHost()) { + break; + } + + } + } else { + + if (showOfflineDesktop) { + e.printStackTrace(); + System.out + .println("Network error: remote side closed connection"); + if (vc != null) { + vc.enableInput(false); + } + if (inSeparateFrame) { + vncFrame.setTitle(rfb.desktopName + " [disconnected]"); + } + if (rfb != null && !rfb.closed()) + rfb.close(); + if (showControls && buttonPanel != null) { + buttonPanel.disableButtonsOnDisconnect(); + if (inSeparateFrame) { + vncFrame.pack(); + } else { + validate(); + } + } + } else { + fatalError("Network error: remote side closed connection", + e); + } + } + } catch (IOException e) { + String str = e.getMessage(); + if (str != null && str.length() != 0) { + fatalError("Network Error: " + str, e); + } else { + fatalError(e.toString(), e); + } + } catch (Exception e) { + String str = e.getMessage(); + if (str != null && str.length() != 0) { + fatalError("Error: " + str, e); + } else { + fatalError(e.toString(), e); + } + } + + } + + // + // Create a VncCanvas instance. + // + + void createCanvas(int maxWidth, int maxHeight) throws IOException { + // Determine if Java 2D API is available and use a special + // version of VncCanvas if it is present. + vc = null; + try { + // This throws ClassNotFoundException if there is no Java 2D API. + Class cl = Class.forName("java.awt.Graphics2D"); + // If we could load Graphics2D class, then we can use VncCanvas2D. + cl = Class.forName("VncCanvas2"); + Class[] argClasses = { this.getClass(), Integer.TYPE, Integer.TYPE }; + java.lang.reflect.Constructor cstr = cl.getConstructor(argClasses); + Object[] argObjects = { this, new Integer(maxWidth), + new Integer(maxHeight) }; + vc = (VncCanvas) cstr.newInstance(argObjects); + } catch (Exception e) { + System.out.println("Warning: Java 2D API is not available"); + } + + // If we failed to create VncCanvas2D, use old VncCanvas. + if (vc == null) + vc = new VncCanvas(this, maxWidth, maxHeight); + } + + // + // Process RFB socket messages. + // If the rfbThread is being stopped, ignore any exceptions, + // otherwise rethrow the exception so it can be handled. + // + + void processNormalProtocol() throws Exception { + try { + vc.processNormalProtocol();// main loop + } catch (Exception e) { + if (rfbThread == null) { + System.out.println("Ignoring RFB socket exceptions" + + " because applet is stopping"); + } else { + throw e; + } + } + } + + // + // Connect to the RFB server and authenticate the user. + // + + void connectAndAuthenticate() throws Exception { + + showConnectionStatus("Initializing..."); + if (inSeparateFrame) { + vncFrame.pack(); + vncFrame.setVisible(true); + } else { + validate(); + } + + showConnectionStatus("Connecting to " + host + ", port " + port + "..."); + + rfb = new MyRfbProtoClient(pHost, port, this); + showConnectionStatus("Connected to server"); + + rfb.readVersionMsg(); + showConnectionStatus("RFB server supports protocol version " + + rfb.serverMajor + "." + rfb.serverMinor); + + rfb.writeVersionMsg(); + showConnectionStatus("Using RFB protocol version " + rfb.clientMajor + + "." + rfb.clientMinor); + + if (rfb.serverMinor == 855) { + /* + * if connect to proxy, userEchoPortFlag is true. if connect to + * client, userEchoPortFlag is false. + */ + boolean useEchoPortFlag = rfb.readProxyFlag(); + if (useEchoPortFlag) { + byte[] b = new byte[4]; + b = rfb.readEchoPort(); + echoPort = castByteInt(b); + + InetAddress addr = InetAddress.getByName(pHost); + String h = new String(addr.getHostAddress()); + + getParentName(); + if (!(h.equals(host))) { + rfb.changeParent(host, port); + rfb.readVersionMsg(); + rfb.writeVersionMsg(); + boolean flag = rfb.readProxyFlag(); + } + } + } + + int secType = rfb.negotiateSecurity(); + int authType; + if (secType == RfbProto.SecTypeTight) { + showConnectionStatus("Enabling TightVNC protocol extensions"); + rfb.setupTunneling(); + authType = rfb.negotiateAuthenticationTight(); + } else { + authType = secType; + } + + switch (authType) { + case RfbProto.AuthNone: + showConnectionStatus("No authentication needed"); + rfb.authenticateNone(); + break; + case RfbProto.AuthVNC: + showConnectionStatus("Performing standard VNC authentication"); + if (passwordParam != null) { + rfb.authenticateVNC(passwordParam); + } else { + String pw = askPassword(); + rfb.authenticateVNC(pw); + } + break; + default: + throw new Exception("Unknown authentication scheme " + authType); + } + } + + void reConnectAndAuthenticate() throws Exception { + + showConnectionStatus("Initializing..."); + + if (inSeparateFrame) { + vncFrame.pack(); + vncFrame.setVisible(true); + } else { + validate(); + } + + showConnectionStatus("Connecting to " + host + ", port " + port + "..."); + + rfb.changeParent(host, port); + + showConnectionStatus("Connected to server"); + + rfb.readVersionMsg(); + showConnectionStatus("RFB server supports protocol version " + + rfb.serverMajor + "." + rfb.serverMinor); + + rfb.writeVersionMsg(); + showConnectionStatus("Using RFB protocol version " + rfb.clientMajor + + "." + rfb.clientMinor); + + if (rfb.serverMinor == 855) { + boolean useEchoPortFlag = rfb.readProxyFlag(); + if (useEchoPortFlag) { + byte[] b = new byte[4]; + b = rfb.readEchoPort(); + echoPort = castByteInt(b); + + InetAddress addr = InetAddress.getByName(pHost); + String h = new String(addr.getHostAddress()); + + getParentName(); + if (!(h.equals(host))) { + rfb.changeParent(host, port); + rfb.readVersionMsg(); + rfb.writeVersionMsg(); + boolean flag = rfb.readProxyFlag(); + } + } + } + int secType = rfb.negotiateSecurity(); + int authType; + if (secType == RfbProto.SecTypeTight) { + showConnectionStatus("Enabling TightVNC protocol extensions"); + rfb.setupTunneling(); + authType = rfb.negotiateAuthenticationTight(); + } else { + authType = secType; + } + + switch (authType) { + case RfbProto.AuthNone: + showConnectionStatus("No authentication needed"); + rfb.authenticateNone(); + break; + case RfbProto.AuthVNC: + showConnectionStatus("Performing standard VNC authentication"); + if (passwordParam != null) { + rfb.authenticateVNC(passwordParam); + } else { + String pw = askPassword(); + rfb.authenticateVNC(pw); + } + break; + default: + throw new Exception("Unknown authentication scheme " + authType); + } + } + + // + // Show a message describing the connection status. + // To hide the connection status label, use (msg == null). + // + + void showConnectionStatus(String msg) { + if (msg == null) { + if (vncContainer.isAncestorOf(connStatusLabel)) { + vncContainer.remove(connStatusLabel); + } + return; + } + + System.out.println(msg); + + if (connStatusLabel == null) { + connStatusLabel = new Label("Status: " + msg); + connStatusLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); + } else { + connStatusLabel.setText("Status: " + msg); + } + + if (!vncContainer.isAncestorOf(connStatusLabel)) { + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.insets = new Insets(20, 30, 20, 30); + gridbag.setConstraints(connStatusLabel, gbc); + vncContainer.add(connStatusLabel); + } + + if (inSeparateFrame) { + vncFrame.pack(); + } else { + validate(); + } + } + + // + // Show an authentication panel. + // + + String askPassword() throws Exception { + showConnectionStatus(null); + + AuthPanel authPanel = new AuthPanel(this); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.ipadx = 100; + gbc.ipady = 50; + gridbag.setConstraints(authPanel, gbc); + vncContainer.add(authPanel); + + if (inSeparateFrame) { + vncFrame.pack(); + } else { + validate(); + } + + authPanel.moveFocusToDefaultField(); + String pw = authPanel.getPassword(); + vncContainer.remove(authPanel); + return pw; + } + + // + // Do the rest of the protocol initialisation. + // + + void doProtocolInitialisation() throws IOException { + rfb.writeClientInit(); + rfb.readServerInit(); + + System.out.println("Desktop name is " + rfb.desktopName); + System.out.println("Desktop size is " + rfb.framebufferWidth + " x " + + rfb.framebufferHeight); + + setEncodings(); + + showConnectionStatus(null); + } + + // + // Send current encoding list to the RFB server. + // + + int[] encodingsSaved; + int nEncodingsSaved; + + void setEncodings() { + setEncodings(false); + } + + void autoSelectEncodings() { + setEncodings(true); + } + + void setEncodings(boolean autoSelectOnly) { + if (options == null || rfb == null || !rfb.inNormalProtocol) + return; + + int preferredEncoding = options.preferredEncoding; + if (preferredEncoding == -1) { + long kbitsPerSecond = rfb.kbitsPerSecond(); + if (nEncodingsSaved < 1) { + // Choose Tight or ZRLE encoding for the very first update. + System.out.println("Using Tight/ZRLE encodings"); + preferredEncoding = RfbProto.EncodingTight; + } else if (kbitsPerSecond > 2000 + && encodingsSaved[0] != RfbProto.EncodingHextile) { + // Switch to Hextile if the connection speed is above 2Mbps. + System.out.println("Throughput " + kbitsPerSecond + + " kbit/s - changing to Hextile encoding"); + preferredEncoding = RfbProto.EncodingHextile; + } else if (kbitsPerSecond < 1000 + && encodingsSaved[0] != RfbProto.EncodingTight) { + // Switch to Tight/ZRLE if the connection speed is below 1Mbps. + System.out.println("Throughput " + kbitsPerSecond + + " kbit/s - changing to Tight/ZRLE encodings"); + preferredEncoding = RfbProto.EncodingTight; + } else { + // Don't change the encoder. + if (autoSelectOnly) + return; + preferredEncoding = encodingsSaved[0]; + } + } else { + // Auto encoder selection is not enabled. + if (autoSelectOnly) + return; + } + + int[] encodings = new int[20]; + int nEncodings = 0; + + encodings[nEncodings++] = preferredEncoding; + if (options.useCopyRect) { + encodings[nEncodings++] = RfbProto.EncodingCopyRect; + } + + if (preferredEncoding != RfbProto.EncodingTight) { + encodings[nEncodings++] = RfbProto.EncodingTight; + } + if (preferredEncoding != RfbProto.EncodingZRLE) { + encodings[nEncodings++] = RfbProto.EncodingZRLE; + } + if (preferredEncoding != RfbProto.EncodingHextile) { + encodings[nEncodings++] = RfbProto.EncodingHextile; + } + if (preferredEncoding != RfbProto.EncodingZlib) { + encodings[nEncodings++] = RfbProto.EncodingZlib; + } + if (preferredEncoding != RfbProto.EncodingCoRRE) { + encodings[nEncodings++] = RfbProto.EncodingCoRRE; + } + if (preferredEncoding != RfbProto.EncodingRRE) { + encodings[nEncodings++] = RfbProto.EncodingRRE; + } + + if (options.compressLevel >= 0 && options.compressLevel <= 9) { + encodings[nEncodings++] = RfbProto.EncodingCompressLevel0 + + options.compressLevel; + } + if (options.jpegQuality >= 0 && options.jpegQuality <= 9) { + encodings[nEncodings++] = RfbProto.EncodingQualityLevel0 + + options.jpegQuality; + } + + if (options.requestCursorUpdates) { + encodings[nEncodings++] = RfbProto.EncodingXCursor; + encodings[nEncodings++] = RfbProto.EncodingRichCursor; + if (!options.ignoreCursorUpdates) + encodings[nEncodings++] = RfbProto.EncodingPointerPos; + } + + encodings[nEncodings++] = RfbProto.EncodingLastRect; + encodings[nEncodings++] = RfbProto.EncodingNewFBSize; + + boolean encodingsWereChanged = false; + if (nEncodings != nEncodingsSaved) { + encodingsWereChanged = true; + } else { + for (int i = 0; i < nEncodings; i++) { + if (encodings[i] != encodingsSaved[i]) { + encodingsWereChanged = true; + break; + } + } + } + + if (encodingsWereChanged) { + try { + rfb.writeSetEncodings(encodings, nEncodings); + if (vc != null) { + vc.softCursorFree(); + } + } catch (Exception e) { + e.printStackTrace(); + } + encodingsSaved = encodings; + nEncodingsSaved = nEncodings; + } + } + + // + // setCutText() - send the given cut text to the RFB server. + // + + void setCutText(String text) { + try { + if (rfb != null && rfb.inNormalProtocol) { + rfb.writeClientCutText(text); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // + // Order change in session recording status. To stop recording, pass + // null in place of the fname argument. + // + + void setRecordingStatus(String fname) { + synchronized (recordingSync) { + sessionFileName = fname; + recordingStatusChanged = true; + } + } + + // + // Start or stop session recording. Returns true if this method call + // causes recording of a new session. + // + + boolean checkRecordingStatus() throws IOException { + synchronized (recordingSync) { + if (recordingStatusChanged) { + recordingStatusChanged = false; + if (sessionFileName != null) { + startRecording(); + return true; + } else { + stopRecording(); + } + } + } + return false; + } + + // + // Start session recording. + // + + protected void startRecording() throws IOException { + synchronized (recordingSync) { + if (!recordingActive) { + // Save settings to restore them after recording the session. + cursorUpdatesDef = options.choices[options.cursorUpdatesIndex] + .getSelectedItem(); + eightBitColorsDef = options.choices[options.eightBitColorsIndex] + .getSelectedItem(); + // Set options to values suitable for recording. + options.choices[options.cursorUpdatesIndex].select("Disable"); + options.choices[options.cursorUpdatesIndex].setEnabled(false); + options.setEncodings(); + options.choices[options.eightBitColorsIndex].select("No"); + options.choices[options.eightBitColorsIndex].setEnabled(false); + options.setColorFormat(); + } else { + rfb.closeSession(); + } + + System.out.println("Recording the session in " + sessionFileName); + rfb.startSession(sessionFileName); + recordingActive = true; + } + } + + // + // Stop session recording. + // + + protected void stopRecording() throws IOException { + synchronized (recordingSync) { + if (recordingActive) { + // Restore options. + options.choices[options.cursorUpdatesIndex] + .select(cursorUpdatesDef); + options.choices[options.cursorUpdatesIndex].setEnabled(true); + options.setEncodings(); + options.choices[options.eightBitColorsIndex] + .select(eightBitColorsDef); + options.choices[options.eightBitColorsIndex].setEnabled(true); + options.setColorFormat(); + + rfb.closeSession(); + System.out.println("Session recording stopped."); + } + sessionFileName = null; + recordingActive = false; + } + } + + /** + * readParameters() - read parameters from the html source or from the + * command line. On the command line, the arguments are just a sequence of + * param_name/param_value pairs where the names and values correspond to + * those expected in the html applet tag source. + */ + + void readParameters() { + /* + * host = readParameter("HOST", !inAnApplet); + * + * if (host == null) { host = getCodeBase().getHost(); if + * (host.equals("")) { fatalError("HOST parameter not specified"); } } + * + * port = readIntParameter("PORT", 5550); + */ + + // Read "ENCPASSWORD" or "PASSWORD" parameter if specified. + readPasswordParameters(); + + String str; + if (inAnApplet) { + str = readParameter("Open New Window", false); + if (str != null && str.equalsIgnoreCase("Yes")) + inSeparateFrame = true; + } + + // "Show Controls" set to "No" disables button panel. + showControls = true; + str = readParameter("Show Controls", false); + if (str != null && str.equalsIgnoreCase("No")) + showControls = false; + + // "Offer Relogin" set to "No" disables "Login again" and "Close + // window" buttons under error messages in applet mode. + offerRelogin = true; + str = readParameter("Offer Relogin", false); + if (str != null && str.equalsIgnoreCase("No")) + offerRelogin = false; + + // Do we continue showing desktop on remote disconnect? + showOfflineDesktop = false; + str = readParameter("Show Offline Desktop", false); + if (str != null && str.equalsIgnoreCase("Yes")) + showOfflineDesktop = true; + + // Fine tuning options. + deferScreenUpdates = readIntParameter("Defer screen updates", 20); + deferCursorUpdates = readIntParameter("Defer cursor updates", 10); + deferUpdateRequests = readIntParameter("Defer update requests", 0); + + // Debugging options. + debugStatsExcludeUpdates = readIntParameter("DEBUG_XU", 0); + debugStatsMeasureUpdates = readIntParameter("DEBUG_CU", 0); + + // SocketFactory. + socketFactory = readParameter("SocketFactory", false); + } + + // + // Read password parameters. If an "ENCPASSWORD" parameter is set, + // then decrypt the password into the passwordParam string. Otherwise, + // try to read the "PASSWORD" parameter directly to passwordParam. + // + + private void readPasswordParameters() { + String encPasswordParam = readParameter("ENCPASSWORD", false); + if (encPasswordParam == null) { + passwordParam = readParameter("PASSWORD", false); + + } else { + // ENCPASSWORD is hexascii-encoded. Decode. + byte[] pw = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int len = encPasswordParam.length() / 2; + if (len > 8) + len = 8; + for (int i = 0; i < len; i++) { + String hex = encPasswordParam.substring(i * 2, i * 2 + 2); + Integer x = new Integer(Integer.parseInt(hex, 16)); + pw[i] = x.byteValue(); + } + // Decrypt the password. + byte[] key = { 23, 82, 107, 6, 35, 78, 88, 7 }; + DesCipher des = new DesCipher(key); + des.decrypt(pw, 0, pw, 0); + passwordParam = new String(pw); + + } + } + + public String readParameter(String name, boolean required) { + if (inAnApplet) { + String s = getParameter(name); + if ((s == null) && required) { + fatalError(name + " parameter not specified"); + } + return s; + } + /* + * for (int i = 0; i < mainArgs.length; i += 2) { if + * (mainArgs[i].equalsIgnoreCase(name)) { try { return mainArgs[i + 1]; + * } catch (Exception e) { if (required) { fatalError(name + + * " parameter not specified"); } return null; } } } + */ + if (required) { + fatalError(name + " parameter not specified"); + } + return null; + } + + int readIntParameter(String name, int defaultValue) { + String str = readParameter(name, false); + int result = defaultValue; + if (str != null) { + try { + result = Integer.parseInt(str); + } catch (NumberFormatException e) { + } + } + return result; + } + + // + // moveFocusToDesktop() - move keyboard focus either to VncCanvas. + // + + void moveFocusToDesktop() { + if (vncContainer != null) { + if (vc != null && vncContainer.isAncestorOf(vc)) + vc.requestFocus(); + } + } + + // + // disconnect() - close connection to server. + // + + synchronized public void disconnect() { + System.out.println("Disconnecting"); + + if (vc != null) { + double sec = (System.currentTimeMillis() - vc.statStartTime) / 1000.0; + double rate = Math.round(vc.statNumUpdates / sec * 100) / 100.0; + int nRealRects = vc.statNumPixelRects; + int nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects; + System.out.println("Updates received: " + vc.statNumUpdates + " (" + + nRealRects + " rectangles + " + nPseudoRects + + " pseudo), " + rate + " updates/sec"); + int numRectsOther = nRealRects - vc.statNumRectsTight + - vc.statNumRectsZRLE - vc.statNumRectsHextile + - vc.statNumRectsRaw - vc.statNumRectsCopy; + System.out.println("Rectangles:" + " Tight=" + vc.statNumRectsTight + + "(JPEG=" + vc.statNumRectsTightJPEG + ") ZRLE=" + + vc.statNumRectsZRLE + " Hextile=" + + vc.statNumRectsHextile + " Raw=" + vc.statNumRectsRaw + + " CopyRect=" + vc.statNumRectsCopy + " other=" + + numRectsOther); + + int raw = vc.statNumBytesDecoded; + int compressed = vc.statNumBytesEncoded; + if (compressed > 0) { + double ratio = Math.round((double) raw / compressed * 1000) / 1000.0; + System.out.println("Pixel data: " + vc.statNumBytesDecoded + + " bytes, " + vc.statNumBytesEncoded + + " compressed, ratio " + ratio); + } + } + + if (rfb != null && !rfb.closed()) + rfb.close(); + options.dispose(); + clipboard.dispose(); + if (rec != null) + rec.dispose(); + + if (inAnApplet) { + showMessage("Disconnected"); + } else { + System.exit(0); + } + } + + // + // fatalError() - print out a fatal error message. + // FIXME: Do we really need two versions of the fatalError() method? + // + + synchronized public void fatalError(String str) { + System.out.println(str); + + if (inAnApplet) { + // vncContainer null, applet not inited, + // can not present the error to the user. + Thread.currentThread().stop(); + } else { + System.exit(1); + } + } + + synchronized public void fatalError(String str, Exception e) { + + if (rfb != null && rfb.closed()) { + // Not necessary to show error message if the error was caused + // by I/O problems after the rfb.close() method call. + System.out.println("RFB thread finished"); + return; + } + + System.out.println(str); + e.printStackTrace(); + + if (rfb != null) + rfb.close(); + + if (inAnApplet) { + showMessage(str); + } else { + System.exit(1); + } + } + + // + // Show message text and optionally "Relogin" and "Close" buttons. + // + + void showMessage(String msg) { + vncContainer.removeAll(); + + Label errLabel = new Label(msg, Label.CENTER); + errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); + + if (offerRelogin) { + + Panel gridPanel = new Panel(new GridLayout(0, 1)); + Panel outerPanel = new Panel(new FlowLayout(FlowLayout.LEFT)); + outerPanel.add(gridPanel); + vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 16)); + vncContainer.add(outerPanel); + Panel textPanel = new Panel(new FlowLayout(FlowLayout.CENTER)); + textPanel.add(errLabel); + gridPanel.add(textPanel); + gridPanel.add(new ReloginPanel(this)); + + } else { + + vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30)); + vncContainer.add(errLabel); + + } + + if (inSeparateFrame) { + vncFrame.pack(); + } else { + validate(); + } + } + + // + // Stop the applet. + // Main applet thread will terminate on first exception + // after seeing that rfbThread has been set to null. + // + + public void stop() { + System.out.println("Stopping applet"); + rfbThread = null; + } + + // + // This method is called before the applet is destroyed. + // + + public void destroy() { + System.out.println("Destroying applet"); + + vncContainer.removeAll(); + options.dispose(); + clipboard.dispose(); + if (rec != null) + rec.dispose(); + if (rfb != null && !rfb.closed()) + rfb.close(); + if (inSeparateFrame) + vncFrame.dispose(); + } + + // + // Start/stop receiving mouse events. + // + + public void enableInput(boolean enable) { + vc.enableInput(enable); + } + + // + // Close application properly on window close event. + // + + public void windowClosing(WindowEvent evt) { + System.out.println("Closing window"); + if (rfb != null) + disconnect(); + + vncContainer.hide(); + + if (!inAnApplet) { + System.exit(0); + } + } + + // + // Ignore window events we're not interested in. + // + + public void windowActivated(WindowEvent evt) { + } + + public void windowDeactivated(WindowEvent evt) { + } + + public void windowOpened(WindowEvent evt) { + } + + public void windowClosed(WindowEvent evt) { + } + + public void windowIconified(WindowEvent evt) { + } + + public void windowDeiconified(WindowEvent evt) { + } + + public void getParentName() { + if (echoValue == null) { + + if (clientSocket == null) { + + // echo = new EchoClient(pHost, this); + echoValue = new EchoClient(pHost, this, echoPort); + echoValue.openport(); + + echoValue = echoValue.requestHostName("1"); + } else { + echoValue = new EchoClient(); + echoValue = echoValue.Interruption(clientSocket); + } + } + // proxyからの返信で接続先を決定する + host = echoValue.responseLine; + parent = echoValue.parent; + if (echoValue.treenum != null) { + treenum = echoValue.treenum; + } else { + treenum = echoValue.treenum; + } + if (echoValue.leaderflag != null) { + leaderflag = echoValue.leaderflag; + } else { + leaderflag = echoValue.leaderflag; + } + System.out.println("Parent =" + parent); + System.out.println("mynumber =" + treenum); + System.out.println("connect host =" + host); + System.out.println("leaderflag(boolean) = " + leaderflag); + + } + + public void setEchoValue(EchoClient value) { + this.echoValue = value; + } + + int castByteInt(byte[] b) { + ByteBuffer bb = ByteBuffer.wrap(b); + int value = bb.getInt(); + return value; + } + + public void setClientSocket(Socket sock) { + clientSocket = sock; + } + + public void close() { + rfb.close(); + vncFrame.setVisible(false); + vncFrame.dispose(); + } + + public Image getScreenImage() { + return vc.rawPixelsImage; + } + + public void writeScreenData(byte[] b, String imageFormat) { + try { + vc.drawBufferedImage(b); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private String getNamePort() { + ConfFileReader cfr = new ConfFileReader(); + cfr.fileRead(); + for (int i = 0; i < cfr.getName().size(); i++) { + if (serverConfirm(cfr.getName().get(i), cfr.getPort().get(i))) + return cfr.getName().get(i) + ":" + cfr.getPort().get(i); + } + return null; + } + + private boolean serverConfirm(String name, int port) { + try { + new Socket(name, port); + return true; + } catch (IOException e) { + return false; + } + } + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/OptionsFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/OptionsFrame.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,411 @@ +package treeVnc; +// +// 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 { + + /** + * + */ + private static final long serialVersionUID = 1L; + +static String[] names = { + "Encoding", + "Compression level", + "JPEG image quality", + "Cursor shape updates", + "Use CopyRect", + "Restricted colors", + "Mouse buttons 2 and 3", + "View only", + "Scale remote cursor", + "Share desktop", + }; + + static String[][] values = { + { "Auto", "Raw", "RRE", "CoRRE", "Hextile", "Zlib", "Tight", "ZRLE" }, + { "Default", "1", "2", "3", "4", "5", "6", "7", "8", "9" }, + { "JPEG off", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }, + { "Enable", "Ignore", "Disable" }, + { "Yes", "No" }, + { "Yes", "No" }, + { "Normal", "Reversed" }, + { "Yes", "No" }, + { "No", "50%", "75%", "125%", "150%" }, + { "Yes", "No" }, + }; + + final int + encodingIndex = 0, + compressLevelIndex = 1, + jpegQualityIndex = 2, + cursorUpdatesIndex = 3, + useCopyRectIndex = 4, + eightBitColorsIndex = 5, + mouseButtonIndex = 6, + viewOnlyIndex = 7, + scaleCursorIndex = 8, + shareDesktopIndex = 9; + + Label[] labels = new Label[names.length]; + Choice[] choices = new Choice[names.length]; + Button closeButton; + VncViewer viewer; + + + // + // The actual data which other classes look at: + // + + int preferredEncoding; + int compressLevel; + int jpegQuality; + boolean useCopyRect; + boolean requestCursorUpdates; + boolean ignoreCursorUpdates; + + boolean eightBitColors; + + boolean reverseMouseButtons2And3; + boolean shareDesktop; + boolean viewOnly; + int scaleCursor; + + boolean autoScale; + int scalingFactor; + + // + // Constructor. Set up the labels and choices from the names and values + // arrays. + // + + OptionsFrame(VncViewer v) { + super("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("Auto"); + choices[compressLevelIndex].select("Default"); + choices[jpegQualityIndex].select("6"); + choices[cursorUpdatesIndex].select("Enable"); + choices[useCopyRectIndex].select("Yes"); + choices[eightBitColorsIndex].select("No"); + choices[mouseButtonIndex].select("Normal"); + choices[viewOnlyIndex].select("No"); + choices[scaleCursorIndex].select("No"); + choices[shareDesktopIndex].select("Yes"); + + // But let them be overridden by parameters + + for (int i = 0; i < names.length; i++) { + String s = viewer.readParameter(names[i], false); + if (s != null) { + for (int j = 0; j < values[i].length; j++) { + if (s.equalsIgnoreCase(values[i][j])) { + choices[i].select(j); + } + } + } + } + + // FIXME: Provide some sort of GUI for "Scaling Factor". + + autoScale = false; + scalingFactor = 100; + String s = viewer.readParameter("Scaling Factor", false); + if (s != null) { + if (s.equalsIgnoreCase("Auto")) { + autoScale = true; + } else { + // Remove the '%' char at the end of string if present. + if (s.charAt(s.length() - 1) == '%') { + s = s.substring(0, s.length() - 1); + } + // Convert to an integer. + try { + scalingFactor = Integer.parseInt(s); + } + catch (NumberFormatException e) { + scalingFactor = 100; + } + // Make sure scalingFactor is in the range of [1..1000]. + if (scalingFactor < 1) { + scalingFactor = 1; + } else if (scalingFactor > 1000) { + scalingFactor = 1000; + } + } + } + + // 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 + // corresponding variables properly. Then it calls the VncViewer's + // setEncodings method to send a SetEncodings message to the RFB + // server. + // + + void setEncodings() { + useCopyRect = choices[useCopyRectIndex].getSelectedItem().equals("Yes"); + + preferredEncoding = RfbProto.EncodingRaw; + boolean enableCompressLevel = false; + + if (choices[encodingIndex].getSelectedItem().equals("RRE")) { + preferredEncoding = RfbProto.EncodingRRE; + } else if (choices[encodingIndex].getSelectedItem().equals("CoRRE")) { + preferredEncoding = RfbProto.EncodingCoRRE; + } else if (choices[encodingIndex].getSelectedItem().equals("Hextile")) { + preferredEncoding = RfbProto.EncodingHextile; + } else if (choices[encodingIndex].getSelectedItem().equals("ZRLE")) { + preferredEncoding = RfbProto.EncodingZRLE; + } else if (choices[encodingIndex].getSelectedItem().equals("Zlib")) { + preferredEncoding = RfbProto.EncodingZlib; + enableCompressLevel = true; + } else if (choices[encodingIndex].getSelectedItem().equals("Tight")) { + preferredEncoding = RfbProto.EncodingTight; + enableCompressLevel = true; + } else if (choices[encodingIndex].getSelectedItem().equals("Auto")) { + preferredEncoding = -1; + } + + // Handle compression level setting. + + try { + compressLevel = + Integer.parseInt(choices[compressLevelIndex].getSelectedItem()); + } + catch (NumberFormatException e) { + compressLevel = -1; + } + if (compressLevel < 1 || compressLevel > 9) { + compressLevel = -1; + } + labels[compressLevelIndex].setEnabled(enableCompressLevel); + choices[compressLevelIndex].setEnabled(enableCompressLevel); + + // Handle JPEG quality setting. + + try { + jpegQuality = + Integer.parseInt(choices[jpegQualityIndex].getSelectedItem()); + } + catch (NumberFormatException e) { + jpegQuality = -1; + } + if (jpegQuality < 0 || jpegQuality > 9) { + jpegQuality = -1; + } + + // Request cursor shape updates if necessary. + + requestCursorUpdates = + !choices[cursorUpdatesIndex].getSelectedItem().equals("Disable"); + + if (requestCursorUpdates) { + ignoreCursorUpdates = + choices[cursorUpdatesIndex].getSelectedItem().equals("Ignore"); + } + + viewer.setEncodings(); + } + + // + // setColorFormat sets eightBitColors variable depending on the GUI + // setting, causing switches between 8-bit and 24-bit colors mode if + // necessary. + // + + void setColorFormat() { + + eightBitColors = + choices[eightBitColorsIndex].getSelectedItem().equals("Yes"); + + boolean enableJPEG = !eightBitColors; + + labels[jpegQualityIndex].setEnabled(enableJPEG); + choices[jpegQualityIndex].setEnabled(enableJPEG); + } + + // + // setOtherOptions looks at the "other" choices (ones that do not + // cause sending any protocol messages) and sets the boolean flags + // appropriately. + // + + void setOtherOptions() { + + reverseMouseButtons2And3 + = choices[mouseButtonIndex].getSelectedItem().equals("Reversed"); + + viewOnly + = choices[viewOnlyIndex].getSelectedItem().equals("Yes"); + if (viewer.vc != null) + viewer.vc.enableInput(!viewOnly); + + shareDesktop + = choices[shareDesktopIndex].getSelectedItem().equals("Yes"); + + String scaleString = choices[scaleCursorIndex].getSelectedItem(); + if (scaleString.endsWith("%")) + scaleString = scaleString.substring(0, scaleString.length() - 1); + try { + scaleCursor = Integer.parseInt(scaleString); + } + catch (NumberFormatException e) { + scaleCursor = 0; + } + if (scaleCursor < 10 || scaleCursor > 500) { + scaleCursor = 0; + } + if (requestCursorUpdates && !ignoreCursorUpdates && !viewOnly) { + labels[scaleCursorIndex].setEnabled(true); + choices[scaleCursorIndex].setEnabled(true); + } else { + labels[scaleCursorIndex].setEnabled(false); + choices[scaleCursorIndex].setEnabled(false); + } + if (viewer.vc != null) + viewer.vc.createSoftCursor(); // update cursor scaling + } + + + // + // Respond to actions on Choice controls + // + + public void itemStateChanged(ItemEvent evt) { + Object source = evt.getSource(); + + if (source == choices[encodingIndex] || + source == choices[compressLevelIndex] || + source == choices[jpegQualityIndex] || + source == choices[cursorUpdatesIndex] || + source == choices[useCopyRectIndex]) { + + setEncodings(); + + if (source == choices[cursorUpdatesIndex]) { + setOtherOptions(); // update scaleCursor state + } + + } else if (source == choices[eightBitColorsIndex]) { + + setColorFormat(); + + } else if (source == choices[mouseButtonIndex] || + source == choices[shareDesktopIndex] || + source == choices[viewOnlyIndex] || + source == choices[scaleCursorIndex]) { + + setOtherOptions(); + + } + } + + // + // 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 -r 000000000000 -r 756bfaf731f3 src/treeVnc/OptionsNoFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/OptionsNoFrame.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,344 @@ +package treeVnc; + +import java.awt.*; +import java.awt.event.*; + +class OptionsNoFrame{ + + static String[] names = { + "Encoding", + "Compression level", + "JPEG image quality", + "Cursor shape updates", + "Use CopyRect", + "Restricted colors", + "Mouse buttons 2 and 3", + "View only", + "Scale remote cursor", + "Share desktop", + }; + + static String[][] values = { + { "Auto", "Raw", "RRE", "CoRRE", "Hextile", "Zlib", "Tight", "ZRLE" }, + { "Default", "1", "2", "3", "4", "5", "6", "7", "8", "9" }, + { "JPEG off", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }, + { "Enable", "Ignore", "Disable" }, + { "Yes", "No" }, + { "Yes", "No" }, + { "Normal", "Reversed" }, + { "Yes", "No" }, + { "No", "50%", "75%", "125%", "150%" }, + { "Yes", "No" }, + }; + + final int + encodingIndex = 0, + compressLevelIndex = 1, + jpegQualityIndex = 2, + cursorUpdatesIndex = 3, + useCopyRectIndex = 4, + eightBitColorsIndex = 5, + mouseButtonIndex = 6, + viewOnlyIndex = 7, + scaleCursorIndex = 8, + shareDesktopIndex = 9; + + Label[] labels = new Label[names.length]; + Choice[] choices = new Choice[names.length]; + Button closeButton; + VncProxyService viewer; + + + // + // The actual data which other classes look at: + // + + int preferredEncoding; + int compressLevel; + int jpegQuality; + boolean useCopyRect; + boolean requestCursorUpdates; + boolean ignoreCursorUpdates; + + boolean eightBitColors; + + boolean reverseMouseButtons2And3; + boolean shareDesktop; + boolean viewOnly; + int scaleCursor; + + boolean autoScale; + int scalingFactor; + CuiMyVncClient viewerc; + + // + // Constructor. Set up the labels and choices from the names and values + // arrays. + // + + + OptionsNoFrame(CuiMyVncClient v) { + + viewerc = v; + } + + + OptionsNoFrame(VncProxyService v) { + viewer = v; + preferredEncoding = -1; + } + + OptionsNoFrame(VncProxyService v, VncCanvas vc) { +// OptionsNoFrame(VncProxyService v) { + viewer = v; + + for (int i = 0; i < names.length; i++) { + labels[i] = new Label(names[i]); + + choices[i] = new Choice(); + + + for (int j = 0; j < values[i]. length; j++) { + choices[i].addItem(values[i][j]); + } + } + + // Set up defaults + + choices[encodingIndex].select("Auto"); + choices[compressLevelIndex].select("Default"); + choices[jpegQualityIndex].select("6"); + choices[cursorUpdatesIndex].select("Enable"); + choices[useCopyRectIndex].select("Yes"); + choices[eightBitColorsIndex].select("No"); + choices[mouseButtonIndex].select("Normal"); + choices[viewOnlyIndex].select("No"); + choices[scaleCursorIndex].select("No"); + choices[shareDesktopIndex].select("Yes"); + + // But let them be overridden by parameters + + for (int i = 0; i < names.length; i++) { + String s = viewer.readParameter(names[i], false); + if (s != null) { + for (int j = 0; j < values[i].length; j++) { + if (s.equalsIgnoreCase(values[i][j])) { + choices[i].select(j); + } + } + } + } + + // FIXME: Provide some sort of GUI for "Scaling Factor". + + autoScale = false; + scalingFactor = 100; + String s = viewer.readParameter("Scaling Factor", false); + if (s != null) { + if (s.equalsIgnoreCase("Auto")) { + autoScale = true; + } else { + // Remove the '%' char at the end of string if present. + if (s.charAt(s.length() - 1) == '%') { + s = s.substring(0, s.length() - 1); + } + // Convert to an integer. + try { + scalingFactor = Integer.parseInt(s); + } + catch (NumberFormatException e) { + scalingFactor = 100; + } + // Make sure scalingFactor is in the range of [1..1000]. + if (scalingFactor < 1) { + scalingFactor = 1; + } else if (scalingFactor > 1000) { + scalingFactor = 1000; + } + } + } + + // 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 + // corresponding variables properly. Then it calls the VncViewer's + // setEncodings method to send a SetEncodings message to the RFB + // server. + // + + void setEncodings() { +// useCopyRect = choices[useCopyRectIndex].getSelectedItem().equals("Yes"); + + preferredEncoding = RfbProto.EncodingRaw; + boolean enableCompressLevel = false; + + if (choices[encodingIndex].getSelectedItem().equals("RRE")) { + preferredEncoding = RfbProto.EncodingRRE; + } else if (choices[encodingIndex].getSelectedItem().equals("CoRRE")) { + preferredEncoding = RfbProto.EncodingCoRRE; + } else if (choices[encodingIndex].getSelectedItem().equals("Hextile")) { + preferredEncoding = RfbProto.EncodingHextile; + } else if (choices[encodingIndex].getSelectedItem().equals("ZRLE")) { + preferredEncoding = RfbProto.EncodingZRLE; + } else if (choices[encodingIndex].getSelectedItem().equals("Zlib")) { + preferredEncoding = RfbProto.EncodingZlib; + enableCompressLevel = true; + } else if (choices[encodingIndex].getSelectedItem().equals("Tight")) { + preferredEncoding = RfbProto.EncodingTight; + enableCompressLevel = true; + } else if (choices[encodingIndex].getSelectedItem().equals("Auto")) { + preferredEncoding = -1; + } + + // Handle compression level setting. + + try { + compressLevel = + Integer.parseInt(choices[compressLevelIndex].getSelectedItem()); + } + catch (NumberFormatException e) { + compressLevel = -1; + } + if (compressLevel < 1 || compressLevel > 9) { + compressLevel = -1; + } + labels[compressLevelIndex].setEnabled(enableCompressLevel); + choices[compressLevelIndex].setEnabled(enableCompressLevel); + + // Handle JPEG quality setting. + + try { + jpegQuality = + Integer.parseInt(choices[jpegQualityIndex].getSelectedItem()); + } + catch (NumberFormatException e) { + jpegQuality = -1; + } + if (jpegQuality < 0 || jpegQuality > 9) { + jpegQuality = -1; + } + + // Request cursor shape updates if necessary. + + requestCursorUpdates = + !choices[cursorUpdatesIndex].getSelectedItem().equals("Disable"); + + if (requestCursorUpdates) { + ignoreCursorUpdates = + choices[cursorUpdatesIndex].getSelectedItem().equals("Ignore"); + } + + viewer.setEncodings(); + } + + // + // setColorFormat sets eightBitColors variable depending on the GUI + // setting, causing switches between 8-bit and 24-bit colors mode if + // necessary. + // + + void setColorFormat() { + + eightBitColors = + choices[eightBitColorsIndex].getSelectedItem().equals("Yes"); + + boolean enableJPEG = !eightBitColors; + + labels[jpegQualityIndex].setEnabled(enableJPEG); + choices[jpegQualityIndex].setEnabled(enableJPEG); + } + + // + // setOtherOptions looks at the "other" choices (ones that do not + // cause sending any protocol messages) and sets the boolean flags + // appropriately. + // + + void setOtherOptions() { + + reverseMouseButtons2And3 + = choices[mouseButtonIndex].getSelectedItem().equals("Reversed"); + + viewOnly + = choices[viewOnlyIndex].getSelectedItem().equals("Yes"); + if (viewer.vc != null) + viewer.vc.enableInput(!viewOnly); + + shareDesktop + = choices[shareDesktopIndex].getSelectedItem().equals("Yes"); + + String scaleString = choices[scaleCursorIndex].getSelectedItem(); + if (scaleString.endsWith("%")) + scaleString = scaleString.substring(0, scaleString.length() - 1); + try { + scaleCursor = Integer.parseInt(scaleString); + } + catch (NumberFormatException e) { + scaleCursor = 0; + } + if (scaleCursor < 10 || scaleCursor > 500) { + scaleCursor = 0; + } + if (requestCursorUpdates && !ignoreCursorUpdates && !viewOnly) { + labels[scaleCursorIndex].setEnabled(true); + choices[scaleCursorIndex].setEnabled(true); + } else { + labels[scaleCursorIndex].setEnabled(false); + choices[scaleCursorIndex].setEnabled(false); + } + if (viewer.vc != null) + viewer.vc.createSoftCursor(); // update cursor scaling + } + + + // + // Respond to actions on Choice controls + // + + public void itemStateChanged(ItemEvent evt) { + Object source = evt.getSource(); + + if (source == choices[encodingIndex] || + source == choices[compressLevelIndex] || + source == choices[jpegQualityIndex] || + source == choices[cursorUpdatesIndex] || + source == choices[useCopyRectIndex]) { + + setEncodings(); + + if (source == choices[cursorUpdatesIndex]) { + setOtherOptions(); // update scaleCursor state + } + + } else if (source == choices[eightBitColorsIndex]) { + + setColorFormat(); + + } else if (source == choices[mouseButtonIndex] || + source == choices[shareDesktopIndex] || + source == choices[viewOnlyIndex] || + source == choices[scaleCursorIndex]) { + + setOtherOptions(); + + } + } + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/ProxyVncCanvas.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/ProxyVncCanvas.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,1975 @@ +package treeVnc; +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import java.io.*; +import java.util.zip.*; + +import javax.imageio.ImageIO; + +// +//VncCanvas is a subclass of Canvas which draws a VNC desktop on it. +// + +class ProxyVncCanvas extends Canvas implements KeyListener, MouseListener, + MouseMotionListener { + + /** + * + */ + private static final long serialVersionUID = 1L; + VncProxyService viewer; + MyRfbProtoProxy rfb; + ColorModel cm8, cm24; + Color[] colors; + int bytesPixel; + + int maxWidth = 0, maxHeight = 0; + int scalingFactor; + int scaledWidth, scaledHeight; + +// Image memImage; + BufferedImage memImage; + Graphics memGraphics; + + Image rawPixelsImage; +// BufferedImage rawPixelsImage; + BufferedImage bimg; + + MemoryImageSource pixelsSource; + byte[] pixels8; + int[] pixels24; + + // Update statistics. + long statStartTime; // time on first framebufferUpdateRequest + int statNumUpdates; // counter for FramebufferUpdate messages + int statNumTotalRects; // rectangles in FramebufferUpdate messages + int statNumPixelRects; // the same, but excluding pseudo-rectangles + int statNumRectsTight; // Tight-encoded rectangles (including JPEG) + int statNumRectsTightJPEG; // JPEG-compressed Tight-encoded rectangles + int statNumRectsZRLE; // ZRLE-encoded rectangles + int statNumRectsHextile; // Hextile-encoded rectangles + int statNumRectsRaw; // Raw-encoded rectangles + int statNumRectsCopy; // CopyRect rectangles + int statNumBytesEncoded; // number of bytes in updates, as received + int statNumBytesDecoded; // number of bytes, as if Raw encoding was used + + // ZRLE encoder's data. + byte[] zrleBuf; + int zrleBufLen = 0; + byte[] zrleTilePixels8; + int[] zrleTilePixels24; + ZlibInStream zrleInStream; + boolean zrleRecWarningShown = false; + + // 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 inputEnabled; +// private int b = 0; + + + + // + // The constructors. + // + + public ProxyVncCanvas(VncProxyService v, int maxWidth_, int maxHeight_) + throws IOException { + + viewer = v; + maxWidth = maxWidth_; + maxHeight = maxHeight_; + + 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(); + + inputEnabled = false; + // Keyboard listener is enabled even in view-only mode, to catch + // 'r' or 'R' key presses used to request screen update. + addKeyListener(this); + } + + public ProxyVncCanvas(VncProxyService v) throws IOException { + this(v, 0, 0); + } + + // + // Callback methods to determine geometry of our Component. + // + + public Dimension getPreferredSize() { + return new Dimension(scaledWidth, scaledHeight); + } + + public Dimension getMinimumSize() { + return new Dimension(scaledWidth, scaledHeight); + } + + public Dimension getMaximumSize() { + return new Dimension(scaledWidth, scaledHeight); + } + + // + // All painting is performed here. + // + + public void update(Graphics g) { + paint(g); + } + + public void paint(Graphics g) { + synchronized (memImage) { + if (rfb.framebufferWidth == scaledWidth) { + g.drawImage(memImage, 0, 0, null); + } else { + paintScaledFrameBuffer(g); + } + } + if (showSoftCursor) { + int x0 = cursorX - hotX, y0 = cursorY - hotY; + Rectangle r = new Rectangle(x0, y0, cursorWidth, cursorHeight); + if (r.intersects(g.getClipBounds())) { + g.drawImage(softCursor, x0, y0, null); + } + } + } + + public void paintScaledFrameBuffer(Graphics g) { + g.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null); + } + + // + // 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 mouse events. Keyboard events are received + // even in view-only mode, because we want to map the 'r' key to the + // screen refreshing function. + // + + public synchronized void enableInput(boolean enable) { + if (enable && !inputEnabled) { + inputEnabled = true; + addMouseListener(this); + addMouseMotionListener(this); + if (viewer.showControls) { + viewer.buttonPanel.enableRemoteAccessControls(true); + } + createSoftCursor(); // scaled cursor + } else if (!enable && inputEnabled) { + inputEnabled = false; + removeMouseListener(this); + removeMouseMotionListener(this); + if (viewer.showControls) { + viewer.buttonPanel.enableRemoteAccessControls(false); + } + createSoftCursor(); // non-scaled cursor + } + } + + public void setPixelFormat() throws IOException { + + if (viewer.options.eightBitColors) { + rfb.writeSetPixelFormat(8, 8, false, true, 7, 7, 3, 0, 3, 6); + bytesPixel = 1; + } else { + rfb.writeSetPixelFormat(32, 24, false, true, 255, 255, 255, 16, 8, + 0); + bytesPixel = 4; + } + + updateFramebufferSize(); + } + + void updateFramebufferSize() { + + // Useful shortcuts. + int fbWidth = rfb.framebufferWidth; + int fbHeight = rfb.framebufferHeight; + + // Calculate scaling factor for auto scaling. + if (maxWidth > 0 && maxHeight > 0) { + int f1 = maxWidth * 100 / fbWidth; + int f2 = maxHeight * 100 / fbHeight; + scalingFactor = Math.min(f1, f2); + if (scalingFactor > 100) + scalingFactor = 100; + System.out.println("Scaling desktop at " + scalingFactor + "%"); + } + + // Update scaled framebuffer geometry. + scaledWidth = (fbWidth * scalingFactor + 50) / 100; + scaledHeight = (fbHeight * scalingFactor + 50) / 100; + + // Create new off-screen image either if it does not exist, or if + // its geometry should be changed. It's not necessary to replace + // existing image if only pixel format should be changed. +/* + if (memImage == null) { + memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); + memGraphics = memImage.getGraphics(); + } else if (memImage.getWidth(null) != fbWidth + || memImage.getHeight(null) != fbHeight) { + synchronized (memImage) { + memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); + memGraphics = memImage.getGraphics(); + } + } +*/ + memImage = new BufferedImage(rfb.framebufferWidth, rfb.framebufferHeight, BufferedImage.TYPE_INT_RGB ); + 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); + + zrleTilePixels24 = null; + zrleTilePixels8 = new byte[64 * 64]; + + } else { + + pixels8 = null; + pixels24 = new int[fbWidth * fbHeight]; + + pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm24, + pixels24, 0, fbWidth); + + zrleTilePixels8 = null; + zrleTilePixels24 = new int[64 * 64]; + + } + pixelsSource.setAnimated(true); + rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource); +// rawPixelsImage = (BufferedImage) Toolkit.getDefaultToolkit().createImage(pixelsSource); + + } + + void resizeDesktopFrame() { + setSize(scaledWidth, scaledHeight); + + // FIXME: Find a better way to determine correct size of a + // ScrollPane. -- const + Insets insets = viewer.desktopScrollPane.getInsets(); + viewer.desktopScrollPane.setSize( + scaledWidth + 2 * Math.min(insets.left, insets.right), + scaledHeight + 2 * Math.min(insets.top, insets.bottom)); + + viewer.vncFrame.pack(); + + // Try to limit the frame size to the screen size. + + Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize(); + Dimension frameSize = viewer.vncFrame.getSize(); + Dimension newSize = frameSize; + + // Reduce Screen Size by 30 pixels in each direction; + // This is a (poor) attempt to account for + // 1) Menu bar on Macintosh (should really also account for + // Dock on OSX). Usually 22px on top of screen. + // 2) Taxkbar on Windows (usually about 28 px on bottom) + // 3) Other obstructions. + + screenSize.height -= 30; + screenSize.width -= 30; + + boolean needToResizeFrame = false; + if (frameSize.height > screenSize.height) { + newSize.height = screenSize.height; + needToResizeFrame = true; + } + if (frameSize.width > screenSize.width) { + newSize.width = screenSize.width; + needToResizeFrame = true; + } + if (needToResizeFrame) { + viewer.vncFrame.setSize(newSize); + } + + viewer.desktopScrollPane.doLayout(); + } + + // + // processNormalProtocol() - executed by the rfbThread to deal with the + // RFB socket. + // + + public void processNormalProtocol() throws Exception { + + // Start/stop session recording if necessary. + viewer.checkRecordingStatus(); + + rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, + rfb.framebufferHeight, false); + + resetStats(); + boolean statsRestarted = false; + + // + // main dispatch loop + // + + long count = 0; + long buf = 0; + while (true) { +// System.out.println("\ncount=" + count); + + + count++; + + /** + * read Data from parents and send Data to Client. + */ + //rfb.sendDataCheckDelay(); + rfb.sendDataToClient(); +// rfb.checkDelayData(); +// long kbitsPerSecond = rfb.kbitsPerSecond(); +// System.out.println("Throughput " + kbitsPerSecond + " kbit/s"); + + + if(rfb.returnMsgtype() == RfbProto.FramebufferUpdate ) { + boolean fullUpdateNeeded = false; + int w = rfb.framebufferWidth; + int h = rfb.framebufferHeight; + rfb.writeFramebufferUpdateRequest(0, 0, w, h, !fullUpdateNeeded); + // rfb.checkDelayData(); + continue; + } + + + + long numBytesRead = rfb.getNumBytesRead(); + + // Read message type from the server. + int msgType = rfb.readServerMessageType(); + + // Process the message depending on its type. + switch (msgType) { + case MyRfbProtoProxy.SpeedCheckMillis: + rfb.readSpeedCheck(); + break; + case RfbProto.FramebufferUpdate: + + if(msgType == RfbProto.FramebufferUpdate){ + rfb.is.reset(); + break; + } + + if (statNumUpdates == viewer.debugStatsExcludeUpdates + && !statsRestarted) { + resetStats(); + statsRestarted = true; + } else if (statNumUpdates == viewer.debugStatsMeasureUpdates + && statsRestarted) { + viewer.disconnect(); + } + + rfb.readFramebufferUpdate(); + statNumUpdates++; +// System.out.println("NRects = "+ rfb.updateNRects); + + boolean cursorPosReceived = false; + + for (int i = 0; i < rfb.updateNRects; i++) { + + rfb.readFramebufferUpdateRectHdr(); + statNumTotalRects++; + int rx = rfb.updateRectX, ry = rfb.updateRectY; + int rw = rfb.updateRectW, rh = rfb.updateRectH; + System.out.println("rx = "+ rx + " ry = "+ry+"\nrw = "+rw+" rh = "+rh); + System.out.println("encoding = "+ rfb.updateRectEncoding); + + if (rfb.updateRectEncoding == rfb.EncodingLastRect) + break; + + if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) { + rfb.setFramebufferSize(rw, rh); + updateFramebufferSize(); + break; + } + + if (rfb.updateRectEncoding == rfb.EncodingXCursor + || rfb.updateRectEncoding == rfb.EncodingRichCursor) { + handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry, + rw, rh); + continue; + } + + if (rfb.updateRectEncoding == rfb.EncodingPointerPos) { + softCursorMove(rx, ry); + cursorPosReceived = true; + continue; + } + + long numBytesReadBefore = rfb.getNumBytesRead(); + + rfb.startTiming(); + + switch (rfb.updateRectEncoding) { + case RfbProto.EncodingRaw: + statNumRectsRaw++; + handleRawRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingCopyRect: + statNumRectsCopy++; + handleCopyRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingRRE: + handleRRERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingCoRRE: + handleCoRRERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingHextile: + statNumRectsHextile++; + handleHextileRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingZRLE: + case RfbProto.EncodingZRLEE: + statNumRectsZRLE++; + handleZRLERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingZlib: + handleZlibRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingTight: + statNumRectsTight++; + handleTightRect(rx, ry, rw, rh); + break; + default: + throw new Exception("Unknown RFB rectangle encoding " + + rfb.updateRectEncoding); + } + + rfb.stopTiming(); +// long kbitsPerSecond = rfb.kbitsPerSecond(); +// System.out.println("Throughput " + kbitsPerSecond + " kbit/s"); + + + statNumPixelRects++; + statNumBytesDecoded += rw * rh * bytesPixel; + statNumBytesEncoded += (int) (rfb.getNumBytesRead() - numBytesReadBefore); + } + + boolean fullUpdateNeeded = false; + + // Start/stop session recording if necessary. Request full + // update if a new session file was opened. + if (viewer.checkRecordingStatus()) + fullUpdateNeeded = true; + + // Defer framebuffer update request if necessary. But wake up + // immediately on keyboard or mouse event. Also, don't sleep + // if there is some data to receive, or if the last update + // included a PointerPos message. + if (viewer.deferUpdateRequests > 0 && rfb.available() == 0 + && !cursorPosReceived) { + synchronized (rfb) { + try { + rfb.wait(viewer.deferUpdateRequests); + } catch (InterruptedException e) { + } + } + } + + viewer.autoSelectEncodings(); + + // Before requesting framebuffer update, check if the pixel + // format should be changed. +/* + if (viewer.options.eightBitColors != (bytesPixel == 1)) { + // Pixel format should be changed. + setPixelFormat(); + fullUpdateNeeded = true; + } +*/ + // Request framebuffer update if needed. + int w = rfb.framebufferWidth; + int h = rfb.framebufferHeight; + rfb.writeFramebufferUpdateRequest(0, 0, w, h, !fullUpdateNeeded); + //rfb.checkDelayData(); + + break; + + case RfbProto.SetColourMapEntries: + throw new Exception("Can't handle SetColourMapEntries message"); + + case RfbProto.Bell: +// Toolkit.getDefaultToolkit().beep(); // OS X Lion VNC server send bell. Caused feed back view bell. + break; + + case RfbProto.ServerCutText: + String s = rfb.readServerCutText(); + viewer.clipboard.setCutText(s); + break; + + case RfbProto.CheckDelay: + rfb.checkDelayData(); + break; + + default: + throw new Exception("Unknown RFB message type " + msgType); + } + + + +// int bufSize = (int)(rfb.getNumBytesRead() - numBytesRead); + long bufSize = rfb.getNumBytesRead() - numBytesRead; +// System.out.println("bufSize="+bufSize); + + buf += rfb.getNumBytesRead() - numBytesRead; +// System.out.println("total buf = " + buf); +// rfb.bufResetSend(bufSize); + + + + if(rfb.createBimgFlag){ +// bimg = createBufferedImage(rawPixelsImage); + bimg = createBufferedImage(memImage); + //bimg(BufferedImage) -> rfb.pngBytes(byte[]) + rfb.createPngBytes(bimg); + rfb.sendPngImage(); + rfb.createBimgFlag = false; + } + + +/* + boolean result = false; + try{ + result = ImageIO.write(bimg, "png", new File("sample.png")); + }catch(Exception e){ + e.printStackTrace(); + result = false; + } +*/ + } + } + + // + // Handle a raw rectangle. The second form with paint==false is used + // by the Hextile decoder for raw-encoded tiles. + // + + void handleRawRect(int x, int y, int w, int h) throws IOException { + handleRawRect(x, y, w, h, true); + } + + void handleRawRect(int x, int y, int w, int h, boolean paint) + throws IOException { + + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w); + if (rfb.rec != null) { + rfb.rec.write(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.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(buf); + } + + offset = dy * rfb.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 | + (buf[i * 4 + 1] & 0xFF) << 8 | + (buf[i * 4] & 0xFF); + } + + } + } + + handleUpdatedPixels(x, y, w, h); +// if (paint) scheduleRepaint(x, y, w, h); + + } + + // + // Handle a CopyRect rectangle. + // + + void handleCopyRect(int x, int y, int w, int h) throws IOException { + + rfb.readCopyRect(); + memGraphics.copyArea(rfb.copyRectSrcX, rfb.copyRectSrcY, w, h, x + - rfb.copyRectSrcX, y - rfb.copyRectSrcY); + + scheduleRepaint(x, y, w, h); + } + + // + // Handle an RRE-encoded rectangle. + // + + void handleRRERect(int x, int y, int w, int h) throws IOException { + + int nSubrects = rfb.readU32(); + + byte[] bg_buf = new byte[bytesPixel]; + rfb.readFully(bg_buf); + Color pixel; + if (bytesPixel == 1) { + pixel = colors[bg_buf[0] & 0xFF]; + } else { + pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, + bg_buf[0] & 0xFF); + } + memGraphics.setColor(pixel); + memGraphics.fillRect(x, y, w, h); + + byte[] buf = new byte[nSubrects * (bytesPixel + 8)]; + rfb.readFully(buf); + DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf)); + + if (rfb.rec != null) { + rfb.rec.writeIntBE(nSubrects); + rfb.rec.write(bg_buf); + rfb.rec.write(buf); + } + + int sx, sy, sw, sh; + + for (int j = 0; j < nSubrects; j++) { + if (bytesPixel == 1) { + pixel = colors[ds.readUnsignedByte()]; + } else { + ds.skip(4); + pixel = new Color(buf[j * 12 + 2] & 0xFF, + buf[j * 12 + 1] & 0xFF, buf[j * 12] & 0xFF); + } + sx = x + ds.readUnsignedShort(); + sy = y + ds.readUnsignedShort(); + sw = ds.readUnsignedShort(); + sh = ds.readUnsignedShort(); + + memGraphics.setColor(pixel); + memGraphics.fillRect(sx, sy, sw, sh); + } + + scheduleRepaint(x, y, w, h); + } + + // + // Handle a CoRRE-encoded rectangle. + // + + void handleCoRRERect(int x, int y, int w, int h) throws IOException { + int nSubrects = rfb.readU32(); + + byte[] bg_buf = new byte[bytesPixel]; + rfb.readFully(bg_buf); + Color pixel; + if (bytesPixel == 1) { + pixel = colors[bg_buf[0] & 0xFF]; + } else { + pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, + bg_buf[0] & 0xFF); + } + memGraphics.setColor(pixel); + memGraphics.fillRect(x, y, w, h); + + byte[] buf = new byte[nSubrects * (bytesPixel + 4)]; + rfb.readFully(buf); + + if (rfb.rec != null) { + rfb.rec.writeIntBE(nSubrects); + rfb.rec.write(bg_buf); + rfb.rec.write(buf); + } + + int sx, sy, sw, sh; + int i = 0; + + for (int j = 0; j < nSubrects; j++) { + if (bytesPixel == 1) { + pixel = colors[buf[i++] & 0xFF]; + } else { + pixel = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF, + buf[i] & 0xFF); + i += 4; + } + sx = x + (buf[i++] & 0xFF); + sy = y + (buf[i++] & 0xFF); + sw = buf[i++] & 0xFF; + sh = buf[i++] & 0xFF; + + memGraphics.setColor(pixel); + memGraphics.fillRect(sx, sy, sw, sh); + } + + scheduleRepaint(x, y, w, h); + } + + // + // Handle a Hextile-encoded rectangle. + // + + // These colors should be kept between handleHextileSubrect() calls. + private Color hextile_bg, hextile_fg; + + void handleHextileRect(int x, int y, int w, int h) throws IOException { + + hextile_bg = new Color(0); + hextile_fg = new Color(0); + + for (int ty = y; ty < y + h; ty += 16) { + int th = 16; + if (y + h - ty < 16) + th = y + h - ty; + + for (int tx = x; tx < x + w; tx += 16) { + int tw = 16; + if (x + w - tx < 16) + tw = x + w - tx; + + handleHextileSubrect(tx, ty, tw, th); + } + + // Finished with a row of tiles, now let's show it. + scheduleRepaint(x, y, w, h); + } + } + + // + // Handle one tile in the Hextile-encoded data. + // + + void handleHextileSubrect(int tx, int ty, int tw, int th) + throws IOException { + + int subencoding = rfb.readU8(); + if (rfb.rec != null) { + rfb.rec.writeByte(subencoding); + } + + // Is it a raw-encoded sub-rectangle? + if ((subencoding & rfb.HextileRaw) != 0) { + handleRawRect(tx, ty, tw, th, false); + return; + } + + // Read and draw the background if specified. + byte[] cbuf = new byte[bytesPixel]; + if ((subencoding & rfb.HextileBackgroundSpecified) != 0) { + rfb.readFully(cbuf); + if (bytesPixel == 1) { + hextile_bg = colors[cbuf[0] & 0xFF]; + } else { + hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, + cbuf[0] & 0xFF); + } + if (rfb.rec != null) { + rfb.rec.write(cbuf); + } + } + memGraphics.setColor(hextile_bg); + memGraphics.fillRect(tx, ty, tw, th); + + // Read the foreground color if specified. + if ((subencoding & rfb.HextileForegroundSpecified) != 0) { + rfb.readFully(cbuf); + if (bytesPixel == 1) { + hextile_fg = colors[cbuf[0] & 0xFF]; + } else { + hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, + cbuf[0] & 0xFF); + } + if (rfb.rec != null) { + rfb.rec.write(cbuf); + } + } + + // Done with this tile if there is no sub-rectangles. + if ((subencoding & rfb.HextileAnySubrects) == 0) + return; + + int nSubrects = rfb.readU8(); + int bufsize = nSubrects * 2; + if ((subencoding & rfb.HextileSubrectsColoured) != 0) { + bufsize += nSubrects * bytesPixel; + } + byte[] buf = new byte[bufsize]; + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.writeByte(nSubrects); + rfb.rec.write(buf); + } + + int b1, b2, sx, sy, sw, sh; + int i = 0; + + if ((subencoding & rfb.HextileSubrectsColoured) == 0) { + + // Sub-rectangles are all of the same color. + memGraphics.setColor(hextile_fg); + for (int j = 0; j < nSubrects; j++) { + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.fillRect(sx, sy, sw, sh); + } + } else if (bytesPixel == 1) { + + // BGR233 (8-bit color) version for colored sub-rectangles. + for (int j = 0; j < nSubrects; j++) { + hextile_fg = colors[buf[i++] & 0xFF]; + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.setColor(hextile_fg); + memGraphics.fillRect(sx, sy, sw, sh); + } + + } else { + + // Full-color (24-bit) version for colored sub-rectangles. + for (int j = 0; j < nSubrects; j++) { + hextile_fg = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF, + buf[i] & 0xFF); + i += 4; + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.setColor(hextile_fg); + memGraphics.fillRect(sx, sy, sw, sh); + } + + } + } + + // + // Handle a ZRLE-encoded rectangle. + // + // FIXME: Currently, session recording is not fully supported for ZRLE. + // + + void handleZRLERect(int x, int y, int w, int h) throws Exception { + + if (zrleInStream == null) + zrleInStream = new ZlibInStream(); + + int nBytes = rfb.readU32(); + if (nBytes > 64 * 1024 * 1024) + throw new Exception("ZRLE decoder: illegal compressed data size"); + + if (zrleBuf == null || zrleBufLen < nBytes) { + zrleBufLen = nBytes + 4096; + zrleBuf = new byte[zrleBufLen]; + } + + // FIXME: Do not wait for all the data before decompression. + rfb.readFully(zrleBuf, 0, nBytes); + + if (rfb.rec != null) { + if (rfb.recordFromBeginning) { + rfb.rec.writeIntBE(nBytes); + rfb.rec.write(zrleBuf, 0, nBytes); + } else if (!zrleRecWarningShown) { + System.out.println("Warning: ZRLE session can be recorded" + + " only from the beginning"); + System.out.println("Warning: Recorded file may be corrupted"); + zrleRecWarningShown = true; + } + + } + + zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes); + + for (int ty = y; ty < y + h; ty += 64) { + + int th = Math.min(y + h - ty, 64); + + for (int tx = x; tx < x + w; tx += 64) { + + int tw = Math.min(x + w - tx, 64); + + int mode = zrleInStream.readU8(); + boolean rle = (mode & 128) != 0; + int palSize = mode & 127; + int[] palette = new int[128]; + + readZrlePalette(palette, palSize); + + if (palSize == 1) { + int pix = palette[0]; + Color c = (bytesPixel == 1) ? colors[pix] : new Color( + 0xFF000000 | pix); + memGraphics.setColor(c); + memGraphics.fillRect(tx, ty, tw, th); + continue; + } + + if (!rle) { + if (palSize == 0) { + readZrleRawPixels(tw, th); + } else { + readZrlePackedPixels(tw, th, palette, palSize); + } + } else { + if (palSize == 0) { + readZrlePlainRLEPixels(tw, th); + } else { + readZrlePackedRLEPixels(tw, th, palette); + } + } + handleUpdatedZrleTile(tx, ty, tw, th); + } + } + + zrleInStream.reset(); + + scheduleRepaint(x, y, w, h); + } + + int readPixel(InStream is) throws Exception { + int pix; + + if (bytesPixel == 1) { + + pix = is.readU8(); + } else { + int p1 = is.readU8(); + int p2 = is.readU8(); + int p3 = is.readU8(); + pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF); + } + return pix; + } + + void readPixels(InStream is, int[] dst, int count) throws Exception { + int pix; + if (bytesPixel == 1) { + byte[] buf = new byte[count]; + is.readBytes(buf, 0, count); + for (int i = 0; i < count; i++) { + dst[i] = (int) buf[i] & 0xFF; + } + } else { + byte[] buf = new byte[count * 3]; + is.readBytes(buf, 0, count * 3); + for (int i = 0; i < count; i++) { + dst[i] = ((buf[i * 3 + 2] & 0xFF) << 16 + | (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3] & 0xFF)); + } + } + } + + void readZrlePalette(int[] palette, int palSize) throws Exception { + readPixels(zrleInStream, palette, palSize); + } + + void readZrleRawPixels(int tw, int th) throws Exception { + if (bytesPixel == 1) { + zrleInStream.readBytes(zrleTilePixels8, 0, tw * th); + } else { + readPixels(zrleInStream, zrleTilePixels24, tw * th); // / + } + } + + void readZrlePackedPixels(int tw, int th, int[] palette, int palSize) + throws Exception { + + int bppp = ((palSize > 16) ? 8 : ((palSize > 4) ? 4 + : ((palSize > 2) ? 2 : 1))); + int ptr = 0; + + for (int i = 0; i < th; i++) { + int eol = ptr + tw; + int b = 0; + int nbits = 0; + + while (ptr < eol) { + if (nbits == 0) { + b = zrleInStream.readU8(); + nbits = 8; + } + nbits -= bppp; + int index = (b >> nbits) & ((1 << bppp) - 1) & 127; + if (bytesPixel == 1) { + zrleTilePixels8[ptr++] = (byte) palette[index]; + } else { + zrleTilePixels24[ptr++] = palette[index]; + } + } + } + } + + void readZrlePlainRLEPixels(int tw, int th) throws Exception { + int ptr = 0; + int end = ptr + tw * th; + while (ptr < end) { + int pix = readPixel(zrleInStream); + int len = 1; + int b; + do { + b = zrleInStream.readU8(); + len += b; + } while (b == 255); + + if (!(len <= end - ptr)) + throw new Exception("ZRLE decoder: assertion failed" + + " (len <= end-ptr)"); + + if (bytesPixel == 1) { + while (len-- > 0) + zrleTilePixels8[ptr++] = (byte) pix; + } else { + while (len-- > 0) + zrleTilePixels24[ptr++] = pix; + } + } + } + + void readZrlePackedRLEPixels(int tw, int th, int[] palette) + throws Exception { + + int ptr = 0; + int end = ptr + tw * th; + while (ptr < end) { + int index = zrleInStream.readU8(); + int len = 1; + if ((index & 128) != 0) { + int b; + do { + b = zrleInStream.readU8(); + len += b; + } while (b == 255); + + if (!(len <= end - ptr)) + throw new Exception("ZRLE decoder: assertion failed" + + " (len <= end - ptr)"); + } + + index &= 127; + int pix = palette[index]; + + if (bytesPixel == 1) { + while (len-- > 0) + zrleTilePixels8[ptr++] = (byte) pix; + } else { + while (len-- > 0) + zrleTilePixels24[ptr++] = pix; + } + } + } + + // + // Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update. + // + + void handleUpdatedZrleTile(int x, int y, int w, int h) { + Object src, dst; + if (bytesPixel == 1) { + src = zrleTilePixels8; + dst = pixels8; + } else { + src = zrleTilePixels24; + dst = pixels24; + } + int offsetSrc = 0; + int offsetDst = (y * rfb.framebufferWidth + x); + for (int j = 0; j < h; j++) { + System.arraycopy(src, offsetSrc, dst, offsetDst, w); + offsetSrc += w; + offsetDst += rfb.framebufferWidth; + } + handleUpdatedPixels(x, y, w, h); + } + + // + // Handle a Zlib-encoded rectangle. + // + + void handleZlibRect(int x, int y, int w, int h) throws Exception { + + int nBytes = rfb.readU32(); + + if (zlibBuf == null || zlibBufLen < nBytes) { + zlibBufLen = nBytes * 2; + zlibBuf = new byte[zlibBufLen]; + } + + rfb.readFully(zlibBuf, 0, nBytes); + + if (rfb.rec != null && rfb.recordFromBeginning) { + rfb.rec.writeIntBE(nBytes); + rfb.rec.write(zlibBuf, 0, nBytes); + } + + if (zlibInflater == null) { + zlibInflater = new Inflater(); + } + zlibInflater.setInput(zlibBuf, 0, nBytes); + + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + zlibInflater.inflate(pixels8, dy * rfb.framebufferWidth + x, w); + if (rfb.rec != null && !rfb.recordFromBeginning) + rfb.rec.write(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 + 2] & 0xFF) << 16 + | (buf[i * 4 + 1] & 0xFF) << 8 + | (buf[i * 4] & 0xFF); + } + if (rfb.rec != null && !rfb.recordFromBeginning) + rfb.rec.write(buf); + } + } + + handleUpdatedPixels(x, y, w, h); + scheduleRepaint(x, y, w, h); + } + + // + // Handle a Tight-encoded rectangle. + // + + void handleTightRect(int x, int y, int w, int h) throws Exception { + + int comp_ctl = rfb.readU8(); + if (rfb.rec != null) { + if (rfb.recordFromBeginning || comp_ctl == (rfb.TightFill << 4) + || comp_ctl == (rfb.TightJpeg << 4)) { + // Send data exactly as received. + rfb.rec.writeByte(comp_ctl); + } else { + // Tell the decoder to flush each of the four zlib streams. + rfb.rec.writeByte(comp_ctl | 0x0F); + } + } + + // 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 Exception("Incorrect tight subencoding: " + comp_ctl); + } + + // Handle solid-color rectangles. + if (comp_ctl == rfb.TightFill) { + + if (bytesPixel == 1) { + int idx = rfb.readU8(); + memGraphics.setColor(colors[idx]); + if (rfb.rec != null) { + rfb.rec.writeByte(idx); + } + } else { + byte[] buf = new byte[3]; + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(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) { + + statNumRectsTightJPEG++; + + // Read JPEG data. + byte[] jpegData = new byte[rfb.readCompactLen()]; + rfb.readFully(jpegData); + if (rfb.rec != null) { + if (!rfb.recordFromBeginning) { + rfb.recordCompactLen(jpegData.length); + } + rfb.rec.write(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 Exception("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.readU8(); + if (rfb.rec != null) { + rfb.rec.writeByte(filter_id); + } + if (filter_id == rfb.TightFilterPalette) { + numColors = rfb.readU8() + 1; + if (rfb.rec != null) { + rfb.rec.writeByte(numColors - 1); + } + if (bytesPixel == 1) { + if (numColors != 2) { + throw new Exception("Incorrect tight palette size: " + + numColors); + } + rfb.readFully(palette8); + if (rfb.rec != null) { + rfb.rec.write(palette8); + } + } else { + byte[] buf = new byte[numColors * 3]; + rfb.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(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 Exception("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.readFully(indexedData); + if (rfb.rec != null) { + rfb.rec.write(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.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(buf); + } + decodeGradientData(x, y, w, h, buf); + } else { + // Raw truecolor data. + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w); + if (rfb.rec != null) { + rfb.rec.write(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.readFully(buf); + if (rfb.rec != null) { + rfb.rec.write(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.readFully(zlibData); + if (rfb.rec != null && rfb.recordFromBeginning) { + rfb.rec.write(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); + byte[] buf = new byte[dataSize]; + myInflater.inflate(buf); + if (rfb.rec != null && !rfb.recordFromBeginning) { + rfb.recordCompressedData(buf); + } + + if (numColors != 0) { + // Indexed colors. + if (numColors == 2) { + // Two colors. + if (bytesPixel == 1) { + decodeMonoData(x, y, w, h, buf, palette8); + } else { + decodeMonoData(x, y, w, h, buf, 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[buf[i++] & 0xFF]; + } + } + } + } else if (useGradient) { + // Compressed "Gradient"-filtered data (assuming bytesPixel == + // 4). + decodeGradientData(x, y, w, h, buf); + } else { + // Compressed truecolor data. + if (bytesPixel == 1) { + int destOffset = y * rfb.framebufferWidth + x; + for (int dy = 0; dy < h; dy++) { + System.arraycopy(buf, dy * w, pixels8, destOffset, w); + destOffset += rfb.framebufferWidth; + } + } else { + int srcOffset = 0; + int destOffset, i; + for (int dy = 0; dy < h; dy++) { + myInflater.inflate(buf); + destOffset = (y + dy) * rfb.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16 + | (buf[srcOffset + 1] & 0xFF) << 8 + | (buf[srcOffset + 2] & 0xFF); + srcOffset += 3; + } + } + } + } + } + + 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) { + + 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) { + + 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) { + + 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. + if (rfb.framebufferWidth == scaledWidth) { + repaint(viewer.deferScreenUpdates, x, y, w, h); + } else { + int sx = x * scalingFactor / 100; + int sy = y * scalingFactor / 100; + int sw = ((x + w) * scalingFactor + 49) / 100 - sx + 1; + int sh = ((y + h) * scalingFactor + 49) / 100 - sy + 1; + repaint(viewer.deferScreenUpdates, sx, sy, sw, sh); + } + } + + // + // Handle events. + // + + public void keyPressed(KeyEvent evt) { + +} + + 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 (viewer.rfb != null && rfb.inNormalProtocol) { + if (!inputEnabled) { + if ((evt.getKeyChar() == 'r' || evt.getKeyChar() == 'R') + && evt.getID() == KeyEvent.KEY_PRESSED) { + // Request screen update. + try { + rfb.writeFramebufferUpdateRequest(0, 0, + rfb.framebufferWidth, rfb.framebufferHeight, + false); + } catch (IOException e) { + e.printStackTrace(); + } + } + } else { + // Input enabled. + synchronized (rfb) { + try { + rfb.writeKeyEvent(evt); + } catch (Exception e) { + e.printStackTrace(); + } + rfb.notify(); + } + } + } + // Don't ever pass keyboard events to AWT for default processing. + // Otherwise, pressing Tab would switch focus to ButtonPanel etc. + evt.consume(); + } + + public void processLocalMouseEvent(MouseEvent evt, boolean moved) { + if (viewer.rfb != null && rfb.inNormalProtocol) { + if (moved) { + softCursorMove(evt.getX(), evt.getY()); + } + if (rfb.framebufferWidth != scaledWidth) { + int sx = (evt.getX() * 100 + scalingFactor / 2) / scalingFactor; + int sy = (evt.getY() * 100 + scalingFactor / 2) / scalingFactor; + evt.translatePoint(sx - evt.getX(), sy - evt.getY()); + } + synchronized (rfb) { + try { + rfb.writePointerEvent(evt); + } catch (Exception e) { + e.printStackTrace(); + } + rfb.notify(); + } + } + } + + // + // Ignored events. + // + + public void mouseClicked(MouseEvent evt) { + } + + public void mouseEntered(MouseEvent evt) { + } + + public void mouseExited(MouseEvent evt) { + } + + // + // Reset update statistics. + // + + void resetStats() { + statStartTime = System.currentTimeMillis(); + statNumUpdates = 0; + statNumTotalRects = 0; + statNumPixelRects = 0; + statNumRectsTight = 0; + statNumRectsTightJPEG = 0; + statNumRectsZRLE = 0; + statNumRectsHextile = 0; + statNumRectsRaw = 0; + statNumRectsCopy = 0; + statNumBytesEncoded = 0; + statNumBytesDecoded = 0; + } + + // //////////////////////////////////////////////////////////////// + // + // Handle cursor shape updates (XCursor and RichCursor encodings). + // + + boolean showSoftCursor = false; + + MemoryImageSource softCursorSource; + Image softCursor; + + int cursorX = 0, cursorY = 0; + int cursorWidth, cursorHeight; + int origCursorWidth, origCursorHeight; + int hotX, hotY; + int origHotX, origHotY; + + // + // Handle cursor shape update (XCursor and RichCursor encodings). + // + + synchronized void handleCursorShapeUpdate(int encodingType, int xhot, + int yhot, int width, int height) throws IOException { + + softCursorFree(); + + if (width * height == 0) + return; + + // Ignore cursor shape data if requested by user. + if (viewer.options.ignoreCursorUpdates) { + int bytesPerRow = (width + 7) / 8; + int bytesMaskData = bytesPerRow * height; + + if (encodingType == rfb.EncodingXCursor) { + rfb.skipBytes(6 + bytesMaskData * 2); + } else { + // rfb.EncodingRichCursor + rfb.skipBytes(width * height * bytesPixel + bytesMaskData); + } + return; + } + + // Decode cursor pixel data. + softCursorSource = decodeCursorShape(encodingType, width, height); + + // Set original (non-scaled) cursor dimensions. + origCursorWidth = width; + origCursorHeight = height; + origHotX = xhot; + origHotY = yhot; + + // Create off-screen cursor image. + createSoftCursor(); + + // Show the cursor. + showSoftCursor = true; + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); + } + + // + // decodeCursorShape(). Decode cursor pixel data and return + // corresponding MemoryImageSource instance. + // + + synchronized MemoryImageSource decodeCursorShape(int encodingType, + int width, int height) throws IOException { + + int bytesPerRow = (width + 7) / 8; + int bytesMaskData = bytesPerRow * height; + + int[] softCursorPixels = new int[width * height]; + + if (encodingType == rfb.EncodingXCursor) { + + // Read foreground and background colors of the cursor. + byte[] rgb = new byte[6]; + rfb.readFully(rgb); + int[] colors = { + (0xFF000000 | (rgb[3] & 0xFF) << 16 | (rgb[4] & 0xFF) << 8 | (rgb[5] & 0xFF)), + (0xFF000000 | (rgb[0] & 0xFF) << 16 | (rgb[1] & 0xFF) << 8 | (rgb[2] & 0xFF)) }; + + // Read pixel and mask data. + byte[] pixBuf = new byte[bytesMaskData]; + rfb.readFully(pixBuf); + byte[] maskBuf = new byte[bytesMaskData]; + rfb.readFully(maskBuf); + + // Decode pixel data into softCursorPixels[]. + byte pixByte, maskByte; + int x, y, n, result; + int i = 0; + for (y = 0; y < height; y++) { + for (x = 0; x < width / 8; x++) { + pixByte = pixBuf[y * bytesPerRow + x]; + maskByte = maskBuf[y * bytesPerRow + x]; + for (n = 7; n >= 0; n--) { + if ((maskByte >> n & 1) != 0) { + result = colors[pixByte >> n & 1]; + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + for (n = 7; n >= 8 - width % 8; n--) { + if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { + result = colors[pixBuf[y * bytesPerRow + x] >> n & 1]; + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + + } else { + // encodingType == rfb.EncodingRichCursor + + // Read pixel and mask data. + byte[] pixBuf = new byte[width * height * bytesPixel]; + rfb.readFully(pixBuf); + byte[] maskBuf = new byte[bytesMaskData]; + rfb.readFully(maskBuf); + + // Decode pixel data into softCursorPixels[]. + byte 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 + 2] & 0xFF) << 16 + | (pixBuf[i * 4 + 1] & 0xFF) << 8 + | (pixBuf[i * 4] & 0xFF); + } + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + for (n = 7; n >= 8 - width % 8; n--) { + if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { + if (bytesPixel == 1) { + result = cm8.getRGB(pixBuf[i]); + } else { + result = 0xFF000000 + | (pixBuf[i * 4 + 2] & 0xFF) << 16 + | (pixBuf[i * 4 + 1] & 0xFF) << 8 + | (pixBuf[i * 4] & 0xFF); + } + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + + } + + return new MemoryImageSource(width, height, softCursorPixels, 0, width); + } + + // + // createSoftCursor(). Assign softCursor new Image (scaled if necessary). + // Uses softCursorSource as a source for new cursor image. + // + + synchronized void createSoftCursor() { + + if (softCursorSource == null) + return; + + int scaleCursor = viewer.options.scaleCursor; + if (scaleCursor == 0 || !inputEnabled) + scaleCursor = 100; + + // Save original cursor coordinates. + int x = cursorX - hotX; + int y = cursorY - hotY; + int w = cursorWidth; + int h = cursorHeight; + + cursorWidth = (origCursorWidth * scaleCursor + 50) / 100; + cursorHeight = (origCursorHeight * scaleCursor + 50) / 100; + hotX = (origHotX * scaleCursor + 50) / 100; + hotY = (origHotY * scaleCursor + 50) / 100; + softCursor = Toolkit.getDefaultToolkit().createImage(softCursorSource); + + if (scaleCursor != 100) { + softCursor = softCursor.getScaledInstance(cursorWidth, + cursorHeight, Image.SCALE_SMOOTH); + } + + if (showSoftCursor) { + // Compute screen area to update. + x = Math.min(x, cursorX - hotX); + y = Math.min(y, cursorY - hotY); + w = Math.max(w, cursorWidth); + h = Math.max(h, cursorHeight); + + repaint(viewer.deferCursorUpdates, x, y, w, h); + } + } + + // + // softCursorMove(). Moves soft cursor into a particular location. + // + + synchronized void softCursorMove(int x, int y) { + int oldX = cursorX; + int oldY = cursorY; + cursorX = x; + cursorY = y; + if (showSoftCursor) { + repaint(viewer.deferCursorUpdates, oldX - hotX, oldY - hotY, + cursorWidth, cursorHeight); + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); + } + } + + // + // softCursorFree(). Remove soft cursor, dispose resources. + // + + synchronized void softCursorFree() { + if (showSoftCursor) { + showSoftCursor = false; + softCursor = null; + softCursorSource = null; + + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); + } + } + + BufferedImage createBufferedImage(Image img){ + BufferedImage bimg = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB ); + + Graphics g = bimg.getGraphics(); + g.drawImage(img, 0, 0, null); + g.dispose(); + return bimg; + } + + byte[] getBytes(BufferedImage img)throws IOException { + byte[] b = getImageBytes(img, "raw"); + return b; + } + + byte[] getImageBytes(BufferedImage image, String imageFormat) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + BufferedOutputStream os = new BufferedOutputStream(bos); + image.flush(); + ImageIO.write(image, imageFormat, os); + os.flush(); + os.close(); + return bos.toByteArray(); + } + + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/RecordingFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/RecordingFrame.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,317 @@ +package treeVnc; +// +// Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved. +// +// This is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +// +// Recording frame. It allows to control recording RFB sessions into +// FBS (FrameBuffer Stream) files. +// + +import java.io.*; +import java.awt.*; +import java.awt.event.*; + +class RecordingFrame extends Frame + implements WindowListener, ActionListener { + + /** + * + */ + private static final long serialVersionUID = 1L; + +boolean recording; + + TextField fnameField; + Button browseButton; + + Label statusLabel; + + Button recordButton, nextButton, closeButton; + VncViewer viewer; + + // + // Check if current security manager allows to create a + // RecordingFrame object. + // + + public static boolean checkSecurity() { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + try { + security.checkPropertyAccess("user.dir"); + security.checkPropertyAccess("file.separator"); + // Work around (rare) checkPropertyAccess bug + System.getProperty("user.dir"); + } catch (SecurityException e) { + System.out.println("SecurityManager restricts session recording."); + return false; + } + } + return true; + } + + // + // Constructor. + // + + RecordingFrame(VncViewer v) { + super("TightVNC Session Recording"); + + viewer = v; + + // Determine initial filename for next saved session. + // FIXME: Check SecurityManager. + + String fname = nextNewFilename(System.getProperty("user.dir") + + System.getProperty("file.separator") + + "vncsession.fbs"); + + // Construct new panel with file name field and "Browse" button. + + Panel fnamePanel = new Panel(); + GridBagLayout fnameGridbag = new GridBagLayout(); + fnamePanel.setLayout(fnameGridbag); + + GridBagConstraints fnameConstraints = new GridBagConstraints(); + fnameConstraints.gridwidth = GridBagConstraints.RELATIVE; + fnameConstraints.fill = GridBagConstraints.BOTH; + fnameConstraints.weightx = 4.0; + + fnameField = new TextField(fname, 64); + fnameGridbag.setConstraints(fnameField, fnameConstraints); + fnamePanel.add(fnameField); + fnameField.addActionListener(this); + + fnameConstraints.gridwidth = GridBagConstraints.REMAINDER; + fnameConstraints.weightx = 1.0; + + browseButton = new Button("Browse"); + fnameGridbag.setConstraints(browseButton, fnameConstraints); + fnamePanel.add(browseButton); + browseButton.addActionListener(this); + + // Construct the frame. + + GridBagLayout gridbag = new GridBagLayout(); + setLayout(gridbag); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.fill = GridBagConstraints.BOTH; + gbc.weighty = 1.0; + gbc.insets = new Insets(10, 0, 0, 0); + + Label helpLabel = + new Label("File name to save next recorded session in:", Label.CENTER); + gridbag.setConstraints(helpLabel, gbc); + add(helpLabel); + + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weighty = 0.0; + gbc.insets = new Insets(0, 0, 0, 0); + + gridbag.setConstraints(fnamePanel, gbc); + add(fnamePanel); + + gbc.fill = GridBagConstraints.BOTH; + gbc.weighty = 1.0; + gbc.insets = new Insets(10, 0, 10, 0); + + statusLabel = new Label("", Label.CENTER); + gridbag.setConstraints(statusLabel, gbc); + add(statusLabel); + + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weightx = 1.0; + gbc.weighty = 0.0; + gbc.gridwidth = 1; + gbc.insets = new Insets(0, 0, 0, 0); + + recordButton = new Button("Record"); + gridbag.setConstraints(recordButton, gbc); + add(recordButton); + recordButton.addActionListener(this); + + nextButton = new Button("Next file"); + gridbag.setConstraints(nextButton, gbc); + add(nextButton); + nextButton.addActionListener(this); + + closeButton = new Button("Close"); + gridbag.setConstraints(closeButton, gbc); + add(closeButton); + closeButton.addActionListener(this); + + // Set correct text, font and color for the statusLabel. + stopRecording(); + + pack(); + + addWindowListener(this); + } + + // + // If the given string ends with ".NNN" where NNN is a decimal + // number, increase this number by one. Otherwise, append ".001" + // to the given string. + // + + protected String nextFilename(String fname) { + int len = fname.length(); + int suffixPos = len; + int suffixNum = 1; + + if (len > 4 && fname.charAt(len - 4) == '.') { + try { + suffixNum = Integer.parseInt(fname.substring(len - 3, len)) + 1; + suffixPos = len - 4; + } catch (NumberFormatException e) { } + } + + char[] zeroes = {'0', '0', '0'}; + String suffix = String.valueOf(suffixNum); + if (suffix.length() < 3) { + suffix = new String(zeroes, 0, 3 - suffix.length()) + suffix; + } + + return fname.substring(0, suffixPos) + '.' + suffix; + } + + // + // Find next name of a file which does not exist yet. + // + + protected String nextNewFilename(String fname) { + String newName = fname; + File f; + try { + do { + newName = nextFilename(newName); + f = new File(newName); + } while (f.exists()); + } catch (SecurityException e) { } + + return newName; + } + + // + // Let the user choose a file name showing a FileDialog. + // + + protected boolean browseFile() { + File currentFile = new File(fnameField.getText()); + + FileDialog fd = + new FileDialog(this, "Save next session as...", FileDialog.SAVE); + fd.setDirectory(currentFile.getParent()); + fd.setVisible(true); + if (fd.getFile() != null) { + String newDir = fd.getDirectory(); + String sep = System.getProperty("file.separator"); + if (newDir.length() > 0) { + if (!sep.equals(newDir.substring(newDir.length() - sep.length()))) + newDir += sep; + } + String newFname = newDir + fd.getFile(); + if (newFname.equals(fnameField.getText())) { + fnameField.setText(newFname); + return true; + } + } + return false; + } + + // + // Start recording. + // + + public void startRecording() { + statusLabel.setText("Status: Recording..."); + statusLabel.setFont(new Font("Helvetica", Font.BOLD, 12)); + statusLabel.setForeground(Color.red); + recordButton.setLabel("Stop recording"); + + recording = true; + + viewer.setRecordingStatus(fnameField.getText()); + } + + // + // Stop recording. + // + + public void stopRecording() { + statusLabel.setText("Status: Not recording."); + statusLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); + statusLabel.setForeground(Color.black); + recordButton.setLabel("Record"); + + recording = false; + + viewer.setRecordingStatus(null); + } + + // + // Close our window properly. + // + + public void windowClosing(WindowEvent evt) { + setVisible(false); + } + + // + // Ignore window events we're not interested in. + // + + public void windowActivated(WindowEvent evt) {} + public void windowDeactivated (WindowEvent evt) {} + public void windowOpened(WindowEvent evt) {} + public void windowClosed(WindowEvent evt) {} + public void windowIconified(WindowEvent evt) {} + public void windowDeiconified(WindowEvent evt) {} + + + // + // Respond to button presses + // + + public void actionPerformed(ActionEvent evt) { + if (evt.getSource() == browseButton) { + if (browseFile() && recording) + startRecording(); + + } else if (evt.getSource() == recordButton) { + if (!recording) { + startRecording(); + } else { + stopRecording(); + fnameField.setText(nextNewFilename(fnameField.getText())); + } + + } else if (evt.getSource() == nextButton) { + fnameField.setText(nextNewFilename(fnameField.getText())); + if (recording) + startRecording(); + + } else if (evt.getSource() == closeButton) { + setVisible(false); + + } + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/ReloginPanel.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/ReloginPanel.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,69 @@ +package treeVnc; +// +// Copyright (C) 2002 Cendio Systems. All Rights Reserved. +// Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved. +// +// This is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +// +// ReloginPanel class implements panel with a button for logging in again, +// after fatal errors or disconnect +// + + +import java.awt.*; +import java.awt.event.*; + +// +// The panel which implements the Relogin button +// + +class ReloginPanel extends Panel implements ActionListener { + /** + * + */ + private static final long serialVersionUID = 1L; +Button reloginButton; + Button closeButton; + VncViewer viewer; + + // + // Constructor. + // + public ReloginPanel(VncViewer v) { + viewer = v; + setLayout(new FlowLayout(FlowLayout.CENTER)); + reloginButton = new Button("Login again"); + add(reloginButton); + reloginButton.addActionListener(this); + if (viewer.inSeparateFrame) { + closeButton = new Button("Close window"); + add(closeButton); + closeButton.addActionListener(this); + } + } + + // + // This method is called when a button is pressed. + // + public synchronized void actionPerformed(ActionEvent evt) { + if (viewer.inSeparateFrame) + viewer.vncFrame.dispose(); + if (evt.getSource() == reloginButton) + viewer.getAppletContext().showDocument(viewer.getDocumentBase()); + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/RequestScreenThread.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/RequestScreenThread.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,43 @@ +package treeVnc; + +import java.io.IOException; + +public class RequestScreenThread implements Runnable { + MyRfbProtoProxy rfb; + + RequestScreenThread(MyRfbProtoProxy _rfb) { + rfb = _rfb; + } + + public void run() { + while (true) { + try { + waitThread(); + Thread.sleep(3000); + rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, + rfb.framebufferHeight, false); + + } catch (Exception e) { + e.printStackTrace(); + break; + } + } + } + + public synchronized void waitThread() { + try{ + wait(); + }catch(InterruptedException e) { + e.printStackTrace(); + } + } + + public synchronized void reStart() { + notify(); + } + + public void checkDelay() throws IOException { + rfb.checkDelayData(); + } + +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/RfbProto.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/RfbProto.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,1427 @@ +package treeVnc; +// +// Copyright (C) 2001-2004 HorizonLive.com, Inc. All Rights Reserved. +// Copyright (C) 2001-2006 Constantin Kaplinsky. All Rights Reserved. +// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +// +// RfbProto.java +// + +import java.io.*; +import java.awt.event.*; +import java.net.Socket; +import java.util.zip.*; + +class RfbProto { + + final static String versionMsg_3_3 = "RFB 003.003\n", + versionMsg_3_7 = "RFB 003.007\n", versionMsg_3_8 = "RFB 003.008\n",versionMsg_3_855 = "RFB 003.855\n"; + + + // Vendor signatures: standard VNC/RealVNC, TridiaVNC, and TightVNC + final static String StandardVendor = "STDV", TridiaVncVendor = "TRDV", + TightVncVendor = "TGHT"; + + // Security types + final static int SecTypeInvalid = 0, SecTypeNone = 1, SecTypeVncAuth = 2, + SecTypeTight = 16; + + // Supported tunneling types + final static int NoTunneling = 0; + final static String SigNoTunneling = "NOTUNNEL"; + + // Supported authentication types + final static int AuthNone = 1, AuthVNC = 2, AuthUnixLogin = 129; + final static String SigAuthNone = "NOAUTH__", SigAuthVNC = "VNCAUTH_", + SigAuthUnixLogin = "ULGNAUTH"; + + // VNC authentication results + final static int VncAuthOK = 0, VncAuthFailed = 1, VncAuthTooMany = 2; + + // Standard server-to-client messages + final static int FramebufferUpdate = 0, SetColourMapEntries = 1, Bell = 2, + ServerCutText = 3; + + // Check Delay Top form Bottom + final static int CheckDelay = 11; + + // Non-standard server-to-client messages + final static int EndOfContinuousUpdates = 150; + final static String SigEndOfContinuousUpdates = "CUS_EOCU"; + + // Standard client-to-server messages + final static int SetPixelFormat = 0, FixColourMapEntries = 1, + SetEncodings = 2, FramebufferUpdateRequest = 3, KeyboardEvent = 4, + PointerEvent = 5, ClientCutText = 6; + + // Non-standard client-to-server messages + final static int EnableContinuousUpdates = 150; + final static String SigEnableContinuousUpdates = "CUC_ENCU"; + + // Supported encodings and pseudo-encodings + final static int EncodingRaw = 0, EncodingCopyRect = 1, EncodingRRE = 2, + EncodingCoRRE = 4, EncodingHextile = 5, EncodingZlib = 6, + EncodingTight = 7, EncodingZRLEE = 15, EncodingZRLE = 16, + EncodingCompressLevel0 = 0xFFFFFF00, + EncodingQualityLevel0 = 0xFFFFFFE0, EncodingXCursor = 0xFFFFFF10, + EncodingRichCursor = 0xFFFFFF11, EncodingPointerPos = 0xFFFFFF18, + EncodingLastRect = 0xFFFFFF20, EncodingNewFBSize = 0xFFFFFF21; + final static String SigEncodingRaw = "RAW_____", + SigEncodingCopyRect = "COPYRECT", SigEncodingRRE = "RRE_____", + SigEncodingCoRRE = "CORRE___", SigEncodingHextile = "HEXTILE_", + SigEncodingZlib = "ZLIB____", SigEncodingTight = "TIGHT___", + SigEncodingZRLEE = "ZRLEE___", + SigEncodingZRLE = "ZRLE____", + SigEncodingCompressLevel0 = "COMPRLVL", + SigEncodingQualityLevel0 = "JPEGQLVL", + SigEncodingXCursor = "X11CURSR", + SigEncodingRichCursor = "RCHCURSR", + SigEncodingPointerPos = "POINTPOS", + SigEncodingLastRect = "LASTRECT", + SigEncodingNewFBSize = "NEWFBSIZ"; + + final static int MaxNormalEncoding = 255; + + // Contstants used in the Hextile decoder + final static int HextileRaw = 1, HextileBackgroundSpecified = 2, + HextileForegroundSpecified = 4, HextileAnySubrects = 8, + HextileSubrectsColoured = 16; + + // Contstants used in the Tight decoder + final static int TightMinToCompress = 12; + final static int TightExplicitFilter = 0x04, TightFill = 0x08, + TightJpeg = 0x09, TightMaxSubencoding = 0x09, + TightFilterCopy = 0x00, TightFilterPalette = 0x01, + TightFilterGradient = 0x02; + + String host; + int port; + Socket sock; + OutputStream os; + SessionRecorder rec; + boolean inNormalProtocol = false; + VncViewer viewer; + MyVncClient myVncClient; + + // Input stream is declared private to make sure it can be accessed + // only via RfbProto methods. We have to do this because we want to + // count how many bytes were read. +// private DataInputStream is; + protected DataInputStream is; +// private long numBytesRead = 0; + protected long numBytesRead = 0; + + public long getNumBytesRead() { + return numBytesRead; + } + + + // Java on UNIX does not call keyPressed() on some keys, for example + // swedish keys To prevent our workaround to produce duplicate + // keypresses on JVMs that actually works, keep track of if + // keyPressed() for a "broken" key was called or not. + boolean brokenKeyPressed = false; + + // This will be set to true on the first framebuffer update + // containing Zlib-, ZRLE- or Tight-encoded data. + boolean wereZlibUpdates = false; + + // This will be set to false if the startSession() was called after + // we have received at least one Zlib-, ZRLE- or Tight-encoded + // framebuffer update. + boolean recordFromBeginning = true; + + // This fields are needed to show warnings about inefficiently saved + // sessions only once per each saved session file. + boolean zlibWarningShown; + boolean tightWarningShown; + + // Before starting to record each saved session, we set this field + // to 0, and increment on each framebuffer update. We don't flush + // the SessionRecorder data into the file before the second update. + // This allows us to write initial framebuffer update with zero + // timestamp, to let the player show initial desktop before + // playback. + int numUpdatesInSession; + + // Measuring network throughput. + boolean timing; + long timeWaitedIn100us; + long timedKbits; + + // Protocol version and TightVNC-specific protocol options. + int serverMajor, serverMinor; + int clientMajor, clientMinor; + boolean protocolTightVNC; + CapsContainer tunnelCaps, authCaps; + CapsContainer serverMsgCaps, clientMsgCaps; + CapsContainer encodingCaps; + + // If true, informs that the RFB socket was closed. +// private boolean closed; + protected boolean closed; + + // + // Constructor. Make TCP connection to RFB server. + // + RfbProto(String h, int p, VncViewer v) throws IOException { + viewer = v; + host = h; + port = p; + + if (viewer.socketFactory == null) { + sock = new Socket(host, port); + } else { + try { + Class factoryClass = Class.forName(viewer.socketFactory); + SocketFactory factory = (SocketFactory) factoryClass + .newInstance(); + if (viewer.inAnApplet) + sock = factory.createSocket(host, port, viewer); + else + sock = factory.createSocket(host, port, viewer.mainArgs); + } catch (Exception e) { + e.printStackTrace(); + throw new IOException(e.getMessage()); + } + } + is = new DataInputStream(new BufferedInputStream(sock.getInputStream(), + 16384)); + os = sock.getOutputStream(); + + timing = false; + timeWaitedIn100us = 5; + timedKbits = 0; + } + + RfbProto(String h, int p) throws IOException { + host = h; + port = p; + + sock = new Socket(host, port); + is = new DataInputStream(new BufferedInputStream(sock.getInputStream(), + 16384)); + os = sock.getOutputStream(); + + timing = false; + timeWaitedIn100us = 5; + timedKbits = 0; + } + + public RfbProto() { + + } + + public void changeRfbProto(String h,int port) throws IOException { + host = h; + sock=null; + sock = new Socket(host, port); + is = new DataInputStream(new BufferedInputStream(sock.getInputStream(), + 16384)); + os = sock.getOutputStream(); + + timing = false; + timeWaitedIn100us = 5; + timedKbits = 0; + } + + + + synchronized void close() { + try { + sock.close(); + closed = true; + System.out.println("RFB socket closed"); + if (rec != null) { + rec.close(); + rec = null; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + synchronized boolean closed() { + return closed; + } + + // + // Read server's protocol version message + // + + void readVersionMsg() throws Exception { + + byte[] b = new byte[12]; + + readFully(b); + + if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ') + || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9') + || (b[6] < '0') || (b[6] > '9') || (b[7] != '.') + || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9') + || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) { + throw new Exception("Host " + host + " port " + port + + " is not an RFB server"); + } + + serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); + serverMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0'); + + if (serverMajor < 3) { + throw new Exception( + "RFB server does not support protocol version 3"); + } + } + + // + // Write our protocol version message + // + + void writeVersionMsg() throws IOException { + clientMajor = 3; + if (serverMajor > 3 || serverMinor >= 8) { + clientMinor = 8; + os.write(versionMsg_3_8.getBytes()); + } else if (serverMinor >= 7) { + clientMinor = 7; + os.write(versionMsg_3_7.getBytes()); + } else { + clientMinor = 3; + os.write(versionMsg_3_3.getBytes()); + } + protocolTightVNC = false; + initCapabilities(); + } + + // + // Negotiate the authentication scheme. + // + + int negotiateSecurity() throws Exception { + return (clientMinor >= 7) ? selectSecurityType() : readSecurityType(); + } + + // + // Read security type from the server (protocol version 3.3). + // + + int readSecurityType() throws Exception { + int secType = readU32(); + + switch (secType) { + case SecTypeInvalid: + readConnFailedReason(); + return SecTypeInvalid; // should never be executed + case SecTypeNone: + case SecTypeVncAuth: + return secType; + default: + throw new Exception("Unknown security type from RFB server: " + + secType); + } + } + + // + // Select security type from the server's list (protocol versions 3.7/3.8). + // + + int selectSecurityType() throws Exception { + int secType = SecTypeInvalid; + + // Read the list of secutiry types. + int nSecTypes = readU8(); + if (nSecTypes == 0) { + readConnFailedReason(); + return SecTypeInvalid; // should never be executed + } + byte[] secTypes = new byte[nSecTypes]; + readFully(secTypes); + + // Find out if the server supports TightVNC protocol extensions + for (int i = 0; i < nSecTypes; i++) { + if (secTypes[i] == SecTypeTight) { + protocolTightVNC = true; + os.write(SecTypeTight); + return SecTypeTight; + } + } + + + // Find first supported security type. + for (int i = 0; i < nSecTypes; i++) { +// if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth) { + if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth + || secTypes[i] == MyRfbProtoProxy.SecTypeReqAccess) { + secType = secTypes[i]; + break; + } + } + + + if (secType == SecTypeInvalid) { + throw new Exception("Server did not offer supported security type"); + } else { + os.write(secType); + } + + return secType; + } + + // + // Perform "no authentication". + // + + void authenticateNone() throws Exception { + if (clientMinor >= 8) + readSecurityResult("No authentication"); + } + + // + // Perform standard VNC Authentication. + // + + void authenticateVNC(String pw) throws Exception { + byte[] challenge = new byte[16]; + readFully(challenge); + + if (pw.length() > 8) + pw = pw.substring(0, 8); // Truncate to 8 chars + + // Truncate password on the first zero byte. + int firstZero = pw.indexOf(0); + if (firstZero != -1) + pw = pw.substring(0, firstZero); + + byte[] key = { 0, 0, 0, 0, 0, 0, 0, 0 }; + System.arraycopy(pw.getBytes(), 0, key, 0, pw.length()); + + DesCipher des = new DesCipher(key); + + des.encrypt(challenge, 0, challenge, 0); + des.encrypt(challenge, 8, challenge, 8); + + os.write(challenge); + + readSecurityResult("VNC authentication"); + } + + // + // Read security result. + // Throws an exception on authentication failure. + // + + void readSecurityResult(String authType) throws Exception { + int securityResult = readU32(); + + switch (securityResult) { + case VncAuthOK: + System.out.println(authType + ": success"); + break; + case VncAuthFailed: + if (clientMinor >= 8) + readConnFailedReason(); + throw new Exception(authType + ": failed"); + case VncAuthTooMany: + throw new Exception(authType + ": failed, too many tries"); + default: + throw new Exception(authType + ": unknown result " + securityResult); + } + } + + // + // Read the string describing the reason for a connection failure, + // and throw an exception. + // + + void readConnFailedReason() throws Exception { + int reasonLen = readU32(); + byte[] reason = new byte[reasonLen]; + readFully(reason); + throw new Exception(new String(reason)); + } + + // + // Initialize capability lists (TightVNC protocol extensions). + // + + void initCapabilities() { + tunnelCaps = new CapsContainer(); + authCaps = new CapsContainer(); + serverMsgCaps = new CapsContainer(); + clientMsgCaps = new CapsContainer(); + encodingCaps = new CapsContainer(); + + // Supported authentication methods + authCaps.add(AuthNone, StandardVendor, SigAuthNone, "No authentication"); + authCaps.add(AuthVNC, StandardVendor, SigAuthVNC, + "Standard VNC password authentication"); + + // Supported non-standard server-to-client messages + // [NONE] + + // Supported non-standard client-to-server messages + // [NONE] + + // Supported encoding types + encodingCaps.add(EncodingCopyRect, StandardVendor, SigEncodingCopyRect, + "Standard CopyRect encoding"); + encodingCaps.add(EncodingRRE, StandardVendor, SigEncodingRRE, + "Standard RRE encoding"); + encodingCaps.add(EncodingCoRRE, StandardVendor, SigEncodingCoRRE, + "Standard CoRRE encoding"); + encodingCaps.add(EncodingHextile, StandardVendor, SigEncodingHextile, + "Standard Hextile encoding"); + encodingCaps.add(EncodingZRLE, StandardVendor, SigEncodingZRLE, + "Standard ZRLE encoding"); + encodingCaps.add(EncodingZRLEE, StandardVendor, SigEncodingZRLEE, + "Standard ZRLE(E) encoding"); + encodingCaps.add(EncodingZlib, TridiaVncVendor, SigEncodingZlib, + "Zlib encoding"); + encodingCaps.add(EncodingTight, TightVncVendor, SigEncodingTight, + "Tight encoding"); + + // Supported pseudo-encoding types + + encodingCaps.add(EncodingCompressLevel0, TightVncVendor, + SigEncodingCompressLevel0, "Compression level"); + encodingCaps.add(EncodingQualityLevel0, TightVncVendor, + SigEncodingQualityLevel0, "JPEG quality level"); + encodingCaps.add(EncodingXCursor, TightVncVendor, SigEncodingXCursor, + "X-style cursor shape update"); + encodingCaps.add(EncodingRichCursor, TightVncVendor, + SigEncodingRichCursor, "Rich-color cursor shape update"); + encodingCaps.add(EncodingPointerPos, TightVncVendor, + SigEncodingPointerPos, "Pointer position update"); + encodingCaps.add(EncodingLastRect, TightVncVendor, SigEncodingLastRect, + "LastRect protocol extension"); + encodingCaps.add(EncodingNewFBSize, TightVncVendor, + SigEncodingNewFBSize, "Framebuffer size change"); + + } + + // + // Setup tunneling (TightVNC protocol extensions) + // + + void setupTunneling() throws IOException { + int nTunnelTypes = readU32(); + if (nTunnelTypes != 0) { + readCapabilityList(tunnelCaps, nTunnelTypes); + + // We don't support tunneling yet. + writeInt(NoTunneling); + } + } + + // + // Negotiate authentication scheme (TightVNC protocol extensions) + // + + int negotiateAuthenticationTight() throws Exception { + int nAuthTypes = readU32(); + if (nAuthTypes == 0) + return AuthNone; + + readCapabilityList(authCaps, nAuthTypes); + for (int i = 0; i < authCaps.numEnabled(); i++) { + int authType = authCaps.getByOrder(i); + if (authType == AuthNone || authType == AuthVNC) { + writeInt(authType); + return authType; + } + } + throw new Exception("No suitable authentication scheme found"); + } + + // + // Read a capability list (TightVNC protocol extensions) + // + + void readCapabilityList(CapsContainer caps, int count) throws IOException { + int code; + byte[] vendor = new byte[4]; + byte[] name = new byte[8]; + for (int i = 0; i < count; i++) { + code = readU32(); + readFully(vendor); + readFully(name); + caps.enable(new CapabilityInfo(code, vendor, name)); + } + } + + // + // Write a 32-bit integer into the output stream. + // + + void writeInt(int value) throws IOException { + byte[] b = new byte[4]; + b[0] = (byte) ((value >> 24) & 0xff); + b[1] = (byte) ((value >> 16) & 0xff); + b[2] = (byte) ((value >> 8) & 0xff); + b[3] = (byte) (value & 0xff); + os.write(b); + } + + // + // Write the client initialisation message + // + + void writeClientInit() throws IOException { +/* + if (viewer.options.shareDesktop) { +*/ + + /** + * shared flag + */ + os.write(1); +// os.write(0); + +// viewer.options.disableShareDesktop(); + } + + // + // Read the server initialisation message + // + + String desktopName; + int framebufferWidth, framebufferHeight; + int bitsPerPixel, depth; + boolean bigEndian, trueColour; + int redMax, greenMax, blueMax, redShift, greenShift, blueShift; + + void readServerInit() throws IOException { + + framebufferWidth = readU16(); + framebufferHeight = readU16(); + bitsPerPixel = readU8(); + depth = readU8(); + bigEndian = (readU8() != 0); + trueColour = (readU8() != 0); + redMax = readU16(); + greenMax = readU16(); + blueMax = readU16(); + redShift = readU8(); + greenShift = readU8(); + blueShift = readU8(); + byte[] pad = new byte[3]; + readFully(pad); + int nameLength = readU32(); + byte[] name = new byte[nameLength]; + readFully(name); + desktopName = new String(name); + + // Read interaction capabilities (TightVNC protocol extensions) + if (protocolTightVNC) { + int nServerMessageTypes = readU16(); + int nClientMessageTypes = readU16(); + int nEncodingTypes = readU16(); + readU16(); + readCapabilityList(serverMsgCaps, nServerMessageTypes); + readCapabilityList(clientMsgCaps, nClientMessageTypes); + readCapabilityList(encodingCaps, nEncodingTypes); + } + + inNormalProtocol = true; + } + + // + // Create session file and write initial protocol messages into it. + // + + void startSession(String fname) throws IOException { + rec = new SessionRecorder(fname); + rec.writeHeader(); + rec.write(versionMsg_3_3.getBytes()); + rec.writeIntBE(SecTypeNone); + rec.writeShortBE(framebufferWidth); + rec.writeShortBE(framebufferHeight); + byte[] fbsServerInitMsg = { 32, 24, 0, 1, 0, (byte) 0xFF, 0, + (byte) 0xFF, 0, (byte) 0xFF, 16, 8, 0, 0, 0, 0 }; + rec.write(fbsServerInitMsg); + rec.writeIntBE(desktopName.length()); + rec.write(desktopName.getBytes()); + numUpdatesInSession = 0; + + // FIXME: If there were e.g. ZRLE updates only, that should not + // affect recording of Zlib and Tight updates. So, actually + // we should maintain separate flags for Zlib, ZRLE and + // Tight, instead of one ``wereZlibUpdates'' variable. + // + if (wereZlibUpdates) + recordFromBeginning = false; + + zlibWarningShown = false; + tightWarningShown = false; + } + + // + // Close session file. + // + + void closeSession() throws IOException { + if (rec != null) { + rec.close(); + rec = null; + } + } + + // + // Set new framebuffer size + // + + void setFramebufferSize(int width, int height) { + framebufferWidth = width; + framebufferHeight = height; + } + + // + // Read the server message type + // + + int readServerMessageType() throws IOException { + int msgType = readU8(); + + // If the session is being recorded: + if (rec != null) { + if (msgType == Bell) { // Save Bell messages in session files. + rec.writeByte(msgType); + if (numUpdatesInSession > 0) + rec.flush(); + } + } + + return msgType; + } + + // + // Read a FramebufferUpdate message + // + + int updateNRects; + + void readFramebufferUpdate() throws IOException { + skipBytes(1); + updateNRects = readU16(); + // System.out.println(updateNRects); + + // If the session is being recorded: + if (rec != null) { + rec.writeByte(FramebufferUpdate); + rec.writeByte(0); + rec.writeShortBE(updateNRects); + } + + numUpdatesInSession++; + } + + // Read a FramebufferUpdate rectangle header + + int updateRectX, updateRectY, updateRectW, updateRectH, updateRectEncoding; + + void readFramebufferUpdateRectHdr() throws Exception { + updateRectX = readU16(); + updateRectY = readU16(); + updateRectW = readU16(); + updateRectH = readU16(); + updateRectEncoding = readU32(); + // System.out.println("readU16&32"); + + if (updateRectEncoding == EncodingZlib + || updateRectEncoding == EncodingZRLE + || updateRectEncoding == EncodingZRLEE + || updateRectEncoding == EncodingTight) + wereZlibUpdates = true; + + // If the session is being recorded: + if (rec != null) { + if (numUpdatesInSession > 1) + rec.flush(); // Flush the output on each rectangle. + rec.writeShortBE(updateRectX); + rec.writeShortBE(updateRectY); + rec.writeShortBE(updateRectW); + rec.writeShortBE(updateRectH); + if (updateRectEncoding == EncodingZlib && !recordFromBeginning) { + // Here we cannot write Zlib-encoded rectangles because the + // decoder won't be able to reproduce zlib stream state. + if (!zlibWarningShown) { + System.out.println("Warning: Raw encoding will be used " + + "instead of Zlib in recorded session."); + zlibWarningShown = true; + } + rec.writeIntBE(EncodingRaw); + } else { + rec.writeIntBE(updateRectEncoding); + if (updateRectEncoding == EncodingTight && !recordFromBeginning + && !tightWarningShown) { + System.out.println("Warning: Re-compressing Tight-encoded " + + "updates for session recording."); + tightWarningShown = true; + } + } + } + + if (updateRectEncoding < 0 || updateRectEncoding > MaxNormalEncoding) + return; + + if (updateRectX + updateRectW > framebufferWidth + || updateRectY + updateRectH > framebufferHeight) { + throw new Exception("Framebuffer update rectangle too large: " + + updateRectW + "x" + updateRectH + " at (" + updateRectX + + "," + updateRectY + ")"); + } + } + + // Read CopyRect source X and Y. + + int copyRectSrcX, copyRectSrcY; + + void readCopyRect() throws IOException { + copyRectSrcX = readU16(); + copyRectSrcY = readU16(); + + // If the session is being recorded: + if (rec != null) { + rec.writeShortBE(copyRectSrcX); + rec.writeShortBE(copyRectSrcY); + } + } + + // + // Read a ServerCutText message + // + + String readServerCutText() throws IOException { + skipBytes(3); + int len = readU32(); + byte[] text = new byte[len]; + readFully(text); + return new String(text); + } + + // + // Read an integer in compact representation (1..3 bytes). + // Such format is used as a part of the Tight encoding. + // Also, this method records data if session recording is active and + // the viewer's recordFromBeginning variable is set to true. + // + + int readCompactLen() throws IOException { + int[] portion = new int[3]; + portion[0] = readU8(); + int byteCount = 1; + int len = portion[0] & 0x7F; + if ((portion[0] & 0x80) != 0) { + portion[1] = readU8(); + byteCount++; + len |= (portion[1] & 0x7F) << 7; + if ((portion[1] & 0x80) != 0) { + portion[2] = readU8(); + byteCount++; + len |= (portion[2] & 0xFF) << 14; + } + } + + if (rec != null && recordFromBeginning) + for (int i = 0; i < byteCount; i++) + rec.writeByte(portion[i]); + + return len; + } + + // + // Write a FramebufferUpdateRequest message + // + + + void checkDelayData() throws IOException { + System.out.println("sousinn"); + byte[] b = new byte[1]; + b[0] = (byte) CheckDelay; + os.write(b); + } + + 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 < 0x20) { + if (evt.isControlDown()) { + key += 0x60; + } else { + switch (key) { + case KeyEvent.VK_BACK_SPACE: + key = 0xff08; + break; + case KeyEvent.VK_TAB: + key = 0xff09; + break; + case KeyEvent.VK_ENTER: + key = 0xff0d; + break; + case KeyEvent.VK_ESCAPE: + key = 0xff1b; + break; + } + } + } else if (key == 0x7f) { + // Delete + key = 0xffff; + } else if (key > 0xff) { + // JDK1.1 on X incorrectly passes some keysyms straight through, + // so we do too. JDK1.1.4 seems to have fixed this. + // The keysyms passed are 0xff00 .. XK_BackSpace .. XK_Delete + // Also, we pass through foreign currency keysyms + // (0x20a0..0x20af). + if ((key < 0xff00 || key > 0xffff) + && !(key >= 0x20a0 && key <= 0x20af)) + return; + } + } + + // Fake keyPresses for keys that only generates keyRelease events + if ((key == 0xe5) || (key == 0xc5) || // XK_aring / XK_Aring + (key == 0xe4) || (key == 0xc4) || // XK_adiaeresis / + // XK_Adiaeresis + (key == 0xf6) || (key == 0xd6) || // XK_odiaeresis / + // XK_Odiaeresis + (key == 0xa7) || (key == 0xbd) || // XK_section / XK_onehalf + (key == 0xa3)) { // XK_sterling + // Make sure we do not send keypress events twice on platforms + // with correct JVMs (those that actually report KeyPress for all + // keys) + if (down) + brokenKeyPressed = true; + + if (!down && !brokenKeyPressed) { + // We've got a release event for this key, but haven't received + // a press. Fake it. + eventBufLen = 0; + writeModifierKeyEvents(evt.getModifiers()); + writeKeyEvent(key, true); + os.write(eventBuf, 0, eventBufLen); + } + + if (!down) + brokenKeyPressed = false; + } + + eventBufLen = 0; + writeModifierKeyEvents(evt.getModifiers()); + writeKeyEvent(key, down); + + // Always release all modifiers after an "up" event + if (!down) + writeModifierKeyEvents(0); + + os.write(eventBuf, 0, eventBufLen); + } + + // + // Add a raw key event with the given X keysym to eventBuf. + // + + void writeKeyEvent(int keysym, boolean down) { + eventBuf[eventBufLen++] = (byte) KeyboardEvent; + eventBuf[eventBufLen++] = (byte) (down ? 1 : 0); + eventBuf[eventBufLen++] = (byte) 0; + eventBuf[eventBufLen++] = (byte) 0; + eventBuf[eventBufLen++] = (byte) ((keysym >> 24) & 0xff); + eventBuf[eventBufLen++] = (byte) ((keysym >> 16) & 0xff); + eventBuf[eventBufLen++] = (byte) ((keysym >> 8) & 0xff); + eventBuf[eventBufLen++] = (byte) (keysym & 0xff); + } + + // + // Write key events to set the correct modifier state. + // + + int oldModifiers = 0; + + void writeModifierKeyEvents(int newModifiers) { + if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) + writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); + + if ((newModifiers & SHIFT_MASK) != (oldModifiers & SHIFT_MASK)) + writeKeyEvent(0xffe1, (newModifiers & SHIFT_MASK) != 0); + + if ((newModifiers & META_MASK) != (oldModifiers & META_MASK)) + writeKeyEvent(0xffe7, (newModifiers & META_MASK) != 0); + + if ((newModifiers & ALT_MASK) != (oldModifiers & ALT_MASK)) + writeKeyEvent(0xffe9, (newModifiers & ALT_MASK) != 0); + + oldModifiers = newModifiers; + } + + // + // Compress and write the data into the recorded session file. This + // method assumes the recording is on (rec != null). + // + + void recordCompressedData(byte[] data, int off, int len) throws IOException { + Deflater deflater = new Deflater(); + deflater.setInput(data, off, len); + int bufSize = len + len / 100 + 12; + byte[] buf = new byte[bufSize]; + deflater.finish(); + int compressedSize = deflater.deflate(buf); + recordCompactLen(compressedSize); + rec.write(buf, 0, compressedSize); + } + + void recordCompressedData(byte[] data) throws IOException { + recordCompressedData(data, 0, data.length); + } + + // + // Write an integer in compact representation (1..3 bytes) into the + // recorded session file. This method assumes the recording is on + // (rec != null). + // + + void recordCompactLen(int len) throws IOException { + byte[] buf = new byte[3]; + int bytes = 0; + buf[bytes++] = (byte) (len & 0x7F); + if (len > 0x7F) { + buf[bytes - 1] |= 0x80; + buf[bytes++] = (byte) (len >> 7 & 0x7F); + if (len > 0x3FFF) { + buf[bytes - 1] |= 0x80; + buf[bytes++] = (byte) (len >> 14 & 0xFF); + } + } + rec.write(buf, 0, bytes); + } + + public void startTiming() { + timing = true; + + // Carry over up to 1s worth of previous rate for smoothing. + + if (timeWaitedIn100us > 10000) { + timedKbits = timedKbits * 10000 / timeWaitedIn100us; + timeWaitedIn100us = 10000; + } + } + + public void stopTiming() { + timing = false; + if (timeWaitedIn100us < timedKbits / 2) + timeWaitedIn100us = timedKbits / 2; // upper limit 20Mbit/s + } + + public long kbitsPerSecond() { + return timedKbits * 10000 / timeWaitedIn100us; + } + + public long timeWaited() { + return timeWaitedIn100us; + } + + // + // Methods for reading data via our DataInputStream member variable (is). + // + // In addition to reading data, the readFully() methods updates variables + // used to estimate data throughput. + // + + public void readFully(byte b[]) throws IOException { + readFully(b, 0, b.length); + } + + long before = System.currentTimeMillis(); + public void readFully(byte b[], int off, int len) throws IOException { + long before = 0; + if (timing) + before = System.currentTimeMillis(); + + is.readFully(b, off, len); + + if (timing) { + long after = System.currentTimeMillis(); + long newTimeWaited = (after - before) * 10; + int newKbits = len * 8 / 1000; + + // limit rate to between 10kbit/s and 40Mbit/s + + if (newTimeWaited > newKbits * 1000) + newTimeWaited = newKbits * 1000; + if (newTimeWaited < newKbits / 4) + newTimeWaited = newKbits / 4; + + timeWaitedIn100us += newTimeWaited; + timedKbits += newKbits; + before = after; + } + + numBytesRead += len; + } + + final int available() throws IOException { + return is.available(); + } + + // FIXME: DataInputStream::skipBytes() is not guaranteed to skip + // exactly n bytes. Probably we don't want to use this method. + final int skipBytes(int n) throws IOException { + int r = is.skipBytes(n); + numBytesRead += r; + return r; + } + + final int readU8() throws IOException { + int r = is.readUnsignedByte(); + numBytesRead++; + + return r; + } + + final int readU16() throws IOException { + int r = is.readUnsignedShort(); + numBytesRead += 2; + return r; + } + + final int readU32() throws IOException { + int r = is.readInt(); + numBytesRead += 4; + return r; + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/SelectType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/SelectType.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,91 @@ +package treeVnc; + +import javax.swing.*; + +import java.awt.*; +import java.awt.event.*; + +public class SelectType extends JFrame implements ActionListener { + + /** + * + */ + private static final long serialVersionUID = 1L; + + private JPanel panel = new JPanel(); + private JButton button = new JButton("Connect"); + private double width; + private double height; + private Checkbox[] check = new Checkbox[2]; + boolean type = false; + boolean wait = true; + + public void selectType() { + setSize(); + checkBox(); + visible(); + } + + public SelectType() { + setTitle("Informatin Connection Address"); + setResizable(false); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + + private void setSize() { + Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); + width = (d.getWidth() / 2); + height = (d.getHeight() / 2); + } + + private void visible() { + Point point = new Point(); + point.setLocation(width - 250, height - 80); + setLocation(point.getLocation()); + pack(); + setVisible(true); + } +/* + private void init(){ + frameInit(); +// panel = null; +// button = null; +// label = null; + setVisible(false); + } + */ + + private void setButton(){ + panel.add(button); + panel.setBackground(Color.blue); + button.addActionListener(this); + Container contentPane = getContentPane(); + contentPane.add(panel, BorderLayout.CENTER); + } + + + private void checkBox() { + CheckboxGroup ch = new CheckboxGroup(); + check[0] = new Checkbox("Client",ch,false); + check[0].setBackground(new Color(0,153,255)); + check[1] = new Checkbox("Proxy",ch,false); + check[1].setBackground(new Color(0,153,255)); + panel.add(check[0]); + panel.add(check[1]); + panel.setLayout(new GridLayout(2, 0)); + setButton(); + } + + public void actionPerformed(ActionEvent e) { + if(check[0].getState()){ + System.out.println("Client"); + type = true; + } else { + System.out.println("Proxy"); + type = false; + } + setVisible(false); + wait = false; + } + +} \ No newline at end of file diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/SendThread.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/SendThread.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,30 @@ +package treeVnc; +import java.net.Socket; +import java.io.IOException; +import java.io.OutputStream; + + +public class SendThread implements Runnable { + + Socket sock; + OutputStream out; + byte b[]; + + SendThread(Socket _sock, byte _b[]){ + sock = _sock; + b = _b; + } + SendThread(OutputStream _out, byte _b[]){ + out = _out; + b = _b; + } + + public void run() { + try{ + out.write(b, 0, b.length); +// sock.getOutputStream().write(b, 0, b.length); + }catch(IOException e){ + + } + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/SessionRecorder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/SessionRecorder.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,194 @@ +package treeVnc; +// +// Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved. +// +// This is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +// +// SessionRecorder is a class to write FBS (FrameBuffer Stream) files. +// FBS files are used to save RFB sessions for later playback. +// + +import java.io.*; + +class SessionRecorder { + + protected FileOutputStream f; + protected DataOutputStream df; + protected long startTime, lastTimeOffset; + + protected byte[] buffer; + protected int bufferSize; + protected int bufferBytes; + + public SessionRecorder(String name, int bufsize) throws IOException { + f = new FileOutputStream(name); + df = new DataOutputStream(f); + startTime = System.currentTimeMillis(); + lastTimeOffset = 0; + + bufferSize = bufsize; + bufferBytes = 0; + buffer = new byte[bufferSize]; + } + + public SessionRecorder(String name) throws IOException { + this(name, 65536); + } + + // + // Close the file, free resources. + // + + public void close() throws IOException { + try { + flush(); + } catch (IOException e) { + } + + df = null; + f.close(); + f = null; + buffer = null; + } + + // + // Write the FBS file header as defined in the rfbproxy utility. + // + + public void writeHeader() throws IOException { + df.write("FBS 001.000\n".getBytes()); + } + + // + // Write one byte. + // + + public void writeByte(int b) throws IOException { + prepareWriting(); + buffer[bufferBytes++] = (byte)b; + } + + // + // Write 16-bit value, big-endian. + // + + public void writeShortBE(int v) throws IOException { + prepareWriting(); + buffer[bufferBytes++] = (byte)(v >> 8); + buffer[bufferBytes++] = (byte)v; + } + + // + // Write 32-bit value, big-endian. + // + + public void writeIntBE(int v) throws IOException { + prepareWriting(); + buffer[bufferBytes] = (byte)(v >> 24); + buffer[bufferBytes + 1] = (byte)(v >> 16); + buffer[bufferBytes + 2] = (byte)(v >> 8); + buffer[bufferBytes + 3] = (byte)v; + bufferBytes += 4; + } + + // + // Write 16-bit value, little-endian. + // + + public void writeShortLE(int v) throws IOException { + prepareWriting(); + buffer[bufferBytes++] = (byte)v; + buffer[bufferBytes++] = (byte)(v >> 8); + } + + // + // Write 32-bit value, little-endian. + // + + public void writeIntLE(int v) throws IOException { + prepareWriting(); + buffer[bufferBytes] = (byte)v; + buffer[bufferBytes + 1] = (byte)(v >> 8); + buffer[bufferBytes + 2] = (byte)(v >> 16); + buffer[bufferBytes + 3] = (byte)(v >> 24); + bufferBytes += 4; + } + + // + // Write byte arrays. + // + + public void write(byte b[], int off, int len) throws IOException { + prepareWriting(); + while (len > 0) { + if (bufferBytes > bufferSize - 4) + flush(false); + + int partLen; + if (bufferBytes + len > bufferSize) { + partLen = bufferSize - bufferBytes; + } else { + partLen = len; + } + System.arraycopy(b, off, buffer, bufferBytes, partLen); + bufferBytes += partLen; + off += partLen; + len -= partLen; + } + } + + public void write(byte b[]) throws IOException { + write(b, 0, b.length); + } + + // + // Flush the output. This method saves buffered data in the + // underlying file object adding data sizes and timestamps. If the + // updateTimeOffset is set to false, then the current time offset + // will not be changed for next write operation. + // + + public void flush(boolean updateTimeOffset) throws IOException { + if (bufferBytes > 0) { + df.writeInt(bufferBytes); + df.write(buffer, 0, (bufferBytes + 3) & 0x7FFFFFFC); + df.writeInt((int)lastTimeOffset); + bufferBytes = 0; + if (updateTimeOffset) + lastTimeOffset = -1; + } + } + + public void flush() throws IOException { + flush(true); + } + + // + // Before writing any data, remember time offset and flush the + // buffer before it becomes full. + // + + protected void prepareWriting() throws IOException { + if (lastTimeOffset == -1) + lastTimeOffset = System.currentTimeMillis() - startTime; + if (bufferBytes > bufferSize - 4) + flush(false); + } + +} + diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/SocketFactory.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/SocketFactory.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,37 @@ +package treeVnc; +// +// Copyright (C) 2002 HorizonLive.com, Inc. All Rights Reserved. +// +// This is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +// +// SocketFactory.java describes an interface used to substitute the +// standard Socket class by its alternative implementations. +// + +import java.applet.*; +import java.net.*; +import java.io.*; + +public interface SocketFactory { + + public Socket createSocket(String host, int port, Applet applet) + throws IOException; + + public Socket createSocket(String host, int port, String[] args) + throws IOException; +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/TextBoxClient.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/TextBoxClient.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,201 @@ +package treeVnc; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.ArrayList; + +public class TextBoxClient extends JFrame implements ActionListener, + ItemListener { + + /** + * + */ + private static final long serialVersionUID = 1L; + private JPanel panel = new JPanel(); + private JButton button = new JButton("Connect"); + private TextField t1; + private TextField t2; + private double width = 750; + private double height = 500; + private JLabel label; + private boolean flag; + private ArrayList temp = new ArrayList(); + private int counter = 0; + // private JCheckBox[] check = new JCheckBox[20]; + private Checkbox[] check = new Checkbox[20]; + private boolean firstFlag = true; + private String hostAddress; + private String port; + private CheckboxGroup ch = new CheckboxGroup(); + + public TextBoxClient() { + setTitle("Informatin Connection Address"); + setResizable(false); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + + public void ipRegister() { + setSize(); + setText(); + visible(); + } + + private void setSize() { + Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); + width = (d.getWidth() / 2); + height = (d.getHeight() / 2); + } + + void visible() { + Point point = new Point(); + point.setLocation(width - 250, height - 80); + setLocation(point.getLocation()); + pack(); + setVisible(true); + } + + public void unVisible() { + setVisible(false); + } + + private void setText() { + t1 = new TextField("Address", 30); + t2 = new TextField("5999", 5); + panel.add(t1); + panel.add(t2); + panel.add(button); + button.addActionListener(this); + label = new JLabel(); + Container contentPane = getContentPane(); + contentPane.add(panel, BorderLayout.CENTER); + contentPane.add(label, BorderLayout.SOUTH); + } + + void checkBox(String str) { + // CheckboxGroup ch = new CheckboxGroup(); + if (counter == 0) + check[counter] = new Checkbox(str, true, ch); + else + check[counter] = new Checkbox(str, false, ch); + // check[counter].setBackground(new Color(0, 153, 255)); + check[counter].addItemListener(this); + panel.add(check[counter]); + panel.setLayout(new GridLayout(counter + 2, 0)); + panel.setLocation((int) width - 250, (int) height - 80); + counter++; + } + + public void setButton() { + panel.add(button); + // panel.setBackground(Color.blue); + button.addActionListener(this); + Container contentPane = getContentPane(); + contentPane.add(panel, BorderLayout.CENTER); + } + + private void reportWindow() { + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + Container pane = getContentPane(); + JLabel label = new JLabel( + "Not found Proxy. If you use Client-mode you must set-up Proxy"); + label.setPreferredSize(new Dimension(580, 50)); + label.setFont(new Font("Arial", Font.PLAIN, 20)); + pane.add(label); + } + + public String getAddressOption() { + while (!(flag)) { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return t1.getText(); + } + + public String getAddress() { + int i = 0; + while (!(flag)) { + if (i >= 50) { + reportWindow(); + visible(); + break; + } + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + i++; + } + return hostAddress; + // return t1.getText(); + } + + public String getPortOption() { + return t2.getText(); + } + + public String getPort() { + return port; + // return t2.getText(); + } + + public void actionPerformed(ActionEvent e) { + flag = true; + for (int t = 0; t < counter; t++) { + if (check[t].getState()) { + System.out.println(check[t].getLabel()); + // if(int t =0) + setStatus(check[t].getLabel()); + unVisible(); + // else + // setStatus() + } + } + } + + private void setStatus(String str) { + String[] temp = str.split(":"); + if (temp.length == 2) { + hostAddress = temp[0]; + port = temp[1]; + } else { + port = temp[0]; + System.out.println("port=" + port); + hostAddress = temp[3]; + } + } + + String splitString(String str) { + String[] split = str.split("\\*"); + String comper; + if (split.length > 4) { + split[4] = null; + } + comper = split[1] + split[3]; + if (firstFlag) { + temp.add(comper); + firstFlag = false; + return "port:" + split[0] + ":host:" + split[1] + ":proxy:" + + split[3]; + } + for (int t = 0; t < temp.size(); t++) { + if (!(comper.equals(temp.get(t)))) { + if (t == temp.size() - 1) { + temp.add(comper); + return "port:" + split[0] + ":host:" + split[1] + ":proxy:" + + split[3]; + } + } else { + break; + } + } + return null; + } + + public void itemStateChanged(ItemEvent e) { + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/TextBoxProxy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/TextBoxProxy.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,126 @@ +package treeVnc; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; + +public class TextBoxProxy extends JFrame implements ActionListener { + + /** + * + */ + private static final long serialVersionUID = 1L; + private JPanel panel; + private JButton button; + private JButton button1; + private TextField t1; + private TextField t2; + private JPasswordField pw; + private double width; + private double height; + private JLabel label; + private boolean flag; + private VncProxyService vps; + + public void ipRegister() { + setSize(); + setText(); + visible(); + } + + public TextBoxProxy(VncProxyService _vps) { + vps = _vps; + setTitle("Informatin Connection Address"); + setResizable(false); + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + } + + private void setSize() { + Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); + width = (d.getWidth() / 2); + height = (d.getHeight() / 2); + } + + private void visible() { + Point point = new Point(); + point.setLocation(width - 250, height - 80); + setLocation(point.getLocation()); + pack(); + setVisible(true); + } + + private void init(){ + frameInit(); +// panel = null; +// button = null; +// label = null; + setVisible(false); + } + + public void changeHost() { + init(); + button1 = new JButton("Change Host"); + t1 = new TextField("Address", 30); + panel = new JPanel(); + panel.add(t1); + panel.add(button1); + button1.addActionListener(this); + label = new JLabel(); + Container contentPane = getContentPane(); + contentPane.add(panel, BorderLayout.CENTER); + contentPane.add(label, BorderLayout.SOUTH); + visible(); + } + + private void setText() { + panel = new JPanel(); + t1 = new TextField("localhost", 30); + t2 = new TextField("5900", 5); +// pw = new JPasswordField("Password",30); + button = new JButton("Connect"); + panel.add(t1); + panel.add(t2); +// panel.add(pw); + panel.add(button); + button.addActionListener(this); + label = new JLabel(); + Container contentPane = getContentPane(); + contentPane.add(panel, BorderLayout.CENTER); + contentPane.add(label, BorderLayout.SOUTH); + } + + public String getAddress() { + while(!(flag)) { + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + return t1.getText(); + } + public String getPort() { + return t2.getText(); + } + + + public String getPassword() { + return String.valueOf(pw.getPassword()); + } + + + public void actionPerformed(ActionEvent e) { + if(e.getSource()==button) { + flag = true; + setVisible(false); + } + if(e.getSource()==button1) + try { + init(); + vps.host = getAddress(); + vps.changeInit(); + } catch (Exception e1) { + e1.printStackTrace(); + } + } +} \ No newline at end of file diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/TreeVnc.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/TreeVnc.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,46 @@ +package treeVnc; + +/* + import myVncClient.MyVncClient; + import myVncProxy.VncProxyService; + */ + +public class TreeVnc { + public static void main(String[] argv) { + /* + * SelectType select = new SelectType(); select.selectType(); + * + * while (select.wait) { try { Thread.sleep(1000); } catch + * (InterruptedException e) { e.printStackTrace(); } } + * System.out.println("test"); + */ + + if (argv.length == 0) { + System.out.println("client"); + MyVncClient client = new MyVncClient(); + client.treeVncClient(argv); + } else if (argv.length == 1||argv.length==3) { + if ("-p".equals(argv[0])) { + System.out.println("proxy"); +// String[] temp = new String[0]; + VncProxyService proxy = new VncProxyService(); + proxy.treeVncProxy(argv); + } else if ("-reg".equals(argv[0])||"-c".equals(argv[0])) { + System.out.println("client"); + MyVncClient client = new MyVncClient(); + client.treeVncClient(argv); + } else { + System.out + .println("usage : java -jar TreeVnc.jar (IpAddress) 5900"); + } + } else if (argv.length == 2) { + System.out.println("proxy"); + VncProxyService proxy = new VncProxyService(); + proxy.treeVncProxy(argv); + } else { + System.out + .println("usage : java -jar TreeVnc.jar (IpAddress) 5900"); + } + + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/VncCanvas.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/VncCanvas.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,1990 @@ +package treeVnc; +// +// Copyright (C) 2004 Horizon Wimba. All Rights Reserved. +// Copyright (C) 2001-2003 HorizonLive.com, Inc. All Rights Reserved. +// Copyright (C) 2001,2002 Constantin Kaplinsky. All Rights Reserved. +// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import java.io.*; +import java.util.zip.*; + +import javax.imageio.ImageIO; + +//import test.TestComet; + + +// +// VncCanvas is a subclass of Canvas which draws a VNC desktop on it. +// + +class VncCanvas extends Canvas implements KeyListener, MouseListener, + MouseMotionListener { + + /** + * + */ + private static final long serialVersionUID = 1L; + VncViewer viewer; + MyRfbProtoClient rfbc; + MyRfbProtoProxy rfbp; + ColorModel cm8, cm24; + Color[] colors; + int bytesPixel; + + int maxWidth = 0, maxHeight = 0; + int scalingFactor; + int scaledWidth, scaledHeight; + + Image memImage; + Graphics memGraphics; + + Image rawPixelsImage; +// BufferedImage rawPixelsImaage; + BufferedImage bimg; + + MemoryImageSource pixelsSource; + byte[] pixels8; + int[] pixels24; + + // Update statistics. + long statStartTime; // time on first framebufferUpdateRequest + int statNumUpdates; // counter for FramebufferUpdate messages + int statNumTotalRects; // rectangles in FramebufferUpdate messages + int statNumPixelRects; // the same, but excluding pseudo-rectangles + int statNumRectsTight; // Tight-encoded rectangles (including JPEG) + int statNumRectsTightJPEG; // JPEG-compressed Tight-encoded rectangles + int statNumRectsZRLE; // ZRLE-encoded rectangles + int statNumRectsHextile; // Hextile-encoded rectangles + int statNumRectsRaw; // Raw-encoded rectangles + int statNumRectsCopy; // CopyRect rectangles + int statNumBytesEncoded; // number of bytes in updates, as received + int statNumBytesDecoded; // number of bytes, as if Raw encoding was used + + // ZRLE encoder's data. + byte[] zrleBuf; + int zrleBufLen = 0; + byte[] zrleTilePixels8; + int[] zrleTilePixels24; + ZlibInStream zrleInStream; + boolean zrleRecWarningShown = false; + + // 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 inputEnabled; + + // + // The constructors. + // + + public VncCanvas(VncViewer v, int maxWidth_, int maxHeight_) + throws IOException { + + viewer = v; + maxWidth = maxWidth_; + maxHeight = maxHeight_; + + rfbc = viewer.rfb; + scalingFactor = viewer.options.scalingFactor; + + 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(); + + inputEnabled = false; + if (!viewer.options.viewOnly) + enableInput(true); + + // Keyboard listener is enabled even in view-only mode, to catch + // 'r' or 'R' key presses used to request screen update. + addKeyListener(this); + } + + public VncCanvas(VncViewer v) throws IOException { + this(v, 0, 0); + } + + // + // Callback methods to determine geometry of our Component. + // + + public Dimension getPreferredSize() { + return new Dimension(scaledWidth, scaledHeight); + } + + public Dimension getMinimumSize() { + return new Dimension(scaledWidth, scaledHeight); + } + + public Dimension getMaximumSize() { + return new Dimension(scaledWidth, scaledHeight); + } + + // + // All painting is performed here. + // + + public void update(Graphics g) { + paint(g); + } + + public void paint(Graphics g) { + synchronized (memImage) { + if (rfbc.framebufferWidth == scaledWidth) { + g.drawImage(memImage, 0, 0, null); + } else { + paintScaledFrameBuffer(g); + } + } + if (showSoftCursor) { + int x0 = cursorX - hotX, y0 = cursorY - hotY; + Rectangle r = new Rectangle(x0, y0, cursorWidth, cursorHeight); + if (r.intersects(g.getClipBounds())) { + g.drawImage(softCursor, x0, y0, null); + } + } + } + + public void paintScaledFrameBuffer(Graphics g) { + g.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null); + } + + // + // 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 mouse events. Keyboard events are received + // even in view-only mode, because we want to map the 'r' key to the + // screen refreshing function. + // + + public synchronized void enableInput(boolean enable) { + if (enable && !inputEnabled) { + inputEnabled = true; + addMouseListener(this); + addMouseMotionListener(this); + if (viewer.showControls) { + viewer.buttonPanel.enableRemoteAccessControls(true); + } + createSoftCursor(); // scaled cursor + } else if (!enable && inputEnabled) { + inputEnabled = false; + removeMouseListener(this); + removeMouseMotionListener(this); + if (viewer.showControls) { + viewer.buttonPanel.enableRemoteAccessControls(false); + } + createSoftCursor(); // non-scaled cursor + } + } + + public void setPixelFormat() throws IOException { + if (viewer.options.eightBitColors) { + rfbc.writeSetPixelFormat(8, 8, false, true, 7, 7, 3, 0, 3, 6); + bytesPixel = 1; + } else { + rfbc.writeSetPixelFormat(32, 24, false, true, 255, 255, 255, 16, 8, + 0); + bytesPixel = 4; + } + updateFramebufferSize(); + } + + void updateFramebufferSize() { + + // Useful shortcuts. + int fbWidth = rfbc.framebufferWidth; + int fbHeight = rfbc.framebufferHeight; + + // Calculate scaling factor for auto scaling. + if (maxWidth > 0 && maxHeight > 0) { + int f1 = maxWidth * 100 / fbWidth; + int f2 = maxHeight * 100 / fbHeight; + scalingFactor = Math.min(f1, f2); + if (scalingFactor > 100) + scalingFactor = 100; + System.out.println("Scaling desktop at " + scalingFactor + "%"); + } + + // Update scaled framebuffer geometry. + scaledWidth = (fbWidth * scalingFactor + 50) / 100; + scaledHeight = (fbHeight * scalingFactor + 50) / 100; + + // Create new off-screen image either if it does not exist, or if + // its geometry should be changed. It's not necessary to replace + // existing image if only pixel format should be changed. + if (memImage == null) { + memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); + memGraphics = memImage.getGraphics(); + } else if (memImage.getWidth(null) != fbWidth + || memImage.getHeight(null) != fbHeight) { + synchronized (memImage) { + memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); + memGraphics = memImage.getGraphics(); + } + } + + // 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); + + zrleTilePixels24 = null; + zrleTilePixels8 = new byte[64 * 64]; + + } else { + + pixels8 = null; + pixels24 = new int[fbWidth * fbHeight]; + + pixelsSource = new MemoryImageSource(fbWidth, fbHeight, cm24, + pixels24, 0, fbWidth); + + zrleTilePixels8 = null; + zrleTilePixels24 = new int[64 * 64]; + + } + pixelsSource.setAnimated(true); + rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource); +// rawPixelsImage = (BufferedImage) Toolkit.getDefaultToolkit().createImage(pixelsSource); + + // Update the size of desktop containers. + if (viewer.inSeparateFrame) { + if (viewer.desktopScrollPane != null) + resizeDesktopFrame(); + } else { + setSize(scaledWidth, scaledHeight); + } + viewer.moveFocusToDesktop(); + } + + void resizeDesktopFrame() { + setSize(scaledWidth, scaledHeight); + + // FIXME: Find a better way to determine correct size of a + // ScrollPane. -- const + Insets insets = viewer.desktopScrollPane.getInsets(); + viewer.desktopScrollPane.setSize( + scaledWidth + 2 * Math.min(insets.left, insets.right), + scaledHeight + 2 * Math.min(insets.top, insets.bottom)); + + viewer.vncFrame.pack(); + + // Try to limit the frame size to the screen size. + + Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize(); + Dimension frameSize = viewer.vncFrame.getSize(); + Dimension newSize = frameSize; + + // Reduce Screen Size by 30 pixels in each direction; + // This is a (poor) attempt to account for + // 1) Menu bar on Macintosh (should really also account for + // Dock on OSX). Usually 22px on top of screen. + // 2) Taxkbar on Windows (usually about 28 px on bottom) + // 3) Other obstructions. + + screenSize.height -= 30; + screenSize.width -= 30; + + boolean needToResizeFrame = false; + if (frameSize.height > screenSize.height) { + newSize.height = screenSize.height; + needToResizeFrame = true; + } + if (frameSize.width > screenSize.width) { + newSize.width = screenSize.width; + needToResizeFrame = true; + } + if (needToResizeFrame) { + viewer.vncFrame.setSize(newSize); + } + + viewer.desktopScrollPane.doLayout(); + } + + // + // processNormalProtocol() - executed by the rfbThread to deal with the + // RFB socket. + // + + public void processNormalProtocol() throws Exception { + + // Start/stop session recording if necessary. + viewer.checkRecordingStatus(); + + rfbc.writeFramebufferUpdateRequest(0, 0, rfbc.framebufferWidth, + rfbc.framebufferHeight, false); + + resetStats(); + boolean statsRestarted = false; + + // + // main dispatch loop + // + + long count = 0; + +// new Thread(){public void run() {TestComet.main(null);}}.start(); + + + while (true) { +// System.out.println("\ncount=" + count); + count++; + + // rfb.printFramebufferUpdate(); + + /* + * read Data from parents and send Data to Client. + * + */ + //rfbc.sendDataCheckDelay(); + rfbc.sendDataToClient(); + +// rfb.printNumBytesRead(); + long bufSize = rfbc.getNumBytesRead(); + // Read message type from the server. + int msgType = rfbc.readServerMessageType(); + + // Process the message depending on its type. + switch (msgType) { + case MyRfbProtoClient.CheckDelay: + System.out.println("---------------into data------------"); + //rfbc.readSpeedCheck(); + break; + case MyRfbProtoClient.SpeedCheckMillis: + rfbc.readSpeedCheck(); + break; + case MyRfbProtoClient.WriteJpegData: + byte[] buf = rfbc.readJpegData(); + drawBufferedImage(buf); + break; + case RfbProto.FramebufferUpdate: + + if (statNumUpdates == viewer.debugStatsExcludeUpdates + && !statsRestarted) { + resetStats(); + statsRestarted = true; + } else if (statNumUpdates == viewer.debugStatsMeasureUpdates + && statsRestarted) { + viewer.disconnect(); + } + + rfbc.readFramebufferUpdate(); + statNumUpdates++; + + boolean cursorPosReceived = false; + + for (int i = 0; i < rfbc.updateNRects; i++) { + + rfbc.readFramebufferUpdateRectHdr(); + statNumTotalRects++; + int rx = rfbc.updateRectX, ry = rfbc.updateRectY; + int rw = rfbc.updateRectW, rh = rfbc.updateRectH; + + if (rfbc.updateRectEncoding == rfbc.EncodingLastRect) + break; + + if (rfbc.updateRectEncoding == rfbc.EncodingNewFBSize) { + rfbc.setFramebufferSize(rw, rh); + updateFramebufferSize(); + break; + } + + if (rfbc.updateRectEncoding == rfbc.EncodingXCursor + || rfbc.updateRectEncoding == rfbc.EncodingRichCursor) { + handleCursorShapeUpdate(rfbc.updateRectEncoding, rx, ry, + rw, rh); + continue; + } + + if (rfbc.updateRectEncoding == rfbc.EncodingPointerPos) { + softCursorMove(rx, ry); + cursorPosReceived = true; + continue; + } + + long numBytesReadBefore = rfbc.getNumBytesRead(); + + rfbc.startTiming(); + + switch (rfbc.updateRectEncoding) { + case RfbProto.EncodingRaw: + statNumRectsRaw++; + handleRawRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingCopyRect: + statNumRectsCopy++; + handleCopyRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingRRE: + handleRRERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingCoRRE: + handleCoRRERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingHextile: + statNumRectsHextile++; + handleHextileRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingZRLE: + statNumRectsZRLE++; + handleZRLERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingZRLEE: + statNumRectsZRLE++; + handleZRLERect(rx, ry, rw, rh); + break; + case RfbProto.EncodingZlib: + handleZlibRect(rx, ry, rw, rh); + break; + case RfbProto.EncodingTight: + statNumRectsTight++; + handleTightRect(rx, ry, rw, rh); + break; + default: + throw new Exception("Unknown RFB rectangle encoding " + + rfbc.updateRectEncoding); + } + + rfbc.stopTiming(); + + long kbitsPerSecond = rfbc.kbitsPerSecond(); +// System.out.println("kbitsPerSecond = " + kbitsPerSecond); + + statNumPixelRects++; + statNumBytesDecoded += rw * rh * bytesPixel; + statNumBytesEncoded += (int) (rfbc.getNumBytesRead() - numBytesReadBefore); + } + + boolean fullUpdateNeeded = false; + + // Start/stop session recording if necessary. Request full + // update if a new session file was opened. + if (viewer.checkRecordingStatus()) + fullUpdateNeeded = true; + + // Defer framebuffer update request if necessary. But wake up + // immediately on keyboard or mouse event. Also, don't sleep + // if there is some data to receive, or if the last update + // included a PointerPos message. + if (viewer.deferUpdateRequests > 0 && rfbc.available() == 0 + && !cursorPosReceived) { + synchronized (rfbc) { + try { + rfbc.wait(viewer.deferUpdateRequests); + } catch (InterruptedException e) { + } + } + } + + viewer.autoSelectEncodings(); + + // Before requesting framebuffer update, check if the pixel + // format should be changed. + if (viewer.options.eightBitColors != (bytesPixel == 1)) { + // Pixel format should be changed. + setPixelFormat(); + fullUpdateNeeded = true; + } + + // Request framebuffer update if needed. + int w = rfbc.framebufferWidth; + int h = rfbc.framebufferHeight; + rfbc.writeFramebufferUpdateRequest(0, 0, w, h, !fullUpdateNeeded); + + break; + + case RfbProto.SetColourMapEntries: + throw new Exception("Can't handle SetColourMapEntries message"); + + case RfbProto.Bell: + Toolkit.getDefaultToolkit().beep(); + break; + + case RfbProto.ServerCutText: + String s = rfbc.readServerCutText(); + viewer.clipboard.setCutText(s); + break; + default: + throw new Exception("Unknown RFB message type " + msgType); + } + bufSize = rfbc.getNumBytesRead() - bufSize; +// System.out.println("bufSize="+bufSize); +// rfb.bufResetSend((int)bufSize); + + if(rfbc.createBimgFlag){ +// bimg = createBufferedImage(rawPixelsImage); + bimg = createBufferedImage(memImage); + //bimg(BufferedImage) -> rfb.pngBytes(byte[]) + rfbc.createPngBytes(bimg); + rfbc.sendPngImage(); + rfbc.createBimgFlag = false; + boolean result = false; + try{ + result = ImageIO.write(bimg, "png", new File("sample.png")); + }catch(Exception e){ + e.printStackTrace(); + result = false; + } + } + } + } + + // + // Handle a raw rectangle. The second form with paint==false is used + // by the Hextile decoder for raw-encoded tiles. + // + + void handleRawRect(int x, int y, int w, int h) throws IOException { + handleRawRect(x, y, w, h, true); + } + + void handleRawRect(int x, int y, int w, int h, boolean paint) + throws IOException { + + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + rfbc.readFully(pixels8, dy * rfbc.framebufferWidth + x, w); + if (rfbc.rec != null) { + rfbc.rec.write(pixels8, dy * rfbc.framebufferWidth + x, w); + } + } + } else { + byte[] buf = new byte[w * 4]; + int i, offset; + for (int dy = y; dy < y + h; dy++) { + rfbc.readFully(buf); + if (rfbc.rec != null) { + rfbc.rec.write(buf); + } + offset = dy * rfbc.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 + | (buf[i * 4 + 1] & 0xFF) << 8 + | (buf[i * 4] & 0xFF); + } + } + } + + handleUpdatedPixels(x, y, w, h); + if (paint) + scheduleRepaint(x, y, w, h); + } + + // + // Handle a CopyRect rectangle. + // + + void handleCopyRect(int x, int y, int w, int h) throws IOException { + + rfbc.readCopyRect(); + memGraphics.copyArea(rfbc.copyRectSrcX, rfbc.copyRectSrcY, w, h, x + - rfbc.copyRectSrcX, y - rfbc.copyRectSrcY); + + scheduleRepaint(x, y, w, h); + } + + // + // Handle an RRE-encoded rectangle. + // + + void handleRRERect(int x, int y, int w, int h) throws IOException { + + int nSubrects = rfbc.readU32(); + + byte[] bg_buf = new byte[bytesPixel]; + rfbc.readFully(bg_buf); + Color pixel; + if (bytesPixel == 1) { + pixel = colors[bg_buf[0] & 0xFF]; + } else { + pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, + bg_buf[0] & 0xFF); + } + memGraphics.setColor(pixel); + memGraphics.fillRect(x, y, w, h); + + byte[] buf = new byte[nSubrects * (bytesPixel + 8)]; + rfbc.readFully(buf); + DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf)); + + if (rfbc.rec != null) { + rfbc.rec.writeIntBE(nSubrects); + rfbc.rec.write(bg_buf); + rfbc.rec.write(buf); + } + + int sx, sy, sw, sh; + + for (int j = 0; j < nSubrects; j++) { + if (bytesPixel == 1) { + pixel = colors[ds.readUnsignedByte()]; + } else { + ds.skip(4); + pixel = new Color(buf[j * 12 + 2] & 0xFF, + buf[j * 12 + 1] & 0xFF, buf[j * 12] & 0xFF); + } + sx = x + ds.readUnsignedShort(); + sy = y + ds.readUnsignedShort(); + sw = ds.readUnsignedShort(); + sh = ds.readUnsignedShort(); + + memGraphics.setColor(pixel); + memGraphics.fillRect(sx, sy, sw, sh); + } + + scheduleRepaint(x, y, w, h); + } + + // + // Handle a CoRRE-encoded rectangle. + // + + void handleCoRRERect(int x, int y, int w, int h) throws IOException { + int nSubrects = rfbc.readU32(); + + byte[] bg_buf = new byte[bytesPixel]; + rfbc.readFully(bg_buf); + Color pixel; + if (bytesPixel == 1) { + pixel = colors[bg_buf[0] & 0xFF]; + } else { + pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, + bg_buf[0] & 0xFF); + } + memGraphics.setColor(pixel); + memGraphics.fillRect(x, y, w, h); + + byte[] buf = new byte[nSubrects * (bytesPixel + 4)]; + rfbc.readFully(buf); + + if (rfbc.rec != null) { + rfbc.rec.writeIntBE(nSubrects); + rfbc.rec.write(bg_buf); + rfbc.rec.write(buf); + } + + int sx, sy, sw, sh; + int i = 0; + + for (int j = 0; j < nSubrects; j++) { + if (bytesPixel == 1) { + pixel = colors[buf[i++] & 0xFF]; + } else { + pixel = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF, + buf[i] & 0xFF); + i += 4; + } + sx = x + (buf[i++] & 0xFF); + sy = y + (buf[i++] & 0xFF); + sw = buf[i++] & 0xFF; + sh = buf[i++] & 0xFF; + + memGraphics.setColor(pixel); + memGraphics.fillRect(sx, sy, sw, sh); + } + + scheduleRepaint(x, y, w, h); + } + + // + // Handle a Hextile-encoded rectangle. + // + + // These colors should be kept between handleHextileSubrect() calls. + private Color hextile_bg, hextile_fg; + boolean noZRLEdecode = false; + + void handleHextileRect(int x, int y, int w, int h) throws IOException { + + hextile_bg = new Color(0); + hextile_fg = new Color(0); + + for (int ty = y; ty < y + h; ty += 16) { + int th = 16; + if (y + h - ty < 16) + th = y + h - ty; + + for (int tx = x; tx < x + w; tx += 16) { + int tw = 16; + if (x + w - tx < 16) + tw = x + w - tx; + + handleHextileSubrect(tx, ty, tw, th); + } + + // Finished with a row of tiles, now let's show it. + scheduleRepaint(x, y, w, h); + } + } + + // + // Handle one tile in the Hextile-encoded data. + // + + void handleHextileSubrect(int tx, int ty, int tw, int th) + throws IOException { + + int subencoding = rfbc.readU8(); + if (rfbc.rec != null) { + rfbc.rec.writeByte(subencoding); + } + + // Is it a raw-encoded sub-rectangle? + if ((subencoding & rfbc.HextileRaw) != 0) { + handleRawRect(tx, ty, tw, th, false); + return; + } + + // Read and draw the background if specified. + byte[] cbuf = new byte[bytesPixel]; + if ((subencoding & rfbc.HextileBackgroundSpecified) != 0) { + rfbc.readFully(cbuf); + if (bytesPixel == 1) { + hextile_bg = colors[cbuf[0] & 0xFF]; + } else { + hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, + cbuf[0] & 0xFF); + } + if (rfbc.rec != null) { + rfbc.rec.write(cbuf); + } + } + memGraphics.setColor(hextile_bg); + memGraphics.fillRect(tx, ty, tw, th); + + // Read the foreground color if specified. + if ((subencoding & rfbc.HextileForegroundSpecified) != 0) { + rfbc.readFully(cbuf); + if (bytesPixel == 1) { + hextile_fg = colors[cbuf[0] & 0xFF]; + } else { + hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, + cbuf[0] & 0xFF); + } + if (rfbc.rec != null) { + rfbc.rec.write(cbuf); + } + } + + // Done with this tile if there is no sub-rectangles. + if ((subencoding & rfbc.HextileAnySubrects) == 0) + return; + + int nSubrects = rfbc.readU8(); + int bufsize = nSubrects * 2; + if ((subencoding & rfbc.HextileSubrectsColoured) != 0) { + bufsize += nSubrects * bytesPixel; + } + byte[] buf = new byte[bufsize]; + rfbc.readFully(buf); + if (rfbc.rec != null) { + rfbc.rec.writeByte(nSubrects); + rfbc.rec.write(buf); + } + + int b1, b2, sx, sy, sw, sh; + int i = 0; + + if ((subencoding & rfbc.HextileSubrectsColoured) == 0) { + + // Sub-rectangles are all of the same color. + memGraphics.setColor(hextile_fg); + for (int j = 0; j < nSubrects; j++) { + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.fillRect(sx, sy, sw, sh); + } + } else if (bytesPixel == 1) { + + // BGR233 (8-bit color) version for colored sub-rectangles. + for (int j = 0; j < nSubrects; j++) { + hextile_fg = colors[buf[i++] & 0xFF]; + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.setColor(hextile_fg); + memGraphics.fillRect(sx, sy, sw, sh); + } + + } else { + + // Full-color (24-bit) version for colored sub-rectangles. + for (int j = 0; j < nSubrects; j++) { + hextile_fg = new Color(buf[i + 2] & 0xFF, buf[i + 1] & 0xFF, + buf[i] & 0xFF); + i += 4; + b1 = buf[i++] & 0xFF; + b2 = buf[i++] & 0xFF; + sx = tx + (b1 >> 4); + sy = ty + (b1 & 0xf); + sw = (b2 >> 4) + 1; + sh = (b2 & 0xf) + 1; + memGraphics.setColor(hextile_fg); + memGraphics.fillRect(sx, sy, sw, sh); + } + + } + } + + // + // Handle a ZRLE-encoded rectangle. + // + // FIXME: Currently, session recording is not fully supported for ZRLE. + // + + void handleZRLERect(int x, int y, int w, int h) throws Exception { + if (noZRLEdecode) return; + if (zrleInStream == null || rfbc.updateRectEncoding==RfbProto.EncodingZRLEE) + zrleInStream = new ZlibInStream(); +// System.out.println("zrleInStream.end="+zrleInStream.inflater.off); + + int nBytes = rfbc.readU32(); + if (nBytes > 64 * 1024 * 1024) + throw new Exception("ZRLE decoder: illegal compressed data size"); + + if (zrleBuf == null || zrleBufLen < nBytes) { + zrleBufLen = nBytes + 4096; + zrleBuf = new byte[zrleBufLen]; + } + + // FIXME: Do not wait for all the data before decompression. + rfbc.readFully(zrleBuf, 0, nBytes); + + if (rfbc.rec != null) { + if (rfbc.recordFromBeginning) { + rfbc.rec.writeIntBE(nBytes); + rfbc.rec.write(zrleBuf, 0, nBytes); + } else if (!zrleRecWarningShown) { + System.out.println("Warning: ZRLE session can be recorded" + + " only from the beginning"); + System.out.println("Warning: Recorded file may be corrupted"); + zrleRecWarningShown = true; + } + } + + zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes); + + for (int ty = y; ty < y + h; ty += 64) { + + int th = Math.min(y + h - ty, 64); + + for (int tx = x; tx < x + w; tx += 64) { + + int tw = Math.min(x + w - tx, 64); + + int mode = zrleInStream.readU8(); + boolean rle = (mode & 128) != 0; + int palSize = mode & 127; + int[] palette = new int[128]; + + readZrlePalette(palette, palSize); + + if (palSize == 1) { + int pix = palette[0]; + Color c = (bytesPixel == 1) ? colors[pix] : new Color( + 0xFF000000 | pix); + memGraphics.setColor(c); + memGraphics.fillRect(tx, ty, tw, th); + continue; + } + + if (!rle) { + if (palSize == 0) { + readZrleRawPixels(tw, th); + } else { + readZrlePackedPixels(tw, th, palette, palSize); + } + } else { + if (palSize == 0) { + readZrlePlainRLEPixels(tw, th); + } else { + readZrlePackedRLEPixels(tw, th, palette); + } + } + handleUpdatedZrleTile(tx, ty, tw, th); + } + } + + zrleInStream.reset(); + + scheduleRepaint(x, y, w, h); + } + + int readPixel(InStream is) throws Exception { + int pix; + + if (bytesPixel == 1) { + + pix = is.readU8(); + } else { + int p1 = is.readU8(); + int p2 = is.readU8(); + int p3 = is.readU8(); + pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF); + } + return pix; + } + + void readPixels(InStream is, int[] dst, int count) throws Exception { + int pix; + if (bytesPixel == 1) { + byte[] buf = new byte[count]; + is.readBytes(buf, 0, count); + for (int i = 0; i < count; i++) { + dst[i] = (int) buf[i] & 0xFF; + } + } else { + byte[] buf = new byte[count * 3]; + is.readBytes(buf, 0, count * 3); + for (int i = 0; i < count; i++) { + dst[i] = ((buf[i * 3 + 2] & 0xFF) << 16 + | (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3] & 0xFF)); + /* + * dst[i] = (0x00 << 16 | 0x00 << 8 | 0xFF); + */ + + } + } + } + + void readZrlePalette(int[] palette, int palSize) throws Exception { + readPixels(zrleInStream, palette, palSize); + } + + void readZrleRawPixels(int tw, int th) throws Exception { + if (bytesPixel == 1) { + zrleInStream.readBytes(zrleTilePixels8, 0, tw * th); + } else { + readPixels(zrleInStream, zrleTilePixels24, tw * th); // / + } + } + + void readZrlePackedPixels(int tw, int th, int[] palette, int palSize) + throws Exception { + + int bppp = ((palSize > 16) ? 8 : ((palSize > 4) ? 4 + : ((palSize > 2) ? 2 : 1))); + int ptr = 0; + + for (int i = 0; i < th; i++) { + int eol = ptr + tw; + int b = 0; + int nbits = 0; + + while (ptr < eol) { + if (nbits == 0) { + b = zrleInStream.readU8(); + nbits = 8; + } + nbits -= bppp; + int index = (b >> nbits) & ((1 << bppp) - 1) & 127; + if (bytesPixel == 1) { + zrleTilePixels8[ptr++] = (byte) palette[index]; + } else { + zrleTilePixels24[ptr++] = palette[index]; + } + } + } + } + + void readZrlePlainRLEPixels(int tw, int th) throws Exception { + int ptr = 0; + int end = ptr + tw * th; + while (ptr < end) { + int pix = readPixel(zrleInStream); + int len = 1; + int b; + do { + b = zrleInStream.readU8(); + len += b; + } while (b == 255); + + if (!(len <= end - ptr)) + throw new Exception("ZRLE decoder: assertion failed" + + " (len <= end-ptr)"); + + if (bytesPixel == 1) { + while (len-- > 0) + zrleTilePixels8[ptr++] = (byte) pix; + } else { + while (len-- > 0) + zrleTilePixels24[ptr++] = pix; + } + } + } + + void readZrlePackedRLEPixels(int tw, int th, int[] palette) + throws Exception { + + int ptr = 0; + int end = ptr + tw * th; + while (ptr < end) { + int index = zrleInStream.readU8(); + int len = 1; + if ((index & 128) != 0) { + int b; + do { + b = zrleInStream.readU8(); + len += b; + } while (b == 255); + + if (!(len <= end - ptr)) + throw new Exception("ZRLE decoder: assertion failed" + + " (len <= end - ptr)"); + } + + index &= 127; + int pix = palette[index]; + + if (bytesPixel == 1) { + while (len-- > 0) + zrleTilePixels8[ptr++] = (byte) pix; + } else { + while (len-- > 0) + zrleTilePixels24[ptr++] = pix; + } + } + } + + // + // Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update. + // + + void handleUpdatedZrleTile(int x, int y, int w, int h) { + Object src, dst; + if (bytesPixel == 1) { + src = zrleTilePixels8; + dst = pixels8; + } else { + src = zrleTilePixels24; + dst = pixels24; + } + int offsetSrc = 0; + int offsetDst = (y * rfbc.framebufferWidth + x); + for (int j = 0; j < h; j++) { + System.arraycopy(src, offsetSrc, dst, offsetDst, w); + offsetSrc += w; + offsetDst += rfbc.framebufferWidth; + } + handleUpdatedPixels(x, y, w, h); + } + + // + // Handle a Zlib-encoded rectangle. + // + + void handleZlibRect(int x, int y, int w, int h) throws Exception { + + int nBytes = rfbc.readU32(); + + if (zlibBuf == null || zlibBufLen < nBytes) { + zlibBufLen = nBytes * 2; + zlibBuf = new byte[zlibBufLen]; + } + + rfbc.readFully(zlibBuf, 0, nBytes); + + if (rfbc.rec != null && rfbc.recordFromBeginning) { + rfbc.rec.writeIntBE(nBytes); + rfbc.rec.write(zlibBuf, 0, nBytes); + } + + if (zlibInflater == null) { + zlibInflater = new Inflater(); + } + zlibInflater.setInput(zlibBuf, 0, nBytes); + + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + zlibInflater.inflate(pixels8, dy * rfbc.framebufferWidth + x, w); + if (rfbc.rec != null && !rfbc.recordFromBeginning) + rfbc.rec.write(pixels8, dy * rfbc.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 * rfbc.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[offset + i] = (buf[i * 4 + 2] & 0xFF) << 16 + | (buf[i * 4 + 1] & 0xFF) << 8 + | (buf[i * 4] & 0xFF); + } + if (rfbc.rec != null && !rfbc.recordFromBeginning) + rfbc.rec.write(buf); + } + } + + handleUpdatedPixels(x, y, w, h); + scheduleRepaint(x, y, w, h); + } + + // + // Handle a Tight-encoded rectangle. + // + + void handleTightRect(int x, int y, int w, int h) throws Exception { + + int comp_ctl = rfbc.readU8(); + if (rfbc.rec != null) { + if (rfbc.recordFromBeginning || comp_ctl == (rfbc.TightFill << 4) + || comp_ctl == (rfbc.TightJpeg << 4)) { + // Send data exactly as received. + rfbc.rec.writeByte(comp_ctl); + } else { + // Tell the decoder to flush each of the four zlib streams. + rfbc.rec.writeByte(comp_ctl | 0x0F); + } + } + + // 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 > rfbc.TightMaxSubencoding) { + throw new Exception("Incorrect tight subencoding: " + comp_ctl); + } + + // Handle solid-color rectangles. + if (comp_ctl == rfbc.TightFill) { + + if (bytesPixel == 1) { + int idx = rfbc.readU8(); + memGraphics.setColor(colors[idx]); + if (rfbc.rec != null) { + rfbc.rec.writeByte(idx); + } + } else { + byte[] buf = new byte[3]; + rfbc.readFully(buf); + if (rfbc.rec != null) { + rfbc.rec.write(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 == rfbc.TightJpeg) { + + statNumRectsTightJPEG++; + + // Read JPEG data. + byte[] jpegData = new byte[rfbc.readCompactLen()]; + rfbc.readFully(jpegData); + if (rfbc.rec != null) { + if (!rfbc.recordFromBeginning) { + rfbc.recordCompactLen(jpegData.length); + } + rfbc.rec.write(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 Exception("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 & rfbc.TightExplicitFilter) != 0) { + int filter_id = rfbc.readU8(); + if (rfbc.rec != null) { + rfbc.rec.writeByte(filter_id); + } + if (filter_id == rfbc.TightFilterPalette) { + numColors = rfbc.readU8() + 1; + if (rfbc.rec != null) { + rfbc.rec.writeByte(numColors - 1); + } + if (bytesPixel == 1) { + if (numColors != 2) { + throw new Exception("Incorrect tight palette size: " + + numColors); + } + rfbc.readFully(palette8); + if (rfbc.rec != null) { + rfbc.rec.write(palette8); + } + } else { + byte[] buf = new byte[numColors * 3]; + rfbc.readFully(buf); + if (rfbc.rec != null) { + rfbc.rec.write(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 == rfbc.TightFilterGradient) { + useGradient = true; + } else if (filter_id != rfbc.TightFilterCopy) { + throw new Exception("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 < rfbc.TightMinToCompress) { + // Data size is small - not compressed with zlib. + if (numColors != 0) { + // Indexed colors. + byte[] indexedData = new byte[dataSize]; + rfbc.readFully(indexedData); + if (rfbc.rec != null) { + rfbc.rec.write(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 * rfbc.framebufferWidth + dx] = palette24[indexedData[i++] & 0xFF]; + } + } + } + } else if (useGradient) { + // "Gradient"-processed data + byte[] buf = new byte[w * h * 3]; + rfbc.readFully(buf); + if (rfbc.rec != null) { + rfbc.rec.write(buf); + } + decodeGradientData(x, y, w, h, buf); + } else { + // Raw truecolor data. + if (bytesPixel == 1) { + for (int dy = y; dy < y + h; dy++) { + rfbc.readFully(pixels8, dy * rfbc.framebufferWidth + x, w); + if (rfbc.rec != null) { + rfbc.rec.write(pixels8, dy * rfbc.framebufferWidth + + x, w); + } + } + } else { + byte[] buf = new byte[w * 3]; + int i, offset; + for (int dy = y; dy < y + h; dy++) { + rfbc.readFully(buf); + if (rfbc.rec != null) { + rfbc.rec.write(buf); + } + offset = dy * rfbc.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 = rfbc.readCompactLen(); + byte[] zlibData = new byte[zlibDataLen]; + rfbc.readFully(zlibData); + if (rfbc.rec != null && rfbc.recordFromBeginning) { + rfbc.rec.write(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); + byte[] buf = new byte[dataSize]; + myInflater.inflate(buf); + if (rfbc.rec != null && !rfbc.recordFromBeginning) { + rfbc.recordCompressedData(buf); + } + + if (numColors != 0) { + // Indexed colors. + if (numColors == 2) { + // Two colors. + if (bytesPixel == 1) { + decodeMonoData(x, y, w, h, buf, palette8); + } else { + decodeMonoData(x, y, w, h, buf, 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 * rfbc.framebufferWidth + dx] = palette24[buf[i++] & 0xFF]; + } + } + } + } else if (useGradient) { + // Compressed "Gradient"-filtered data (assuming bytesPixel == + // 4). + decodeGradientData(x, y, w, h, buf); + } else { + // Compressed truecolor data. + if (bytesPixel == 1) { + int destOffset = y * rfbc.framebufferWidth + x; + for (int dy = 0; dy < h; dy++) { + System.arraycopy(buf, dy * w, pixels8, destOffset, w); + destOffset += rfbc.framebufferWidth; + } + } else { + int srcOffset = 0; + int destOffset, i; + for (int dy = 0; dy < h; dy++) { + myInflater.inflate(buf); + destOffset = (y + dy) * rfbc.framebufferWidth + x; + for (i = 0; i < w; i++) { + pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16 + | (buf[srcOffset + 1] & 0xFF) << 8 + | (buf[srcOffset + 2] & 0xFF); + srcOffset += 3; + } + } + } + } + } + + 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) { + + int dx, dy, n; + int i = y * rfbc.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 += (rfbc.framebufferWidth - w); + } + } + + void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) { + + int dx, dy, n; + int i = y * rfbc.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 += (rfbc.framebufferWidth - w); + } + } + + // + // Decode data processed with the "Gradient" filter. + // + + void decodeGradientData(int x, int y, int w, int h, byte[] buf) { + + 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 * rfbc.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 += (rfbc.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, rfbc.framebufferWidth, rfbc.framebufferHeight); + +// test.TestComet.imageUpdate(rawPixelsImage, x, y, w, h); + } + + // + // Tell JVM to repaint specified desktop area. + // + + void scheduleRepaint(int x, int y, int w, int h) { + // Request repaint, deferred if necessary. + if (rfbc.framebufferWidth == scaledWidth) { + repaint(viewer.deferScreenUpdates, x, y, w, h); + } else { + int sx = x * scalingFactor / 100; + int sy = y * scalingFactor / 100; + int sw = ((x + w) * scalingFactor + 49) / 100 - sx + 1; + int sh = ((y + h) * scalingFactor + 49) / 100 - sy + 1; + repaint(viewer.deferScreenUpdates, sx, sy, sw, sh); + } + } + + // + // Handle events. + // + + public void keyPressed(KeyEvent evt) { + processLocalKeyEvent(evt); + } + + public void keyReleased(KeyEvent evt) { + processLocalKeyEvent(evt); + } + + public void keyTyped(KeyEvent evt) { + evt.consume(); + } + + public void mousePressed(MouseEvent evt) { + processLocalMouseEvent(evt, false); + } + + public void mouseReleased(MouseEvent evt) { + processLocalMouseEvent(evt, false); + } + + public void mouseMoved(MouseEvent evt) { + processLocalMouseEvent(evt, true); + } + + public void mouseDragged(MouseEvent evt) { + processLocalMouseEvent(evt, true); + } + + public void processLocalKeyEvent(KeyEvent evt) { + if (viewer.rfb != null && rfbc.inNormalProtocol) { + if (!inputEnabled) { + if ((evt.getKeyChar() == 'r' || evt.getKeyChar() == 'R') + && evt.getID() == KeyEvent.KEY_PRESSED) { + // Request screen update. + try { + rfbc.writeFramebufferUpdateRequest(0, 0, + rfbc.framebufferWidth, rfbc.framebufferHeight, + false); + } catch (IOException e) { + e.printStackTrace(); + } + } + } else { + // Input enabled. + synchronized (rfbc) { + try { + rfbc.writeKeyEvent(evt); + } catch (Exception e) { + e.printStackTrace(); + } + rfbc.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 (viewer.rfb != null && rfbc.inNormalProtocol) { + if (moved) { + softCursorMove(evt.getX(), evt.getY()); + } + if (rfbc.framebufferWidth != scaledWidth) { + int sx = (evt.getX() * 100 + scalingFactor / 2) / scalingFactor; + int sy = (evt.getY() * 100 + scalingFactor / 2) / scalingFactor; + evt.translatePoint(sx - evt.getX(), sy - evt.getY()); + } + synchronized (rfbc) { + try { + rfbc.writePointerEvent(evt); + } catch (Exception e) { + e.printStackTrace(); + } + rfbc.notify(); + } + } + } + + // + // Ignored events. + // + + public void mouseClicked(MouseEvent evt) { + } + + public void mouseEntered(MouseEvent evt) { + } + + public void mouseExited(MouseEvent evt) { + } + + // + // Reset update statistics. + // + + void resetStats() { + statStartTime = System.currentTimeMillis(); + statNumUpdates = 0; + statNumTotalRects = 0; + statNumPixelRects = 0; + statNumRectsTight = 0; + statNumRectsTightJPEG = 0; + statNumRectsZRLE = 0; + statNumRectsHextile = 0; + statNumRectsRaw = 0; + statNumRectsCopy = 0; + statNumBytesEncoded = 0; + statNumBytesDecoded = 0; + } + + // //////////////////////////////////////////////////////////////// + // + // Handle cursor shape updates (XCursor and RichCursor encodings). + // + + boolean showSoftCursor = false; + + MemoryImageSource softCursorSource; + Image softCursor; + + int cursorX = 0, cursorY = 0; + int cursorWidth, cursorHeight; + int origCursorWidth, origCursorHeight; + int hotX, hotY; + int origHotX, origHotY; + + // + // Handle cursor shape update (XCursor and RichCursor encodings). + // + + synchronized void handleCursorShapeUpdate(int encodingType, int xhot, + int yhot, int width, int height) throws IOException { + + softCursorFree(); + + if (width * height == 0) + return; + + // Ignore cursor shape data if requested by user. + if (viewer.options.ignoreCursorUpdates) { + int bytesPerRow = (width + 7) / 8; + int bytesMaskData = bytesPerRow * height; + + if (encodingType == rfbc.EncodingXCursor) { + rfbc.skipBytes(6 + bytesMaskData * 2); + } else { + // rfb.EncodingRichCursor + rfbc.skipBytes(width * height * bytesPixel + bytesMaskData); + } + return; + } + + // Decode cursor pixel data. + softCursorSource = decodeCursorShape(encodingType, width, height); + + // Set original (non-scaled) cursor dimensions. + origCursorWidth = width; + origCursorHeight = height; + origHotX = xhot; + origHotY = yhot; + + // Create off-screen cursor image. + createSoftCursor(); + + // Show the cursor. + showSoftCursor = true; + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); + } + + // + // decodeCursorShape(). Decode cursor pixel data and return + // corresponding MemoryImageSource instance. + // + + synchronized MemoryImageSource decodeCursorShape(int encodingType, + int width, int height) throws IOException { + + int bytesPerRow = (width + 7) / 8; + int bytesMaskData = bytesPerRow * height; + + int[] softCursorPixels = new int[width * height]; + + if (encodingType == rfbc.EncodingXCursor) { + + // Read foreground and background colors of the cursor. + byte[] rgb = new byte[6]; + rfbc.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]; + rfbc.readFully(pixBuf); + byte[] maskBuf = new byte[bytesMaskData]; + rfbc.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]; + rfbc.readFully(pixBuf); + byte[] maskBuf = new byte[bytesMaskData]; + rfbc.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 + 2] & 0xFF) << 16 + | (pixBuf[i * 4 + 1] & 0xFF) << 8 + | (pixBuf[i * 4] & 0xFF); + } + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + for (n = 7; n >= 8 - width % 8; n--) { + if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { + if (bytesPixel == 1) { + result = cm8.getRGB(pixBuf[i]); + } else { + result = 0xFF000000 + | (pixBuf[i * 4 + 2] & 0xFF) << 16 + | (pixBuf[i * 4 + 1] & 0xFF) << 8 + | (pixBuf[i * 4] & 0xFF); + } + } else { + result = 0; // Transparent pixel + } + softCursorPixels[i++] = result; + } + } + + } + + return new MemoryImageSource(width, height, softCursorPixels, 0, width); + } + + // + // createSoftCursor(). Assign softCursor new Image (scaled if necessary). + // Uses softCursorSource as a source for new cursor image. + // + + synchronized void createSoftCursor() { + + if (softCursorSource == null) + return; + + int scaleCursor = viewer.options.scaleCursor; + if (scaleCursor == 0 || !inputEnabled) + scaleCursor = 100; + + // Save original cursor coordinates. + int x = cursorX - hotX; + int y = cursorY - hotY; + int w = cursorWidth; + int h = cursorHeight; + + cursorWidth = (origCursorWidth * scaleCursor + 50) / 100; + cursorHeight = (origCursorHeight * scaleCursor + 50) / 100; + hotX = (origHotX * scaleCursor + 50) / 100; + hotY = (origHotY * scaleCursor + 50) / 100; + softCursor = Toolkit.getDefaultToolkit().createImage(softCursorSource); + + if (scaleCursor != 100) { + softCursor = softCursor.getScaledInstance(cursorWidth, + cursorHeight, Image.SCALE_SMOOTH); + } + + if (showSoftCursor) { + // Compute screen area to update. + x = Math.min(x, cursorX - hotX); + y = Math.min(y, cursorY - hotY); + w = Math.max(w, cursorWidth); + h = Math.max(h, cursorHeight); + + repaint(viewer.deferCursorUpdates, x, y, w, h); + } + } + + // + // softCursorMove(). Moves soft cursor into a particular location. + // + + synchronized void softCursorMove(int x, int y) { + int oldX = cursorX; + int oldY = cursorY; + cursorX = x; + cursorY = y; + if (showSoftCursor) { + repaint(viewer.deferCursorUpdates, oldX - hotX, oldY - hotY, + cursorWidth, cursorHeight); + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); + } + } + + // + // softCursorFree(). Remove soft cursor, dispose resources. + // + + synchronized void softCursorFree() { + if (showSoftCursor) { + showSoftCursor = false; + softCursor = null; + softCursorSource = null; + + repaint(viewer.deferCursorUpdates, cursorX - hotX, cursorY - hotY, + cursorWidth, cursorHeight); + } + } + void drawFirstImage()throws IOException { + BufferedImage bimg = rfbc.createBimg(); + memGraphics.setClip(0,0, rfbc.framebufferWidth, rfbc.framebufferHeight ); + memGraphics.drawImage( bimg, 0,0, null); + } + + BufferedImage createBufferedImage(Image img){ + BufferedImage bimg = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_RGB ); + System.out.println("img.getWidth="+img.getWidth(null)); + + Graphics g = bimg.getGraphics(); + g.drawImage(img, 0, 0, null); + g.dispose(); + return bimg; + } + + void drawBufferedImage(byte[] b) throws IOException{ + BufferedImage bimg = ImageIO.read(new ByteArrayInputStream(b)); +// ImageIO.write(bimg, "jpeg", new File("sample.jpeg")); + memGraphics.setClip(0,0, rfbc.framebufferWidth, rfbc.framebufferHeight ); + memGraphics.drawImage( bimg, 0,0, null); + scheduleRepaint(0, 0, rfbc.framebufferWidth, rfbc.framebufferHeight ); + } + byte[] getBytes(BufferedImage img)throws IOException { + byte[] b = getImageBytes(img, "png"); + return b; + } + + byte[] getImageBytes(BufferedImage image, String imageFormat) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + BufferedOutputStream os = new BufferedOutputStream(bos); + image.flush(); + ImageIO.write(image, imageFormat, os); + os.flush(); + os.close(); + return bos.toByteArray(); + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/VncCanvas2.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/VncCanvas2.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,69 @@ +package treeVnc; +// +// Copyright (C) 2006 Constantin Kaplinsky. All Rights Reserved. +// +// This is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +import java.awt.*; +import java.io.*; + +// +// VncCanvas2 is a special version of VncCanvas which may use Java 2 API. +// + +class VncCanvas2 extends VncCanvas { + + /** + * + */ + private static final long serialVersionUID = 1L; + +public VncCanvas2(VncViewer v) throws IOException { + super(v); + disableFocusTraversalKeys(); + } + + public VncCanvas2(VncViewer v, int maxWidth_, int maxHeight_) + throws IOException { + + super(v, maxWidth_, maxHeight_); + disableFocusTraversalKeys(); + } + + public void paintScaledFrameBuffer(Graphics g) { + Graphics2D g2d = (Graphics2D)g; + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + g2d.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null); + } + + // + // Try to disable focus traversal keys (JVMs 1.4 and higher). + // + + private void disableFocusTraversalKeys() { + try { + Class[] argClasses = { Boolean.TYPE }; + java.lang.reflect.Method method = + getClass().getMethod("setFocusTraversalKeysEnabled", argClasses); + Object[] argObjects = { new Boolean(false) }; + method.invoke(this, argObjects); + } catch (Exception e) {} + } + +} + diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/VncProxyService.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/VncProxyService.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,1042 @@ +package treeVnc; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.net.*; + +public class VncProxyService implements java.lang.Runnable { + + + public void treeVncProxy(String[] argv) { + VncProxyService v = new VncProxyService(); + + // v.checkArgs(argv); + + v.mainArgs = argv; + + v.init(); + v.start_threads(); + } + + String[] mainArgs; + String username; + + //CreateHtmlFile hgtmlFile; + + // RfbProto rfb; + MyRfbProtoProxy rfb; + MyRfbProtoProxy testrfb; + Thread rfbThread; + Thread accThread; + Thread clientThread; + private Thread bCast; + CreateThread geth; + AcceptClient acc; + private AcceptThread acceptThread; + private TextBoxProxy getHost; + private GetBroadCastProxy getCast; + + Frame vncFrame; + Container vncContainer; + ScrollPane desktopScrollPane; + GridBagLayout gridbag; + ButtonPanel buttonPanel; + Label connStatusLabel; + ProxyVncCanvas vc; + // OptionsFrame options; + OptionsNoFrame options; + ClipboardFrame clipboard; + RecordingFrame rec; + + // Control session recording. + Object recordingSync; + String sessionFileName; + boolean recordingActive; + boolean recordingStatusChanged; + String cursorUpdatesDef; + String eightBitColorsDef; + + // Variables read from parameter values. + String socketFactory; + String host; + int port = 5900; + String passwordParam; + boolean showControls; + boolean offerRelogin; + boolean showOfflineDesktop; + int deferScreenUpdates; + int deferCursorUpdates; + int deferUpdateRequests; + int debugStatsExcludeUpdates; + int debugStatsMeasureUpdates; + + int echoPort = 9999; + private boolean changeFlag; + + String url; + + // XmlRpc client; + String wpUsername, wpPassword, blogId; + String title = "TreeVNC"; + + void checkArgs(String[] argv) { + int len = argv.length; + + for (int i = 0; i < len; i++) { + String str = argv[i]; + if (str.equals("-h") || str.equals("--host")) { + host = argv[++i]; + } else if (str.equals("-pn") || str.equals("--port")) { + port = Integer.parseInt(argv[++i]); + } else if (str.equals("-pw") || str.equals("--password")) { + passwordParam = argv[++i]; + } else if (str.equals("-w") || str.equals("--wpurl")) { + url = argv[++i]; + } else if (str.equals("-wu") || str.equals("--wpusername")) { + wpUsername = argv[++i]; + } else if (str.equals("-wp") || str.equals("--wppassword")) { + wpPassword = argv[++i]; + } else if (str.equals("-wb") || str.equals("--wpblogId")) { + blogId = argv[++i]; + } else if (str.equals("--help")) { + printHelp(); + System.exit(0); + } else if (len == 3) { + host = argv[0]; + port = Integer.parseInt(argv[1]); + passwordParam = argv[2]; + } else if (len == 2) { + host = argv[0]; + port = Integer.parseInt(argv[1]); + } else if (len == 1) { + host = argv[0]; + } + } + } + + void printHelp() { + System.out + .println("usage: java -jar VncProxyService [-h hostname] " + + "[-pn portnumber] [-pw password] [-w WordPressURL] [-wu WPUsername] " + + "[-wp WPPassword] [-wb blogId]"); + } + + // + // init() + // + + public void init() { + + readParameters(); + + options = new OptionsNoFrame(this); + recordingSync = new Object(); + + sessionFileName = null; + recordingActive = false; + recordingStatusChanged = false; + cursorUpdatesDef = null; + eightBitColorsDef = null; + + try { + connectAndAuthenticate(); + doProtocolInitialisation(); + } catch (NoRouteToHostException e) { + fatalError("Network error: no route to server: " + host, e); + } catch (UnknownHostException e) { + fatalError("Network error: server name unknown: " + host, e); + } catch (ConnectException e) { + fatalError("Network error: could not connect to server: " + host + + ":" + port, e); + } catch (IOException e) { + fatalError("Network error: faild connect or authenticate to server"); + } catch (Exception e) { + + } +/* + if(changeFlag){ + acceptThread.changeRfb(rfb); + } else {*/ + rfb.selectPort(5999); + rfbThread = new Thread(this); + acceptThread = new AcceptThread(rfb, 5999); + accThread = new Thread(acceptThread); + getCast = new GetBroadCastProxy(this); + bCast = new Thread(getCast); + //broadCast.udpTransmission(rfb.acceptPort+"-"+host+"-"+rfb.desktopName); + //} + /* + * if(url != null) { try { client = new XmlRpc(blogId, wpUsername, + * wpPassword, url ); InetAddress addr = InetAddress.getLocalHost(); + * String add = new String(addr.getHostAddress()); + * client.addDescription(rfb.desktopName); setStatusXmlRpc(client, + * title, add, rfb.acceptPort); client.editPost(); + * System.out.println("URL:\n" + client.getUrl() + "\n"); } catch + * (MalformedURLException e) { + * System.out.println("Faild create instance of class XmlRpc"); + * e.printStackTrace(); } catch (UnknownHostException e) { + * e.printStackTrace(); } } + */ + + } + + + public void changeInit() throws Exception { + VncProxyService v = new VncProxyService(); + v.changeFlag = true; + //v.checkArgs(argv); + + v.mainArgs = new String[0]; + v.host = host; + v.init(); + v.start_threads(); + + Thread.sleep(10000); + //rfb = v.rfb; + rfb.is = v.rfb.is; + rfb.os = v.rfb.os; + rfb.initData = v.rfb.initData; + + //changeConnection(); +// doProtocolInitialisation(); + + //readParameters(); + options = new OptionsNoFrame(this); + recordingSync = new Object(); + + sessionFileName = null; + recordingActive = false; + recordingStatusChanged = false; + cursorUpdatesDef = null; + eightBitColorsDef = null; + + try { + changeConnection(); + doProtocolInitialisation(); + } catch (NoRouteToHostException e) { + fatalError("Network error: no route to server: " + host, e); + } catch (UnknownHostException e) { + fatalError("Network error: server name unknown: " + host, e); + } catch (ConnectException e) { + fatalError("Network error: could not connect to server: " + host + + ":" + port, e); + } catch (IOException e) { + fatalError("Network error: faild connect or authenticate to server"); + } catch (Exception e) { + + } + + //acceptThread.changeRfb(rfb); + //vc.rfb = rfb; + //rfbThread.start(); + + } + + /* + * void setStatusXmlRpc(XmlRpc client, String title, String hostname, int + * port) { client.setTitle(title); String description = + * makeDescription(hostname, Integer.toString(port)); + * client.addDescription(description); } + */ + String makeDescription(String hostname, String port) { + String description = "

" + hostname + " " + port + "

\n"; + return description; + } + + public void start_threads() { + rfbThread.start(); + accThread.start(); + bCast.start(); + rfb.requestThreadStart(); + } + + // + // run() - executed by the rfbThread to deal with the RFB socket. + // + public void run() { + + try { + // connectAndAuthenticate(); + // doProtocolInitialisation(); + /* + * htmlFile = new CreateHtmlFile(rfb, host, username); + * htmlFile.createHtml(); + */ + vc = new ProxyVncCanvas(this, 0, 0); + vc.updateFramebufferSize(); + + processNormalProtocol();// main loop + + } catch (NoRouteToHostException e) { + fatalError("Network error: no route to server: " + host, e); + } catch (UnknownHostException e) { + fatalError("Network error: server name unknown: " + host, e); + } catch (ConnectException e) { + fatalError("Network error: could not connect to server: " + host + + ":" + port, e); + } catch (EOFException e) { + if (showOfflineDesktop) { + e.printStackTrace(); + System.out + .println("Network error: remote side closed connection"); + if (vc != null) { + vc.enableInput(false); + } + if (rfb != null && !rfb.closed()) + rfb.close(); + if (showControls && buttonPanel != null) { + buttonPanel.disableButtonsOnDisconnect(); + } + } else { + fatalError("Network error: remote side closed connection", e); + } + } catch (IOException e) { + String str = e.getMessage(); + if (str != null && str.length() != 0) { + fatalError("Network Error: " + str, e); + } else { + fatalError(e.toString(), e); + } + } catch (Exception e) { + String str = e.getMessage(); + if (str != null && str.length() != 0) { + fatalError("Error: " + str, e); + } else { + fatalError(e.toString(), e); + } + } + + } + + // + // Process RFB socket messages. + // If the rfbThread is being stopped, ignore any exceptions, + // otherwise rethrow the exception so it can be handled. + // + + void processNormalProtocol() throws Exception { + try { + vc.processNormalProtocol();// main loop + } catch (Exception e) { + if (rfbThread == null) { + System.out.println("Ignoring RFB socket exceptions" + + " because applet is stopping"); + } else { + throw e; + } + } + } + + // + // Connect to the RFB server and authenticate the user. + // + + void connectAndAuthenticate() throws Exception { + if (mainArgs.length == 0) + acc = new AcceptClient(host); + //acc = new AcceptClient(getHost.getAddress()); + // acc = new AcceptClient(); + else + acc = new AcceptClient(mainArgs[0]); + geth = new CreateThread(echoPort,acc); + Thread thread = new Thread(geth); + thread.start(); + + showConnectionStatus("Initializing..."); + + showConnectionStatus("Connecting to " + host + ", port " + port + "..."); + + rfb = new MyRfbProtoProxy(host, port, geth); + + showConnectionStatus("Connected to server"); + + rfb.readVersionMsg(); + showConnectionStatus("RFB server supports protocol version " + + rfb.serverMajor + "." + rfb.serverMinor); + + rfb.writeVersionMsg(); + showConnectionStatus("Using RFB protocol version " + rfb.clientMajor + + "." + rfb.clientMinor); + + int secType = rfb.negotiateSecurity(); + int authType; + if (secType == RfbProto.SecTypeTight) { + showConnectionStatus("Enabling TightVNC protocol extensions"); + rfb.setupTunneling(); + authType = rfb.negotiateAuthenticationTight(); + } else { + authType = secType; + } + + switch (authType) { + case MyRfbProtoProxy.AuthAccess: + rfb.authenticationRequestAccess(); + System.out.println("authenticateion Request right of Acces"); + break; + case RfbProto.AuthNone: + showConnectionStatus("No authentication needed"); + rfb.authenticateNone(); + break; + case RfbProto.AuthVNC: + showConnectionStatus("Performing standard VNC authentication"); + if (passwordParam != null) { + rfb.authenticateVNC(passwordParam); + } else { + String pw = askPassword(); + rfb.authenticateVNC(pw); + } + break; + default: + throw new Exception("Unknown authentication scheme " + authType); + } + } + + void changeConnection() throws Exception { + /* + acc = new AcceptClient(getHost.getAddress()); + geth = new CreateThread(acc, echoPort); + Thread thread = new Thread(geth); + thread.start(); + */ + showConnectionStatus("Initializing..."); + + showConnectionStatus("Connecting to " + host + ", port " + port + "..."); + + //rfb = null; + //rfb.is = null; + + testrfb = new MyRfbProtoProxy(host, port); + showConnectionStatus("Connected to server"); + + testrfb.readVersionMsg(); + showConnectionStatus("RFB server supports protocol version " + + testrfb.serverMajor + "." + testrfb.serverMinor); + + testrfb.writeVersionMsg(); + showConnectionStatus("Using RFB protocol version " + testrfb.clientMajor + + "." + testrfb.clientMinor); + + int secType = testrfb.negotiateSecurity(); + int authType; + if (secType == RfbProto.SecTypeTight) { + showConnectionStatus("Enabling TightVNC protocol extensions"); + testrfb.setupTunneling(); + authType = testrfb.negotiateAuthenticationTight(); + } else { + authType = secType; + } + + switch (authType) { + case MyRfbProtoProxy.AuthAccess: + testrfb.authenticationRequestAccess(); + System.out.println("authenticateion Request right of Acces"); + break; + case RfbProto.AuthNone: + showConnectionStatus("No authentication needed"); + testrfb.authenticateNone(); + break; + case RfbProto.AuthVNC: + showConnectionStatus("Performing standard VNC authentication"); + if (passwordParam != null) { + testrfb.authenticateVNC(passwordParam); + } else { + String pw = askPassword(); + testrfb.authenticateVNC(pw); + } + break; + default: + throw new Exception("Unknown authentication scheme " + authType); + } + //rfb.is = testrfb.is; + //rfb.os = testrfb.os; + //rfb = testrfb; + } + + // + // Show a message describing the connection status. + // To hide the connection status label, use (msg == null). + // + + void showConnectionStatus(String msg) { + System.out.println(msg); + } + + // + // Show an authentication panel. + // + + String askPassword() throws Exception { + /* + * showConnectionStatus(null); AuthPanel authPanel = new + * AuthPanel(this); + * + * GridBagConstraints gbc = new GridBagConstraints(); gbc.gridwidth = + * GridBagConstraints.REMAINDER; gbc.anchor = + * GridBagConstraints.NORTHWEST; gbc.weightx = 1.0; gbc.weighty = 1.0; + * gbc.ipadx = 100; gbc.ipady = 50; gridbag.setConstraints(authPanel, + * gbc); vncContainer.add(authPanel); + * + * + * authPanel.moveFocusToDefaultField(); vncContainer.remove(authPanel); + */ + + showConnectionStatus("ask password..."); + String pw; + if (mainArgs.length != 0) + pw = mainArgs[2]; + else + pw = getHost.getPassword(); + return pw; + } + + // + // Do the rest of the protocol initialisation. + // + + void doProtocolInitialisation() throws IOException { + rfb.writeClientInit(); + rfb.readServerInit(); + + System.out.println("Desktop name is " + rfb.desktopName); + System.out.println("Desktop size is " + rfb.framebufferWidth + " x " + + rfb.framebufferHeight); + + setEncodings(); + + // showConnectionStatus(null); + } + + // + // Send current encoding list to the RFB server. + // + + int[] encodingsSaved; + int nEncodingsSaved; + + void setEncodings() { + setEncodings(false); + } + + void autoSelectEncodings() { + setEncodings(true); + } + + void setEncodings(boolean autoSelectOnly) { + if (options == null || rfb == null || !rfb.inNormalProtocol) + return; + + int preferredEncoding = options.preferredEncoding; + if (preferredEncoding == -1) { + // long kbitsPerSecond = rfb.kbitsPerSecond(); + /* + * if (nEncodingsSaved < 1) { // Choose Tight or ZRLE encoding for + * the very first update. + * System.out.println("Using Tight/ZRLE encodings"); + * preferredEncoding = RfbProto.EncodingTight; } else if + * (kbitsPerSecond > 2000 && encodingsSaved[0] != + * RfbProto.EncodingHextile) { // Switch to Hextile if the + * connection speed is above 2Mbps. System.out.println("Throughput " + * + kbitsPerSecond + " kbit/s - changing to Hextile encoding"); + * preferredEncoding = RfbProto.EncodingHextile; } else if + * (kbitsPerSecond < 1000 && encodingsSaved[0] != + * RfbProto.EncodingTight) { // Switch to Tight/ZRLE if the + * connection speed is below 1Mbps. System.out.println("Throughput " + * + kbitsPerSecond + " kbit/s - changing to Tight/ZRLE encodings"); + * preferredEncoding = RfbProto.EncodingTight; } else { // Don't + * change the encoder. if (autoSelectOnly) return; preferredEncoding + * = encodingsSaved[0]; } + */ + } else { + // Auto encoder selection is not enabled. + if (autoSelectOnly) + return; + } + + int[] encodings = new int[20]; + int nEncodings = 0; + + encodings[nEncodings++] = preferredEncoding; + + if (options.useCopyRect) { + encodings[nEncodings++] = RfbProto.EncodingCopyRect; + } + /* + * if (preferredEncoding != RfbProto.EncodingTight) { + * encodings[nEncodings++] = RfbProto.EncodingTight; } + */ + + if (preferredEncoding != RfbProto.EncodingZRLE) { + encodings[nEncodings++] = RfbProto.EncodingZRLE; + } + /* + * if (preferredEncoding != RfbProto.EncodingHextile) { + * encodings[nEncodings++] = RfbProto.EncodingHextile; } if + * (preferredEncoding != RfbProto.EncodingZlib) { + * encodings[nEncodings++] = RfbProto.EncodingZlib; } + */ + /* + * if (preferredEncoding != RfbProto.EncodingCoRRE) { + * encodings[nEncodings++] = RfbProto.EncodingCoRRE; } if + * (preferredEncoding != RfbProto.EncodingRRE) { encodings[nEncodings++] + * = RfbProto.EncodingRRE; } + */ + /* + * if (options.compressLevel >= 0 && options.compressLevel <= 9) { + * encodings[nEncodings++] = RfbProto.EncodingCompressLevel0 + + * options.compressLevel; } if (options.jpegQuality >= 0 && + * options.jpegQuality <= 9) { encodings[nEncodings++] = + * RfbProto.EncodingQualityLevel0 + options.jpegQuality; } if + * (options.requestCursorUpdates) { encodings[nEncodings++] = + * RfbProto.EncodingXCursor; encodings[nEncodings++] = + * RfbProto.EncodingRichCursor; if (!options.ignoreCursorUpdates) + * encodings[nEncodings++] = RfbProto.EncodingPointerPos; } + */ + + encodings[nEncodings++] = RfbProto.EncodingLastRect; + encodings[nEncodings++] = RfbProto.EncodingNewFBSize; + + boolean encodingsWereChanged = false; + if (nEncodings != nEncodingsSaved) { + encodingsWereChanged = true; + } else { + for (int i = 0; i < nEncodings; i++) { + if (encodings[i] != encodingsSaved[i]) { + encodingsWereChanged = true; + break; + } + } + } + + if (encodingsWereChanged) { + try { + rfb.writeSetEncodings(encodings, nEncodings); + if (vc != null) { + vc.softCursorFree(); + } + } catch (Exception e) { + e.printStackTrace(); + } + encodingsSaved = encodings; + nEncodingsSaved = nEncodings; + } + } + + // + // setCutText() - send the given cut text to the RFB server. + // + + void setCutText(String text) { + try { + if (rfb != null && rfb.inNormalProtocol) { + rfb.writeClientCutText(text); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // + // Order change in session recording status. To stop recording, pass + // null in place of the fname argument. + // + + void setRecordingStatus(String fname) { + synchronized (recordingSync) { + sessionFileName = fname; + recordingStatusChanged = true; + } + } + + // + // Start or stop session recording. Returns true if this method call + // causes recording of a new session. + // + + boolean checkRecordingStatus() throws IOException { + synchronized (recordingSync) { + if (recordingStatusChanged) { + recordingStatusChanged = false; + if (sessionFileName != null) { + startRecording(); + return true; + } else { + stopRecording(); + } + } + } + return false; + } + + // + // Start session recording. + // + + protected void startRecording() throws IOException { + synchronized (recordingSync) { + if (!recordingActive) { + // Save settings to restore them after recording the session. + cursorUpdatesDef = options.choices[options.cursorUpdatesIndex] + .getSelectedItem(); + eightBitColorsDef = options.choices[options.eightBitColorsIndex] + .getSelectedItem(); + // Set options to values suitable for recording. + options.choices[options.cursorUpdatesIndex].select("Disable"); + options.choices[options.cursorUpdatesIndex].setEnabled(false); + options.setEncodings(); + options.choices[options.eightBitColorsIndex].select("No"); + options.choices[options.eightBitColorsIndex].setEnabled(false); + options.setColorFormat(); + } else { + rfb.closeSession(); + } + + System.out.println("Recording the session in " + sessionFileName); + rfb.startSession(sessionFileName); + recordingActive = true; + } + } + + // + // Stop session recording. + // + + protected void stopRecording() throws IOException { + synchronized (recordingSync) { + if (recordingActive) { + // Restore options. + options.choices[options.cursorUpdatesIndex] + .select(cursorUpdatesDef); + options.choices[options.cursorUpdatesIndex].setEnabled(true); + options.setEncodings(); + options.choices[options.eightBitColorsIndex] + .select(eightBitColorsDef); + options.choices[options.eightBitColorsIndex].setEnabled(true); + options.setColorFormat(); + + rfb.closeSession(); + System.out.println("Session recording stopped."); + } + sessionFileName = null; + recordingActive = false; + } + } + + // + // readParameters() - read parameters from the html source or from the + // command line. On the command line, the arguments are just a sequence of + // param_name/param_value pairs where the names and values correspond to + // those expected in the html applet tag source. + // + + void readParameters() { + if (!(changeFlag)) { + if ("-p".equals(mainArgs[0])) { + //getHost = new TextBoxProxy(this); + //getHost.ipRegister(); + //host = getHost.getAddress(); + host = "localhost"; + } else { + host = mainArgs[0]; + } + + if ("-p".equals(mainArgs[0])) { + ///port = Integer.parseInt(getHost.getPort()); + port = 5900; + } else { + port = Integer.parseInt(mainArgs[1]); + } + } else { + getHost = new TextBoxProxy(this); + port = 5900; + } + /* + if(getHost==null){ +// getHost = new TextBoxProxy(this); + //getHost.changeHost(); + } else { + //getHost.changeHost(); + } + */ + + // Read "ENCPASSWORD" or "PASSWORD" parameter if specified. + // readPasswordParameters(); + + String str; + + // "Show Controls" set to "No" disables button panel. + showControls = true; + str = readParameter("Show Controls", false); + if (str != null && str.equalsIgnoreCase("No")) + showControls = false; + + // "Offer Relogin" set to "No" disables "Login again" and "Close + // window" buttons under error messages in applet mode. + offerRelogin = true; + str = readParameter("Offer Relogin", false); + if (str != null && str.equalsIgnoreCase("No")) + offerRelogin = false; + + // Do we continue showing desktop on remote disconnect? + showOfflineDesktop = false; + str = readParameter("Show Offline Desktop", false); + if (str != null && str.equalsIgnoreCase("Yes")) + showOfflineDesktop = true; + + // Fine tuning options. + deferScreenUpdates = readIntParameter("Defer screen updates", 20); + deferCursorUpdates = readIntParameter("Defer cursor updates", 10); + deferUpdateRequests = readIntParameter("Defer update requests", 0); + + // Debugging options. + debugStatsExcludeUpdates = readIntParameter("DEBUG_XU", 0); + debugStatsMeasureUpdates = readIntParameter("DEBUG_CU", 0); + + // SocketFactory. + socketFactory = readParameter("SocketFactory", false); + } + + // + // Read password parameters. If an "ENCPASSWORD" parameter is set, + // then decrypt the password into the passwordParam string. Otherwise, + // try to read the "PASSWORD" parameter directly to passwordParam. + // +/* + private void readPasswordParameters() { + String encPasswordParam; + // String encPasswordParam = readParameter("ENCPASSWORD", false); + if (mainArgs.length != 0) + encPasswordParam = mainArgs[2]; + else + encPasswordParam = getHost.getPassword(); + + if (encPasswordParam == null) { + // passwordParam = readParameter("PASSWORD", false); + + } else { + // ENCPASSWORD is hexascii-encoded. Decode. + byte[] pw = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int len = encPasswordParam.length() / 2; + if (len > 8) + len = 8; + for (int i = 0; i < len; i++) { + String hex = encPasswordParam.substring(i * 2, i * 2 + 2); + Integer x = new Integer(Integer.parseInt(hex, 16)); + pw[i] = x.byteValue(); + } + // Decrypt the password. + byte[] key = { 23, 82, 107, 6, 35, 78, 88, 7 }; + DesCipher des = new DesCipher(key); + des.decrypt(pw, 0, pw, 0); + passwordParam = new String(pw); + + } + } +*/ + public String readParameter(String name, boolean required) { + if(mainArgs!=null){ + 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; + } + + // + // disconnect() - close connection to server. + // + + synchronized public void disconnect() { + System.out.println("Disconnecting"); + + if (vc != null) { + double sec = (System.currentTimeMillis() - vc.statStartTime) / 1000.0; + double rate = Math.round(vc.statNumUpdates / sec * 100) / 100.0; + int nRealRects = vc.statNumPixelRects; + int nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects; + System.out.println("Updates received: " + vc.statNumUpdates + " (" + + nRealRects + " rectangles + " + nPseudoRects + + " pseudo), " + rate + " updates/sec"); + int numRectsOther = nRealRects - vc.statNumRectsTight + - vc.statNumRectsZRLE - vc.statNumRectsHextile + - vc.statNumRectsRaw - vc.statNumRectsCopy; + System.out.println("Rectangles:" + " Tight=" + vc.statNumRectsTight + + "(JPEG=" + vc.statNumRectsTightJPEG + ") ZRLE=" + + vc.statNumRectsZRLE + " Hextile=" + + vc.statNumRectsHextile + " Raw=" + vc.statNumRectsRaw + + " CopyRect=" + vc.statNumRectsCopy + " other=" + + numRectsOther); + + int raw = vc.statNumBytesDecoded; + int compressed = vc.statNumBytesEncoded; + if (compressed > 0) { + double ratio = Math.round((double) raw / compressed * 1000) / 1000.0; + System.out.println("Pixel data: " + vc.statNumBytesDecoded + + " bytes, " + vc.statNumBytesEncoded + + " compressed, ratio " + ratio); + } + } + + if (rfb != null && !rfb.closed()) + rfb.close(); + // options.dispose(); + clipboard.dispose(); + if (rec != null) + rec.dispose(); + + System.exit(0); + + } + + // + // fatalError() - print out a fatal error message. + // FIXME: Do we really need two versions of the fatalError() method? + // + + synchronized public void fatalError(String str) { + System.out.println(str); + System.exit(1); + } + + synchronized public void fatalError(String str, Exception e) { + + if (rfb != null && rfb.closed()) { + // Not necessary to show error message if the error was caused + // by I/O problems after the rfb.close() method call. + System.out.println("RFB thread finished"); + return; + } + + System.out.println(str); + e.printStackTrace(); + + if (rfb != null) + rfb.close(); + + System.exit(1); + + } + + // + // Show message text and optionally "Relogin" and "Close" buttons. + // + + void showMessage(String msg) { + vncContainer.removeAll(); + + Label errLabel = new Label(msg, Label.CENTER); + errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); + + if (offerRelogin) { + /* + * Panel gridPanel = new Panel(new GridLayout(0, 1)); Panel + * outerPanel = new Panel(new FlowLayout(FlowLayout.LEFT)); + * outerPanel.add(gridPanel); vncContainer.setLayout(new + * FlowLayout(FlowLayout.LEFT, 30, 16)); + * vncContainer.add(outerPanel); Panel textPanel = new Panel(new + * FlowLayout(FlowLayout.CENTER)); textPanel.add(errLabel); + * gridPanel.add(textPanel); gridPanel.add(new ReloginPanel(this)); + */ + } else { + /* + * vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30)); + * vncContainer.add(errLabel); + */ + } + + } + + // + // Stop the applet. + // Main applet thread will terminate on first exception + // after seeing that rfbThread has been set to null. + // + + public void stop() { + System.out.println("Stopping applet"); + rfbThread = null; + } + + // + // This method is called before the applet is destroyed. + // + + public void destroy() { + System.out.println("Destroying applet"); + + vncContainer.removeAll(); + // options.dispose(); + clipboard.dispose(); + if (rec != null) + rec.dispose(); + if (rfb != null && !rfb.closed()) + rfb.close(); + } + + // + // Start/stop receiving mouse events. + // + + public void enableInput(boolean enable) { + vc.enableInput(enable); + } + + // + // Close application properly on window close event. + // + + public void windowClosing(WindowEvent evt) { + System.out.println("Closing window"); + if (rfb != null) + disconnect(); + + vncContainer.setVisible(false); + + } + + // + // Ignore window events we're not interested in. + // + + public void windowActivated(WindowEvent evt) { + } + + public void windowDeactivated(WindowEvent evt) { + } + + public void windowOpened(WindowEvent evt) { + } + + public void windowClosed(WindowEvent evt) { + } + + public void windowIconified(WindowEvent evt) { + } + + public void windowDeiconified(WindowEvent evt) { + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/VncViewer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/VncViewer.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,1041 @@ +package treeVnc; +// +// Copyright (C) 2001-2004 HorizonLive.com, Inc. All Rights Reserved. +// Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved. +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this software; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, +// USA. +// + +// +// 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.*; +import java.net.*; + +public class VncViewer extends java.applet.Applet implements + java.lang.Runnable, WindowListener { + + /** + * + */ + private static final long serialVersionUID = 1L; + boolean inAnApplet = true; + boolean inSeparateFrame = false; + protected String[] mainArgs; + + // + // 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; + /* + * if(argv.length > 1){ v.host = argv[0]; v.port = + * Integer.parseInt(argv[1]); } + */ + v.init(); + v.start(); + } + + String[] echoClient; + + // RfbProto rfb; + MyRfbProtoClient rfb; + Thread rfbThread; + Thread accThread; + + Frame vncFrame; + Container vncContainer; + ScrollPane desktopScrollPane; + GridBagLayout gridbag; + ButtonPanel buttonPanel; + Label connStatusLabel; + VncCanvas vc; + OptionsFrame options; + ClipboardFrame clipboard; + RecordingFrame rec; + + // Control session recording. + Object recordingSync; + String sessionFileName; + boolean recordingActive; + boolean recordingStatusChanged; + String cursorUpdatesDef; + String eightBitColorsDef; + + // Variables read from parameter values. + String socketFactory; + String host; + int port; + String passwordParam; + boolean showControls; + boolean offerRelogin; + boolean showOfflineDesktop; + int deferScreenUpdates; + int deferCursorUpdates; + int deferUpdateRequests; + int debugStatsExcludeUpdates; + int debugStatsMeasureUpdates; + + // Reference to this applet for inter-applet communication. + public static java.applet.Applet refApplet; + + // + // init() + // + + public void init() { + + readParameters(); + + refApplet = this; + + if (inSeparateFrame) { + vncFrame = new Frame("TightVNC"); + if (!inAnApplet) { + vncFrame.add("Center", this); + } + vncContainer = vncFrame; + } else { + vncContainer = this; + } + + recordingSync = new Object(); + + options = new OptionsFrame(this); + clipboard = new ClipboardFrame(this); + if (RecordingFrame.checkSecurity()) + rec = new RecordingFrame(this); + + sessionFileName = null; + recordingActive = false; + recordingStatusChanged = false; + cursorUpdatesDef = null; + eightBitColorsDef = null; + + if (inSeparateFrame) + vncFrame.addWindowListener(this); + + rfbThread = new Thread(this); + rfbThread.start(); + accThread = new Thread(new AcceptThread(rfb)); + accThread.start(); + } + + public void update(Graphics g) { + } + + // + // run() - executed by the rfbThread to deal with the RFB socket. + // + + public void run() { + + gridbag = new GridBagLayout(); + vncContainer.setLayout(gridbag); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.anchor = GridBagConstraints.NORTHWEST; + + if (showControls) { + buttonPanel = new ButtonPanel(this); + gridbag.setConstraints(buttonPanel, gbc); + vncContainer.add(buttonPanel); + } + +/*****************************************************************************/ + if(inSeparateFrame) { + vncFrame.pack(); + vncFrame.setVisible(true); + } else { + validate(); + } + try { + + rfb = new MyRfbProtoClient(host, port, this); + rfb.readServerInit(); +// rfb.readPngData(); + + createCanvas(0, 0); + vc.drawFirstImage(); + } catch (IOException e) { + System.out.println("Socket error"); + System.exit(0); + } + + gbc.weightx = 1.0; + gbc.weighty = 1.0; + + if (inSeparateFrame) { + + // Create a panel which itself is resizeable and can hold + // non-resizeable VncCanvas component at the top left corner. + Panel canvasPanel = new Panel(); + canvasPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); + canvasPanel.add(vc); + + // Create a ScrollPane which will hold a panel with VncCanvas + // inside. + desktopScrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); + gbc.fill = GridBagConstraints.BOTH; + gridbag.setConstraints(desktopScrollPane, gbc); + desktopScrollPane.add(canvasPanel); + + // 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(); + + } +/*****************************************************************************/ + + + try { + + if (showControls) + buttonPanel.enableButtons(); + + moveFocusToDesktop(); + processNormalProtocol();// main loop + + } catch (NoRouteToHostException e) { + fatalError("Network error: no route to server: " + host, e); + } catch (UnknownHostException e) { + fatalError("Network error: server name unknown: " + host, e); + } catch (ConnectException e) { + fatalError("Network error: could not connect to server: " + host + + ":" + port, e); + } catch (EOFException e) { + + + if (showOfflineDesktop) { + e.printStackTrace(); + System.out + .println("Network error: remote side closed connection"); + if (vc != null) { + vc.enableInput(false); + } + if (inSeparateFrame) { + vncFrame.setTitle(rfb.desktopName + " [disconnected]"); + } + if (rfb != null && !rfb.closed()) + rfb.close(); + if (showControls && buttonPanel != null) { + buttonPanel.disableButtonsOnDisconnect(); + if (inSeparateFrame) { + vncFrame.pack(); + } else { + validate(); + } + } + } else { + fatalError("Network error: remote side closed connection", e); + } + } catch (IOException e) { + String str = e.getMessage(); + if (str != null && str.length() != 0) { + fatalError("Network Error: " + str, e); + } else { + fatalError(e.toString(), e); + } + } catch (Exception e) { + String str = e.getMessage(); + if (str != null && str.length() != 0) { + fatalError("Error: " + str, e); + } else { + fatalError(e.toString(), e); + } + } + + } + + // + // Create a VncCanvas instance. + // + + void createCanvas(int maxWidth, int maxHeight) throws IOException { + // Determine if Java 2D API is available and use a special + // version of VncCanvas if it is present. + vc = null; + try { + // This throws ClassNotFoundException if there is no Java 2D API. + Class cl = Class.forName("java.awt.Graphics2D"); + // If we could load Graphics2D class, then we can use VncCanvas2D. + cl = Class.forName("VncCanvas2"); + Class[] argClasses = { this.getClass(), Integer.TYPE, Integer.TYPE }; + java.lang.reflect.Constructor cstr = cl.getConstructor(argClasses); + Object[] argObjects = { this, new Integer(maxWidth), + new Integer(maxHeight) }; + vc = (VncCanvas) cstr.newInstance(argObjects); + } catch (Exception e) { + System.out.println("Warning: Java 2D API is not available"); + } + + // If we failed to create VncCanvas2D, use old VncCanvas. + if (vc == null) + vc = new VncCanvas(this, maxWidth, maxHeight); + } + + // + // Process RFB socket messages. + // If the rfbThread is being stopped, ignore any exceptions, + // otherwise rethrow the exception so it can be handled. + // + + void processNormalProtocol() throws Exception { + try { + vc.processNormalProtocol();// main loop + } catch (Exception e) { + if (rfbThread == null) { + System.out.println("Ignoring RFB socket exceptions" + + " because applet is stopping"); + } else { + throw e; + } + } + } + + // + // Connect to the RFB server and authenticate the user. + // + + void connectAndAuthenticate() throws Exception { + showConnectionStatus("Initializing..."); + if (inSeparateFrame) { + vncFrame.pack(); + vncFrame.setVisible(true); + } else { + validate(); + } + + showConnectionStatus("Connecting to " + host + ", port " + port + "..."); + + rfb = new MyRfbProtoClient(host, port, this); + showConnectionStatus("Connected to server"); + + rfb.readVersionMsg(); + showConnectionStatus("RFB server supports protocol version " + + rfb.serverMajor + "." + rfb.serverMinor); + + rfb.writeVersionMsg(); + showConnectionStatus("Using RFB protocol version " + rfb.clientMajor + + "." + rfb.clientMinor); + + int secType = rfb.negotiateSecurity(); + int authType; + if (secType == RfbProto.SecTypeTight) { + showConnectionStatus("Enabling TightVNC protocol extensions"); + rfb.setupTunneling(); + authType = rfb.negotiateAuthenticationTight(); + } else { + authType = secType; + } + + switch (authType) { + case RfbProto.AuthNone: + showConnectionStatus("No authentication needed"); + rfb.authenticateNone(); + break; + case RfbProto.AuthVNC: + showConnectionStatus("Performing standard VNC authentication"); + if (passwordParam != null) { + rfb.authenticateVNC(passwordParam); + } else { + String pw = askPassword(); + rfb.authenticateVNC(pw); + } + break; + default: + throw new Exception("Unknown authentication scheme " + authType); + } + } + + // + // Show a message describing the connection status. + // To hide the connection status label, use (msg == null). + // + + void showConnectionStatus(String msg) { + if (msg == null) { + if (vncContainer.isAncestorOf(connStatusLabel)) { + vncContainer.remove(connStatusLabel); + } + return; + } + + System.out.println(msg); + + if (connStatusLabel == null) { + connStatusLabel = new Label("Status: " + msg); + connStatusLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); + } else { + connStatusLabel.setText("Status: " + msg); + } + + if (!vncContainer.isAncestorOf(connStatusLabel)) { + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.insets = new Insets(20, 30, 20, 30); + gridbag.setConstraints(connStatusLabel, gbc); + vncContainer.add(connStatusLabel); + } + + if (inSeparateFrame) { + vncFrame.pack(); + } else { + validate(); + } + } + + // + // Show an authentication panel. + // + + String askPassword() throws Exception { + showConnectionStatus(null); + + AuthPanel authPanel = new AuthPanel(this); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.anchor = GridBagConstraints.NORTHWEST; + gbc.weightx = 1.0; + gbc.weighty = 1.0; + gbc.ipadx = 100; + gbc.ipady = 50; + gridbag.setConstraints(authPanel, gbc); + vncContainer.add(authPanel); + + if (inSeparateFrame) { + vncFrame.pack(); + } else { + validate(); + } + + authPanel.moveFocusToDefaultField(); + String pw = authPanel.getPassword(); + vncContainer.remove(authPanel); + + return pw; + } + + // + // Do the rest of the protocol initialisation. + // + + void doProtocolInitialisation() throws IOException { + rfb.writeClientInit(); + rfb.readServerInit(); + + System.out.println("Desktop name is " + rfb.desktopName); + System.out.println("Desktop size is " + rfb.framebufferWidth + " x " + + rfb.framebufferHeight); + + setEncodings(); + + showConnectionStatus(null); + } + + // + // Send current encoding list to the RFB server. + // + + int[] encodingsSaved; + int nEncodingsSaved; + + void setEncodings() { + setEncodings(false); + } + + void autoSelectEncodings() { + setEncodings(true); + } + + void setEncodings(boolean autoSelectOnly) { + if (options == null || rfb == null || !rfb.inNormalProtocol) + return; + + int preferredEncoding = options.preferredEncoding; + if (preferredEncoding == -1) { + long kbitsPerSecond = rfb.kbitsPerSecond(); + if (nEncodingsSaved < 1) { + // Choose Tight or ZRLE encoding for the very first update. + System.out.println("Using Tight/ZRLE encodings"); + preferredEncoding = RfbProto.EncodingTight; + } else if (kbitsPerSecond > 2000 + && encodingsSaved[0] != RfbProto.EncodingHextile) { + // Switch to Hextile if the connection speed is above 2Mbps. + System.out.println("Throughput " + kbitsPerSecond + + " kbit/s - changing to Hextile encoding"); + preferredEncoding = RfbProto.EncodingHextile; + } else if (kbitsPerSecond < 1000 + && encodingsSaved[0] != RfbProto.EncodingTight) { + // Switch to Tight/ZRLE if the connection speed is below 1Mbps. + System.out.println("Throughput " + kbitsPerSecond + + " kbit/s - changing to Tight/ZRLE encodings"); + preferredEncoding = RfbProto.EncodingTight; + } else { + // Don't change the encoder. + if (autoSelectOnly) + return; + preferredEncoding = encodingsSaved[0]; + } + } else { + // Auto encoder selection is not enabled. + if (autoSelectOnly) + return; + } + + int[] encodings = new int[20]; + int nEncodings = 0; + + encodings[nEncodings++] = preferredEncoding; + if (options.useCopyRect) { + encodings[nEncodings++] = RfbProto.EncodingCopyRect; + } + + if (preferredEncoding != RfbProto.EncodingTight) { + encodings[nEncodings++] = RfbProto.EncodingTight; + } + if (preferredEncoding != RfbProto.EncodingZRLE) { + encodings[nEncodings++] = RfbProto.EncodingZRLE; + } + if (preferredEncoding != RfbProto.EncodingHextile) { + encodings[nEncodings++] = RfbProto.EncodingHextile; + } + if (preferredEncoding != RfbProto.EncodingZlib) { + encodings[nEncodings++] = RfbProto.EncodingZlib; + } + if (preferredEncoding != RfbProto.EncodingCoRRE) { + encodings[nEncodings++] = RfbProto.EncodingCoRRE; + } + if (preferredEncoding != RfbProto.EncodingRRE) { + encodings[nEncodings++] = RfbProto.EncodingRRE; + } + + if (options.compressLevel >= 0 && options.compressLevel <= 9) { + encodings[nEncodings++] = RfbProto.EncodingCompressLevel0 + + options.compressLevel; + } + if (options.jpegQuality >= 0 && options.jpegQuality <= 9) { + encodings[nEncodings++] = RfbProto.EncodingQualityLevel0 + + options.jpegQuality; + } + + if (options.requestCursorUpdates) { + encodings[nEncodings++] = RfbProto.EncodingXCursor; + encodings[nEncodings++] = RfbProto.EncodingRichCursor; + if (!options.ignoreCursorUpdates) + encodings[nEncodings++] = RfbProto.EncodingPointerPos; + } + + encodings[nEncodings++] = RfbProto.EncodingLastRect; + encodings[nEncodings++] = RfbProto.EncodingNewFBSize; + + boolean encodingsWereChanged = false; + if (nEncodings != nEncodingsSaved) { + encodingsWereChanged = true; + } else { + for (int i = 0; i < nEncodings; i++) { + if (encodings[i] != encodingsSaved[i]) { + encodingsWereChanged = true; + break; + } + } + } + + if (encodingsWereChanged) { + try { + rfb.writeSetEncodings(encodings, nEncodings); + if (vc != null) { + vc.softCursorFree(); + } + } catch (Exception e) { + e.printStackTrace(); + } + encodingsSaved = encodings; + nEncodingsSaved = nEncodings; + } + } + + // + // setCutText() - send the given cut text to the RFB server. + // + + void setCutText(String text) { + try { + if (rfb != null && rfb.inNormalProtocol) { + rfb.writeClientCutText(text); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + // + // Order change in session recording status. To stop recording, pass + // null in place of the fname argument. + // + + void setRecordingStatus(String fname) { + synchronized (recordingSync) { + sessionFileName = fname; + recordingStatusChanged = true; + } + } + + // + // Start or stop session recording. Returns true if this method call + // causes recording of a new session. + // + + boolean checkRecordingStatus() throws IOException { + synchronized (recordingSync) { + if (recordingStatusChanged) { + recordingStatusChanged = false; + if (sessionFileName != null) { + startRecording(); + return true; + } else { + stopRecording(); + } + } + } + return false; + } + + // + // Start session recording. + // + + protected void startRecording() throws IOException { + synchronized (recordingSync) { + if (!recordingActive) { + // Save settings to restore them after recording the session. + cursorUpdatesDef = options.choices[options.cursorUpdatesIndex] + .getSelectedItem(); + eightBitColorsDef = options.choices[options.eightBitColorsIndex] + .getSelectedItem(); + // Set options to values suitable for recording. + options.choices[options.cursorUpdatesIndex].select("Disable"); + options.choices[options.cursorUpdatesIndex].setEnabled(false); + options.setEncodings(); + options.choices[options.eightBitColorsIndex].select("No"); + options.choices[options.eightBitColorsIndex].setEnabled(false); + options.setColorFormat(); + } else { + rfb.closeSession(); + } + + System.out.println("Recording the session in " + sessionFileName); + rfb.startSession(sessionFileName); + recordingActive = true; + } + } + + // + // Stop session recording. + // + + protected void stopRecording() throws IOException { + synchronized (recordingSync) { + if (recordingActive) { + // Restore options. + options.choices[options.cursorUpdatesIndex] + .select(cursorUpdatesDef); + options.choices[options.cursorUpdatesIndex].setEnabled(true); + options.setEncodings(); + options.choices[options.eightBitColorsIndex] + .select(eightBitColorsDef); + options.choices[options.eightBitColorsIndex].setEnabled(true); + options.setColorFormat(); + + rfb.closeSession(); + System.out.println("Session recording stopped."); + } + sessionFileName = null; + recordingActive = false; + } + } + + // + // readParameters() - read parameters from the html source or from the + // command line. On the command line, the arguments are just a sequence of + // param_name/param_value pairs where the names and values correspond to + // those expected in the html applet tag source. + // + + void readParameters() { + host = readParameter("HOST", !inAnApplet); + + + if (host == null) { + host = getCodeBase().getHost(); + if (host.equals("")) { + fatalError("HOST parameter not specified"); + } + } + + port = readIntParameter("PORT", 5550); + + + // Read "ENCPASSWORD" or "PASSWORD" parameter if specified. + readPasswordParameters(); + + String str; + + if (inAnApplet) { + str = readParameter("Open New Window", false); + if (str != null && str.equalsIgnoreCase("Yes")) + inSeparateFrame = true; + } + + + // "Show Controls" set to "No" disables button panel. + showControls = true; + str = readParameter("Show Controls", false); + if (str != null && str.equalsIgnoreCase("No")) + showControls = false; + + // "Offer Relogin" set to "No" disables "Login again" and "Close + // window" buttons under error messages in applet mode. + offerRelogin = true; + str = readParameter("Offer Relogin", false); + if (str != null && str.equalsIgnoreCase("No")) + offerRelogin = false; + + // Do we continue showing desktop on remote disconnect? + showOfflineDesktop = false; + str = readParameter("Show Offline Desktop", false); + if (str != null && str.equalsIgnoreCase("Yes")) + showOfflineDesktop = true; + + // Fine tuning options. + deferScreenUpdates = readIntParameter("Defer screen updates", 20); + deferCursorUpdates = readIntParameter("Defer cursor updates", 10); + deferUpdateRequests = readIntParameter("Defer update requests", 0); + + // Debugging options. + debugStatsExcludeUpdates = readIntParameter("DEBUG_XU", 0); + debugStatsMeasureUpdates = readIntParameter("DEBUG_CU", 0); + + // SocketFactory. + socketFactory = readParameter("SocketFactory", false); + } + + // + // Read password parameters. If an "ENCPASSWORD" parameter is set, + // then decrypt the password into the passwordParam string. Otherwise, + // try to read the "PASSWORD" parameter directly to passwordParam. + // + + private void readPasswordParameters() { + String encPasswordParam = readParameter("ENCPASSWORD", false); + if (encPasswordParam == null) { + passwordParam = readParameter("PASSWORD", false); + + } else { + // ENCPASSWORD is hexascii-encoded. Decode. + byte[] pw = { 0, 0, 0, 0, 0, 0, 0, 0 }; + int len = encPasswordParam.length() / 2; + if (len > 8) + len = 8; + for (int i = 0; i < len; i++) { + String hex = encPasswordParam.substring(i * 2, i * 2 + 2); + Integer x = new Integer(Integer.parseInt(hex, 16)); + pw[i] = x.byteValue(); + } + // Decrypt the password. + byte[] key = { 23, 82, 107, 6, 35, 78, 88, 7 }; + DesCipher des = new DesCipher(key); + des.decrypt(pw, 0, pw, 0); + passwordParam = new String(pw); + + } + } + + public String readParameter(String name, boolean required) { + if (inAnApplet) { + String s = getParameter(name); + if ((s == null) && required) { + fatalError(name + " parameter not specified"); + } + return s; + } + + for (int i = 0; i < mainArgs.length; i += 2) { + if (mainArgs[i].equalsIgnoreCase(name)) { + try { + return mainArgs[i + 1]; + } catch (Exception e) { + if (required) { + fatalError(name + " parameter not specified"); + } + return null; + } + } + } + if (required) { + fatalError(name + " parameter not specified"); + } + return null; + } + + int readIntParameter(String name, int defaultValue) { + String str = readParameter(name, false); + int result = defaultValue; + if (str != null) { + try { + result = Integer.parseInt(str); + } catch (NumberFormatException e) { + } + } + return result; + } + + // + // moveFocusToDesktop() - move keyboard focus either to VncCanvas. + // + + void moveFocusToDesktop() { + if (vncContainer != null) { + if (vc != null && vncContainer.isAncestorOf(vc)) + vc.requestFocus(); + } + } + + // + // disconnect() - close connection to server. + // + + synchronized public void disconnect() { + System.out.println("Disconnecting"); + + if (vc != null) { + double sec = (System.currentTimeMillis() - vc.statStartTime) / 1000.0; + double rate = Math.round(vc.statNumUpdates / sec * 100) / 100.0; + int nRealRects = vc.statNumPixelRects; + int nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects; + System.out.println("Updates received: " + vc.statNumUpdates + " (" + + nRealRects + " rectangles + " + nPseudoRects + + " pseudo), " + rate + " updates/sec"); + int numRectsOther = nRealRects - vc.statNumRectsTight + - vc.statNumRectsZRLE - vc.statNumRectsHextile + - vc.statNumRectsRaw - vc.statNumRectsCopy; + System.out.println("Rectangles:" + " Tight=" + vc.statNumRectsTight + + "(JPEG=" + vc.statNumRectsTightJPEG + ") ZRLE=" + + vc.statNumRectsZRLE + " Hextile=" + + vc.statNumRectsHextile + " Raw=" + vc.statNumRectsRaw + + " CopyRect=" + vc.statNumRectsCopy + " other=" + + numRectsOther); + + int raw = vc.statNumBytesDecoded; + int compressed = vc.statNumBytesEncoded; + if (compressed > 0) { + double ratio = Math.round((double) raw / compressed * 1000) / 1000.0; + System.out.println("Pixel data: " + vc.statNumBytesDecoded + + " bytes, " + vc.statNumBytesEncoded + + " compressed, ratio " + ratio); + } + } + + if (rfb != null && !rfb.closed()) + rfb.close(); + options.dispose(); + clipboard.dispose(); + if (rec != null) + rec.dispose(); + + if (inAnApplet) { + showMessage("Disconnected"); + } else { + System.exit(0); + } + } + + // + // fatalError() - print out a fatal error message. + // FIXME: Do we really need two versions of the fatalError() method? + // + + synchronized public void fatalError(String str) { + System.out.println(str); + + if (inAnApplet) { + // vncContainer null, applet not inited, + // can not present the error to the user. + Thread.currentThread().stop(); + } else { + System.exit(1); + } + } + + synchronized public void fatalError(String str, Exception e) { + + if (rfb != null && rfb.closed()) { + // Not necessary to show error message if the error was caused + // by I/O problems after the rfb.close() method call. + System.out.println("RFB thread finished"); + return; + } + + System.out.println(str); + e.printStackTrace(); + + if (rfb != null) + rfb.close(); + + if (inAnApplet) { + showMessage(str); + } else { + System.exit(1); + } + } + + // + // Show message text and optionally "Relogin" and "Close" buttons. + // + + void showMessage(String msg) { + vncContainer.removeAll(); + + Label errLabel = new Label(msg, Label.CENTER); + errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); + + if (offerRelogin) { + + Panel gridPanel = new Panel(new GridLayout(0, 1)); + Panel outerPanel = new Panel(new FlowLayout(FlowLayout.LEFT)); + outerPanel.add(gridPanel); + vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 16)); + vncContainer.add(outerPanel); + Panel textPanel = new Panel(new FlowLayout(FlowLayout.CENTER)); + textPanel.add(errLabel); + gridPanel.add(textPanel); + gridPanel.add(new ReloginPanel(this)); + + } else { + + vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30)); + vncContainer.add(errLabel); + + } + + if (inSeparateFrame) { + vncFrame.pack(); + } else { + validate(); + } + } + + // + // Stop the applet. + // Main applet thread will terminate on first exception + // after seeing that rfbThread has been set to null. + // + + public void stop() { + System.out.println("Stopping applet"); + rfbThread = null; + } + + // + // This method is called before the applet is destroyed. + // + + public void destroy() { + System.out.println("Destroying applet"); + + vncContainer.removeAll(); + options.dispose(); + clipboard.dispose(); + if (rec != null) + rec.dispose(); + if (rfb != null && !rfb.closed()) + rfb.close(); + if (inSeparateFrame) + vncFrame.dispose(); + } + + // + // Start/stop receiving mouse events. + // + + public void enableInput(boolean enable) { + vc.enableInput(enable); + } + + // + // Close application properly on window close event. + // + + public void windowClosing(WindowEvent evt) { + System.out.println("Closing window"); + if (rfb != null) + disconnect(); + + vncContainer.setVisible(false); + + if (!inAnApplet) { + System.exit(0); + } + } + + // + // Ignore window events we're not interested in. + // + + public void windowActivated(WindowEvent evt) { + } + + public void windowDeactivated(WindowEvent evt) { + } + + public void windowOpened(WindowEvent evt) { + } + + public void windowClosed(WindowEvent evt) { + } + + public void windowIconified(WindowEvent evt) { + } + + public void windowDeiconified(WindowEvent evt) { + } +} diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/WaitReply.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/WaitReply.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,62 @@ +package treeVnc; + +import java.net.*; +import java.io.*; + +public class WaitReply extends Thread { + // MyVncClient client; + InterfaceForViewer client; + private String treenum; + boolean passflag; + + + /* + * public WaitReply(String treenum,MyVncClient client) { this.client = + * client; this.treenum = treenum; } + */ + + public WaitReply(String treenum, InterfaceForViewer client) { + this.client = client; + this.treenum = treenum; + } + + public boolean checkPath() { + return passflag; + } + + public void run() { + Socket clientSocket = null; + ServerSocket echoServer = null; + // boolean mainFlag; + + while (true) { + try { + //echoServer = new ServerSocket(10001 + Integer.parseInt(treenum)); + echoServer = new ServerSocket(10001); + clientSocket = echoServer.accept(); + + if (clientSocket != null) { + client.close(); + passflag = true; + // client.clientSocket = clientSocket; + client.setClientSocket(clientSocket); + client.init(); + client.setEchoValue(null); + client.getParentName(); + client.start_threads(); + client.start(); + // MyVncClient.main(clientSocket); + // echo.Interruption(clientSocket); + echoServer.close(); + } + + } catch (IOException e) { + System.out.println(e); + } + /* + * try { Thread.sleep(100); } catch (InterruptedException e) { + * e.printStackTrace(); } + */ + } + } +} \ No newline at end of file diff -r 000000000000 -r 756bfaf731f3 src/treeVnc/ZlibInStream.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/treeVnc/ZlibInStream.java Tue Feb 21 04:10:12 2012 +0900 @@ -0,0 +1,114 @@ +package treeVnc; +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// A ZlibInStream reads from a zlib.io.InputStream +// + +public class ZlibInStream extends InStream { + + static final int defaultBufSize = 16384; + + public ZlibInStream(int bufSize_) { + bufSize = bufSize_; + b = new byte[bufSize]; + ptr = end = ptrOffset = 0; + inflater = new java.util.zip.Inflater(); + } + + public ZlibInStream() { this(defaultBufSize); } + + public void setUnderlying(InStream is, int bytesIn_) { + underlying = is; + bytesIn = bytesIn_; + ptr = end = 0; + } + + public void reset() throws Exception { + ptr = end = 0; + if (underlying == null) return; + + while (bytesIn > 0) { + decompress(); + end = 0; // throw away any data + } + underlying = null; + } + + public int pos() { return ptrOffset + ptr; } + + + protected int overrun(int itemSize, int nItems) throws Exception { + if (itemSize > bufSize) + throw new Exception("ZlibInStream overrun: max itemSize exceeded"); + if (underlying == null) + throw new Exception("ZlibInStream overrun: no underlying stream"); + + if (end - ptr != 0) + System.arraycopy(b, ptr, b, 0, end - ptr); + + ptrOffset += ptr; + end -= ptr; + ptr = 0; + + while (end < itemSize) { + decompress(); + } + + if (itemSize * nItems > end) + nItems = end / itemSize; + + return nItems; + } + + // decompress() calls the decompressor once. Note that this won't + // necessarily generate any output data - it may just consume some input + // data. Returns false if wait is false and we would block on the underlying + // stream. + + private void decompress() throws Exception { + try { + underlying.check(1); + int avail_in = underlying.getend() - underlying.getptr(); + if (avail_in > bytesIn) + avail_in = bytesIn; + + if (inflater.needsInput()) { + inflater.setInput(underlying.getbuf(), underlying.getptr(), avail_in); + } + + int n = inflater.inflate(b, end, bufSize - end); + + end += n; + if (inflater.needsInput()) { + bytesIn -= avail_in; + underlying.setptr(underlying.getptr() + avail_in); + } + } catch (java.util.zip.DataFormatException e) { + throw new Exception("ZlibInStream: inflate failed"); + } + } + + + private InStream underlying; + private int bufSize; + private int ptrOffset; + private java.util.zip.Inflater inflater; + private int bytesIn; +}