# HG changeset patch # User Yu Taninari # Date 1312642638 -32400 # Node ID 5b124b0ceaa7de2a5c92506d1165a0f88463e237 # Parent 7d9e9dfd7eb844ddba363048c56936515d314def# Parent 02016fcb9105047fcc4d93e49a9faf9cad54fe85 merge diff -r 7d9e9dfd7eb8 -r 5b124b0ceaa7 .classpath --- a/.classpath Sat Aug 06 23:56:16 2011 +0900 +++ b/.classpath Sat Aug 06 23:57:18 2011 +0900 @@ -2,5 +2,6 @@ + diff -r 7d9e9dfd7eb8 -r 5b124b0ceaa7 src/myVncProxy/AcceptThread.java --- a/src/myVncProxy/AcceptThread.java Sat Aug 06 23:56:16 2011 +0900 +++ b/src/myVncProxy/AcceptThread.java Sat Aug 06 23:57:18 2011 +0900 @@ -1,31 +1,36 @@ package myVncProxy; -import java.net.ServerSocket; import java.net.Socket; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class AcceptThread implements Runnable { - MyRfbProto rfb; - byte[] imageBytes; + MyRfbProto rfb; + byte[] imageBytes; + int port; + + AcceptThread(MyRfbProto _rfb) { + rfb = _rfb; + } + + AcceptThread(MyRfbProto _rfb, int p) { + rfb = _rfb; + port = p; + } - AcceptThread(MyRfbProto _rfb ) { - rfb = _rfb; - } - public void run() { - rfb.selectPort(); - 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); - } - } - } + 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 7d9e9dfd7eb8 -r 5b124b0ceaa7 src/myVncProxy/CreateThread.java --- a/src/myVncProxy/CreateThread.java Sat Aug 06 23:56:16 2011 +0900 +++ b/src/myVncProxy/CreateThread.java Sat Aug 06 23:57:18 2011 +0900 @@ -22,20 +22,13 @@ public void run() { while (true) { - try { echoServer = new ServerSocket(9999); - } - catch (IOException e) { - System.out.println(e); - } - try { Socket clientSocket = echoServer.accept(); BufferedReader is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); PrintStream os = new PrintStream(clientSocket.getOutputStream()); acceptClient.transferParentAddrerss(is,os); } catch (IOException e){ - e.printStackTrace(); System.out.println(e); } try { diff -r 7d9e9dfd7eb8 -r 5b124b0ceaa7 src/myVncProxy/MostRecentMultiCast.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/myVncProxy/MostRecentMultiCast.java Sat Aug 06 23:57:18 2011 +0900 @@ -0,0 +1,27 @@ +package myVncProxy; + +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 7d9e9dfd7eb8 -r 5b124b0ceaa7 src/myVncProxy/MulticastQueue.java --- a/src/myVncProxy/MulticastQueue.java Sat Aug 06 23:56:16 2011 +0900 +++ b/src/myVncProxy/MulticastQueue.java Sat Aug 06 23:57:18 2011 +0900 @@ -33,21 +33,24 @@ node = tail; } - public T poll() + synchronized public T poll() { Node next = null; - - try { - next = node.next(); - }catch(InterruptedException _e){ - _e.printStackTrace(); - } - node = next; - return next.item; + T item = null; + do { + try { + next = node.next(); + }catch(InterruptedException _e){ + continue; + } + item = node.getItem(); + node = next; + } while ( item == null); + return item; } } - private static class Node + static class Node { private T item; private Node next; @@ -60,6 +63,10 @@ latch = new CountDownLatch(1); } + synchronized public T getItem() { + return item; + } + public void set(Node next) { this.next = next; @@ -71,5 +78,9 @@ latch.await(); return next; } + + synchronized public void clear() { + item = null; + } } } diff -r 7d9e9dfd7eb8 -r 5b124b0ceaa7 src/myVncProxy/MyRfbProto.java --- a/src/myVncProxy/MyRfbProto.java Sat Aug 06 23:56:16 2011 +0900 +++ b/src/myVncProxy/MyRfbProto.java Sat Aug 06 23:57:18 2011 +0900 @@ -1,5 +1,7 @@ package myVncProxy; +import static org.junit.Assert.*; + import java.awt.Graphics; import java.awt.Image; import java.awt.image.BufferedImage; @@ -13,19 +15,34 @@ import java.net.BindException; import java.net.ServerSocket; import java.net.Socket; +import java.nio.ByteBuffer; +import java.util.Iterator; import java.util.LinkedList; import javax.imageio.ImageIO; +import org.junit.Test; + import myVncProxy.MulticastQueue.Client; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; +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 MyRfbProto extends RfbProto { - final static String versionMsg_3_998 = "RFB 003.998\n"; + /** + * CheckMillis is one of new msgType for RFB 3.998. + */ + final static byte SpeedCheckMillis = 4; + private static final int INFLATE_BUFSIZE = 1024*100; + boolean printStatusFlag = false; + long startCheckTime; private int messageType; private int rectangles; @@ -35,21 +52,29 @@ 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; - private LinkedList sendThreads; boolean createBimgFlag; ExecutorService executor; byte[] pngBytes; - private MulticastQueue multicastqueue = new MulticastQueue(); + // 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 + MyRfbProto() throws IOException { + } + MyRfbProto(String h, int p, VncViewer v) throws IOException { super(h, p, v); cliList = new LinkedList(); @@ -99,20 +124,20 @@ } // 5550を開けるが、開いてないなら+1のポートを開ける。 - void selectPort() { - int i = 5550; + void selectPort(int p) { + int port = p; while (true) { try { - initServSock(i); + initServSock(port); break; } catch (BindException e) { - i++; + port++; continue; } catch (IOException e) { } } - System.out.println("accept port = " + i); + System.out.println("accept port = " + port); } int getAcceptPort() { @@ -136,30 +161,22 @@ cliListTmp.add(sock); } - void mark(int len) throws IOException { - is.mark(len); - } - - void reset() throws IOException { - is.reset(); - } - boolean markSupported() { return is.markSupported(); } void readServerInit() throws IOException { - mark(255); + is.mark(255); skipBytes(20); int nlen = readU32(); int blen = 20 + 4 + nlen; initData = new byte[blen]; - reset(); + is.reset(); - mark(blen); + is.mark(blen); readFully(initData); - reset(); + is.reset(); framebufferWidth = readU16(); framebufferHeight = readU16(); @@ -251,20 +268,6 @@ os.write(initData); } - void sendData(byte b[]) { - try { - multicastqueue.put(b); - - /* - * // for(Socket cli : cliList){ // try{ // - * cli.getOutputStream().write(b, 0, b.length); // - * }catch(IOException e){ // // if socket closed // - * cliList.remove(cli); // } // } - */ - // System.out.println("cliSize="+cliSize()); - } catch (Exception e) { - } - } void sendPngImage() { try { @@ -296,30 +299,25 @@ System.out.println("numBytesRead=" + numBytesRead); } - void bufResetSend(int size) throws IOException { - reset(); - int len = size; - if (available() < size) - len = available(); - byte buffer[] = new byte[len]; - readFully(buffer); - sendData(buffer); - } void regiFramebufferUpdate() throws IOException { - mark(20); - messageType = readU8(); - skipBytes(1); - rectangles = readU16(); - rectX = readU16(); - rectY = readU16(); - rectW = readU16(); - rectH = readU16(); - encoding = readU32(); - if (encoding == 16) + 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(); - reset(); + else + zLen = 0; + is.reset(); + } int checkAndMark() throws IOException { @@ -327,39 +325,42 @@ switch (encoding) { case RfbProto.EncodingRaw: dataLen = rectW * rectH * 4 + 16; - mark(dataLen); + 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; - mark(dataLen); + 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; - mark(dataLen); + is.mark(dataLen); } return dataLen; } - void readSendData(int dataLen) throws IOException { - byte buffer[] = new byte[dataLen]; - readFully(buffer); - multicastqueue.put(buffer); - reset(); -/* - for (Socket cli : cliList) { - try { - OutputStream out = cli.getOutputStream(); - executor.execute(new SendThread(out, buffer)); - } catch (IOException e) { - // if client socket closed - cliListTmp.remove(cli); - } catch (Exception e) { + - } - - } -*/ - } - void sendDataToClient() throws IOException { + void sendDataToClient() throws Exception { regiFramebufferUpdate(); int dataLen = checkAndMark(); readSendData(dataLen); @@ -415,16 +416,13 @@ return bimg; } - void readPngData() throws IOException { - pngBytes = new byte[is.available()]; - readFully(pngBytes); - } - 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 @@ -433,17 +431,285 @@ default: } } + + 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 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. + } void newClient(AcceptThread acceptThread, final Socket newCli, final OutputStream os, final InputStream is) throws IOException { // createBimgFlag = true; // rfb.addSockTmp(newCli); // addSock(newCli); - final Client c = multicastqueue.newClient(); + final int myId = clients; + final 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 = 30000/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); sendSecurityType(os); @@ -451,21 +717,170 @@ sendSecResult(os); readClientInit(is); sendInitData(os); - + new Thread(reader).start(); // discard incoming packet here after. for (;;) { - byte[] b = c.poll(); - os.write(b, 0, b.length); + LinkedList bufs = c.poll(); + int inputIndex = 0; + ByteBuffer header = bufs.get(inputIndex); + if (header==null) continue; + 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) { - //接続が切れた処理 - //lockしないと駄目 - // cliList.remove(newCli); + 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(); + } + + @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 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 7d9e9dfd7eb8 -r 5b124b0ceaa7 src/myVncProxy/ProxyVncCanvas.java --- a/src/myVncProxy/ProxyVncCanvas.java Sat Aug 06 23:56:16 2011 +0900 +++ b/src/myVncProxy/ProxyVncCanvas.java Sat Aug 06 23:57:18 2011 +0900 @@ -79,6 +79,7 @@ // True if we process keyboard and mouse events. boolean inputEnabled; + private int b = 0; @@ -359,29 +360,29 @@ // // main dispatch loop // - - - long count = 0; while (true) { -// System.out.println("\ncount=" + count); - + // System.out.println("\ncount=" + count); + count++; - /* + /** * read Data from parents and send Data to Client. - * */ - rfb.sendDataToClient(); - - int bufSize = (int)rfb.getNumBytesRead(); + 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 MyRfbProto.SpeedCheckMillis: + rfb.readSpeedCheck(); + + break; case RfbProto.FramebufferUpdate: if (statNumUpdates == viewer.debugStatsExcludeUpdates @@ -451,6 +452,7 @@ handleHextileRect(rx, ry, rw, rh); break; case RfbProto.EncodingZRLE: + case RfbProto.EncodingZRLEE: statNumRectsZRLE++; handleZRLERect(rx, ry, rw, rh); break; @@ -504,8 +506,7 @@ setPixelFormat(); fullUpdateNeeded = true; } -*/ - +*/ // Request framebuffer update if needed. int w = rfb.framebufferWidth; int h = rfb.framebufferHeight; @@ -529,7 +530,7 @@ throw new Exception("Unknown RFB message type " + msgType); } - bufSize = (int)rfb.getNumBytesRead() - bufSize; + int bufSize = (int)(rfb.getNumBytesRead() - numBytesRead); // System.out.println("bufSize="+bufSize); // rfb.bufResetSend(bufSize); diff -r 7d9e9dfd7eb8 -r 5b124b0ceaa7 src/myVncProxy/RfbProto.java --- a/src/myVncProxy/RfbProto.java Sat Aug 06 23:56:16 2011 +0900 +++ b/src/myVncProxy/RfbProto.java Sat Aug 06 23:57:18 2011 +0900 @@ -26,12 +26,9 @@ // import java.io.*; -import java.awt.*; import java.awt.event.*; import java.net.Socket; -import java.net.ServerSocket; import java.util.zip.*; -import java.nio.*; class RfbProto { @@ -78,8 +75,8 @@ // Supported encodings and pseudo-encodings final static int EncodingRaw = 0, EncodingCopyRect = 1, EncodingRRE = 2, - EncodingCoRRE = 4, EncodingHextile = 5, EncodingZlib = 6, - EncodingTight = 7, EncodingZRLE = 16, + EncodingCoRRE = 4, EncodingHextile = 5, EncodingZlib = 6, + EncodingTight = 7, EncodingZRLEE = 15, EncodingZRLE = 16, EncodingCompressLevel0 = 0xFFFFFF00, EncodingQualityLevel0 = 0xFFFFFFE0, EncodingXCursor = 0xFFFFFF10, EncodingRichCursor = 0xFFFFFF11, EncodingPointerPos = 0xFFFFFF18, @@ -88,6 +85,7 @@ SigEncodingCopyRect = "COPYRECT", SigEncodingRRE = "RRE_____", SigEncodingCoRRE = "CORRE___", SigEncodingHextile = "HEXTILE_", SigEncodingZlib = "ZLIB____", SigEncodingTight = "TIGHT___", + SigEncodingZRLEE = "ZRLEE___", SigEncodingZRLE = "ZRLE____", SigEncodingCompressLevel0 = "COMPRLVL", SigEncodingQualityLevel0 = "JPEGQLVL", @@ -223,6 +221,10 @@ timeWaitedIn100us = 5; timedKbits = 0; } + + public RfbProto() { + + } @@ -467,13 +469,16 @@ encodingCaps.add(EncodingHextile, StandardVendor, SigEncodingHextile, "Standard Hextile encoding"); encodingCaps.add(EncodingZRLE, StandardVendor, SigEncodingZRLE, - "Standard ZRLE encoding"); + "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, @@ -488,6 +493,7 @@ "LastRect protocol extension"); encodingCaps.add(EncodingNewFBSize, TightVncVendor, SigEncodingNewFBSize, "Framebuffer size change"); + } // @@ -561,6 +567,10 @@ /* if (viewer.options.shareDesktop) { */ + + /** + * shared flag + */ os.write(1); // os.write(0); @@ -716,6 +726,7 @@ if (updateRectEncoding == EncodingZlib || updateRectEncoding == EncodingZRLE + || updateRectEncoding == EncodingZRLEE || updateRectEncoding == EncodingTight) wereZlibUpdates = true; @@ -948,6 +959,8 @@ 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. diff -r 7d9e9dfd7eb8 -r 5b124b0ceaa7 src/myVncProxy/VncCanvas.java --- a/src/myVncProxy/VncCanvas.java Sat Aug 06 23:56:16 2011 +0900 +++ b/src/myVncProxy/VncCanvas.java Sat Aug 06 23:57:18 2011 +0900 @@ -26,11 +26,8 @@ import java.awt.event.*; import java.awt.image.*; import java.io.*; -import java.lang.*; -import java.nio.ByteBuffer; import java.util.zip.*; -import java.net.Socket; import javax.imageio.ImageIO; @@ -478,6 +475,7 @@ handleHextileRect(rx, ry, rw, rh); break; case RfbProto.EncodingZRLE: + case RfbProto.EncodingZRLEE: statNumRectsZRLE++; handleZRLERect(rx, ry, rw, rh); break; @@ -740,6 +738,7 @@ // 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 { @@ -887,8 +886,8 @@ // void handleZRLERect(int x, int y, int w, int h) throws Exception { - - if (zrleInStream == null) + if (noZRLEdecode) return; + if (zrleInStream == null || rfb.updateRectEncoding==RfbProto.EncodingZRLEE) zrleInStream = new ZlibInStream(); // System.out.println("zrleInStream.end="+zrleInStream.inflater.off); diff -r 7d9e9dfd7eb8 -r 5b124b0ceaa7 src/myVncProxy/VncProxyService.java --- a/src/myVncProxy/VncProxyService.java Sat Aug 06 23:56:16 2011 +0900 +++ b/src/myVncProxy/VncProxyService.java Sat Aug 06 23:57:18 2011 +0900 @@ -15,7 +15,6 @@ v.init(); v.start_threads(); - } String[] mainArgs; @@ -38,7 +37,8 @@ ButtonPanel buttonPanel; Label connStatusLabel; ProxyVncCanvas vc; - OptionsFrame options; +// OptionsFrame options; + OptionsNoFrame options; ClipboardFrame clipboard; RecordingFrame rec; @@ -85,6 +85,7 @@ readParameters(); + options = new OptionsNoFrame(this); recordingSync = new Object(); sessionFileName = null; @@ -105,7 +106,7 @@ }catch(Exception e){} rfbThread = new Thread(this); - accThread = new Thread(new AcceptThread(rfb)); + accThread = new Thread(new AcceptThread(rfb, 5999)); } @@ -352,11 +353,11 @@ 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; } @@ -369,6 +370,7 @@ if (preferredEncoding != RfbProto.EncodingZlib) { encodings[nEncodings++] = RfbProto.EncodingZlib; } + /* if (preferredEncoding != RfbProto.EncodingCoRRE) { encodings[nEncodings++] = RfbProto.EncodingCoRRE; } @@ -384,14 +386,14 @@ 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; @@ -676,7 +678,7 @@ if (rfb != null && !rfb.closed()) rfb.close(); - options.dispose(); +// options.dispose(); clipboard.dispose(); if (rec != null) rec.dispose(); @@ -762,7 +764,7 @@ System.out.println("Destroying applet"); vncContainer.removeAll(); - options.dispose(); +// options.dispose(); clipboard.dispose(); if (rec != null) rec.dispose();