diff options
author | DRC <dcommander@users.sourceforge.net> | 2011-10-07 05:38:00 +0000 |
---|---|---|
committer | DRC <dcommander@users.sourceforge.net> | 2011-10-07 05:38:00 +0000 |
commit | c19ab9ec7f3ac4823802388ac953e9494c613575 (patch) | |
tree | 63513ffd7ce0b3ab3de2d9b619cc4e5b892eea31 /java/com/tigervnc | |
parent | bba54b0b14fded1d457f426cdc8843a34d6c9dc5 (diff) | |
download | tigervnc-c19ab9ec7f3ac4823802388ac953e9494c613575.tar.gz tigervnc-c19ab9ec7f3ac4823802388ac953e9494c613575.zip |
Move Java source up one level and allow Java viewer to be built as a standalone project (per community request)
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4715 3789f03b-4d11-0410-bbf8-ca57d06f2519
Diffstat (limited to 'java/com/tigervnc')
84 files changed, 11159 insertions, 0 deletions
diff --git a/java/com/tigervnc/rdr/EndOfStream.java b/java/com/tigervnc/rdr/EndOfStream.java new file mode 100644 index 00000000..bdcf7c27 --- /dev/null +++ b/java/com/tigervnc/rdr/EndOfStream.java @@ -0,0 +1,25 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rdr; + +public class EndOfStream extends Exception { + public EndOfStream() { + super("EndOfStream"); + } +} diff --git a/java/com/tigervnc/rdr/Exception.java b/java/com/tigervnc/rdr/Exception.java new file mode 100644 index 00000000..a5fe938d --- /dev/null +++ b/java/com/tigervnc/rdr/Exception.java @@ -0,0 +1,25 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rdr; + +public class Exception extends RuntimeException { + public Exception(String s) { + super(s); + } +} diff --git a/java/com/tigervnc/rdr/IOException.java b/java/com/tigervnc/rdr/IOException.java new file mode 100644 index 00000000..6343d7a4 --- /dev/null +++ b/java/com/tigervnc/rdr/IOException.java @@ -0,0 +1,27 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rdr; + +class IOException extends Exception { + public IOException(java.io.IOException ex_) { + super(ex_.toString()); + ex = ex_; + } + java.io.IOException ex; +} diff --git a/java/com/tigervnc/rdr/InStream.java b/java/com/tigervnc/rdr/InStream.java new file mode 100644 index 00000000..1e0d226e --- /dev/null +++ b/java/com/tigervnc/rdr/InStream.java @@ -0,0 +1,192 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// rdr::InStream marshalls data from a buffer stored in RDR (RFB Data +// Representation). +// + +package com.tigervnc.rdr; + +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 int check(int itemSize, int nItems, boolean wait) { + if (ptr + itemSize * nItems > end) { + if (ptr + itemSize > end) + return overrun(itemSize, nItems, wait); + + nItems = (end - ptr) / itemSize; + } + return nItems; + } + + public int check(int itemSize, int nItems) { return check(itemSize, nItems, true); } + public int check(int itemSize) { return check(itemSize, 1); } + + // checkNoWait() tries to make sure that the given number of bytes can + // be read without blocking. It returns true if this is the case, false + // otherwise. The length must be "small" (less than the buffer size). + + public final boolean checkNoWait(int length) { return check(length, 1, false)!=0; } + + // readU/SN() methods read unsigned and signed N-bit integers. + + public final int readS8() { check(1); return b[ptr++]; } + public final int readS16() { check(2); int b0 = b[ptr++]; + int b1 = b[ptr++] & 0xff; return b0 << 8 | b1; } + public final int readS32() { 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() { return readS8() & 0xff; } + public final int readU16() { return readS16() & 0xffff; } + public final int readU32() { return readS32() & 0xffffffff; } + + // readString() reads a string - a U32 length followed by the data. + + public final String readString() { + int len = readU32(); + if (len > maxStringLength) + throw new Exception("InStream max string length exceeded"); + + byte[] str = new byte[len]; + readBytes(str, 0, len); + String utf8string = new String(); + try { + utf8string = new String(str,"UTF8"); + } catch(java.io.UnsupportedEncodingException e) { + e.printStackTrace(); + } + return utf8string; + } + + // 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) { + 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 dataPtr, int length) { + int dataEnd = dataPtr + length; + while (dataPtr < dataEnd) { + int n = check(1, dataEnd - dataPtr); + System.arraycopy(b, ptr, data, dataPtr, n); + ptr += n; + dataPtr += n; + } + } + + public void readBytes(int[] data, int dataPtr, int length) { + int dataEnd = dataPtr + length; + while (dataPtr < dataEnd) { + int n = check(1, dataEnd - dataPtr); + System.arraycopy(b, ptr, data, dataPtr, n); + ptr += n; + dataPtr += n; + } + } + + // readOpaqueN() reads a quantity "without byte-swapping". Because java has + // no byte-ordering, we just use big-endian. + + public final int readOpaque8() { return readU8(); } + public final int readOpaque16() { return readU16(); } + public final int readOpaque32() { return readU32(); } + public final int readOpaque24A() { 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() { check(3); int b0 = b[ptr++]; + int b1 = b[ptr++]; int b2 = b[ptr++]; + return b0 << 16 | b1 << 8 | b2; } + + public final int readPixel(int bytesPerPixel, boolean e) { + int[] pix = new int[4]; + for (int i=0; i < bytesPerPixel; i++) + pix[i] = readU8(); + if (e) { + return pix[0] << 16 | pix[1] << 8 | pix[2] | (0xff << 24); + } else { + return pix[2] << 16 | pix[1] << 8 | pix[0] | (0xff << 24); + } + } + + public final void readPixels(int[] buf, int length, int bytesPerPixel, boolean e) { + for (int i = 0; i < length; i++) + buf[i] = readPixel(bytesPerPixel, e); + } + + public final int readCompactLength() { + int b = readU8(); + int result = b & 0x7F; + if ((b & 0x80) != 0) { + b = readU8(); + result |= (b & 0x7F) << 7; + if ((b & 0x80) != 0) { + b = readU8(); + result |= (b & 0xFF) << 14; + } + } + return result; + } + + // 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, boolean wait); + + protected InStream() {} + protected byte[] b; + protected int ptr; + protected int end; +} diff --git a/java/com/tigervnc/rdr/JavaInStream.java b/java/com/tigervnc/rdr/JavaInStream.java new file mode 100644 index 00000000..ce8efddc --- /dev/null +++ b/java/com/tigervnc/rdr/JavaInStream.java @@ -0,0 +1,148 @@ +/* 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 JavaInStream reads from a java.io.InputStream +// + +package com.tigervnc.rdr; + +public class JavaInStream extends InStream { + + static final int defaultBufSize = 8192; + static final int minBulkSize = 1024; + + public JavaInStream(java.io.InputStream jis_, int bufSize_) { + jis = jis_; + bufSize = bufSize_; + b = new byte[bufSize]; + ptr = end = offset = 0; + timeWaitedIn100us = 5; + timedKbits = 0; + } + + public JavaInStream(java.io.InputStream jis_) { this(jis_, defaultBufSize); } + + public void readBytes(byte[] data, int dataPtr, int length) { + if (length < minBulkSize) { + super.readBytes(data, dataPtr, length); + return; + } + + int n = end - ptr; + if (n > length) n = length; + + System.arraycopy(b, ptr, data, dataPtr, n); + dataPtr += n; + length -= n; + ptr += n; + + while (length > 0) { + n = read(data, dataPtr, length); + dataPtr += n; + length -= n; + offset += n; + } + } + + public int pos() { return offset + ptr; } + + 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; } + + protected int overrun(int itemSize, int nItems, boolean wait) { + if (itemSize > bufSize) + throw new Exception("JavaInStream overrun: max itemSize exceeded"); + + if (end - ptr != 0) + System.arraycopy(b, ptr, b, 0, end - ptr); + + offset += ptr; + end -= ptr; + ptr = 0; + + while (end < itemSize) { + int n = read(b, end, bufSize - end, wait); + end += n; + } + + if (itemSize * nItems > end) + nItems = end / itemSize; + + return nItems; + } + + private int read(byte[] buf, int bufPtr, int len, boolean wait) { + try { + long before = 0; + if (timing) + before = System.currentTimeMillis(); + + int n = jis.read(buf, bufPtr, len); + if (n < 0) throw new EndOfStream(); + + if (timing) { + long after = System.currentTimeMillis(); + long newTimeWaited = (after - before) * 10; + int newKbits = n * 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; + } + + return n; + + } catch (java.io.IOException e) { + throw new IOException(e); + } + } + private int read(byte[] buf, int bufPtr, int len) { return read(buf, bufPtr, len, true); } + + private java.io.InputStream jis; + private int offset; + private int bufSize; + + boolean timing; + long timeWaitedIn100us; + long timedKbits; +} diff --git a/java/com/tigervnc/rdr/JavaOutStream.java b/java/com/tigervnc/rdr/JavaOutStream.java new file mode 100644 index 00000000..015f81f5 --- /dev/null +++ b/java/com/tigervnc/rdr/JavaOutStream.java @@ -0,0 +1,77 @@ +/* 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 JavaOutStream writes to a java.io.OutputStream +// + +package com.tigervnc.rdr; + +public class JavaOutStream extends OutStream { + + static final int defaultBufSize = 16384; + static final int minBulkSize = 1024; + + public JavaOutStream(java.io.OutputStream jos_, int bufSize_) { + jos = jos_; + bufSize = bufSize_; + b = new byte[bufSize]; + ptr = offset = start = 0; + end = start + bufSize; + } + + public JavaOutStream(java.io.OutputStream jos) { this(jos, defaultBufSize); } + + public int length() + { + return offset + ptr - start; + } + + public void flush() + { + int sentUpTo = start; + while (sentUpTo < ptr) { + try { + jos.write(b, sentUpTo, ptr - sentUpTo); + sentUpTo += ptr - sentUpTo; + offset += ptr - sentUpTo; + } catch (java.io.IOException e) { + throw new IOException(e); + } + } + ptr = start; + } + + protected int overrun(int itemSize, int nItems) + { + if (itemSize > bufSize) + throw new Exception("JavaOutStream overrun: max itemSize exceeded"); + + flush(); + + if (itemSize * nItems > end - ptr) + nItems = (end - ptr) / itemSize; + + return nItems; + } + + private java.io.OutputStream jos; + private int start; + private int offset; + private int bufSize; +} diff --git a/java/com/tigervnc/rdr/MemInStream.java b/java/com/tigervnc/rdr/MemInStream.java new file mode 100644 index 00000000..32911a3a --- /dev/null +++ b/java/com/tigervnc/rdr/MemInStream.java @@ -0,0 +1,34 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rdr; + +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, boolean wait) { + throw new EndOfStream(); + } +} diff --git a/java/com/tigervnc/rdr/MemOutStream.java b/java/com/tigervnc/rdr/MemOutStream.java new file mode 100644 index 00000000..b3040793 --- /dev/null +++ b/java/com/tigervnc/rdr/MemOutStream.java @@ -0,0 +1,53 @@ +/* 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 MemOutStream grows as needed when data is written to it. +// + +package com.tigervnc.rdr; + +public class MemOutStream extends OutStream { + + public MemOutStream(int len) { + b = new byte[len]; + ptr = 0; + end = len; + } + public MemOutStream() { this(1024); } + + public int length() { return ptr; } + public void clear() { ptr = 0; }; + public void reposition(int pos) { ptr = pos; } + + // overrun() either doubles the buffer or adds enough space for nItems of + // size itemSize bytes. + + protected int overrun(int itemSize, int nItems) { + int len = ptr + itemSize * nItems; + if (len < end * 2) + len = end * 2; + + byte[] newBuf = new byte[len]; + System.arraycopy(b, 0, newBuf, 0, ptr); + b = newBuf; + end = len; + + return nItems; + } +} diff --git a/java/com/tigervnc/rdr/OutStream.java b/java/com/tigervnc/rdr/OutStream.java new file mode 100644 index 00000000..0919453e --- /dev/null +++ b/java/com/tigervnc/rdr/OutStream.java @@ -0,0 +1,160 @@ +/* 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::OutStream marshalls data into a buffer stored in RDR (RFB Data +// Representation). +// + +package com.tigervnc.rdr; + +abstract public class OutStream { + + // check() ensures there is buffer space for at least one item of size + // itemSize bytes. Returns the number of items which fit (up to a maximum + // of nItems). + + public final int check(int itemSize, int nItems) { + 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) { + if (ptr + itemSize > end) + overrun(itemSize, 1); + } + + // writeU/SN() methods write unsigned and signed N-bit integers. + + public final void writeU8( int u) { check(1); b[ptr++] = (byte)u; } + public final void writeU16(int u) { check(2); b[ptr++] = (byte)(u >> 8); + b[ptr++] = (byte)u; } + public final void writeU32(int u) { check(4); b[ptr++] = (byte)(u >> 24); + b[ptr++] = (byte)(u >> 16); + b[ptr++] = (byte)(u >> 8); + b[ptr++] = (byte)u; } + + public final void writeS8( int s) { writeU8( s); } + public final void writeS16(int s) { writeU16(s); } + public final void writeS32(int s) { writeU32(s); } + + // writeCompactLength() writes 1..3 bytes representing length of the data + // following. This method is used by the Tight encoder. + + public final void writeCompactLength(int len) { + byte b = (byte)(len & 0x7F); + if (len <= 0x7F) { + writeU8(b); + } else { + writeU8(b | 0x80); + b = (byte)(len >> 7 & 0x7F); + if (len <= 0x3FFF) { + writeU8(b); + } else { + writeU8(b | 0x80); + writeU8(len >> 14 & 0xFF); + } + } + } + + // writeString() writes a string - a U32 length followed by the data. + + public final void writeString(String str) { + int len = str.length(); + writeU32(len); + try { + byte[] utf8str = str.getBytes("UTF8"); + writeBytes(utf8str, 0, len); + } catch(java.io.UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + + public final void pad(int bytes) { + while (bytes-- > 0) writeU8(0); + } + + public final void skip(int bytes) { + while (bytes > 0) { + int n = check(1, bytes); + ptr += n; + bytes -= n; + } + } + + // writeBytes() writes an exact number of bytes from an array at an offset. + + public void writeBytes(byte[] data, int dataPtr, int length) { + int dataEnd = dataPtr + length; + while (dataPtr < dataEnd) { + int n = check(1, dataEnd - dataPtr); + System.arraycopy(data, dataPtr, b, ptr, n); + ptr += n; + dataPtr += n; + } + } + + // writeOpaqueN() writes a quantity without byte-swapping. Because java has + // no byte-ordering, we just use big-endian. + + public final void writeOpaque8( int u) { writeU8( u); } + public final void writeOpaque16(int u) { writeU16(u); } + public final void writeOpaque32(int u) { writeU32(u); } + public final void writeOpaque24A(int u) { check(3); + b[ptr++] = (byte)(u >> 24); + b[ptr++] = (byte)(u >> 16); + b[ptr++] = (byte)(u >> 8); } + public final void writeOpaque24B(int u) { check(3); + b[ptr++] = (byte)(u >> 16); + b[ptr++] = (byte)(u >> 8); + b[ptr++] = (byte)u; } + + // length() returns the length of the stream. + + abstract public int length(); + + // flush() requests that the stream be flushed. + + public void flush() {} + + // 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 space. Returns + // the number of items which fit (up to a maximum of nItems). itemSize is + // supposed to be "small" (a few bytes). + + abstract protected int overrun(int itemSize, int nItems); + + protected OutStream() {} + protected byte[] b; + protected int ptr; + protected int end; +} diff --git a/java/com/tigervnc/rdr/ZlibInStream.java b/java/com/tigervnc/rdr/ZlibInStream.java new file mode 100644 index 00000000..62c45bd8 --- /dev/null +++ b/java/com/tigervnc/rdr/ZlibInStream.java @@ -0,0 +1,138 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// A ZlibInStream reads from a zlib.io.InputStream +// + +package com.tigervnc.rdr; +import com.jcraft.jzlib.*; + +public class ZlibInStream extends InStream { + + static final int defaultBufSize = 16384; + + public ZlibInStream(int bufSize_) + { + bufSize = bufSize_; + b = new byte[bufSize]; + bytesIn = offset = 0; + zs = new ZStream(); + zs.next_in = null; + zs.next_in_index = 0; + zs.avail_in = 0; + if (zs.inflateInit() != JZlib.Z_OK) { + zs = null; + throw new Exception("ZlinInStream: inflateInit failed"); + } + ptr = end = start = 0; + } + + public ZlibInStream() { this(defaultBufSize); } + + protected void finalize() throws Throwable { + b = null; + zs.inflateEnd(); + } + + public void setUnderlying(InStream is, int bytesIn_) + { + underlying = is; + bytesIn = bytesIn_; + ptr = end = start; + } + + public int pos() + { + return offset + ptr - start; + } + + public void reset() + { + ptr = end = start; + if (underlying == null) return; + + while (bytesIn > 0) { + decompress(true); + end = start; // throw away any data + } + underlying = null; + } + + protected int overrun(int itemSize, int nItems, boolean wait) + { + 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, start, end - ptr); + + offset += ptr - start; + end -= ptr - start; + ptr = start; + + while (end - ptr < itemSize) { + if (!decompress(wait)) + return 0; + } + + if (itemSize * nItems > end - ptr) + nItems = (end - ptr) / 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 boolean decompress(boolean wait) + { + zs.next_out = b; + zs.next_out_index = end; + zs.avail_out = start + bufSize - end; + + int n = underlying.check(1, 1, wait); + if (n == 0) return false; + zs.next_in = underlying.getbuf(); + zs.next_in_index = underlying.getptr(); + zs.avail_in = underlying.getend() - underlying.getptr(); + if (zs.avail_in > bytesIn) + zs.avail_in = bytesIn; + + int rc = zs.inflate(JZlib.Z_SYNC_FLUSH); + if (rc != JZlib.Z_OK) { + throw new Exception("ZlibInStream: inflate failed"); + } + + bytesIn -= zs.next_in_index - underlying.getptr(); + end = zs.next_out_index; + underlying.setptr(zs.next_in_index); + return true; + } + + private InStream underlying; + private int bufSize; + private int offset; + private com.jcraft.jzlib.ZStream zs; + private int bytesIn; + private int start; +} diff --git a/java/com/tigervnc/rfb/AliasParameter.java b/java/com/tigervnc/rfb/AliasParameter.java new file mode 100644 index 00000000..2570b877 --- /dev/null +++ b/java/com/tigervnc/rfb/AliasParameter.java @@ -0,0 +1,35 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class AliasParameter extends VoidParameter { + public AliasParameter(String name_, String desc_, VoidParameter v) { + super(name_, desc_); + param = v; + } + + public boolean setParam(String v) { return param.setParam(v); } + public boolean setParam() { return param.setParam(); } + + public String getDefaultStr() { return param.getDefaultStr(); } + public String getValueStr() { return param.getValueStr(); } + public boolean isBool() { return param.isBool(); } + + protected VoidParameter param; +} diff --git a/java/com/tigervnc/rfb/AuthFailureException.java b/java/com/tigervnc/rfb/AuthFailureException.java new file mode 100644 index 00000000..35fabef0 --- /dev/null +++ b/java/com/tigervnc/rfb/AuthFailureException.java @@ -0,0 +1,23 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class AuthFailureException extends Exception { + public AuthFailureException(String s) { super(s); } +} diff --git a/java/com/tigervnc/rfb/BoolParameter.java b/java/com/tigervnc/rfb/BoolParameter.java new file mode 100644 index 00000000..06c6ed79 --- /dev/null +++ b/java/com/tigervnc/rfb/BoolParameter.java @@ -0,0 +1,51 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class BoolParameter extends VoidParameter { + public BoolParameter(String name_, String desc_, boolean v) { + super(name_, desc_); + value = v; + defValue = v; + } + + public boolean setParam(String v) { + if (v.equals("1") || v.equalsIgnoreCase("on") || + v.equalsIgnoreCase("true") || v.equalsIgnoreCase("yes")) + value = true; + else if (v.equals("0") || v.equalsIgnoreCase("off") || + v.equalsIgnoreCase("false") || v.equalsIgnoreCase("no")) + value = false; + else + return false; + return true; + } + + public boolean setParam() { setParam(true); return true; } + public void setParam(boolean b) { value = b; } + + public String getDefaultStr() { return defValue ? "1" : "0"; } + public String getValueStr() { return value ? "1" : "0"; } + public boolean isBool() { return true; } + + final public boolean getValue() { return value; } + + protected boolean value; + protected boolean defValue; +} diff --git a/java/com/tigervnc/rfb/CConnection.java b/java/com/tigervnc/rfb/CConnection.java new file mode 100644 index 00000000..15e19896 --- /dev/null +++ b/java/com/tigervnc/rfb/CConnection.java @@ -0,0 +1,356 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import java.util.*; + +import com.tigervnc.rdr.*; + +abstract public class CConnection extends CMsgHandler { + + public CConnection() { + security = new SecurityClient(); + } + + // setStreams() sets the streams to be used for the connection. These must + // be set before initialiseProtocol() and processMsg() are called. The + // CSecurity object may call setStreams() again to provide alternative + // streams over which the RFB protocol is sent (i.e. encrypting/decrypting + // streams). Ownership of the streams remains with the caller + // (i.e. SConnection will not delete them). + public void setStreams(InStream is_, OutStream os_) { + is = is_; + os = os_; + } + + // initialiseProtocol() should be called once the streams and security + // types are set. Subsequently, processMsg() should be called whenever + // there is data to read on the InStream. + public void initialiseProtocol() { + state_ = RFBSTATE_PROTOCOL_VERSION; + } + + // processMsg() should be called whenever there is data to read on the + // InStream. You must have called initialiseProtocol() first. + public void processMsg() { + switch (state_) { + + case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break; + case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break; + case RFBSTATE_SECURITY: processSecurityMsg(); break; + case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break; + case RFBSTATE_INITIALISATION: processInitMsg(); break; + case RFBSTATE_NORMAL: reader_.readMsg(); break; + case RFBSTATE_UNINITIALISED: + throw new Exception("CConnection.processMsg: not initialised yet?"); + default: + throw new Exception("CConnection.processMsg: invalid state"); + } + } + + private void processVersionMsg() { + vlog.debug("reading protocol version"); + Boolean done = new Boolean(true); + if (!cp.readVersion(is, done)) { + state_ = RFBSTATE_INVALID; + throw new Exception("reading version failed: not an RFB server?"); + } + if (!done.booleanValue()) return; + + vlog.info("Server supports RFB protocol version "+cp.majorVersion+"."+ + cp.minorVersion); + + // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8 + if (cp.beforeVersion(3,3)) { + String msg = ("Server gave unsupported RFB protocol version "+ + cp.majorVersion+"."+cp.minorVersion); + vlog.error(msg); + state_ = RFBSTATE_INVALID; + throw new Exception(msg); + } else if (useProtocol3_3 || cp.beforeVersion(3,7)) { + cp.setVersion(3,3); + } else if (cp.afterVersion(3,8)) { + cp.setVersion(3,8); + } + + cp.writeVersion(os); + state_ = RFBSTATE_SECURITY_TYPES; + + vlog.info("Using RFB protocol version "+ + cp.majorVersion+"."+cp.minorVersion); + } + + private void processSecurityTypesMsg() { + vlog.info("processing security types message"); + + int secType = Security.secTypeInvalid; + + List<Integer> secTypes = new ArrayList<Integer>(); + secTypes = security.GetEnabledSecTypes(); + //for (Iterator i = secTypes.iterator(); i.hasNext(); ) + // vlog.info(((Integer)i.next()).toString()); + + if (cp.isVersion(3,3)) { + + // legacy 3.3 server may only offer "vnc authentication" or "none" + + secType = is.readU32(); + if (secType == Security.secTypeInvalid) { + throwConnFailedException(); + + } else if (secType == Security.secTypeNone || secType == Security.secTypeVncAuth) { + Iterator i; + for (i = secTypes.iterator(); i.hasNext(); ) { + int refType = (Integer)i.next(); + if (refType == secType) { + secType = refType; + break; + } + if (!i.hasNext()) + secType = Security.secTypeInvalid; + } + + } else { + vlog.error("Unknown 3.3 security type "+secType); + throw new Exception("Unknown 3.3 security type"); + } + + } else { + + // 3.7 server will offer us a list + + int nServerSecTypes = is.readU8(); + if (nServerSecTypes == 0) + throwConnFailedException(); + + for (int i = 0; i < nServerSecTypes; i++) { + int serverSecType = is.readU8(); + vlog.info("Server offers security type "+ + Security.secTypeName(serverSecType)+"("+serverSecType+")"); + + /* + * Use the first type sent by server which matches client's type. + * It means server's order specifies priority. + */ + if (secType == Security.secTypeInvalid) { + for (Iterator j = secTypes.iterator(); j.hasNext(); ) { + int refType = (Integer)j.next(); + if (refType == serverSecType) { + secType = refType; + break; + } + } + } + } + + // Inform the server of our decision + if (secType != Security.secTypeInvalid) { + os.writeU8(secType); + os.flush(); + vlog.info("Choosing security type "+Security.secTypeName(secType)+ + "("+secType+")"); + } + } + + if (secType == Security.secTypeInvalid) { + state_ = RFBSTATE_INVALID; + vlog.error("No matching security types"); + throw new Exception("No matching security types"); + } + + state_ = RFBSTATE_SECURITY; + csecurity = security.GetCSecurity(secType); + processSecurityMsg(); + } + + private void processSecurityMsg() { + vlog.debug("processing security message"); + if (csecurity.processMsg(this)) { + state_ = RFBSTATE_SECURITY_RESULT; + processSecurityResultMsg(); + } + } + + private void processSecurityResultMsg() { + vlog.debug("processing security result message"); + int result; + if (cp.beforeVersion(3,8) && csecurity.getType() == Security.secTypeNone) { + result = Security.secResultOK; + } else { + if (!is.checkNoWait(1)) return; + result = is.readU32(); + } + switch (result) { + case Security.secResultOK: + securityCompleted(); + return; + case Security.secResultFailed: + vlog.debug("auth failed"); + break; + case Security.secResultTooMany: + vlog.debug("auth failed - too many tries"); + break; + default: + throw new Exception("Unknown security result from server"); + } + String reason; + if (cp.beforeVersion(3,8)) + reason = "Authentication failure"; + else + reason = is.readString(); + state_ = RFBSTATE_INVALID; + throw new AuthFailureException(reason); + } + + private void processInitMsg() { + vlog.debug("reading server initialisation"); + reader_.readServerInit(); + } + + private void throwConnFailedException() { + state_ = RFBSTATE_INVALID; + String reason; + reason = is.readString(); + throw new ConnFailedException(reason); + } + + private void securityCompleted() { + state_ = RFBSTATE_INITIALISATION; + reader_ = new CMsgReaderV3(this, is); + writer_ = new CMsgWriterV3(cp, os); + vlog.debug("Authentication success!"); + authSuccess(); + writer_.writeClientInit(shared); + } + + // Methods to initialise the connection + + // setServerName() is used to provide a unique(ish) name for the server to + // which we are connected. This might be the result of getPeerEndpoint on + // a TcpSocket, for example, or a host specified by DNS name & port. + // The serverName is used when verifying the Identity of a host (see RA2). + public void setServerName(String name) { + serverName = name; + } + + public void setServerPort(int port) { + serverPort = port; + } + + public void initSecTypes() { + nSecTypes = 0; + } + + // setShared sets the value of the shared flag which will be sent to the + // server upon initialisation. + public void setShared(boolean s) { shared = s; } + + // setProtocol3_3 configures whether or not the CConnection should + // only ever support protocol version 3.3 + public void setProtocol3_3(boolean s) { useProtocol3_3 = s; } + + // Methods to be overridden in a derived class + + // getCSecurity() gets the CSecurity object for the given type. The type + // is guaranteed to be one of the secTypes passed in to addSecType(). The + // CSecurity object's destroy() method will be called by the CConnection + // from its destructor. + //abstract public CSecurity getCSecurity(int secType); + + // getCurrentCSecurity() gets the CSecurity instance used for this + // connection. + //public CSecurity getCurrentCSecurity() { return security; } + + // setClientSecTypeOrder() determines whether the client should obey the + // server's security type preference, by picking the first server security + // type that the client supports, or whether it should pick the first type + // that the server supports, from the client-supported list of types. + public void setClientSecTypeOrder( boolean csto ) { + clientSecTypeOrder = csto; + } + + // authSuccess() is called when authentication has succeeded. + public void authSuccess() {} + + // serverInit() is called when the ServerInit message is received. The + // derived class must call on to CConnection::serverInit(). + public void serverInit() { + state_ = RFBSTATE_NORMAL; + vlog.debug("initialisation done"); + } + + // Other methods + + public CMsgReaderV3 reader() { return reader_; } + public CMsgWriterV3 writer() { return writer_; } + + public InStream getInStream() { return is; } + public OutStream getOutStream() { return os; } + + public String getServerName() { return serverName; } + public int getServerPort() { return serverPort; } + + public static final int RFBSTATE_UNINITIALISED = 0; + public static final int RFBSTATE_PROTOCOL_VERSION = 1; + public static final int RFBSTATE_SECURITY_TYPES = 2; + public static final int RFBSTATE_SECURITY = 3; + public static final int RFBSTATE_SECURITY_RESULT = 4; + public static final int RFBSTATE_INITIALISATION = 5; + public static final int RFBSTATE_NORMAL = 6; + public static final int RFBSTATE_INVALID = 7; + + public int state() { return state_; } + + protected void setState(int s) { state_ = s; } + + private void throwAuthFailureException() { + String reason; + vlog.debug("state="+state()+", ver="+cp.majorVersion+"."+cp.minorVersion); + if (state() == RFBSTATE_SECURITY_RESULT && !cp.beforeVersion(3,8)) { + reason = is.readString(); + } else { + reason = "Authentication failure"; + } + state_ = RFBSTATE_INVALID; + vlog.error(reason); + throw new AuthFailureException(reason); + } + + InStream is = null; + OutStream os = null; + CMsgReaderV3 reader_ = null; + CMsgWriterV3 writer_ = null; + boolean shared = false; + public CSecurity csecurity; + public SecurityClient security; + public static final int maxSecTypes = 8; + int nSecTypes; + int[] secTypes; + int state_ = RFBSTATE_UNINITIALISED; + String serverName; + int serverPort; + boolean useProtocol3_3 = false; + boolean clientSecTypeOrder; + public static java.net.Socket sock; + + public static java.net.Socket getSocket() { return sock; } + public static void setSocket(java.net.Socket sock_) { sock = sock_; } + + static LogWriter vlog = new LogWriter("CConnection"); +} diff --git a/java/com/tigervnc/rfb/CMsgHandler.java b/java/com/tigervnc/rfb/CMsgHandler.java new file mode 100644 index 00000000..81fd2a1b --- /dev/null +++ b/java/com/tigervnc/rfb/CMsgHandler.java @@ -0,0 +1,88 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// CMsgHandler +// + +package com.tigervnc.rfb; + +public class CMsgHandler { + + public CMsgHandler() { + cp = new ConnParams(); + } + + public void setDesktopSize(int width, int height) + { + cp.width = width; + cp.height = height; + } + + public void setExtendedDesktopSize(int reason, int result, + int width, int height, + ScreenSet layout) + { + cp.supportsSetDesktopSize = true; + + if ((reason == screenTypes.reasonClient) && (result != screenTypes.resultSuccess)) + return; + + if (!layout.validate(width, height)) + vlog.error("Server sent us an invalid screen layout"); + + cp.width = width; + cp.height = height; + cp.screenLayout = layout; + } + + public void setPixelFormat(PixelFormat pf) + { + cp.setPF(pf); + } + + public void setName(String name) + { + cp.setName(name); + } + + public void clientRedirect(int port, String host, + String x509subject) {} + + public void setCursor(int width, int height, Point hotspot, + int[] data, byte[] mask) {} + public void serverInit() {} + + public void framebufferUpdateStart() {} + public void framebufferUpdateEnd() {} + public void beginRect(Rect r, int encoding) {} + public void endRect(Rect r, int encoding) {} + + public void setColourMapEntries(int firstColour, int nColours, + int[] rgbs) { } + public void bell() {} + public void serverCutText(String str, int len) {} + + public void fillRect(Rect r, int pix) {} + public void imageRect(Rect r, int[] pixels) {} + public void copyRect(Rect r, int srcX, int srcY) {} + + public ConnParams cp; + + static LogWriter vlog = new LogWriter("CMsgHandler"); +} diff --git a/java/com/tigervnc/rfb/CMsgReader.java b/java/com/tigervnc/rfb/CMsgReader.java new file mode 100644 index 00000000..41230560 --- /dev/null +++ b/java/com/tigervnc/rfb/CMsgReader.java @@ -0,0 +1,179 @@ +/* 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. + */ + +// +// CMsgReader - class for reading RFB messages on the client side +// (i.e. messages from server to client). +// + +package com.tigervnc.rfb; + +import com.tigervnc.rdr.*; + +abstract public class CMsgReader { + + protected CMsgReader(CMsgHandler handler_, InStream is_) + { + imageBufIdealSize = 0; + handler = handler_; + is = is_; + imageBuf = null; + imageBufSize = 0; + decoders = new Decoder[Encodings.encodingMax+1]; + } + + protected void readSetColourMapEntries() + { + is.skip(1); + int firstColour = is.readU16(); + int nColours = is.readU16(); + int[] rgbs = new int[nColours * 3]; + for (int i = 0; i < nColours * 3; i++) + rgbs[i] = is.readU16(); + handler.setColourMapEntries(firstColour, nColours, rgbs); + } + + protected void readBell() + { + handler.bell(); + } + + protected void readServerCutText() + { + is.skip(3); + int len = is.readU32(); + if (len > 256*1024) { + is.skip(len); + vlog.error("cut text too long ("+len+" bytes) - ignoring"); + return; + } + byte[] buf = new byte[len]; + is.readBytes(buf, 0, len); + String str = new String(); + try { + str = new String(buf,"UTF8"); + } catch(java.io.UnsupportedEncodingException e) { + e.printStackTrace(); + } + handler.serverCutText(str, len); + } + + protected void readFramebufferUpdateStart() + { + handler.framebufferUpdateStart(); + } + + protected void readFramebufferUpdateEnd() + { + handler.framebufferUpdateEnd(); + } + + protected void readRect(Rect r, int encoding) + { + if ((r.br.x > handler.cp.width) || (r.br.y > handler.cp.height)) { + vlog.error("Rect too big: "+r.width()+"x"+r.height()+" at "+ + r.tl.x+","+r.tl.y+" exceeds "+handler.cp.width+"x"+ + handler.cp.height); + throw new Exception("Rect too big"); + } + + if (r.is_empty()) + vlog.error("Ignoring zero size rect"); + + handler.beginRect(r, encoding); + + if (encoding == Encodings.encodingCopyRect) { + readCopyRect(r); + } else { + + if (decoders[encoding] == null) { + decoders[encoding] = Decoder.createDecoder(encoding, this); + if (decoders[encoding] == null) { + vlog.error("Unknown rect encoding "+encoding); + throw new Exception("Unknown rect encoding"); + } + } + decoders[encoding].readRect(r, handler); + } + + handler.endRect(r, encoding); + } + + protected void readCopyRect(Rect r) + { + int srcX = is.readU16(); + int srcY = is.readU16(); + handler.copyRect(r, srcX, srcY); + } + + protected void readSetCursor(int width, int height, Point hotspot) + { + int data_len = width * height; + int mask_len = ((width+7)/8) * height; + int[] data = new int[data_len]; + byte[] mask = new byte[mask_len]; + + is.readPixels(data, data_len, (handler.cp.pf().bpp/8), handler.cp.pf().bigEndian); + is.readBytes(mask, 0, mask_len); + + handler.setCursor(width, height, hotspot, data, mask); + } + + public int[] getImageBuf(int required) { return getImageBuf(required, 0, 0); } + + public int[] getImageBuf(int required, int requested, int nPixels) + { + int requiredBytes = required * (handler.cp.pf().bpp / 8); + int requestedBytes = requested * (handler.cp.pf().bpp / 8); + int size = requestedBytes; + if (size > imageBufIdealSize) size = imageBufIdealSize; + + if (size < requiredBytes) + size = requiredBytes; + + if (imageBufSize < size) { + imageBufSize = size; + imageBuf = new int[imageBufSize]; + } + if (nPixels != 0) + nPixels = imageBufSize / (handler.cp.pf().bpp / 8); + return imageBuf; + } + + public final int bpp() + { + return handler.cp.pf().bpp; + } + + abstract public void readServerInit(); + + // readMsg() reads a message, calling the handler as appropriate. + abstract public void readMsg(); + + public InStream getInStream() { return is; } + + public int imageBufIdealSize; + + protected CMsgHandler handler; + protected InStream is; + protected Decoder[] decoders; + protected int[] imageBuf; + protected int imageBufSize; + + static LogWriter vlog = new LogWriter("CMsgReader"); +} diff --git a/java/com/tigervnc/rfb/CMsgReaderV3.java b/java/com/tigervnc/rfb/CMsgReaderV3.java new file mode 100644 index 00000000..6d9e254b --- /dev/null +++ b/java/com/tigervnc/rfb/CMsgReaderV3.java @@ -0,0 +1,155 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import com.tigervnc.rdr.*; + +public class CMsgReaderV3 extends CMsgReader { + + public CMsgReaderV3(CMsgHandler handler_, InStream is_) + { + super(handler_, is_); + nUpdateRectsLeft = 0; + } + + public void readServerInit() + { + int width = is.readU16(); + int height = is.readU16(); + handler.setDesktopSize(width, height); + PixelFormat pf = new PixelFormat(); + pf.read(is); + handler.setPixelFormat(pf); + String name = is.readString(); + handler.setName(name); + handler.serverInit(); + } + + public void readMsg() + { + if (nUpdateRectsLeft == 0) { + + int type = is.readU8(); + switch (type) { + case MsgTypes.msgTypeFramebufferUpdate: readFramebufferUpdate(); break; + case MsgTypes.msgTypeSetColourMapEntries: readSetColourMapEntries(); break; + case MsgTypes.msgTypeBell: readBell(); break; + case MsgTypes.msgTypeServerCutText: readServerCutText(); break; + default: + vlog.error("unknown message type "+type); + throw new Exception("unknown message type"); + } + + } else { + + int x = is.readU16(); + int y = is.readU16(); + int w = is.readU16(); + int h = is.readU16(); + int encoding = is.readS32(); + + switch (encoding) { + case Encodings.pseudoEncodingDesktopSize: + handler.setDesktopSize(w, h); + break; + case Encodings.pseudoEncodingExtendedDesktopSize: + readExtendedDesktopSize(x, y, w, h); + break; + case Encodings.pseudoEncodingDesktopName: + readSetDesktopName(x, y, w, h); + break; + case Encodings.pseudoEncodingCursor: + readSetCursor(w, h, new Point(x,y)); + break; + case Encodings.pseudoEncodingLastRect: + nUpdateRectsLeft = 1; // this rectangle is the last one + break; + case Encodings.pseudoEncodingClientRedirect: + readClientRedirect(x, y, w, h); + break; + default: + readRect(new Rect(x, y, x+w, y+h), encoding); + break; + } + + nUpdateRectsLeft--; + if (nUpdateRectsLeft == 0) handler.framebufferUpdateEnd(); + } + } + + void readFramebufferUpdate() + { + is.skip(1); + nUpdateRectsLeft = is.readU16(); + handler.framebufferUpdateStart(); + } + + void readSetDesktopName(int x, int y, int w, int h) + { + String name = is.readString(); + + if (x != 0 || y != 0 || w != 0 || h != 0) { + vlog.error("Ignoring DesktopName rect with non-zero position/size"); + } else { + handler.setName(name); + } + + } + + void readExtendedDesktopSize(int x, int y, int w, int h) + { + int screens, i; + int id, flags; + int sx, sy, sw, sh; + ScreenSet layout = new ScreenSet(); + + screens = is.readU8(); + is.skip(3); + + for (i = 0;i < screens;i++) { + id = is.readU32(); + sx = is.readU16(); + sy = is.readU16(); + sw = is.readU16(); + sh = is.readU16(); + flags = is.readU32(); + + layout.add_screen(new Screen(id, sx, sy, sw, sh, flags)); + } + + handler.setExtendedDesktopSize(x, y, w, h, layout); + } + + void readClientRedirect(int x, int y, int w, int h) + { + int port = is.readU16(); + String host = is.readString(); + String x509subject = is.readString(); + + if (x != 0 || y != 0 || w != 0 || h != 0) { + vlog.error("Ignoring ClientRedirect rect with non-zero position/size"); + } else { + handler.clientRedirect(port, host, x509subject); + } + } + + int nUpdateRectsLeft; + + static LogWriter vlog = new LogWriter("CMsgReaderV3"); +} diff --git a/java/com/tigervnc/rfb/CMsgWriter.java b/java/com/tigervnc/rfb/CMsgWriter.java new file mode 100644 index 00000000..7cafddde --- /dev/null +++ b/java/com/tigervnc/rfb/CMsgWriter.java @@ -0,0 +1,171 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import com.tigervnc.rdr.*; + +abstract public class CMsgWriter { + + abstract public void writeClientInit(boolean shared); + + public void writeSetPixelFormat(PixelFormat pf) + { + startMsg(MsgTypes.msgTypeSetPixelFormat); + os.pad(3); + pf.write(os); + endMsg(); + } + + public void writeSetEncodings(int nEncodings, int[] encodings) + { + startMsg(MsgTypes.msgTypeSetEncodings); + os.skip(1); + os.writeU16(nEncodings); + for (int i = 0; i < nEncodings; i++) + os.writeU32(encodings[i]); + endMsg(); + } + + // Ask for encodings based on which decoders are supported. Assumes higher + // encoding numbers are more desirable. + + public void writeSetEncodings(int preferredEncoding, boolean useCopyRect) + { + int nEncodings = 0; + int[] encodings = new int[Encodings.encodingMax+3]; + if (cp.supportsLocalCursor) + encodings[nEncodings++] = Encodings.pseudoEncodingCursor; + if (cp.supportsDesktopResize) + encodings[nEncodings++] = Encodings.pseudoEncodingDesktopSize; + if (cp.supportsExtendedDesktopSize) + encodings[nEncodings++] = Encodings.pseudoEncodingExtendedDesktopSize; + if (cp.supportsDesktopRename) + encodings[nEncodings++] = Encodings.pseudoEncodingDesktopName; + if (cp.supportsClientRedirect) + encodings[nEncodings++] = Encodings.pseudoEncodingClientRedirect; + if (Decoder.supported(preferredEncoding)) { + encodings[nEncodings++] = preferredEncoding; + } + if (useCopyRect) { + encodings[nEncodings++] = Encodings.encodingCopyRect; + } + + /* + * Prefer encodings in this order: + * + * Tight, ZRLE, Hextile, * + */ + + if ((preferredEncoding != Encodings.encodingTight) && + Decoder.supported(Encodings.encodingTight)) + encodings[nEncodings++] = Encodings.encodingTight; + + if ((preferredEncoding != Encodings.encodingZRLE) && + Decoder.supported(Encodings.encodingZRLE)) + encodings[nEncodings++] = Encodings.encodingZRLE; + + if ((preferredEncoding != Encodings.encodingHextile) && + Decoder.supported(Encodings.encodingHextile)) + encodings[nEncodings++] = Encodings.encodingHextile; + + // Remaining encodings + for (int i = Encodings.encodingMax; i >= 0; i--) { + switch (i) { + case Encodings.encodingTight: + case Encodings.encodingZRLE: + case Encodings.encodingHextile: + break; + default: + if ((i != preferredEncoding) && Decoder.supported(i)) + encodings[nEncodings++] = i; + } + } + + encodings[nEncodings++] = Encodings.pseudoEncodingLastRect; + if (cp.customCompressLevel && cp.compressLevel >= 0 && cp.compressLevel <= 9) + encodings[nEncodings++] = Encodings.pseudoEncodingCompressLevel0 + cp.compressLevel; + if (!cp.noJpeg && cp.qualityLevel >= 0 && cp.qualityLevel <= 9) + encodings[nEncodings++] = Encodings.pseudoEncodingQualityLevel0 + cp.qualityLevel; + + writeSetEncodings(nEncodings, encodings); + } + + public void writeFramebufferUpdateRequest(Rect r, boolean incremental) + { + startMsg(MsgTypes.msgTypeFramebufferUpdateRequest); + os.writeU8(incremental?1:0); + os.writeU16(r.tl.x); + os.writeU16(r.tl.y); + os.writeU16(r.width()); + os.writeU16(r.height()); + endMsg(); + } + + public void writeKeyEvent(int key, boolean down) + { + startMsg(MsgTypes.msgTypeKeyEvent); + os.writeU8(down?1:0); + os.pad(2); + os.writeU32(key); + endMsg(); + } + + public void writePointerEvent(Point pos, int buttonMask) + { + Point p = new Point(pos.x,pos.y); + if (p.x < 0) p.x = 0; + if (p.y < 0) p.y = 0; + if (p.x >= cp.width) p.x = cp.width - 1; + if (p.y >= cp.height) p.y = cp.height - 1; + + startMsg(MsgTypes.msgTypePointerEvent); + os.writeU8(buttonMask); + os.writeU16(p.x); + os.writeU16(p.y); + endMsg(); + } + + public void writeClientCutText(String str, int len) + { + startMsg(MsgTypes.msgTypeClientCutText); + os.pad(3); + os.writeU32(len); + try { + byte[] utf8str = str.getBytes("UTF8"); + os.writeBytes(utf8str, 0, len); + } catch(java.io.UnsupportedEncodingException e) { + e.printStackTrace(); + } + endMsg(); + } + + abstract public void startMsg(int type); + abstract public void endMsg(); + + public void setOutStream(OutStream os_) { os = os_; } + + ConnParams getConnParams() { return cp; } + OutStream getOutStream() { return os; } + + protected CMsgWriter(ConnParams cp_, OutStream os_) {cp = cp_; os = os_;} + + ConnParams cp; + OutStream os; + static LogWriter vlog = new LogWriter("CMsgWriter"); +} diff --git a/java/com/tigervnc/rfb/CMsgWriterV3.java b/java/com/tigervnc/rfb/CMsgWriterV3.java new file mode 100644 index 00000000..ec30a826 --- /dev/null +++ b/java/com/tigervnc/rfb/CMsgWriterV3.java @@ -0,0 +1,68 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import com.tigervnc.rdr.*; +import java.util.*; + +public class CMsgWriterV3 extends CMsgWriter { + + public CMsgWriterV3(ConnParams cp_, OutStream os_) { super(cp_, os_); } + + public void writeClientInit(boolean shared) { + os.writeU8(shared?1:0); + endMsg(); + } + + public void startMsg(int type) { + os.writeU8(type); + } + + public void endMsg() { + os.flush(); + } + + public void writeSetDesktopSize(int width, int height, + ScreenSet layout) + { + if (!cp.supportsSetDesktopSize) + throw new Exception("Server does not support SetDesktopSize"); + + startMsg(MsgTypes.msgTypeSetDesktopSize); + os.pad(1); + + os.writeU16(width); + os.writeU16(height); + + os.writeU8(layout.num_screens()); + os.pad(1); + + for (Iterator iter = layout.screens.iterator(); iter.hasNext(); ) { + Screen refScreen = (Screen)iter.next(); + os.writeU32(refScreen.id); + os.writeU16(refScreen.dimensions.tl.x); + os.writeU16(refScreen.dimensions.tl.y); + os.writeU16(refScreen.dimensions.width()); + os.writeU16(refScreen.dimensions.height()); + os.writeU32(refScreen.flags); + } + + endMsg(); + } +} diff --git a/java/com/tigervnc/rfb/CSecurity.java b/java/com/tigervnc/rfb/CSecurity.java new file mode 100644 index 00000000..e5b300f6 --- /dev/null +++ b/java/com/tigervnc/rfb/CSecurity.java @@ -0,0 +1,46 @@ +/* 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. + */ + +// +// CSecurity - class on the client side for handling security handshaking. A +// derived class for a particular security type overrides the processMsg() +// method. processMsg() is called first when the security type has been +// decided on, and will keep being called whenever there is data to read from +// the server until either it returns 0, indicating authentication/security +// failure, or it returns 1, to indicate success. A return value of 2 +// (actually anything other than 0 or 1) indicates that it should be called +// back when there is more data to read. +// +// Note that the first time processMsg() is called, there is no guarantee that +// there is any data to read from the CConnection's InStream, but subsequent +// calls guarantee there is at least one byte which can be read without +// blocking. + +package com.tigervnc.rfb; + +abstract public class CSecurity { + abstract public boolean processMsg(CConnection cc); + abstract public int getType(); + abstract public String description(); + + /* + * Use variable directly instead of dumb get/set methods. + * It MUST be set by viewer. + */ + static UserPasswdGetter upg; +} diff --git a/java/com/tigervnc/rfb/CSecurityIdent.java b/java/com/tigervnc/rfb/CSecurityIdent.java new file mode 100644 index 00000000..6523e411 --- /dev/null +++ b/java/com/tigervnc/rfb/CSecurityIdent.java @@ -0,0 +1,58 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import java.io.IOException; + +import com.tigervnc.rdr.*; +import com.tigervnc.vncviewer.*; + +public class CSecurityIdent extends CSecurity { + + public CSecurityIdent() { } + + public boolean processMsg(CConnection cc) { + InStream is = cc.getInStream(); + OutStream os = cc.getOutStream(); + + StringBuffer username = new StringBuffer(); + + CConn.upg.getUserPasswd(username, null); + + // Return the response to the server + os.writeU32(username.length()); + try { + byte[] utf8str = username.toString().getBytes("UTF8"); + os.writeBytes(utf8str, 0, username.length()); + } catch(java.io.UnsupportedEncodingException e) { + e.printStackTrace(); + } + os.flush(); + return true; + } + + public int getType() { return Security.secTypeIdent; } + + java.net.Socket sock; + UserPasswdGetter upg; + + static LogWriter vlog = new LogWriter("Ident"); + public String description() { return "No Encryption"; } + +} diff --git a/java/com/tigervnc/rfb/CSecurityManaged.java b/java/com/tigervnc/rfb/CSecurityManaged.java new file mode 100644 index 00000000..2461682d --- /dev/null +++ b/java/com/tigervnc/rfb/CSecurityManaged.java @@ -0,0 +1,76 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import java.io.IOException; + +import com.tigervnc.rdr.*; +import com.tigervnc.vncviewer.*; + +public class CSecurityManaged extends CSecurity { + + public CSecurityManaged() { } + + public boolean processMsg(CConnection cc) { + InStream is = cc.getInStream(); + OutStream os = cc.getOutStream(); + + StringBuffer username = new StringBuffer(); + + CConn.upg.getUserPasswd(username, null); + + // Return the response to the server + os.writeU8(username.length()); + try { + byte[] utf8str = username.toString().getBytes("UTF8"); + os.writeBytes(utf8str, 0, username.length()); + } catch(java.io.UnsupportedEncodingException e) { + e.printStackTrace(); + } + os.flush(); + int serverPort = is.readU16(); + //if (serverPort==0) { return true; }; + String serverName = cc.getServerName(); + vlog.debug("Redirected to "+serverName+" port "+serverPort); + try { + CConn.getSocket().close(); + cc.setServerPort(serverPort); + sock = new java.net.Socket(serverName, serverPort); + sock.setTcpNoDelay(true); + sock.setTrafficClass(0x10); + CConn.setSocket(sock); + vlog.debug("connected to host "+serverName+" port "+serverPort); + cc.setStreams(new JavaInStream(sock.getInputStream()), + new JavaOutStream(sock.getOutputStream())); + cc.initialiseProtocol(); + } catch (java.io.IOException e) { + e.printStackTrace(); + } + return false; + } + + public int getType() { return Security.secTypeManaged; } + + java.net.Socket sock; + UserPasswdGetter upg; + + static LogWriter vlog = new LogWriter("Managed"); + public String description() { return "No Encryption"; } + +} diff --git a/java/com/tigervnc/rfb/CSecurityNone.java b/java/com/tigervnc/rfb/CSecurityNone.java new file mode 100644 index 00000000..e31056da --- /dev/null +++ b/java/com/tigervnc/rfb/CSecurityNone.java @@ -0,0 +1,27 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class CSecurityNone extends CSecurity { + + public boolean processMsg(CConnection cc) { return true; } + public int getType() { return Security.secTypeNone; } + public String description() { return "No Encryption"; } + static LogWriter vlog = new LogWriter("CSecurityNone"); +} diff --git a/java/com/tigervnc/rfb/CSecurityPlain.java b/java/com/tigervnc/rfb/CSecurityPlain.java new file mode 100644 index 00000000..707915cc --- /dev/null +++ b/java/com/tigervnc/rfb/CSecurityPlain.java @@ -0,0 +1,57 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import com.tigervnc.rdr.*; +import com.tigervnc.vncviewer.*; + +public class CSecurityPlain extends CSecurity { + + public CSecurityPlain() { } + + public boolean processMsg(CConnection cc) + { + OutStream os = cc.getOutStream(); + + StringBuffer username = new StringBuffer(); + StringBuffer password = new StringBuffer(); + + CConn.upg.getUserPasswd(username, password); + + // Return the response to the server + os.writeU32(username.length()); + os.writeU32(password.length()); + byte[] utf8str; + try { + utf8str = username.toString().getBytes("UTF8"); + os.writeBytes(utf8str, 0, username.length()); + utf8str = password.toString().getBytes("UTF8"); + os.writeBytes(utf8str, 0, password.length()); + } catch(java.io.UnsupportedEncodingException e) { + e.printStackTrace(); + } + os.flush(); + return true; + } + + public int getType() { return Security.secTypePlain; } + public String description() { return "ask for username and password"; } + + static LogWriter vlog = new LogWriter("Plain"); +} diff --git a/java/com/tigervnc/rfb/CSecurityStack.java b/java/com/tigervnc/rfb/CSecurityStack.java new file mode 100644 index 00000000..5886268e --- /dev/null +++ b/java/com/tigervnc/rfb/CSecurityStack.java @@ -0,0 +1,69 @@ +/* Copyright (C) 2005 Martin Koegler + * Copyright (C) 2010 TigerVNC Team + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class CSecurityStack extends CSecurity { + + public CSecurityStack(int Type, String Name, CSecurity s0, + CSecurity s1) + { + name = Name; + type = Type; + state = 0; + state0 = s0; + state1 = s1; + } + + public boolean processMsg(CConnection cc) + { + boolean res = true; + if (state == 0) { + if (state0 != null) + res = state0.processMsg(cc); + + if (!res) + return res; + + state++; + } + + if (state == 1) { + if(state1 != null) + res = state1.processMsg(cc); + + if(!res) + return res; + + state++; + } + + return res; + } + + public final int getType() { return type; } + public final String description() { return name; } + + private int state; + private CSecurity state0; + private CSecurity state1; + private String name; + private int type; + +} diff --git a/java/com/tigervnc/rfb/CSecurityTLS.java b/java/com/tigervnc/rfb/CSecurityTLS.java new file mode 100644 index 00000000..73eb6199 --- /dev/null +++ b/java/com/tigervnc/rfb/CSecurityTLS.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import javax.net.ssl.*; +import java.security.*; +import java.security.cert.*; +import java.security.KeyStore; +import java.io.File; +import java.io.InputStream; +import java.io.FileInputStream; +import java.util.ArrayList; +import java.util.Collection; +import javax.swing.JOptionPane; + +import com.tigervnc.vncviewer.UserPrefs; +import com.tigervnc.rdr.*; + +public class CSecurityTLS extends CSecurity { + + public static StringParameter x509ca + = new StringParameter("x509ca", + "X509 CA certificate", ""); + public static StringParameter x509crl + = new StringParameter("x509crl", + "X509 CRL file", ""); + + private void initGlobal() + { + try { + SSLSocketFactory sslfactory; + SSLContext ctx = SSLContext.getInstance("TLS"); + if (anon) { + ctx.init(null, null, null); + } else { + TrustManager[] myTM = new TrustManager[] { + new MyX509TrustManager() + }; + ctx.init (null, myTM, null); + } + sslfactory = ctx.getSocketFactory(); + try { + ssl = (SSLSocket)sslfactory.createSocket(cc.sock, + cc.sock.getInetAddress().getHostName(), + cc.sock.getPort(), true); + } catch (java.io.IOException e) { + throw new Exception(e.toString()); + } + + if (anon) { + String[] supported; + ArrayList<String> enabled = new ArrayList<String>(); + + supported = ssl.getSupportedCipherSuites(); + + for (int i = 0; i < supported.length; i++) + if (supported[i].matches("TLS_DH_anon.*")) + enabled.add(supported[i]); + + ssl.setEnabledCipherSuites(enabled.toArray(new String[0])); + } else { + ssl.setEnabledCipherSuites(ssl.getSupportedCipherSuites()); + } + + ssl.setEnabledProtocols(new String[]{"SSLv3","TLSv1"}); + ssl.addHandshakeCompletedListener(new MyHandshakeListener()); + } + catch (java.security.GeneralSecurityException e) + { + vlog.error ("TLS handshake failed " + e.toString ()); + return; + } + } + + public CSecurityTLS(boolean _anon) + { + anon = _anon; + setDefaults(); + cafile = x509ca.getData(); + crlfile = x509crl.getData(); + } + + public static void setDefaults() + { + String homeDir = null; + + if ((homeDir=UserPrefs.getHomeDir()) == null) { + vlog.error("Could not obtain VNC home directory path"); + return; + } + + String vnchomedir = homeDir+UserPrefs.getFileSeparator()+".vnc"+ + UserPrefs.getFileSeparator(); + String caDefault = new String(vnchomedir+"x509_ca.pem"); + String crlDefault = new String(vnchomedir+"x509_crl.pem"); + + if (new File(caDefault).exists()) + x509ca.setDefaultStr(caDefault); + if (new File(crlDefault).exists()) + x509crl.setDefaultStr(crlDefault); + } + + public boolean processMsg(CConnection cc) { + is = cc.getInStream(); + os = cc.getOutStream(); + + initGlobal(); + + if (!is.checkNoWait(1)) + return false; + + if (is.readU8() == 0) { + int result = is.readU32(); + String reason; + if (result == Security.secResultFailed || + result == Security.secResultTooMany) + reason = is.readString(); + else + reason = new String("Authentication failure (protocol error)"); + throw new AuthFailureException(reason); + } + + // SSLSocket.getSession blocks until the handshake is complete + session = ssl.getSession(); + if (!session.isValid()) + throw new Exception("TLS Handshake failed!"); + + try { + cc.setStreams(new JavaInStream(ssl.getInputStream()), + new JavaOutStream(ssl.getOutputStream())); + } catch (java.io.IOException e) { + throw new Exception("Failed to set streams"); + } + + return true; + } + + class MyHandshakeListener implements HandshakeCompletedListener { + public void handshakeCompleted(HandshakeCompletedEvent e) { + vlog.info("Handshake succesful!"); + vlog.info("Using cipher suite: " + e.getCipherSuite()); + } + } + + class MyX509TrustManager implements X509TrustManager + { + + X509TrustManager tm; + + MyX509TrustManager() throws java.security.GeneralSecurityException + { + TrustManagerFactory tmf = + TrustManagerFactory.getInstance("PKIX"); + KeyStore ks = KeyStore.getInstance("JKS"); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + try { + ks.load(null, null); + File cacert = new File(cafile); + if (!cacert.exists() || !cacert.canRead()) + return; + InputStream caStream = new FileInputStream(cafile); + X509Certificate ca = (X509Certificate)cf.generateCertificate(caStream); + ks.setCertificateEntry("CA", ca); + PKIXBuilderParameters params = new PKIXBuilderParameters(ks, new X509CertSelector()); + File crlcert = new File(crlfile); + if (!crlcert.exists() || !crlcert.canRead()) { + params.setRevocationEnabled(false); + } else { + InputStream crlStream = new FileInputStream(crlfile); + Collection<? extends CRL> crls = cf.generateCRLs(crlStream); + CertStoreParameters csp = new CollectionCertStoreParameters(crls); + CertStore store = CertStore.getInstance("Collection", csp); + params.addCertStore(store); + params.setRevocationEnabled(true); + } + tmf.init(new CertPathTrustManagerParameters(params)); + } catch (java.io.FileNotFoundException e) { + vlog.error(e.toString()); + } catch (java.io.IOException e) { + vlog.error(e.toString()); + } + tm = (X509TrustManager)tmf.getTrustManagers()[0]; + } + + public void checkClientTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + tm.checkClientTrusted(chain, authType); + } + + public void checkServerTrusted(X509Certificate[] chain, String authType) + throws CertificateException + { + try { + tm.checkServerTrusted(chain, authType); + } catch (CertificateException e) { + Object[] answer = {"Proceed", "Exit"}; + int ret = JOptionPane.showOptionDialog(null, + e.getCause().getLocalizedMessage()+"\n"+ + "Continue connecting to this host?", + "Confirm certificate exception?", + JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, + null, answer, answer[0]); + if (ret == JOptionPane.NO_OPTION) + System.exit(1); + } catch (java.lang.Exception e) { + throw new Exception(e.toString()); + } + } + + public X509Certificate[] getAcceptedIssuers () + { + return tm.getAcceptedIssuers(); + } + } + + public final int getType() { return anon ? Security.secTypeTLSNone : Security.secTypeX509None; } + public final String description() + { return anon ? "TLS Encryption without VncAuth" : "X509 Encryption without VncAuth"; } + + + //protected void setParam(); + //protected void checkSession(); + protected CConnection cc; + + private boolean anon; + private SSLSession session; + private String cafile, crlfile; + private InStream is; + private OutStream os; + private SSLSocket ssl; + + static LogWriter vlog = new LogWriter("CSecurityTLS"); +} diff --git a/java/com/tigervnc/rfb/CSecurityVeNCrypt.java b/java/com/tigervnc/rfb/CSecurityVeNCrypt.java new file mode 100644 index 00000000..ae758e7f --- /dev/null +++ b/java/com/tigervnc/rfb/CSecurityVeNCrypt.java @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2003 Sun Microsystems, Inc. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import java.util.*; + +import com.tigervnc.rdr.*; + +public class CSecurityVeNCrypt extends CSecurity { + + public CSecurityVeNCrypt(SecurityClient sec) + { + haveRecvdMajorVersion = false; + haveRecvdMinorVersion = false; + haveSentVersion = false; + haveAgreedVersion = false; + haveListOfTypes = false; + haveNumberOfTypes = false; + haveChosenType = false; + majorVersion = 0; + minorVersion = 0; + chosenType = Security.secTypeVeNCrypt; + nAvailableTypes = 0; + availableTypes = null; + iAvailableType = 0; + security = sec; + } + + public boolean processMsg(CConnection cc) { + InStream is = cc.getInStream(); + OutStream os = cc.getOutStream(); + + /* get major, minor versions, send what we can support (or 0.0 for can't support it) */ + if (!haveRecvdMinorVersion) { + minorVersion = is.readU8(); + haveRecvdMinorVersion = true; + + return false; + } + + if (!haveRecvdMajorVersion) { + majorVersion = is.readU8(); + haveRecvdMajorVersion = true; + } + + /* major version in upper 8 bits and minor version in lower 8 bits */ + int Version = (majorVersion << 8) | minorVersion; + + if (!haveSentVersion) { + /* Currently we don't support former VeNCrypt 0.1 */ + if (Version >= 0x0002) { + majorVersion = 0; + minorVersion = 2; + os.writeU8(majorVersion); + os.writeU8(minorVersion); + os.flush(); + } else { + /* Send 0.0 to indicate no support */ + majorVersion = 0; + minorVersion = 0; + os.writeU8(majorVersion); + os.writeU8(minorVersion); + os.flush(); + throw new Exception("Server reported an unsupported VeNCrypt version"); + } + + haveSentVersion = true; + return false; + } + + /* Check that the server is OK */ + if (!haveAgreedVersion) { + if (is.readU8() != 0) + throw new Exception("Server reported it could not support the VeNCrypt version"); + + haveAgreedVersion = true; + return false; + } + + /* get a number of types */ + if (!haveNumberOfTypes) { + nAvailableTypes = is.readU8(); + iAvailableType = 0; + + if (nAvailableTypes <= 0) + throw new Exception("The server reported no VeNCrypt sub-types"); + + availableTypes = new int[nAvailableTypes]; + haveNumberOfTypes = true; + return false; + } + + if (nAvailableTypes > 0) { + /* read in the types possible */ + if (!haveListOfTypes) { + if (is.checkNoWait(4)) { + availableTypes[iAvailableType++] = is.readU32(); + haveListOfTypes = (iAvailableType >= nAvailableTypes); + vlog.debug("Server offers security type "+ + Security.secTypeName(availableTypes[iAvailableType - 1])+" ("+ + availableTypes[iAvailableType - 1]+")"); + + if (!haveListOfTypes) + return false; + + } else + return false; + } + + /* make a choice and send it to the server, meanwhile set up the stack */ + if (!haveChosenType) { + chosenType = Security.secTypeInvalid; + int i; + Iterator j; + List<Integer> secTypes = new ArrayList<Integer>(); + + secTypes = security.GetEnabledExtSecTypes(); + + /* Honor server's security type order */ + for (i = 0; i < nAvailableTypes; i++) { + for (j = secTypes.iterator(); j.hasNext(); ) { + int refType = (Integer)j.next(); + if (refType == availableTypes[i]) { + chosenType = refType; + break; + } + } + + if (chosenType != Security.secTypeInvalid) + break; + } + + vlog.debug("Choosing security type "+Security.secTypeName(chosenType)+ + " ("+chosenType+")"); + + /* Set up the stack according to the chosen type: */ + if (chosenType == Security.secTypeInvalid || chosenType == Security.secTypeVeNCrypt) + throw new AuthFailureException("No valid VeNCrypt sub-type"); + + csecurity = security.GetCSecurity(chosenType); + + /* send chosen type to server */ + os.writeU32(chosenType); + os.flush(); + + haveChosenType = true; + } + } else { + /* + * Server told us that there are 0 types it can support - this should not + * happen, since if the server supports 0 sub-types, it doesn't support + * this security type + */ + throw new AuthFailureException("The server reported 0 VeNCrypt sub-types"); + } + + return csecurity.processMsg(cc); + } + + public final int getType() { return chosenType; } + public final String description() { return Security.secTypeName(chosenType); } + + public static StringParameter secTypesStr; + + private CSecurity csecurity; + SecurityClient security; + private boolean haveRecvdMajorVersion; + private boolean haveRecvdMinorVersion; + private boolean haveSentVersion; + private boolean haveAgreedVersion; + private boolean haveListOfTypes; + private boolean haveNumberOfTypes; + private boolean haveChosenType; + private int majorVersion, minorVersion; + private int chosenType; + private int nAvailableTypes; + private int[] availableTypes; + private int iAvailableType; + //private final String desc; + + static LogWriter vlog = new LogWriter("CSecurityVeNCrypt"); +} diff --git a/java/com/tigervnc/rfb/CSecurityVncAuth.java b/java/com/tigervnc/rfb/CSecurityVncAuth.java new file mode 100644 index 00000000..405e79fa --- /dev/null +++ b/java/com/tigervnc/rfb/CSecurityVncAuth.java @@ -0,0 +1,66 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import com.tigervnc.rdr.*; +import com.tigervnc.vncviewer.*; + +public class CSecurityVncAuth extends CSecurity { + + public CSecurityVncAuth() { } + + private static final int vncAuthChallengeSize = 16; + + public boolean processMsg(CConnection cc) + { + InStream is = cc.getInStream(); + OutStream os = cc.getOutStream(); + + // Read the challenge & obtain the user's password + byte[] challenge = new byte[vncAuthChallengeSize]; + is.readBytes(challenge, 0, vncAuthChallengeSize); + StringBuffer passwd = new StringBuffer(); + CConn.upg.getUserPasswd(null, passwd); + + // Calculate the correct response + byte[] key = new byte[8]; + int pwdLen = passwd.length(); + byte[] utf8str = new byte[pwdLen]; + try { + utf8str = passwd.toString().getBytes("UTF8"); + } catch(java.io.UnsupportedEncodingException e) { + e.printStackTrace(); + } + for (int i=0; i<8; i++) + key[i] = i<pwdLen ? utf8str[i] : 0; + DesCipher des = new DesCipher(key); + for (int j = 0; j < vncAuthChallengeSize; j += 8) + des.encrypt(challenge,j,challenge,j); + + // Return the response to the server + os.writeBytes(challenge, 0, vncAuthChallengeSize); + os.flush(); + return true; + } + + public int getType() { return Security.secTypeVncAuth; } + public String description() { return "No Encryption"; } + + static LogWriter vlog = new LogWriter("VncAuth"); +} diff --git a/java/com/tigervnc/rfb/Configuration.java b/java/com/tigervnc/rfb/Configuration.java new file mode 100644 index 00000000..bc676087 --- /dev/null +++ b/java/com/tigervnc/rfb/Configuration.java @@ -0,0 +1,91 @@ +/* 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. + */ + +// +// Configuration - class for dealing with configuration parameters. +// + +package com.tigervnc.rfb; + +public class Configuration { + + // - Set named parameter to value + public static boolean setParam(String name, String value) { + VoidParameter param = getParam(name); + if (param == null) return false; + return param.setParam(value); + } + + // - Set parameter to value (separated by "=") + public static boolean setParam(String config) { + boolean hyphen = false; + if (config.charAt(0) == '-') { + hyphen = true; + if (config.charAt(1) == '-') + config = config.substring(2); // allow gnu-style --<option> + else + config = config.substring(1); + } + int equal = config.indexOf('='); + if (equal != -1) { + return setParam(config.substring(0, equal), config.substring(equal+1)); + } else if (hyphen) { + VoidParameter param = getParam(config); + if (param == null) return false; + return param.setParam(); + } + return false; + } + + // - Get named parameter + public static VoidParameter getParam(String name) { + VoidParameter current = head; + while (current != null) { + if (name.equalsIgnoreCase(current.getName())) + return current; + current = current.next; + } + return null; + } + + public static String listParams() { + StringBuffer s = new StringBuffer(); + + VoidParameter current = head; + while (current != null) { + String def_str = current.getDefaultStr(); + String desc = current.getDescription(); + s.append(" "+current.getName()+" - "+desc+" (default="+def_str+")\n"); + current = current.next; + } + + return s.toString(); + } + + public static void readAppletParams(java.applet.Applet applet) { + VoidParameter current = head; + while (current != null) { + String str = applet.getParameter(current.getName()); + if (str != null) + current.setParam(str); + current = current.next; + } + } + + public static VoidParameter head; +} diff --git a/java/com/tigervnc/rfb/ConnFailedException.java b/java/com/tigervnc/rfb/ConnFailedException.java new file mode 100644 index 00000000..d1ddcb4e --- /dev/null +++ b/java/com/tigervnc/rfb/ConnFailedException.java @@ -0,0 +1,23 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class ConnFailedException extends Exception { + public ConnFailedException(String s) { super(s); } +} diff --git a/java/com/tigervnc/rfb/ConnParams.java b/java/com/tigervnc/rfb/ConnParams.java new file mode 100644 index 00000000..70d6114f --- /dev/null +++ b/java/com/tigervnc/rfb/ConnParams.java @@ -0,0 +1,171 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import com.tigervnc.rdr.*; + +public class ConnParams { + static LogWriter vlog = new LogWriter("ConnParams"); + + public ConnParams() { + majorVersion = 0; minorVersion = 0; + width = 0; height = 0; useCopyRect = false; + supportsLocalCursor = false; supportsLocalXCursor = false; + supportsDesktopResize = false; supportsExtendedDesktopSize = false; + supportsDesktopRename = false; supportsLastRect = false; + supportsSetDesktopSize = false; supportsClientRedirect = false; + customCompressLevel = false; compressLevel = 6; + noJpeg = false; qualityLevel = -1; + name_ = null; nEncodings_ = 0; encodings_ = null; + currentEncoding_ = Encodings.encodingRaw; verStrPos = 0; + screenLayout = new ScreenSet(); + + setName(""); + } + + public boolean readVersion(InStream is, Boolean done) + { + if (verStrPos >= 12) return false; + verStr = new StringBuilder(13); + while (verStrPos < 12 && is.checkNoWait(1)) { + verStr.insert(verStrPos++,(char)is.readU8()); + } + + if (verStrPos < 12) { + done = Boolean.valueOf(false); + return true; + } + done = Boolean.valueOf(true); + verStr.insert(12,'0'); + verStrPos = 0; + if (verStr.toString().matches("RFB \\d{3}\\.\\d{3}\\n0")) { + majorVersion = Integer.parseInt(verStr.substring(4,7)); + minorVersion = Integer.parseInt(verStr.substring(8,11)); + return true; + } + return false; + } + + public void writeVersion(OutStream os) { + String str = String.format("RFB %03d.%03d\n", majorVersion, minorVersion); + os.writeBytes(str.getBytes(), 0, 12); + os.flush(); + } + + public int majorVersion; + public int minorVersion; + + public void setVersion(int major, int minor) { + majorVersion = major; minorVersion = minor; + } + public boolean isVersion(int major, int minor) { + return majorVersion == major && minorVersion == minor; + } + public boolean beforeVersion(int major, int minor) { + return (majorVersion < major || + (majorVersion == major && minorVersion < minor)); + } + public boolean afterVersion(int major, int minor) { + return !beforeVersion(major,minor+1); + } + + public int width; + public int height; + public ScreenSet screenLayout; + + public PixelFormat pf() { return pf_; } + public void setPF(PixelFormat pf) { + pf_ = pf; + if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32) { + throw new Exception("setPF: not 8, 16 or 32 bpp?"); + } + } + + public String name() { return name_; } + public void setName(String name) + { + name_ = name; + } + + public int currentEncoding() { return currentEncoding_; } + public int nEncodings() { return nEncodings_; } + public int[] encodings() { return encodings_; } + public void setEncodings(int nEncodings, int[] encodings) + { + if (nEncodings > nEncodings_) { + encodings_ = new int[nEncodings]; + } + nEncodings_ = nEncodings; + useCopyRect = false; + supportsLocalCursor = false; + supportsDesktopResize = false; + customCompressLevel = false; + compressLevel = -1; + noJpeg = true; + qualityLevel = -1; + currentEncoding_ = Encodings.encodingRaw; + + for (int i = nEncodings-1; i >= 0; i--) { + encodings_[i] = encodings[i]; + if (encodings[i] == Encodings.encodingCopyRect) + useCopyRect = true; + else if (encodings[i] == Encodings.pseudoEncodingCursor) + supportsLocalCursor = true; + else if (encodings[i] == Encodings.pseudoEncodingDesktopSize) + supportsDesktopResize = true; + else if (encodings[i] == Encodings.pseudoEncodingClientRedirect) + supportsClientRedirect = true; + else if (encodings[i] >= Encodings.pseudoEncodingCompressLevel0 && + encodings[i] <= Encodings.pseudoEncodingCompressLevel9) { + customCompressLevel = true; + compressLevel = encodings[i] - Encodings.pseudoEncodingCompressLevel0; + } else if (encodings[i] >= Encodings.pseudoEncodingQualityLevel0 && + encodings[i] <= Encodings.pseudoEncodingQualityLevel9) { + noJpeg = false; + qualityLevel = encodings[i] - Encodings.pseudoEncodingQualityLevel0; + } else if (encodings[i] <= Encodings.encodingMax && + Encoder.supported(encodings[i])) + currentEncoding_ = encodings[i]; + } + } + public boolean useCopyRect; + + public boolean supportsLocalCursor; + public boolean supportsLocalXCursor; + public boolean supportsDesktopResize; + public boolean supportsExtendedDesktopSize; + public boolean supportsDesktopRename; + public boolean supportsClientRedirect; + public boolean supportsLastRect; + + public boolean supportsSetDesktopSize; + + public boolean customCompressLevel; + public int compressLevel; + public boolean noJpeg; + public int qualityLevel; + + private PixelFormat pf_; + private String name_; + private int nEncodings_; + private int[] encodings_; + private int currentEncoding_; + private StringBuilder verStr; + private int verStrPos; +} diff --git a/java/com/tigervnc/rfb/Cursor.java b/java/com/tigervnc/rfb/Cursor.java new file mode 100644 index 00000000..420eb82a --- /dev/null +++ b/java/com/tigervnc/rfb/Cursor.java @@ -0,0 +1,34 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import java.awt.*; + +public class Cursor extends ManagedPixelBuffer { + + public void setSize(int w, int h) { + super.setSize(w, h); + if (mask == null || mask.length < maskLen()) + mask = new byte[maskLen()]; + } + public int maskLen() { return (width() + 7) / 8 * height(); } + + public Point hotspot; + public byte[] mask; +} diff --git a/java/com/tigervnc/rfb/Decoder.java b/java/com/tigervnc/rfb/Decoder.java new file mode 100644 index 00000000..8d42ea5e --- /dev/null +++ b/java/com/tigervnc/rfb/Decoder.java @@ -0,0 +1,51 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +abstract public class Decoder { + + abstract public void readRect(Rect r, CMsgHandler handler); + + static public boolean supported(int encoding) + { +/* + return encoding <= Encodings.encodingMax && createFns[encoding]; +*/ + return (encoding == Encodings.encodingRaw || + encoding == Encodings.encodingRRE || + encoding == Encodings.encodingHextile || + encoding == Encodings.encodingTight || + encoding == Encodings.encodingZRLE); + } + static public Decoder createDecoder(int encoding, CMsgReader reader) { +/* + if (encoding <= Encodings.encodingMax && createFns[encoding]) + return (createFns[encoding])(reader); + return 0; +*/ + switch(encoding) { + case Encodings.encodingRaw: return new RawDecoder(reader); + case Encodings.encodingRRE: return new RREDecoder(reader); + case Encodings.encodingHextile: return new HextileDecoder(reader); + case Encodings.encodingTight: return new TightDecoder(reader); + case Encodings.encodingZRLE: return new ZRLEDecoder(reader); + } + return null; + } +} diff --git a/java/com/tigervnc/rfb/DesCipher.java b/java/com/tigervnc/rfb/DesCipher.java new file mode 100644 index 00000000..f7ae9db9 --- /dev/null +++ b/java/com/tigervnc/rfb/DesCipher.java @@ -0,0 +1,496 @@ +// +// This DES class has been extracted from package Acme.Crypto for use in VNC. +// The bytebit[] array has been reversed so that the most significant bit +// in each byte of the key is ignored, not the least significant. Also the +// unnecessary odd parity code has been removed. +// +// These changes are: +// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. +// +// This software is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// + +// DesCipher - the DES encryption method +// +// The meat of this code is by Dave Zimmerman <dzimm@widget.com>, and is: +// +// Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved. +// +// Permission to use, copy, modify, and distribute this software +// and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and +// without fee is hereby granted, provided that this copyright notice is kept +// intact. +// +// WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY +// OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE +// FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR +// DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. +// +// THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE +// CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE +// PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT +// NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE +// SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE +// SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE +// PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET WORKSHOP +// SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR +// HIGH RISK ACTIVITIES. +// +// +// The rest is: +// +// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. +// +// Visit the ACME Labs Java page for up-to-date versions of this and other +// fine Java utilities: http://www.acme.com/java/ + + +/// The DES encryption method. +// <P> +// This is surprisingly fast, for pure Java. On a SPARC 20, wrapped +// in Acme.Crypto.EncryptedOutputStream or Acme.Crypto.EncryptedInputStream, +// it does around 7000 bytes/second. +// <P> +// Most of this code is by Dave Zimmerman <dzimm@widget.com>, and is +// Copyright (c) 1996 Widget Workshop, Inc. See the source file for details. +// <P> +// <A HREF="/resources/classes/Acme/Crypto/DesCipher.java">Fetch the software.</A><BR> +// <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A> +// <P> +// @see Des3Cipher +// @see EncryptedOutputStream +// @see EncryptedInputStream + +package com.tigervnc.rfb; + +public class DesCipher + { + + // Constructor, byte-array key. + public DesCipher( byte[] key ) + { + setKey( key ); + } + + // Key routines. + + private int[] encryptKeys = new int[32]; + private int[] decryptKeys = new int[32]; + + /// Set the key. + public void setKey( byte[] key ) + { + deskey( key, true, encryptKeys ); + deskey( key, false, decryptKeys ); + } + + // Turn an 8-byte key into internal keys. + private void deskey( byte[] keyBlock, boolean encrypting, int[] KnL ) + { + int i, j, l, m, n; + int[] pc1m = new int[56]; + int[] pcr = new int[56]; + int[] kn = new int[32]; + + for ( j = 0; j < 56; ++j ) + { + l = pc1[j]; + m = l & 07; + pc1m[j] = ( (keyBlock[l >>> 3] & bytebit[m]) != 0 )? 1: 0; + } + + for ( i = 0; i < 16; ++i ) + { + if ( encrypting ) + m = i << 1; + else + m = (15-i) << 1; + n = m+1; + kn[m] = kn[n] = 0; + for ( j = 0; j < 28; ++j ) + { + l = j+totrot[i]; + if ( l < 28 ) + pcr[j] = pc1m[l]; + else + pcr[j] = pc1m[l-28]; + } + for ( j=28; j < 56; ++j ) + { + l = j+totrot[i]; + if ( l < 56 ) + pcr[j] = pc1m[l]; + else + pcr[j] = pc1m[l-28]; + } + for ( j = 0; j < 24; ++j ) + { + if ( pcr[pc2[j]] != 0 ) + kn[m] |= bigbyte[j]; + if ( pcr[pc2[j+24]] != 0 ) + kn[n] |= bigbyte[j]; + } + } + cookey( kn, KnL ); + } + + private void cookey( int[] raw, int KnL[] ) + { + int raw0, raw1; + int rawi, KnLi; + int i; + + for ( i = 0, rawi = 0, KnLi = 0; i < 16; ++i ) + { + raw0 = raw[rawi++]; + raw1 = raw[rawi++]; + KnL[KnLi] = (raw0 & 0x00fc0000) << 6; + KnL[KnLi] |= (raw0 & 0x00000fc0) << 10; + KnL[KnLi] |= (raw1 & 0x00fc0000) >>> 10; + KnL[KnLi] |= (raw1 & 0x00000fc0) >>> 6; + ++KnLi; + KnL[KnLi] = (raw0 & 0x0003f000) << 12; + KnL[KnLi] |= (raw0 & 0x0000003f) << 16; + KnL[KnLi] |= (raw1 & 0x0003f000) >>> 4; + KnL[KnLi] |= (raw1 & 0x0000003f); + ++KnLi; + } + } + + + // Block encryption routines. + + private int[] tempInts = new int[2]; + + /// Encrypt a block of eight bytes. + public void encrypt( byte[] clearText, int clearOff, byte[] cipherText, int cipherOff ) + { + squashBytesToInts( clearText, clearOff, tempInts, 0, 2 ); + des( tempInts, tempInts, encryptKeys ); + spreadIntsToBytes( tempInts, 0, cipherText, cipherOff, 2 ); + } + + /// Decrypt a block of eight bytes. + public void decrypt( byte[] cipherText, int cipherOff, byte[] clearText, int clearOff ) + { + squashBytesToInts( cipherText, cipherOff, tempInts, 0, 2 ); + des( tempInts, tempInts, decryptKeys ); + spreadIntsToBytes( tempInts, 0, clearText, clearOff, 2 ); + } + + // The DES function. + private void des( int[] inInts, int[] outInts, int[] keys ) + { + int fval, work, right, leftt; + int round; + int keysi = 0; + + leftt = inInts[0]; + right = inInts[1]; + + work = ((leftt >>> 4) ^ right) & 0x0f0f0f0f; + right ^= work; + leftt ^= (work << 4); + + work = ((leftt >>> 16) ^ right) & 0x0000ffff; + right ^= work; + leftt ^= (work << 16); + + work = ((right >>> 2) ^ leftt) & 0x33333333; + leftt ^= work; + right ^= (work << 2); + + work = ((right >>> 8) ^ leftt) & 0x00ff00ff; + leftt ^= work; + right ^= (work << 8); + right = (right << 1) | ((right >>> 31) & 1); + + work = (leftt ^ right) & 0xaaaaaaaa; + leftt ^= work; + right ^= work; + leftt = (leftt << 1) | ((leftt >>> 31) & 1); + + for ( round = 0; round < 8; ++round ) + { + work = (right << 28) | (right >>> 4); + work ^= keys[keysi++]; + fval = SP7[ work & 0x0000003f ]; + fval |= SP5[(work >>> 8) & 0x0000003f ]; + fval |= SP3[(work >>> 16) & 0x0000003f ]; + fval |= SP1[(work >>> 24) & 0x0000003f ]; + work = right ^ keys[keysi++]; + fval |= SP8[ work & 0x0000003f ]; + fval |= SP6[(work >>> 8) & 0x0000003f ]; + fval |= SP4[(work >>> 16) & 0x0000003f ]; + fval |= SP2[(work >>> 24) & 0x0000003f ]; + leftt ^= fval; + work = (leftt << 28) | (leftt >>> 4); + work ^= keys[keysi++]; + fval = SP7[ work & 0x0000003f ]; + fval |= SP5[(work >>> 8) & 0x0000003f ]; + fval |= SP3[(work >>> 16) & 0x0000003f ]; + fval |= SP1[(work >>> 24) & 0x0000003f ]; + work = leftt ^ keys[keysi++]; + fval |= SP8[ work & 0x0000003f ]; + fval |= SP6[(work >>> 8) & 0x0000003f ]; + fval |= SP4[(work >>> 16) & 0x0000003f ]; + fval |= SP2[(work >>> 24) & 0x0000003f ]; + right ^= fval; + } + + right = (right << 31) | (right >>> 1); + work = (leftt ^ right) & 0xaaaaaaaa; + leftt ^= work; + right ^= work; + leftt = (leftt << 31) | (leftt >>> 1); + work = ((leftt >>> 8) ^ right) & 0x00ff00ff; + right ^= work; + leftt ^= (work << 8); + work = ((leftt >>> 2) ^ right) & 0x33333333; + right ^= work; + leftt ^= (work << 2); + work = ((right >>> 16) ^ leftt) & 0x0000ffff; + leftt ^= work; + right ^= (work << 16); + work = ((right >>> 4) ^ leftt) & 0x0f0f0f0f; + leftt ^= work; + right ^= (work << 4); + outInts[0] = right; + outInts[1] = leftt; + } + + + // Tables, permutations, S-boxes, etc. + + private static byte[] bytebit = { + (byte)0x01, (byte)0x02, (byte)0x04, (byte)0x08, + (byte)0x10, (byte)0x20, (byte)0x40, (byte)0x80 + }; + private static int[] bigbyte = { + 0x800000, 0x400000, 0x200000, 0x100000, + 0x080000, 0x040000, 0x020000, 0x010000, + 0x008000, 0x004000, 0x002000, 0x001000, + 0x000800, 0x000400, 0x000200, 0x000100, + 0x000080, 0x000040, 0x000020, 0x000010, + 0x000008, 0x000004, 0x000002, 0x000001 + }; + private static byte[] pc1 = { + (byte)56, (byte)48, (byte)40, (byte)32, (byte)24, (byte)16, (byte) 8, + (byte) 0, (byte)57, (byte)49, (byte)41, (byte)33, (byte)25, (byte)17, + (byte) 9, (byte) 1, (byte)58, (byte)50, (byte)42, (byte)34, (byte)26, + (byte)18, (byte)10, (byte) 2, (byte)59, (byte)51, (byte)43, (byte)35, + (byte)62, (byte)54, (byte)46, (byte)38, (byte)30, (byte)22, (byte)14, + (byte) 6, (byte)61, (byte)53, (byte)45, (byte)37, (byte)29, (byte)21, + (byte)13, (byte) 5, (byte)60, (byte)52, (byte)44, (byte)36, (byte)28, + (byte)20, (byte)12, (byte) 4, (byte)27, (byte)19, (byte)11, (byte)3 + }; + private static int[] totrot = { + 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 + }; + + private static byte[] pc2 = { + (byte)13, (byte)16, (byte)10, (byte)23, (byte) 0, (byte) 4, + (byte) 2, (byte)27, (byte)14, (byte) 5, (byte)20, (byte) 9, + (byte)22, (byte)18, (byte)11, (byte)3 , (byte)25, (byte) 7, + (byte)15, (byte) 6, (byte)26, (byte)19, (byte)12, (byte) 1, + (byte)40, (byte)51, (byte)30, (byte)36, (byte)46, (byte)54, + (byte)29, (byte)39, (byte)50, (byte)44, (byte)32, (byte)47, + (byte)43, (byte)48, (byte)38, (byte)55, (byte)33, (byte)52, + (byte)45, (byte)41, (byte)49, (byte)35, (byte)28, (byte)31, + }; + + private static int[] SP1 = { + 0x01010400, 0x00000000, 0x00010000, 0x01010404, + 0x01010004, 0x00010404, 0x00000004, 0x00010000, + 0x00000400, 0x01010400, 0x01010404, 0x00000400, + 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, + 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, + 0x00000000, 0x00000404, 0x00010404, 0x01000000, + 0x00010000, 0x01010404, 0x00000004, 0x01010000, + 0x01010400, 0x01000000, 0x01000000, 0x00000400, + 0x01010004, 0x00010000, 0x00010400, 0x01000004, + 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, + 0x01000004, 0x00000404, 0x00010404, 0x01010400, + 0x00000404, 0x01000400, 0x01000400, 0x00000000, + 0x00010004, 0x00010400, 0x00000000, 0x01010004 + }; + private static int[] SP2 = { + 0x80108020, 0x80008000, 0x00008000, 0x00108020, + 0x00100000, 0x00000020, 0x80100020, 0x80008020, + 0x80000020, 0x80108020, 0x80108000, 0x80000000, + 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, + 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, + 0x00008020, 0x80108000, 0x80100000, 0x00008020, + 0x00000000, 0x00108020, 0x80100020, 0x00100000, + 0x80008020, 0x80100000, 0x80108000, 0x00008000, + 0x80100000, 0x80008000, 0x00000020, 0x80108020, + 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, + 0x00100020, 0x80008020, 0x80000020, 0x00100020, + 0x00108000, 0x00000000, 0x80008000, 0x00008020, + 0x80000000, 0x80100020, 0x80108020, 0x00108000 + }; + private static int[] SP3 = { + 0x00000208, 0x08020200, 0x00000000, 0x08020008, + 0x08000200, 0x00000000, 0x00020208, 0x08000200, + 0x00020008, 0x08000008, 0x08000008, 0x00020000, + 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, + 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, + 0x00000008, 0x08020208, 0x00000200, 0x08000000, + 0x08020200, 0x08000000, 0x00020008, 0x00000208, + 0x00020000, 0x08020200, 0x08000200, 0x00000000, + 0x00000200, 0x00020008, 0x08020208, 0x08000200, + 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, + 0x00000008, 0x00020208, 0x00020200, 0x08000008, + 0x08020000, 0x08000208, 0x00000208, 0x08020000, + 0x00020208, 0x00000008, 0x08020008, 0x00020200 + }; + private static int[] SP4 = { + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802080, 0x00800081, 0x00800001, 0x00002001, + 0x00000000, 0x00802000, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, + 0x00002000, 0x00802080, 0x00802081, 0x00000081, + 0x00800080, 0x00800001, 0x00802000, 0x00802081, + 0x00000081, 0x00000000, 0x00000000, 0x00802000, + 0x00002080, 0x00800080, 0x00800081, 0x00000001, + 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, + 0x00800001, 0x00002001, 0x00802080, 0x00800081, + 0x00002001, 0x00002080, 0x00800000, 0x00802001, + 0x00000080, 0x00800000, 0x00002000, 0x00802080 + }; + private static int[] SP5 = { + 0x00000100, 0x02080100, 0x02080000, 0x42000100, + 0x00080000, 0x00000100, 0x40000000, 0x02080000, + 0x40080100, 0x00080000, 0x02000100, 0x40080100, + 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, + 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, + 0x02080100, 0x02000000, 0x42000000, 0x00080100, + 0x00080000, 0x42000100, 0x00000100, 0x02000000, + 0x40000000, 0x02080000, 0x42000100, 0x40080100, + 0x02000100, 0x40000000, 0x42080000, 0x02080100, + 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, + 0x02080000, 0x00000000, 0x40080000, 0x42000000, + 0x00080100, 0x02000100, 0x40000100, 0x00080000, + 0x00000000, 0x40080000, 0x02080100, 0x40000100 + }; + private static int[] SP6 = { + 0x20000010, 0x20400000, 0x00004000, 0x20404010, + 0x20400000, 0x00000010, 0x20404010, 0x00400000, + 0x20004000, 0x00404010, 0x00400000, 0x20000010, + 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, + 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, + 0x00004010, 0x00404000, 0x20404000, 0x20000000, + 0x20004000, 0x00000010, 0x20400010, 0x00404000, + 0x20404010, 0x00400000, 0x00004010, 0x20000010, + 0x00400000, 0x20004000, 0x20000000, 0x00004010, + 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, + 0x00000010, 0x00004000, 0x20400000, 0x00404010, + 0x00004000, 0x00400010, 0x20004010, 0x00000000, + 0x20404000, 0x20000000, 0x00400010, 0x20004010 + }; + private static int[] SP7 = { + 0x00200000, 0x04200002, 0x04000802, 0x00000000, + 0x00000800, 0x04000802, 0x00200802, 0x04200800, + 0x04200802, 0x00200000, 0x00000000, 0x04000002, + 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, + 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, + 0x00200800, 0x00000002, 0x04000000, 0x00200800, + 0x04000000, 0x00200800, 0x00200000, 0x04000802, + 0x04000802, 0x04200002, 0x04200002, 0x00000002, + 0x00200002, 0x04000000, 0x04000800, 0x00200000, + 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, + 0x00200800, 0x00000000, 0x00000002, 0x04200802, + 0x00000000, 0x00200802, 0x04200000, 0x00000800, + 0x04000002, 0x04000800, 0x00000800, 0x00200002 + }; + private static int[] SP8 = { + 0x10001040, 0x00001000, 0x00040000, 0x10041040, + 0x10000000, 0x10001040, 0x00000040, 0x10000000, + 0x00040040, 0x10040000, 0x10041040, 0x00041000, + 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, + 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, + 0x10000040, 0x10001000, 0x00041040, 0x00040000, + 0x00041040, 0x00040000, 0x10041000, 0x00001000, + 0x00000040, 0x10040040, 0x00001000, 0x00041040, + 0x10001000, 0x00000040, 0x10000040, 0x10040000, + 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, + 0x10040000, 0x10001000, 0x10001040, 0x00000000, + 0x10041040, 0x00041000, 0x00041000, 0x00001040, + 0x00001040, 0x00040040, 0x10000000, 0x10041000 + }; + + // Routines taken from other parts of the Acme utilities. + + /// Squash bytes down to ints. + public static void squashBytesToInts( byte[] inBytes, int inOff, int[] outInts, int outOff, int intLen ) + { + for ( int i = 0; i < intLen; ++i ) + outInts[outOff + i] = + ( ( inBytes[inOff + i * 4 ] & 0xff ) << 24 ) | + ( ( inBytes[inOff + i * 4 + 1] & 0xff ) << 16 ) | + ( ( inBytes[inOff + i * 4 + 2] & 0xff ) << 8 ) | + ( inBytes[inOff + i * 4 + 3] & 0xff ); + } + + /// Spread ints into bytes. + public static void spreadIntsToBytes( int[] inInts, int inOff, byte[] outBytes, int outOff, int intLen ) + { + for ( int i = 0; i < intLen; ++i ) + { + outBytes[outOff + i * 4 ] = (byte) ( inInts[inOff + i] >>> 24 ); + outBytes[outOff + i * 4 + 1] = (byte) ( inInts[inOff + i] >>> 16 ); + outBytes[outOff + i * 4 + 2] = (byte) ( inInts[inOff + i] >>> 8 ); + outBytes[outOff + i * 4 + 3] = (byte) inInts[inOff + i]; + } + } + } diff --git a/java/com/tigervnc/rfb/Encoder.java b/java/com/tigervnc/rfb/Encoder.java new file mode 100644 index 00000000..0964f88e --- /dev/null +++ b/java/com/tigervnc/rfb/Encoder.java @@ -0,0 +1,25 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class Encoder { + static public boolean supported(int encoding) { + return false; + } +} diff --git a/java/com/tigervnc/rfb/Encodings.java b/java/com/tigervnc/rfb/Encodings.java new file mode 100644 index 00000000..493d5488 --- /dev/null +++ b/java/com/tigervnc/rfb/Encodings.java @@ -0,0 +1,70 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class Encodings { + + public static final int encodingRaw = 0; + public static final int encodingCopyRect = 1; + public static final int encodingRRE = 2; + public static final int encodingCoRRE = 4; + public static final int encodingHextile = 5; + public static final int encodingTight = 7; + public static final int encodingZRLE = 16; + + public static final int encodingMax = 255; + + public static final int pseudoEncodingXCursor = -240; + public static final int pseudoEncodingCursor = -239; + public static final int pseudoEncodingDesktopSize = -223; + public static final int pseudoEncodingExtendedDesktopSize = -308; + public static final int pseudoEncodingDesktopName = -307; + public static final int pseudoEncodingClientRedirect = -311; + + // TightVNC-specific + public static final int pseudoEncodingLastRect = -224; + public static final int pseudoEncodingQualityLevel0 = -32; + public static final int pseudoEncodingQualityLevel9 = -23; + public static final int pseudoEncodingCompressLevel0 = -256; + public static final int pseudoEncodingCompressLevel9 = -247; + + public static int encodingNum(String name) { + if (name.equalsIgnoreCase("raw")) return encodingRaw; + if (name.equalsIgnoreCase("copyRect")) return encodingCopyRect; + if (name.equalsIgnoreCase("RRE")) return encodingRRE; + if (name.equalsIgnoreCase("coRRE")) return encodingCoRRE; + if (name.equalsIgnoreCase("hextile")) return encodingHextile; + if (name.equalsIgnoreCase("Tight")) return encodingTight; + if (name.equalsIgnoreCase("ZRLE")) return encodingZRLE; + return -1; + } + + public static String encodingName(int num) { + switch (num) { + case encodingRaw: return "raw"; + case encodingCopyRect: return "copyRect"; + case encodingRRE: return "RRE"; + case encodingCoRRE: return "CoRRE"; + case encodingHextile: return "hextile"; + case encodingTight: return "Tight"; + case encodingZRLE: return "ZRLE"; + default: return "[unknown encoding]"; + } + } +} diff --git a/java/com/tigervnc/rfb/Exception.java b/java/com/tigervnc/rfb/Exception.java new file mode 100644 index 00000000..26ac355b --- /dev/null +++ b/java/com/tigervnc/rfb/Exception.java @@ -0,0 +1,23 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class Exception extends com.tigervnc.rdr.Exception { + public Exception(String s) { super(s); } +} diff --git a/java/com/tigervnc/rfb/Hextile.java b/java/com/tigervnc/rfb/Hextile.java new file mode 100644 index 00000000..9c05b729 --- /dev/null +++ b/java/com/tigervnc/rfb/Hextile.java @@ -0,0 +1,27 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class Hextile { + public static final int raw = (1 << 0); + public static final int bgSpecified = (1 << 1); + public static final int fgSpecified = (1 << 2); + public static final int anySubrects = (1 << 3); + public static final int subrectsColoured = (1 << 4); +} diff --git a/java/com/tigervnc/rfb/HextileDecoder.java b/java/com/tigervnc/rfb/HextileDecoder.java new file mode 100644 index 00000000..4c32b52c --- /dev/null +++ b/java/com/tigervnc/rfb/HextileDecoder.java @@ -0,0 +1,101 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import com.tigervnc.rdr.*; + +public class HextileDecoder extends Decoder { + + public HextileDecoder(CMsgReader reader_) { reader = reader_; } + + public void readRect(Rect r, CMsgHandler handler) { + InStream is = reader.getInStream(); + int bytesPerPixel = handler.cp.pf().bpp / 8; + boolean bigEndian = handler.cp.pf().bigEndian; + + int[] buf = reader.getImageBuf(16 * 16 * 4); + + Rect t = new Rect(); + int bg = 0; + int fg = 0; + + for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) { + + t.br.y = Math.min(r.br.y, t.tl.y + 16); + + for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) { + + t.br.x = Math.min(r.br.x, t.tl.x + 16); + + int tileType = is.readU8(); + + if ((tileType & Hextile.raw) != 0) { + is.readPixels(buf, t.area(), bytesPerPixel, bigEndian); + handler.imageRect(t, buf); + continue; + } + + if ((tileType & Hextile.bgSpecified) != 0) + bg = is.readPixel(bytesPerPixel, bigEndian); + + int len = t.area(); + int ptr = 0; + while (len-- > 0) buf[ptr++] = bg; + + if ((tileType & Hextile.fgSpecified) != 0) + fg = is.readPixel(bytesPerPixel, bigEndian); + + if ((tileType & Hextile.anySubrects) != 0) { + int nSubrects = is.readU8(); + + for (int i = 0; i < nSubrects; i++) { + + if ((tileType & Hextile.subrectsColoured) != 0) + fg = is.readPixel(bytesPerPixel, bigEndian); + + int xy = is.readU8(); + int wh = is.readU8(); + +/* + Rect s = new Rect(); + s.tl.x = t.tl.x + ((xy >> 4) & 15); + s.tl.y = t.tl.y + (xy & 15); + s.br.x = s.tl.x + ((wh >> 4) & 15) + 1; + s.br.y = s.tl.y + (wh & 15) + 1; +*/ + int x = ((xy >> 4) & 15); + int y = (xy & 15); + int w = ((wh >> 4) & 15) + 1; + int h = (wh & 15) + 1; + ptr = y * t.width() + x; + int rowAdd = t.width() - w; + while (h-- > 0) { + len = w; + while (len-- > 0) buf[ptr++] = fg; + ptr += rowAdd; + } + } + } + handler.imageRect(t, buf); + } + } + } + + CMsgReader reader; +} diff --git a/java/com/tigervnc/rfb/Hostname.java b/java/com/tigervnc/rfb/Hostname.java new file mode 100644 index 00000000..42fda537 --- /dev/null +++ b/java/com/tigervnc/rfb/Hostname.java @@ -0,0 +1,41 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class Hostname { + + public static String getHost(String vncServerName) { + int colonPos = vncServerName.indexOf(':'); + if (colonPos == 0) + return "localhost"; + if (colonPos == -1) + colonPos = vncServerName.length(); + return vncServerName.substring(0, colonPos); + } + + public static int getPort(String vncServerName) { + int colonPos = vncServerName.indexOf(':'); + if (colonPos == -1 || colonPos == vncServerName.length()-1) + return 5900; + if (vncServerName.charAt(colonPos+1) == ':') { + return Integer.parseInt(vncServerName.substring(colonPos+2)); + } + return Integer.parseInt(vncServerName.substring(colonPos+1)) + 5900; + } +} diff --git a/java/com/tigervnc/rfb/IntParameter.java b/java/com/tigervnc/rfb/IntParameter.java new file mode 100644 index 00000000..877063e7 --- /dev/null +++ b/java/com/tigervnc/rfb/IntParameter.java @@ -0,0 +1,44 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class IntParameter extends VoidParameter { + public IntParameter(String name_, String desc_, int v) { + super(name_, desc_); + value = v; + defValue = v; + } + + public boolean setParam(String v) { + try { + value = Integer.parseInt(v); + } catch (NumberFormatException e) { + return false; + } + return true; + } + + public String getDefaultStr() { return Integer.toString(defValue); } + public String getValueStr() { return Integer.toString(value); } + + public int getValue() { return value; } + + protected int value; + protected int defValue; +} diff --git a/java/com/tigervnc/rfb/Keysyms.java b/java/com/tigervnc/rfb/Keysyms.java new file mode 100644 index 00000000..6bfafeaf --- /dev/null +++ b/java/com/tigervnc/rfb/Keysyms.java @@ -0,0 +1,88 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// Keysyms - defines X keysyms for non-character keys. All keysyms +// corresponding to characters should be generated by calling +// UnicodeToKeysym.translate(). +// + +package com.tigervnc.rfb; + +public class Keysyms { + + public static final int BackSpace = 0xFF08; + public static final int Tab = 0xFF09; + public static final int Linefeed = 0xFF0A; + public static final int Clear = 0xFF0B; + public static final int Return = 0xFF0D; + public static final int Pause = 0xFF13; + public static final int Scroll_Lock = 0xFF14; + public static final int Sys_Req = 0xFF15; + public static final int Escape = 0xFF1B; + public static final int Delete = 0xFFFF; + + public static final int Home = 0xFF50; + public static final int Left = 0xFF51; + public static final int Up = 0xFF52; + public static final int Right = 0xFF53; + public static final int Down = 0xFF54; + public static final int Prior = 0xFF55; + public static final int Page_Up = 0xFF55; + public static final int Next = 0xFF56; + public static final int Page_Down = 0xFF56; + public static final int End = 0xFF57; + public static final int Begin = 0xFF58; + + public static final int Select = 0xFF60; + public static final int Print = 0xFF61; + public static final int Execute = 0xFF62; + public static final int Insert = 0xFF63; + public static final int Undo = 0xFF65; + public static final int Redo = 0xFF66; + public static final int Menu = 0xFF67; + public static final int Find = 0xFF68; + public static final int Cancel = 0xFF69; + public static final int Help = 0xFF6A; + public static final int Break = 0xFF6B; + public static final int Mode_switch = 0xFF7E; + public static final int script_switch = 0xFF7E; + public static final int Num_Lock = 0xFF7F; + + public static final int F1 = 0xFFBE; + public static final int F2 = 0xFFBF; + public static final int F3 = 0xFFC0; + public static final int F4 = 0xFFC1; + public static final int F5 = 0xFFC2; + public static final int F6 = 0xFFC3; + public static final int F7 = 0xFFC4; + public static final int F8 = 0xFFC5; + public static final int F9 = 0xFFC6; + public static final int F10 = 0xFFC7; + public static final int F11 = 0xFFC8; + public static final int F12 = 0xFFC9; + + public static final int Shift_L = 0xFFE1; + public static final int Shift_R = 0xFFE2; + public static final int Control_L = 0xFFE3; + public static final int Control_R = 0xFFE4; + public static final int Meta_L = 0xFFE7; + public static final int Meta_R = 0xFFE8; + public static final int Alt_L = 0xFFE9; + public static final int Alt_R = 0xFFEA; +} diff --git a/java/com/tigervnc/rfb/LogWriter.java b/java/com/tigervnc/rfb/LogWriter.java new file mode 100644 index 00000000..c5730531 --- /dev/null +++ b/java/com/tigervnc/rfb/LogWriter.java @@ -0,0 +1,99 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class LogWriter { + + public LogWriter(String name_) { + name = name_; + level = globalLogLevel; + next = log_writers; + log_writers = this; + } + + public void setLevel(int level_) { level = level_; } + + public void write(int level, String str) { + if (level <= this.level) { + System.err.println(name+": "+str); + } + } + + public void error(String str) { write(0, str); } + public void status(String str) { write(10, str); } + public void info(String str) { write(30, str); } + public void debug(String str) { write(100, str); } + + public static boolean setLogParams(String params) { + globalLogLevel = Integer.parseInt(params); + LogWriter current = log_writers; + while (current != null) { + current.setLevel(globalLogLevel); + current = current.next; + } + return true; +// int colon = params.indexOf(':'); +// String logwriter_name = params.substring(0, colon); +// params = params.substring(colon+1); +// colon = params.indexOf(':'); +// String logger_name = params.substring(0, colon); +// params = params.substring(colon+1); +// int level = Integer.parseInt(params); +// // XXX ignore logger name for the moment + +// System.err.println("setting level to "+level); +// System.err.println("logwriters is "+log_writers); +// if (logwriter_name.equals("*")) { +// LogWriter current = log_writers; +// while (current != null) { +// //current.setLog(logger); +// System.err.println("setting level of "+current.name+" to "+level); +// current.setLevel(level); +// current = current.next; +// } +// return true; +// } + +// LogWriter logwriter = getLogWriter(logwriter_name); +// if (logwriter == null) { +// System.err.println("no logwriter found: "+logwriter_name); +// return false; +// } + +// //logwriter.setLog(logger); +// logwriter.setLevel(level); +// return true; + } + + + static LogWriter getLogWriter(String name) { + LogWriter current = log_writers; + while (current != null) { + if (name.equalsIgnoreCase(current.name)) return current; + current = current.next; + } + return null; + } + + String name; + int level; + LogWriter next; + static LogWriter log_writers; + static int globalLogLevel = 30; +} diff --git a/java/com/tigervnc/rfb/ManagedPixelBuffer.java b/java/com/tigervnc/rfb/ManagedPixelBuffer.java new file mode 100644 index 00000000..46b5acf7 --- /dev/null +++ b/java/com/tigervnc/rfb/ManagedPixelBuffer.java @@ -0,0 +1,38 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class ManagedPixelBuffer extends PixelBuffer { + public void setSize(int w, int h) { + width_ = w; + height_ = h; + checkDataSize(); + } + public void setPF(PixelFormat pf) { + super.setPF(pf); + checkDataSize(); + } + + public int dataLen() { return area(); } + + final void checkDataSize() { + if (data == null || data.length < dataLen()) + data = new int[dataLen()]; + } +} diff --git a/java/com/tigervnc/rfb/MsgTypes.java b/java/com/tigervnc/rfb/MsgTypes.java new file mode 100644 index 00000000..a009b396 --- /dev/null +++ b/java/com/tigervnc/rfb/MsgTypes.java @@ -0,0 +1,40 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class MsgTypes { + // server to client + + public static final int msgTypeFramebufferUpdate = 0; + public static final int msgTypeSetColourMapEntries = 1; + public static final int msgTypeBell = 2; + public static final int msgTypeServerCutText = 3; + + // client to server + + public static final int msgTypeSetPixelFormat = 0; + public static final int msgTypeFixColourMapEntries = 1; + public static final int msgTypeSetEncodings = 2; + public static final int msgTypeFramebufferUpdateRequest = 3; + public static final int msgTypeKeyEvent = 4; + public static final int msgTypePointerEvent = 5; + public static final int msgTypeClientCutText = 6; + + public static final int msgTypeSetDesktopSize = 251; +} diff --git a/java/com/tigervnc/rfb/PixelBuffer.java b/java/com/tigervnc/rfb/PixelBuffer.java new file mode 100644 index 00000000..f87fead8 --- /dev/null +++ b/java/com/tigervnc/rfb/PixelBuffer.java @@ -0,0 +1,116 @@ +/* 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. + */ + +// +// PixelBuffer - note that this code is only written for the 8, 16, and 32 bpp cases at the +// moment. +// + +package com.tigervnc.rfb; + +import java.awt.image.*; + +public class PixelBuffer { + + public PixelBuffer() { + setPF(new PixelFormat()); + } + + public void setPF(PixelFormat pf) { + if (!(pf.bpp == 32) && !(pf.bpp == 16) && !(pf.bpp == 8)) + throw new Exception("Internal error: bpp must be 8, 16, or 32 in PixelBuffer ("+pf.bpp+")"); + format = pf; + switch (pf.depth) { + case 8: + //cm = new IndexColorModel(8, 256, new byte[256], new byte[256], new byte[256]); + cm = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); + break; + case 16: + cm = new DirectColorModel(32, 0xF800, 0x07C0, 0x003E, (0xff << 24)); + break; + case 24: + cm = new DirectColorModel(32, (0xff << 16), (0xff << 8), 0xff, (0xff << 24)); + break; + } + } + public PixelFormat getPF() { return format; } + + public final int width() { return width_; } + public final int height() { return height_; } + public final int area() { return width_ * height_; } + + public void fillRect(int x, int y, int w, int h, int pix) { + for (int ry = y; ry < y + h; ry++) + for (int rx = x; rx < x + w; rx++) + data[ry * width_ + rx] = pix; + } + + public void imageRect(int x, int y, int w, int h, int[] pix) { + for (int j = 0; j < h; j++) + System.arraycopy(pix, (w * j), data, width_ * (y + j) + x, w); + } + + public void copyRect(int x, int y, int w, int h, int srcX, int srcY) { + int dest = (width_ * y) + x; + int src = (width_ * srcY) + srcX; + int inc = width_; + + if (y > srcY) { + src += (h-1) * inc; + dest += (h-1) * inc; + inc = -inc; + } + int destEnd = dest + h * inc; + + while (dest != destEnd) { + System.arraycopy(data, src, data, dest, w); + src += inc; + dest += inc; + } + } + + public void maskRect(int x, int y, int w, int h, int[] pix, byte[] mask) { + int maskBytesPerRow = (w + 7) / 8; + + for (int j = 0; j < h; j++) { + int cy = y + j; + + if (cy < 0 || cy >= height_) + continue; + + for (int i = 0; i < w; i++) { + int cx = x + i; + + if (cx < 0 || cx >= width_) + continue; + + int byte_ = j * maskBytesPerRow + i / 8; + int bit = 7 - i % 8; + + if ((mask[byte_] & (1 << bit)) != 0) + data[cy * width_ + cx] = pix[j * w + i]; + } + } + } + + public int[] data; + public ColorModel cm; + + protected PixelFormat format; + protected int width_, height_; +} diff --git a/java/com/tigervnc/rfb/PixelFormat.java b/java/com/tigervnc/rfb/PixelFormat.java new file mode 100644 index 00000000..a8ab5f11 --- /dev/null +++ b/java/com/tigervnc/rfb/PixelFormat.java @@ -0,0 +1,163 @@ +/* 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. + */ + +// +// PixelFormat +// + +package com.tigervnc.rfb; + +import com.tigervnc.rdr.*; + +public class PixelFormat { + + public PixelFormat(int b, int d, boolean e, boolean t) { + bpp = b; + depth = d; + bigEndian = e; + trueColour = t; + } + public PixelFormat(int b, int d, boolean e, boolean t, + int rm, int gm, int bm, int rs, int gs, int bs) { + this(b, d, e, t); + redMax = rm; + greenMax = gm; + blueMax = bm; + redShift = rs; + greenShift = gs; + blueShift = bs; + } + public PixelFormat() { this(8,8,false,true,7,7,3,0,3,6); } + + public boolean equal(PixelFormat x) { + return (bpp == x.bpp && + depth == x.depth && + (bigEndian == x.bigEndian || bpp == 8) && + trueColour == x.trueColour && + (!trueColour || (redMax == x.redMax && + greenMax == x.greenMax && + blueMax == x.blueMax && + redShift == x.redShift && + greenShift == x.greenShift && + blueShift == x.blueShift))); + } + + public void read(InStream is) { + bpp = is.readU8(); + depth = is.readU8(); + bigEndian = is.readU8()!=0; + trueColour = is.readU8()!=0; + redMax = is.readU16(); + greenMax = is.readU16(); + blueMax = is.readU16(); + redShift = is.readU8(); + greenShift = is.readU8(); + blueShift = is.readU8(); + is.skip(3); + } + + public void write(OutStream os) { + os.writeU8(bpp); + os.writeU8(depth); + os.writeU8(bigEndian?1:0); + os.writeU8(trueColour?1:0); + os.writeU16(redMax); + os.writeU16(greenMax); + os.writeU16(blueMax); + os.writeU8(redShift); + os.writeU8(greenShift); + os.writeU8(blueShift); + os.pad(3); + } + + public final boolean is888() { + if(!trueColour) + return false; + if(bpp != 32) + return false; + if(depth != 24) + return false; + if(redMax != 255) + return false; + if(greenMax != 255) + return false; + if(blueMax != 255) + return false; + + return true; + } + + public void bufferFromRGB(int dst, byte[] src) { + if (bigEndian) { + dst = + (src[0] & 0xFF) << 16 | (src[1] & 0xFF) << 8 | (src[2] & 0xFF) | 0xFF << 24; + } else { + dst = + (src[2] & 0xFF) << 16 | (src[1] & 0xFF) << 8 | (src[0] & 0xFF) | 0xFF << 24; + } + } + + public String print() { + StringBuffer s = new StringBuffer(); + s.append("depth "+depth+" ("+bpp+"bpp)"); + if (bpp != 8) { + if (bigEndian) + s.append(" big-endian"); + else + s.append(" little-endian"); + } + + if (!trueColour) { + s.append(" colour-map"); + return s.toString(); + } + + if (blueShift == 0 && greenShift > blueShift && redShift > greenShift && + blueMax == (1 << greenShift) - 1 && + greenMax == (1 << (redShift-greenShift)) - 1 && + redMax == (1 << (depth-redShift)) - 1) + { + s.append(" rgb"+(depth-redShift)+(redShift-greenShift)+greenShift); + return s.toString(); + } + + if (redShift == 0 && greenShift > redShift && blueShift > greenShift && + redMax == (1 << greenShift) - 1 && + greenMax == (1 << (blueShift-greenShift)) - 1 && + blueMax == (1 << (depth-blueShift)) - 1) + { + s.append(" bgr"+(depth-blueShift)+(blueShift-greenShift)+greenShift); + return s.toString(); + } + + s.append(" rgb max "+redMax+","+greenMax+","+blueMax+" shift "+redShift+ + ","+greenShift+","+blueShift); + return s.toString(); + } + + public int bpp; + public int depth; + public boolean bigEndian; + public boolean trueColour; + public int redMax; + public int greenMax; + public int blueMax; + public int redShift; + public int greenShift; + public int blueShift; +} diff --git a/java/com/tigervnc/rfb/Point.java b/java/com/tigervnc/rfb/Point.java new file mode 100644 index 00000000..25de8c20 --- /dev/null +++ b/java/com/tigervnc/rfb/Point.java @@ -0,0 +1,40 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class Point { + + // Point + // + // Represents a point in 2D space, by X and Y coordinates. + // Can also be used to represent a delta, or offset, between + // two Points. + // Functions are provided to allow Points to be compared for + // equality and translated by a supplied offset. + // Functions are also provided to negate offset Points. + + public Point() {x=0; y=0;} + public Point(int x_, int y_) { x=x_; y=y_;} + public final Point negate() {return new Point(-x, -y);} + public final boolean equals(Point p) {return (x==p.x && y==p.y);} + public final Point translate(Point p) {return new Point(x+p.x, y+p.y);} + public final Point subtract(Point p) {return new Point(x-p.x, y-p.y);} + public int x, y; + +} diff --git a/java/com/tigervnc/rfb/RREDecoder.java b/java/com/tigervnc/rfb/RREDecoder.java new file mode 100644 index 00000000..e0ff5cfe --- /dev/null +++ b/java/com/tigervnc/rfb/RREDecoder.java @@ -0,0 +1,46 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import com.tigervnc.rdr.*; + +public class RREDecoder extends Decoder { + + public RREDecoder(CMsgReader reader_) { reader = reader_; } + + public void readRect(Rect r, CMsgHandler handler) { + InStream is = reader.getInStream(); + int bytesPerPixel = handler.cp.pf().bpp / 8; + boolean bigEndian = handler.cp.pf().bigEndian; + int nSubrects = is.readU32(); + int bg = is.readPixel(bytesPerPixel, bigEndian); + handler.fillRect(r, bg); + + for (int i = 0; i < nSubrects; i++) { + int pix = is.readPixel(bytesPerPixel, bigEndian); + int x = is.readU16(); + int y = is.readU16(); + int w = is.readU16(); + int h = is.readU16(); + handler.fillRect(new Rect(r.tl.x+x, r.tl.y+y, r.tl.x+x+w, r.tl.y+y+h), pix); + } + } + + CMsgReader reader; +} diff --git a/java/com/tigervnc/rfb/RawDecoder.java b/java/com/tigervnc/rfb/RawDecoder.java new file mode 100644 index 00000000..79db8260 --- /dev/null +++ b/java/com/tigervnc/rfb/RawDecoder.java @@ -0,0 +1,45 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class RawDecoder extends Decoder { + + public RawDecoder(CMsgReader reader_) { reader = reader_; } + + public void readRect(Rect r, CMsgHandler handler) { + int x = r.tl.x; + int y = r.tl.y; + int w = r.width(); + int h = r.height(); + int[] imageBuf = reader.getImageBuf(w * h); + int nPixels = imageBuf.length / (reader.bpp() / 8); + int bytesPerRow = w * (reader.bpp() / 8); + while (h > 0) { + int nRows = nPixels / w; + if (nRows > h) nRows = h; + reader.is.readPixels(imageBuf, w * h, (reader.bpp() / 8), handler.cp.pf().bigEndian); + handler.imageRect(new Rect(x, y, x+w, y+nRows), imageBuf); + h -= nRows; + y += nRows; + } + } + + CMsgReader reader; + static LogWriter vlog = new LogWriter("RawDecoder"); +} diff --git a/java/com/tigervnc/rfb/Rect.java b/java/com/tigervnc/rfb/Rect.java new file mode 100644 index 00000000..fab4f5dd --- /dev/null +++ b/java/com/tigervnc/rfb/Rect.java @@ -0,0 +1,89 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class Rect { + + // Rect + // + // Represents a rectangular region defined by its top-left (tl) + // and bottom-right (br) Points. + // Rects may be compared for equality, checked to determine whether + // or not they are empty, cleared (made empty), or intersected with + // one another. The bounding rectangle of two existing Rects + // may be calculated, as may the area of a Rect. + // Rects may also be translated, in the same way as Points, by + // an offset specified in a Point structure. + + public Rect() { + tl=new Point(0,0); + br=new Point(0,0); + } + public Rect(Point tl_, Point br_) { + tl=new Point(tl_.x, tl_.y); + br=new Point(br_.x,br_.y); + } + public Rect(int x1, int y1, int x2, int y2) { + tl=new Point(x1, y1); + br=new Point(x2, y2); + } + public final void setXYWH(int x, int y, int w, int h) { + tl.x = x; tl.y = y; br.x = x+w; br.y = y+h; + } + public final Rect intersect(Rect r) { + Rect result = new Rect(); + result.tl.x = Math.max(tl.x, r.tl.x); + result.tl.y = Math.max(tl.y, r.tl.y); + result.br.x = Math.max(Math.min(br.x, r.br.x), result.tl.x); + result.br.y = Math.max(Math.min(br.y, r.br.y), result.tl.y); + return result; + } + public final Rect union_boundary(Rect r) { + if (r.is_empty()) return this; + if (is_empty()) return r; + Rect result = new Rect(); + result.tl.x = Math.min(tl.x, r.tl.x); + result.tl.y = Math.min(tl.y, r.tl.y); + result.br.x = Math.max(br.x, r.br.x); + result.br.y = Math.max(br.y, r.br.y); + return result; + } + public final Rect translate(Point p) { + return new Rect(tl.translate(p), br.translate(p)); + } + public final boolean equals(Rect r) {return r.tl.equals(tl) && r.br.equals(br);} + public final boolean is_empty() {return (tl.x >= br.x) || (tl.y >= br.y);} + public final void clear() {tl = new Point(); br = new Point();} + public final boolean enclosed_by(Rect r) { + return (tl.x>=r.tl.x) && (tl.y>=r.tl.y) && (br.x<=r.br.x) && (br.y<=r.br.y); + } + public final boolean overlaps(Rect r) { + return tl.x < r.br.x && tl.y < r.br.y && br.x > r.tl.x && br.y > r.tl.y; + } + public final int area() {return is_empty() ? 0 : (br.x-tl.x)*(br.y-tl.y);} + public final Point dimensions() {return new Point(width(), height());} + public final int width() {return br.x-tl.x;} + public final int height() {return br.y-tl.y;} + public final boolean contains(Point p) { + return (tl.x<=p.x) && (tl.y<=p.y) && (br.x>p.x) && (br.y>p.y); + } + public Point tl; + public Point br; + +} diff --git a/java/com/tigervnc/rfb/Screen.java b/java/com/tigervnc/rfb/Screen.java new file mode 100644 index 00000000..90b22b6d --- /dev/null +++ b/java/com/tigervnc/rfb/Screen.java @@ -0,0 +1,48 @@ +/* Copyright 2009 Pierre Ossman for Cendio AB + * + * 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. + */ + +// Represents a single RFB virtual screen, which includes +// coordinates, an id and flags. + +package com.tigervnc.rfb; + +public class Screen { + + public Screen() { id=0; flags=0; dimensions = new Rect(); } + + public Screen(int id_, int x_, int y_, int w_, int h_, int flags_) { + id = id_; + dimensions = new Rect(x_, y_, x_+w_, y_+h_); + flags = flags_; + } + + public final static boolean operator(Screen r) { + if (id != r.id) + return false; + if (!dimensions.equals(r.dimensions)) + return false; + if (flags != r.flags) + return false; + return true; + } + + public static int id; + public static Rect dimensions; + public static int flags; + +} diff --git a/java/com/tigervnc/rfb/ScreenSet.java b/java/com/tigervnc/rfb/ScreenSet.java new file mode 100644 index 00000000..071282ff --- /dev/null +++ b/java/com/tigervnc/rfb/ScreenSet.java @@ -0,0 +1,89 @@ +/* Copyright 2009 Pierre Ossman for Cendio AB + * + * 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. + */ + +// Management class for the RFB virtual screens + +package com.tigervnc.rfb; + +import java.util.*; + +public class ScreenSet { + + // Represents a complete screen configuration, excluding framebuffer + // dimensions. + + public ScreenSet() { + screens = new ArrayList<Screen>(); + } + + public static final int num_screens() { return screens.size(); } + + public static final void add_screen(Screen screen) { screens.add(screen); } + public static final void remove_screen(int id) { + for (Iterator iter = screens.iterator(); iter.hasNext(); ) { + Screen refScreen = (Screen)iter.next(); + if (refScreen.id == id) + iter.remove(); + } + } + + public static final boolean validate(int fb_width, int fb_height) { + List<Integer> seen_ids = new ArrayList<Integer>(); + Rect fb_rect = new Rect(); + + if (screens.isEmpty()) + return false; + if (num_screens() > 255) + return false; + + fb_rect.setXYWH(0, 0, fb_width, fb_height); + + for (Iterator iter = screens.iterator(); iter.hasNext(); ) { + Screen refScreen = (Screen)iter.next(); + if (refScreen.dimensions.is_empty()) + return false; + if (!refScreen.dimensions.enclosed_by(fb_rect)) + return false; + //if (seen_ids.lastIndexOf(refScreen.id) != seen_ids.get(-1)) + // return false; + seen_ids.add(refScreen.id); + } + + return true; + } + + public final void debug_print() { + for (Iterator iter = screens.iterator(); iter.hasNext(); ) { + Screen refScreen = (Screen)iter.next(); + vlog.error(" "+refScreen.id+" (0x"+refScreen.id+"): "+ + refScreen.dimensions.width()+"x"+refScreen.dimensions.height()+ + "+"+refScreen.dimensions.tl.x+"+"+refScreen.dimensions.tl.y+ + " (flags 0x"+refScreen.flags+")"); + } + } + + // FIXME: List order shouldn't matter + //inline bool operator(const ScreenSet& r) const { return screens == r.screens; } + //inline bool operator(const ScreenSet& r) const { return screens != r.screens; } + + public static List<Screen> screens; + + static LogWriter vlog = new LogWriter("ScreenSet"); + +} + diff --git a/java/com/tigervnc/rfb/Security.java b/java/com/tigervnc/rfb/Security.java new file mode 100644 index 00000000..379851d8 --- /dev/null +++ b/java/com/tigervnc/rfb/Security.java @@ -0,0 +1,195 @@ +/* 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. + */ + +// +// SecTypes.java - constants for the various security types. +// + +package com.tigervnc.rfb; +import java.util.*; + +public class Security { + + public static final int secTypeInvalid = 0; + public static final int secTypeNone = 1; + public static final int secTypeVncAuth = 2; + + public static final int secTypeRA2 = 5; + public static final int secTypeRA2ne = 6; + + public static final int secTypeSSPI = 7; + public static final int secTypeSSPIne = 8; + + public static final int secTypeTight = 16; + public static final int secTypeUltra = 17; + public static final int secTypeTLS = 18; + public static final int secTypeVeNCrypt = 19; + + /* VeNCrypt subtypes */ + public static final int secTypePlain = 256; + public static final int secTypeTLSNone = 257; + public static final int secTypeTLSVnc = 258; + public static final int secTypeTLSPlain = 259; + public static final int secTypeX509None = 260; + public static final int secTypeX509Vnc = 261; + public static final int secTypeX509Plain = 262; + public static final int secTypeIdent = 265; + public static final int secTypeTLSIdent = 266; + public static final int secTypeX509Ident = 267; + + // result types + + public static final int secResultOK = 0; + public static final int secResultFailed = 1; + public static final int secResultTooMany = 2; // deprecated + + public Security(StringParameter secTypes) + { + String secTypesStr; + + secTypesStr = secTypes.getData(); + enabledSecTypes = parseSecTypes(secTypesStr); + + secTypesStr = null; + } + + public static List<Integer> enabledSecTypes = new ArrayList<Integer>(); + + public static final List<Integer> GetEnabledSecTypes() + { + List<Integer> result = new ArrayList<Integer>(); + + result.add(secTypeVeNCrypt); + for (Iterator i = enabledSecTypes.iterator(); i.hasNext(); ) { + int refType = (Integer)i.next(); + if (refType < 0x100) + result.add(refType); + } + + return (result); + } + + public static final List<Integer> GetEnabledExtSecTypes() + { + List<Integer> result = new ArrayList<Integer>(); + + for (Iterator i = enabledSecTypes.iterator(); i.hasNext(); ) { + int refType = (Integer)i.next(); + if (refType != secTypeVeNCrypt) /* Do not include VeNCrypt to avoid loops */ + result.add(refType); + } + + return (result); + } + + public static final void EnableSecType(int secType) + { + + for (Iterator i = enabledSecTypes.iterator(); i.hasNext(); ) + if ((Integer)i.next() == secType) + return; + + enabledSecTypes.add(secType); + } + + public boolean IsSupported(int secType) + { + Iterator i; + + for (i = enabledSecTypes.iterator(); i.hasNext(); ) + if ((Integer)i.next() == secType) + return true; + if (secType == secTypeVeNCrypt) + return true; + + return false; + } + + public static void DisableSecType(int secType) { enabledSecTypes.remove((Object)secType); } + + public static int secTypeNum(String name) { + if (name.equalsIgnoreCase("None")) return secTypeNone; + if (name.equalsIgnoreCase("VncAuth")) return secTypeVncAuth; + if (name.equalsIgnoreCase("Tight")) return secTypeTight; + if (name.equalsIgnoreCase("RA2")) return secTypeRA2; + if (name.equalsIgnoreCase("RA2ne")) return secTypeRA2ne; + if (name.equalsIgnoreCase("SSPI")) return secTypeSSPI; + if (name.equalsIgnoreCase("SSPIne")) return secTypeSSPIne; + //if (name.equalsIgnoreCase("ultra")) return secTypeUltra; + //if (name.equalsIgnoreCase("TLS")) return secTypeTLS; + if (name.equalsIgnoreCase("VeNCrypt")) return secTypeVeNCrypt; + + /* VeNCrypt subtypes */ + if (name.equalsIgnoreCase("Plain")) return secTypePlain; + if (name.equalsIgnoreCase("Ident")) return secTypeIdent; + if (name.equalsIgnoreCase("TLSNone")) return secTypeTLSNone; + if (name.equalsIgnoreCase("TLSVnc")) return secTypeTLSVnc; + if (name.equalsIgnoreCase("TLSPlain")) return secTypeTLSPlain; + if (name.equalsIgnoreCase("TLSIdent")) return secTypeTLSIdent; + if (name.equalsIgnoreCase("X509None")) return secTypeX509None; + if (name.equalsIgnoreCase("X509Vnc")) return secTypeX509Vnc; + if (name.equalsIgnoreCase("X509Plain")) return secTypeX509Plain; + if (name.equalsIgnoreCase("X509Ident")) return secTypeX509Ident; + + return secTypeInvalid; + } + + public static String secTypeName(int num) { + switch (num) { + case secTypeNone: return "None"; + case secTypeVncAuth: return "VncAuth"; + case secTypeTight: return "Tight"; + case secTypeRA2: return "RA2"; + case secTypeRA2ne: return "RA2ne"; + case secTypeSSPI: return "SSPI"; + case secTypeSSPIne: return "SSPIne"; + //case secTypeUltra: return "Ultra"; + //case secTypeTLS: return "TLS"; + case secTypeVeNCrypt: return "VeNCrypt"; + + /* VeNCrypt subtypes */ + case secTypePlain: return "Plain"; + case secTypeIdent: return "Ident"; + case secTypeTLSNone: return "TLSNone"; + case secTypeTLSVnc: return "TLSVnc"; + case secTypeTLSPlain: return "TLSPlain"; + case secTypeTLSIdent: return "TLSIdent"; + case secTypeX509None: return "X509None"; + case secTypeX509Vnc: return "X509Vnc"; + case secTypeX509Plain: return "X509Plain"; + case secTypeX509Ident: return "X509Ident"; + default: return "[unknown secType]"; + } + } + + public final static List<Integer> parseSecTypes(String types_) + { + List<Integer> result = new ArrayList<Integer>(); + String[] types = types_.split(","); + for (int i = 0; i < types.length; i++) { + int typeNum = secTypeNum(types[i]); + if (typeNum != secTypeInvalid) + result.add(typeNum); + } + return (result); + } + + public final void SetSecTypes(List<Integer> secTypes) { enabledSecTypes = secTypes; } + + static LogWriter vlog = new LogWriter("Security"); +} diff --git a/java/com/tigervnc/rfb/SecurityClient.java b/java/com/tigervnc/rfb/SecurityClient.java new file mode 100644 index 00000000..a8abd9e1 --- /dev/null +++ b/java/com/tigervnc/rfb/SecurityClient.java @@ -0,0 +1,85 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2010 TigerVNC Team + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import com.tigervnc.vncviewer.CConn; + +public class SecurityClient extends Security { + + public SecurityClient() { super(secTypes); } + + public CSecurity GetCSecurity(int secType) + { + assert (CConn.upg != null); /* (upg == null) means bug in the viewer */ + assert (msg != null); + + if (!IsSupported(secType)) + throw new Exception("Security type not supported"); + + switch (secType) { + case Security.secTypeNone: return (new CSecurityNone()); + case Security.secTypeVncAuth: return (new CSecurityVncAuth()); + case Security.secTypeVeNCrypt: return (new CSecurityVeNCrypt(this)); + case Security.secTypePlain: return (new CSecurityPlain()); + case Security.secTypeIdent: return (new CSecurityIdent()); + case Security.secTypeTLSNone: + return (new CSecurityStack(secTypeTLSNone, "TLS with no password", + new CSecurityTLS(true), null)); + case Security.secTypeTLSVnc: + return (new CSecurityStack(secTypeTLSVnc, "TLS with VNCAuth", + new CSecurityTLS(true), new CSecurityVncAuth())); + case Security.secTypeTLSPlain: + return (new CSecurityStack(secTypeTLSPlain, "TLS with Username/Password", + new CSecurityTLS(true), new CSecurityPlain())); + case Security.secTypeTLSIdent: + return (new CSecurityStack(secTypeTLSIdent, "TLS with username only", + new CSecurityTLS(true), new CSecurityIdent())); + case Security.secTypeX509None: + return (new CSecurityStack(secTypeX509None, "X509 with no password", + new CSecurityTLS(false), null)); + case Security.secTypeX509Vnc: + return (new CSecurityStack(secTypeX509Vnc, "X509 with VNCAuth", + new CSecurityTLS(false), new CSecurityVncAuth())); + case Security.secTypeX509Plain: + return (new CSecurityStack(secTypeX509Plain, "X509 with Username/Password", + new CSecurityTLS(false), new CSecurityPlain())); + case Security.secTypeX509Ident: + return (new CSecurityStack(secTypeX509Ident, "X509 with username only", + new CSecurityTLS(false), new CSecurityIdent())); + default: + throw new Exception("Security type not supported"); + } + + } + + public static void setDefaults() + { + CSecurityTLS.setDefaults(); + } + + //UserPasswdGetter upg = null; + String msg = null; + + static StringParameter secTypes + = new StringParameter("SecurityTypes", + "Specify which security scheme to use (None, VncAuth)", + "Ident,TLSIdent,X509Ident,X509Plain,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone,VncAuth,None"); + +} diff --git a/java/com/tigervnc/rfb/StringParameter.java b/java/com/tigervnc/rfb/StringParameter.java new file mode 100644 index 00000000..d7fd1a51 --- /dev/null +++ b/java/com/tigervnc/rfb/StringParameter.java @@ -0,0 +1,46 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class StringParameter extends VoidParameter { + public StringParameter(String name_, String desc_, String v) { + super(name_, desc_); + value = v; + defValue = v; + } + + public boolean setParam(String v) { + value = v; + return value != null; + } + + public boolean setDefaultStr(String v) { + value = defValue = v; + return defValue != null; + } + + public String getDefaultStr() { return defValue; } + public String getValueStr() { return value; } + + public String getValue() { return value; } + public String getData() { return value; } + + protected String value; + protected String defValue; +} diff --git a/java/com/tigervnc/rfb/TightDecoder.java b/java/com/tigervnc/rfb/TightDecoder.java new file mode 100644 index 00000000..5cce3a93 --- /dev/null +++ b/java/com/tigervnc/rfb/TightDecoder.java @@ -0,0 +1,426 @@ +/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved. + * Copyright 2004-2005 Cendio AB. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import com.tigervnc.rdr.InStream; +import com.tigervnc.rdr.ZlibInStream; +import java.awt.image.PixelGrabber; +import java.awt.Image; +import java.util.ArrayList; + +public class TightDecoder extends Decoder { + + final static int TIGHT_MAX_WIDTH = 2048; + + // Compression control + final static int rfbTightExplicitFilter = 0x04; + final static int rfbTightFill = 0x08; + final static int rfbTightJpeg = 0x09; + final static int rfbTightMaxSubencoding = 0x09; + + // Filters to improve compression efficiency + final static int rfbTightFilterCopy = 0x00; + final static int rfbTightFilterPalette = 0x01; + final static int rfbTightFilterGradient = 0x02; + final static int rfbTightMinToCompress = 12; + + public TightDecoder(CMsgReader reader_) { + reader = reader_; + zis = new ZlibInStream[4]; + for (int i = 0; i < 4; i++) + zis[i] = new ZlibInStream(); + } + + public void readRect(Rect r, CMsgHandler handler) + { + InStream is = reader.getInStream(); + int[] buf = reader.getImageBuf(r.width() * r.height()); + boolean cutZeros = false; + PixelFormat myFormat = handler.cp.pf(); + int bpp = handler.cp.pf().bpp; + if (bpp == 32) { + if (myFormat.is888()) { + cutZeros = true; + } + } + + int comp_ctl = is.readU8(); + + int bytesPerPixel = handler.cp.pf().bpp / 8; + boolean bigEndian = handler.cp.pf().bigEndian; + + // Flush zlib streams if we are told by the server to do so. + for (int i = 0; i < 4; i++) { + if ((comp_ctl & 1) != 0) { + zis[i].reset(); + } + comp_ctl >>= 1; + } + + // "Fill" compression type. + if (comp_ctl == rfbTightFill) { + int pix; + if (cutZeros) { + byte[] elem = new byte[3]; + is.readBytes(elem, 0, 3); + if (bigEndian) { + pix = + (elem[2] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[0] & 0xFF) | 0xFF << 24; + } else { + pix = + (elem[0] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[2] & 0xFF) | 0xFF << 24; + } + } else { + pix = (bpp == 8) ? is.readOpaque8() : is.readOpaque24B(); + } + handler.fillRect(r, pix); + return; + } + + // "JPEG" compression type. + if (comp_ctl == rfbTightJpeg) { + // Read length + int compressedLen = is.readCompactLength(); + if (compressedLen <= 0) + vlog.info("Incorrect data received from the server."); + + // Allocate netbuf and read in data + byte[] netbuf = new byte[compressedLen]; + is.readBytes(netbuf, 0, compressedLen); + + // Create an Image object from the JPEG data. + Image jpeg = java.awt.Toolkit.getDefaultToolkit().createImage(netbuf); + PixelGrabber pg = new PixelGrabber(jpeg, 0, 0, r.width(), r.height(), true); + try { + boolean ret = pg.grabPixels(); + if (!ret) + vlog.info("failed to grab pixels"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + Object pixels = pg.getPixels(); + buf = (pixels instanceof byte[]) ? + convertByteArrayToIntArray((byte[])pixels) : (int[])pixels; + handler.imageRect(r, buf); + return; + } + + // Quit on unsupported compression type. + if (comp_ctl > rfbTightMaxSubencoding) { + throw new Exception("TightDecoder: bad subencoding value received"); + } + + // "Basic" compression type. + int palSize = 0; + int[] palette = new int[256]; + boolean useGradient = false; + + if ((comp_ctl & rfbTightExplicitFilter) != 0) { + int filterId = is.readU8(); + + switch (filterId) { + case rfbTightFilterPalette: + palSize = is.readU8() + 1; + if (cutZeros) { + byte[] elem = new byte[3]; + for (int i = 0; i < palSize; i++) { + is.readBytes(elem, 0, 3); + if (bigEndian) { + palette[i] = + (elem[2] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[0] & 0xFF) | 0xFF << 24; + } else { + palette[i] = + (elem[0] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[2] & 0xFF) | 0xFF << 24; + } + } + } else { + for (int i = 0; i < palSize; i++) { + palette[i] = (bpp == 8) ? is.readOpaque8() : is.readOpaque24B(); + } + } + break; + case rfbTightFilterGradient: + useGradient = true; + break; + case rfbTightFilterCopy: + break; + default: + throw new Exception("TightDecoder: unknown filter code recieved"); + } + } + + int bppp = bpp; + if (palSize != 0) { + bppp = (palSize <= 2) ? 1 : 8; + } else if (cutZeros) { + bppp = 24; + } + + // Determine if the data should be decompressed or just copied. + int rowSize = (r.width() * bppp + 7) / 8; + int dataSize = r.height() * rowSize; + int streamId = -1; + InStream input; + if (dataSize < rfbTightMinToCompress) { + input = is; + } else { + int length = is.readCompactLength(); + streamId = comp_ctl & 0x03; + zis[streamId].setUnderlying(is, length); + input = (ZlibInStream)zis[streamId]; + } + + if (palSize == 0) { + // Truecolor data. + if (useGradient) { + vlog.info("useGradient"); + if (bpp == 32 && cutZeros) { + vlog.info("FilterGradient24"); + FilterGradient24(r, input, dataSize, buf, handler); + } else { + vlog.info("FilterGradient"); + FilterGradient(r, input, dataSize, buf, handler); + } + } else { + if (cutZeros) { + byte[] elem = new byte[3]; + for (int i = 0; i < r.area(); i++) { + input.readBytes(elem, 0, 3); + if (bigEndian) { + buf[i] = + (elem[2] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[0] & 0xFF) | 0xFF << 24; + } else { + buf[i] = + (elem[0] & 0xFF) << 16 | (elem[1] & 0xFF) << 8 | (elem[2] & 0xFF) | 0xFF << 24; + } + } + } else { + for (int ptr=0; ptr < dataSize; ptr++) + buf[ptr] = input.readU8(); + } + } + } else { + int x, y, b; + int ptr = 0; + int bits; + if (palSize <= 2) { + // 2-color palette + for (y = 0; y < r.height(); y++) { + for (x = 0; x < r.width() / 8; x++) { + bits = input.readU8(); + for(b = 7; b >= 0; b--) { + buf[ptr++] = palette[bits >> b & 1]; + } + } + if (r.width() % 8 != 0) { + bits = input.readU8(); + for (b = 7; b >= 8 - r.width() % 8; b--) { + buf[ptr++] = palette[bits >> b & 1]; + } + } + } + } else { + // 256-color palette + for (y = 0; y < r.height(); y++) { + for (x = 0; x < r.width(); x++) { + buf[ptr++] = palette[input.readU8()]; + } + } + } + } + + handler.imageRect(r, buf); + + if (streamId != -1) { + zis[streamId].reset(); + } + } + + private CMsgReader reader; + private ZlibInStream[] zis; + static LogWriter vlog = new LogWriter("TightDecoder"); + + private static int convertByteArrayToInt(byte[] bytes) { + return (bytes[0] << 32) | (bytes[1] << 24) | (bytes[2] << 16) | (bytes[3] << 8) | bytes[4]; + } + + private static byte[] convertIntToByteArray(int integer) { + byte[] bytes = new byte[4]; + bytes[0] =(byte)( integer >> 24 ); + bytes[1] =(byte)( (integer << 8) >> 24 ); + bytes[2] =(byte)( (integer << 16) >> 24 ); + bytes[3] =(byte)( (integer << 24) >> 24 ); + return bytes; + } + private static int[] convertByteArrayToIntArray(byte[] bytes) { + vlog.info("convertByteArrayToIntArray"); + ArrayList<Integer> integers = new ArrayList<Integer>(); + for (int index = 0; index < bytes.length; index += 4) { + byte[] fourBytes = new byte[4]; + fourBytes[0] = bytes[index]; + fourBytes[1] = bytes[index+1]; + fourBytes[2] = bytes[index+2]; + fourBytes[3] = bytes[index+3]; + int integer = convertByteArrayToInt(fourBytes); + integers.add(new Integer(integer)); + } + int[] ints = new int[bytes.length/4]; + for (int index = 0; index < integers.size() ; index++) { + ints[index] = (integers.get(index)).intValue(); + } + return ints; + } + + private static byte[] convertIntArrayToByteArray(int[] integers) { + byte[] bytes = new byte[integers.length*4]; + for (int index = 0; index < integers.length; index++) { + byte[] integerBytes = convertIntToByteArray(integers[index]); + bytes[index*4] = integerBytes[0]; + bytes[1 + (index*4)] = integerBytes[1]; + bytes[2 + (index*4)] = integerBytes[2]; + bytes[3 + (index*4)] = integerBytes[3]; + } + return bytes; + } + + // + // Decode data processed with the "Gradient" filter. + // + + final private void FilterGradient24(Rect r, InStream is, int dataSize, int[] buf, CMsgHandler handler) { + + int x, y, c; + int[] prevRow = new int[TIGHT_MAX_WIDTH * 3]; + int[] thisRow = new int[TIGHT_MAX_WIDTH * 3]; + int[] pix = new int[3]; + int[] est = new int[3]; + + // Allocate netbuf and read in data + int[] netbuf = new int[dataSize]; + for (int i = 0; i < dataSize; i++) + netbuf[i] = is.readU8(); + //is.readBytes(netbuf, 0, dataSize); + + PixelFormat myFormat = handler.cp.pf(); + int rectHeight = r.height(); + int rectWidth = r.width(); + + for (y = 0; y < rectHeight; y++) { + /* First pixel in a row */ + for (c = 0; c < 3; c++) { + pix[c] = netbuf[y*rectWidth*3+c] + prevRow[c]; + thisRow[c] = pix[c]; + } + if (myFormat.bigEndian) { + buf[y*rectWidth] = + (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF) | 0xFF << 24; + } else { + buf[y*rectWidth] = + (pix[2] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[0] & 0xFF) | 0xFF << 24; + } + + /* Remaining pixels of a row */ + for (x = 1; x < rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c]; + if (est[c] > 0xFF) { + est[c] = 0xFF; + } else if (est[c] < 0) { + est[c] = 0; + } + pix[c] = netbuf[(y*rectWidth+x)*3+c] + est[c]; + thisRow[x*3+c] = pix[c]; + } + if (myFormat.bigEndian) { + buf[y*rectWidth] = + (pix[2] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[0] & 0xFF) | 0xFF << 24; + } else { + buf[y*rectWidth] = + (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF) | 0xFF << 24; + } + } + + System.arraycopy(thisRow, 0, prevRow, 0, prevRow.length); + } + } + + final private void FilterGradient(Rect r, InStream is, int dataSize, int[] buf, CMsgHandler handler) { + + int x, y, c; + int[] prevRow = new int[TIGHT_MAX_WIDTH]; + int[] thisRow = new int[TIGHT_MAX_WIDTH]; + int[] pix = new int[3]; + int[] est = new int[3]; + + // Allocate netbuf and read in data + int[] netbuf = new int[dataSize]; + for (int i = 0; i < dataSize; i++) + netbuf[i] = is.readU8(); + //is.readBytes(netbuf, 0, dataSize); + + PixelFormat myFormat = handler.cp.pf(); + int rectHeight = r.height(); + int rectWidth = r.width(); + + for (y = 0; y < rectHeight; y++) { + /* First pixel in a row */ + if (myFormat.bigEndian) { + buf[y*rectWidth] = + (pix[2] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[0] & 0xFF) | 0xFF << 24; + } else { + buf[y*rectWidth] = + (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF) | 0xFF << 24; + } + for (c = 0; c < 3; c++) + pix[c] += prevRow[c]; + + /* Remaining pixels of a row */ + for (x = 1; x < rectWidth; x++) { + for (c = 0; c < 3; c++) { + est[c] = prevRow[x*3+c] + pix[c] - prevRow[(x-1)*3+c]; + if (est[c] > 255) { + est[c] = 255; + } else if (est[c] < 0) { + est[c] = 0; + } + } + + // FIXME? + System.arraycopy(pix, 0, netbuf, 0, netbuf.length); + for (c = 0; c < 3; c++) + pix[c] += est[c]; + + System.arraycopy(thisRow, x*3, pix, 0, pix.length); + + if (myFormat.bigEndian) { + buf[y*rectWidth+x] = + (pix[2] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[0] & 0xFF) | 0xFF << 24; + } else { + buf[y*rectWidth+x] = + (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF) | 0xFF << 24; + } + + } + + System.arraycopy(thisRow, 0, prevRow, 0, prevRow.length); + } + } + +} diff --git a/java/com/tigervnc/rfb/UnicodeToKeysym.java b/java/com/tigervnc/rfb/UnicodeToKeysym.java new file mode 100644 index 00000000..44f61ba6 --- /dev/null +++ b/java/com/tigervnc/rfb/UnicodeToKeysym.java @@ -0,0 +1,795 @@ +/* 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. + */ +// +// UnicodeToKeysym - provides a translate() method to convert from unicode +// characters to the equivalent X keysym. +// + +package com.tigervnc.rfb; + +public class UnicodeToKeysym { + + public static short[][] table = { + { 0x03c0, 0x0100 }, + { 0x03e0, 0x0101 }, + { 0x01c3, 0x0102 }, + { 0x01e3, 0x0103 }, + { 0x01a1, 0x0104 }, + { 0x01b1, 0x0105 }, + { 0x01c6, 0x0106 }, + { 0x01e6, 0x0107 }, + { 0x02c6, 0x0108 }, + { 0x02e6, 0x0109 }, + { 0x02c5, 0x010a }, + { 0x02e5, 0x010b }, + { 0x01c8, 0x010c }, + { 0x01e8, 0x010d }, + { 0x01cf, 0x010e }, + { 0x01ef, 0x010f }, + { 0x01d0, 0x0110 }, + { 0x01f0, 0x0111 }, + { 0x03aa, 0x0112 }, + { 0x03ba, 0x0113 }, + { 0x03cc, 0x0116 }, + { 0x03ec, 0x0117 }, + { 0x01ca, 0x0118 }, + { 0x01ea, 0x0119 }, + { 0x01cc, 0x011a }, + { 0x01ec, 0x011b }, + { 0x02d8, 0x011c }, + { 0x02f8, 0x011d }, + { 0x02ab, 0x011e }, + { 0x02bb, 0x011f }, + { 0x02d5, 0x0120 }, + { 0x02f5, 0x0121 }, + { 0x03ab, 0x0122 }, + { 0x03bb, 0x0123 }, + { 0x02a6, 0x0124 }, + { 0x02b6, 0x0125 }, + { 0x02a1, 0x0126 }, + { 0x02b1, 0x0127 }, + { 0x03a5, 0x0128 }, + { 0x03b5, 0x0129 }, + { 0x03cf, 0x012a }, + { 0x03ef, 0x012b }, + { 0x03c7, 0x012e }, + { 0x03e7, 0x012f }, + { 0x02a9, 0x0130 }, + { 0x02b9, 0x0131 }, + { 0x02ac, 0x0134 }, + { 0x02bc, 0x0135 }, + { 0x03d3, 0x0136 }, + { 0x03f3, 0x0137 }, + { 0x03a2, 0x0138 }, + { 0x01c5, 0x0139 }, + { 0x01e5, 0x013a }, + { 0x03a6, 0x013b }, + { 0x03b6, 0x013c }, + { 0x01a5, 0x013d }, + { 0x01b5, 0x013e }, + { 0x01a3, 0x0141 }, + { 0x01b3, 0x0142 }, + { 0x01d1, 0x0143 }, + { 0x01f1, 0x0144 }, + { 0x03d1, 0x0145 }, + { 0x03f1, 0x0146 }, + { 0x01d2, 0x0147 }, + { 0x01f2, 0x0148 }, + { 0x03bd, 0x014a }, + { 0x03bf, 0x014b }, + { 0x03d2, 0x014c }, + { 0x03f2, 0x014d }, + { 0x01d5, 0x0150 }, + { 0x01f5, 0x0151 }, + { 0x13bc, 0x0152 }, + { 0x13bd, 0x0153 }, + { 0x01c0, 0x0154 }, + { 0x01e0, 0x0155 }, + { 0x03a3, 0x0156 }, + { 0x03b3, 0x0157 }, + { 0x01d8, 0x0158 }, + { 0x01f8, 0x0159 }, + { 0x01a6, 0x015a }, + { 0x01b6, 0x015b }, + { 0x02de, 0x015c }, + { 0x02fe, 0x015d }, + { 0x01aa, 0x015e }, + { 0x01ba, 0x015f }, + { 0x01a9, 0x0160 }, + { 0x01b9, 0x0161 }, + { 0x01de, 0x0162 }, + { 0x01fe, 0x0163 }, + { 0x01ab, 0x0164 }, + { 0x01bb, 0x0165 }, + { 0x03ac, 0x0166 }, + { 0x03bc, 0x0167 }, + { 0x03dd, 0x0168 }, + { 0x03fd, 0x0169 }, + { 0x03de, 0x016a }, + { 0x03fe, 0x016b }, + { 0x02dd, 0x016c }, + { 0x02fd, 0x016d }, + { 0x01d9, 0x016e }, + { 0x01f9, 0x016f }, + { 0x01db, 0x0170 }, + { 0x01fb, 0x0171 }, + { 0x03d9, 0x0172 }, + { 0x03f9, 0x0173 }, + { 0x13be, 0x0178 }, + { 0x01ac, 0x0179 }, + { 0x01bc, 0x017a }, + { 0x01af, 0x017b }, + { 0x01bf, 0x017c }, + { 0x01ae, 0x017d }, + { 0x01be, 0x017e }, + { 0x08f6, 0x0192 }, + { 0x01b7, 0x02c7 }, + { 0x01a2, 0x02d8 }, + { 0x01ff, 0x02d9 }, + { 0x01b2, 0x02db }, + { 0x01bd, 0x02dd }, + { 0x07ae, 0x0385 }, + { 0x07a1, 0x0386 }, + { 0x07a2, 0x0388 }, + { 0x07a3, 0x0389 }, + { 0x07a4, 0x038a }, + { 0x07a7, 0x038c }, + { 0x07a8, 0x038e }, + { 0x07ab, 0x038f }, + { 0x07b6, 0x0390 }, + { 0x07c1, 0x0391 }, + { 0x07c2, 0x0392 }, + { 0x07c3, 0x0393 }, + { 0x07c4, 0x0394 }, + { 0x07c5, 0x0395 }, + { 0x07c6, 0x0396 }, + { 0x07c7, 0x0397 }, + { 0x07c8, 0x0398 }, + { 0x07c9, 0x0399 }, + { 0x07ca, 0x039a }, + { 0x07cb, 0x039b }, + { 0x07cc, 0x039c }, + { 0x07cd, 0x039d }, + { 0x07ce, 0x039e }, + { 0x07cf, 0x039f }, + { 0x07d0, 0x03a0 }, + { 0x07d1, 0x03a1 }, + { 0x07d2, 0x03a3 }, + { 0x07d4, 0x03a4 }, + { 0x07d5, 0x03a5 }, + { 0x07d6, 0x03a6 }, + { 0x07d7, 0x03a7 }, + { 0x07d8, 0x03a8 }, + { 0x07d9, 0x03a9 }, + { 0x07a5, 0x03aa }, + { 0x07a9, 0x03ab }, + { 0x07b1, 0x03ac }, + { 0x07b2, 0x03ad }, + { 0x07b3, 0x03ae }, + { 0x07b4, 0x03af }, + { 0x07ba, 0x03b0 }, + { 0x07e1, 0x03b1 }, + { 0x07e2, 0x03b2 }, + { 0x07e3, 0x03b3 }, + { 0x07e4, 0x03b4 }, + { 0x07e5, 0x03b5 }, + { 0x07e6, 0x03b6 }, + { 0x07e7, 0x03b7 }, + { 0x07e8, 0x03b8 }, + { 0x07e9, 0x03b9 }, + { 0x07ea, 0x03ba }, + { 0x07eb, 0x03bb }, + { 0x07ec, 0x03bc }, + { 0x07ed, 0x03bd }, + { 0x07ee, 0x03be }, + { 0x07ef, 0x03bf }, + { 0x07f0, 0x03c0 }, + { 0x07f1, 0x03c1 }, + { 0x07f3, 0x03c2 }, + { 0x07f2, 0x03c3 }, + { 0x07f4, 0x03c4 }, + { 0x07f5, 0x03c5 }, + { 0x07f6, 0x03c6 }, + { 0x07f7, 0x03c7 }, + { 0x07f8, 0x03c8 }, + { 0x07f9, 0x03c9 }, + { 0x07b5, 0x03ca }, + { 0x07b9, 0x03cb }, + { 0x07b7, 0x03cc }, + { 0x07b8, 0x03cd }, + { 0x07bb, 0x03ce }, + { 0x06b3, 0x0401 }, + { 0x06b1, 0x0402 }, + { 0x06b2, 0x0403 }, + { 0x06b4, 0x0404 }, + { 0x06b5, 0x0405 }, + { 0x06b6, 0x0406 }, + { 0x06b7, 0x0407 }, + { 0x06b8, 0x0408 }, + { 0x06b9, 0x0409 }, + { 0x06ba, 0x040a }, + { 0x06bb, 0x040b }, + { 0x06bc, 0x040c }, + { 0x06be, 0x040e }, + { 0x06bf, 0x040f }, + { 0x06e1, 0x0410 }, + { 0x06e2, 0x0411 }, + { 0x06f7, 0x0412 }, + { 0x06e7, 0x0413 }, + { 0x06e4, 0x0414 }, + { 0x06e5, 0x0415 }, + { 0x06f6, 0x0416 }, + { 0x06fa, 0x0417 }, + { 0x06e9, 0x0418 }, + { 0x06ea, 0x0419 }, + { 0x06eb, 0x041a }, + { 0x06ec, 0x041b }, + { 0x06ed, 0x041c }, + { 0x06ee, 0x041d }, + { 0x06ef, 0x041e }, + { 0x06f0, 0x041f }, + { 0x06f2, 0x0420 }, + { 0x06f3, 0x0421 }, + { 0x06f4, 0x0422 }, + { 0x06f5, 0x0423 }, + { 0x06e6, 0x0424 }, + { 0x06e8, 0x0425 }, + { 0x06e3, 0x0426 }, + { 0x06fe, 0x0427 }, + { 0x06fb, 0x0428 }, + { 0x06fd, 0x0429 }, + { 0x06ff, 0x042a }, + { 0x06f9, 0x042b }, + { 0x06f8, 0x042c }, + { 0x06fc, 0x042d }, + { 0x06e0, 0x042e }, + { 0x06f1, 0x042f }, + { 0x06c1, 0x0430 }, + { 0x06c2, 0x0431 }, + { 0x06d7, 0x0432 }, + { 0x06c7, 0x0433 }, + { 0x06c4, 0x0434 }, + { 0x06c5, 0x0435 }, + { 0x06d6, 0x0436 }, + { 0x06da, 0x0437 }, + { 0x06c9, 0x0438 }, + { 0x06ca, 0x0439 }, + { 0x06cb, 0x043a }, + { 0x06cc, 0x043b }, + { 0x06cd, 0x043c }, + { 0x06ce, 0x043d }, + { 0x06cf, 0x043e }, + { 0x06d0, 0x043f }, + { 0x06d2, 0x0440 }, + { 0x06d3, 0x0441 }, + { 0x06d4, 0x0442 }, + { 0x06d5, 0x0443 }, + { 0x06c6, 0x0444 }, + { 0x06c8, 0x0445 }, + { 0x06c3, 0x0446 }, + { 0x06de, 0x0447 }, + { 0x06db, 0x0448 }, + { 0x06dd, 0x0449 }, + { 0x06df, 0x044a }, + { 0x06d9, 0x044b }, + { 0x06d8, 0x044c }, + { 0x06dc, 0x044d }, + { 0x06c0, 0x044e }, + { 0x06d1, 0x044f }, + { 0x06a3, 0x0451 }, + { 0x06a1, 0x0452 }, + { 0x06a2, 0x0453 }, + { 0x06a4, 0x0454 }, + { 0x06a5, 0x0455 }, + { 0x06a6, 0x0456 }, + { 0x06a7, 0x0457 }, + { 0x06a8, 0x0458 }, + { 0x06a9, 0x0459 }, + { 0x06aa, 0x045a }, + { 0x06ab, 0x045b }, + { 0x06ac, 0x045c }, + { 0x06ae, 0x045e }, + { 0x06af, 0x045f }, + { 0x0ce0, 0x05d0 }, + { 0x0ce1, 0x05d1 }, + { 0x0ce2, 0x05d2 }, + { 0x0ce3, 0x05d3 }, + { 0x0ce4, 0x05d4 }, + { 0x0ce5, 0x05d5 }, + { 0x0ce6, 0x05d6 }, + { 0x0ce7, 0x05d7 }, + { 0x0ce8, 0x05d8 }, + { 0x0ce9, 0x05d9 }, + { 0x0cea, 0x05da }, + { 0x0ceb, 0x05db }, + { 0x0cec, 0x05dc }, + { 0x0ced, 0x05dd }, + { 0x0cee, 0x05de }, + { 0x0cef, 0x05df }, + { 0x0cf0, 0x05e0 }, + { 0x0cf1, 0x05e1 }, + { 0x0cf2, 0x05e2 }, + { 0x0cf3, 0x05e3 }, + { 0x0cf4, 0x05e4 }, + { 0x0cf5, 0x05e5 }, + { 0x0cf6, 0x05e6 }, + { 0x0cf7, 0x05e7 }, + { 0x0cf8, 0x05e8 }, + { 0x0cf9, 0x05e9 }, + { 0x0cfa, 0x05ea }, + { 0x05ac, 0x060c }, + { 0x05bb, 0x061b }, + { 0x05bf, 0x061f }, + { 0x05c1, 0x0621 }, + { 0x05c2, 0x0622 }, + { 0x05c3, 0x0623 }, + { 0x05c4, 0x0624 }, + { 0x05c5, 0x0625 }, + { 0x05c6, 0x0626 }, + { 0x05c7, 0x0627 }, + { 0x05c8, 0x0628 }, + { 0x05c9, 0x0629 }, + { 0x05ca, 0x062a }, + { 0x05cb, 0x062b }, + { 0x05cc, 0x062c }, + { 0x05cd, 0x062d }, + { 0x05ce, 0x062e }, + { 0x05cf, 0x062f }, + { 0x05d0, 0x0630 }, + { 0x05d1, 0x0631 }, + { 0x05d2, 0x0632 }, + { 0x05d3, 0x0633 }, + { 0x05d4, 0x0634 }, + { 0x05d5, 0x0635 }, + { 0x05d6, 0x0636 }, + { 0x05d7, 0x0637 }, + { 0x05d8, 0x0638 }, + { 0x05d9, 0x0639 }, + { 0x05da, 0x063a }, + { 0x05e0, 0x0640 }, + { 0x05e1, 0x0641 }, + { 0x05e2, 0x0642 }, + { 0x05e3, 0x0643 }, + { 0x05e4, 0x0644 }, + { 0x05e5, 0x0645 }, + { 0x05e6, 0x0646 }, + { 0x05e7, 0x0647 }, + { 0x05e8, 0x0648 }, + { 0x05e9, 0x0649 }, + { 0x05ea, 0x064a }, + { 0x05eb, 0x064b }, + { 0x05ec, 0x064c }, + { 0x05ed, 0x064d }, + { 0x05ee, 0x064e }, + { 0x05ef, 0x064f }, + { 0x05f0, 0x0650 }, + { 0x05f1, 0x0651 }, + { 0x05f2, 0x0652 }, + { 0x0da1, 0x0e01 }, + { 0x0da2, 0x0e02 }, + { 0x0da3, 0x0e03 }, + { 0x0da4, 0x0e04 }, + { 0x0da5, 0x0e05 }, + { 0x0da6, 0x0e06 }, + { 0x0da7, 0x0e07 }, + { 0x0da8, 0x0e08 }, + { 0x0da9, 0x0e09 }, + { 0x0daa, 0x0e0a }, + { 0x0dab, 0x0e0b }, + { 0x0dac, 0x0e0c }, + { 0x0dad, 0x0e0d }, + { 0x0dae, 0x0e0e }, + { 0x0daf, 0x0e0f }, + { 0x0db0, 0x0e10 }, + { 0x0db1, 0x0e11 }, + { 0x0db2, 0x0e12 }, + { 0x0db3, 0x0e13 }, + { 0x0db4, 0x0e14 }, + { 0x0db5, 0x0e15 }, + { 0x0db6, 0x0e16 }, + { 0x0db7, 0x0e17 }, + { 0x0db8, 0x0e18 }, + { 0x0db9, 0x0e19 }, + { 0x0dba, 0x0e1a }, + { 0x0dbb, 0x0e1b }, + { 0x0dbc, 0x0e1c }, + { 0x0dbd, 0x0e1d }, + { 0x0dbe, 0x0e1e }, + { 0x0dbf, 0x0e1f }, + { 0x0dc0, 0x0e20 }, + { 0x0dc1, 0x0e21 }, + { 0x0dc2, 0x0e22 }, + { 0x0dc3, 0x0e23 }, + { 0x0dc4, 0x0e24 }, + { 0x0dc5, 0x0e25 }, + { 0x0dc6, 0x0e26 }, + { 0x0dc7, 0x0e27 }, + { 0x0dc8, 0x0e28 }, + { 0x0dc9, 0x0e29 }, + { 0x0dca, 0x0e2a }, + { 0x0dcb, 0x0e2b }, + { 0x0dcc, 0x0e2c }, + { 0x0dcd, 0x0e2d }, + { 0x0dce, 0x0e2e }, + { 0x0dcf, 0x0e2f }, + { 0x0dd0, 0x0e30 }, + { 0x0dd1, 0x0e31 }, + { 0x0dd2, 0x0e32 }, + { 0x0dd3, 0x0e33 }, + { 0x0dd4, 0x0e34 }, + { 0x0dd5, 0x0e35 }, + { 0x0dd6, 0x0e36 }, + { 0x0dd7, 0x0e37 }, + { 0x0dd8, 0x0e38 }, + { 0x0dd9, 0x0e39 }, + { 0x0dda, 0x0e3a }, + { 0x0ddf, 0x0e3f }, + { 0x0de0, 0x0e40 }, + { 0x0de1, 0x0e41 }, + { 0x0de2, 0x0e42 }, + { 0x0de3, 0x0e43 }, + { 0x0de4, 0x0e44 }, + { 0x0de5, 0x0e45 }, + { 0x0de6, 0x0e46 }, + { 0x0de7, 0x0e47 }, + { 0x0de8, 0x0e48 }, + { 0x0de9, 0x0e49 }, + { 0x0dea, 0x0e4a }, + { 0x0deb, 0x0e4b }, + { 0x0dec, 0x0e4c }, + { 0x0ded, 0x0e4d }, + { 0x0df0, 0x0e50 }, + { 0x0df1, 0x0e51 }, + { 0x0df2, 0x0e52 }, + { 0x0df3, 0x0e53 }, + { 0x0df4, 0x0e54 }, + { 0x0df5, 0x0e55 }, + { 0x0df6, 0x0e56 }, + { 0x0df7, 0x0e57 }, + { 0x0df8, 0x0e58 }, + { 0x0df9, 0x0e59 }, + { 0x0ed4, 0x11a8 }, + { 0x0ed5, 0x11a9 }, + { 0x0ed6, 0x11aa }, + { 0x0ed7, 0x11ab }, + { 0x0ed8, 0x11ac }, + { 0x0ed9, 0x11ad }, + { 0x0eda, 0x11ae }, + { 0x0edb, 0x11af }, + { 0x0edc, 0x11b0 }, + { 0x0edd, 0x11b1 }, + { 0x0ede, 0x11b2 }, + { 0x0edf, 0x11b3 }, + { 0x0ee0, 0x11b4 }, + { 0x0ee1, 0x11b5 }, + { 0x0ee2, 0x11b6 }, + { 0x0ee3, 0x11b7 }, + { 0x0ee4, 0x11b8 }, + { 0x0ee5, 0x11b9 }, + { 0x0ee6, 0x11ba }, + { 0x0ee7, 0x11bb }, + { 0x0ee8, 0x11bc }, + { 0x0ee9, 0x11bd }, + { 0x0eea, 0x11be }, + { 0x0eeb, 0x11bf }, + { 0x0eec, 0x11c0 }, + { 0x0eed, 0x11c1 }, + { 0x0eee, 0x11c2 }, + { 0x0ef8, 0x11eb }, + { 0x0ef9, 0x11f0 }, + { 0x0efa, 0x11f9 }, + { 0x0aa2, 0x2002 }, + { 0x0aa1, 0x2003 }, + { 0x0aa3, 0x2004 }, + { 0x0aa4, 0x2005 }, + { 0x0aa5, 0x2007 }, + { 0x0aa6, 0x2008 }, + { 0x0aa7, 0x2009 }, + { 0x0aa8, 0x200a }, + { 0x0abb, 0x2012 }, + { 0x0aaa, 0x2013 }, + { 0x0aa9, 0x2014 }, + { 0x07af, 0x2015 }, + { 0x0cdf, 0x2017 }, + { 0x0ad0, 0x2018 }, + { 0x0ad1, 0x2019 }, + { 0x0afd, 0x201a }, + { 0x0ad2, 0x201c }, + { 0x0ad3, 0x201d }, + { 0x0afe, 0x201e }, + { 0x0af1, 0x2020 }, + { 0x0af2, 0x2021 }, + { 0x0ae6, 0x2022 }, + { 0x0aaf, 0x2025 }, + { 0x0aae, 0x2026 }, + { 0x0ad6, 0x2032 }, + { 0x0ad7, 0x2033 }, + { 0x0afc, 0x2038 }, + { 0x047e, 0x203e }, + { 0x0eff, 0x20a9 }, + { 0x20ac, 0x20ac }, + { 0x0ab8, 0x2105 }, + { 0x06b0, 0x2116 }, + { 0x0afb, 0x2117 }, + { 0x0ad4, 0x211e }, + { 0x0ac9, 0x2122 }, + { 0x0ab0, 0x2153 }, + { 0x0ab1, 0x2154 }, + { 0x0ab2, 0x2155 }, + { 0x0ab3, 0x2156 }, + { 0x0ab4, 0x2157 }, + { 0x0ab5, 0x2158 }, + { 0x0ab6, 0x2159 }, + { 0x0ab7, 0x215a }, + { 0x0ac3, 0x215b }, + { 0x0ac4, 0x215c }, + { 0x0ac5, 0x215d }, + { 0x0ac6, 0x215e }, + { 0x08fb, 0x2190 }, + { 0x08fc, 0x2191 }, + { 0x08fd, 0x2192 }, + { 0x08fe, 0x2193 }, + { 0x08ce, 0x21d2 }, + { 0x08cd, 0x21d4 }, + { 0x08ef, 0x2202 }, + { 0x08c5, 0x2207 }, + { 0x0bca, 0x2218 }, + { 0x08d6, 0x221a }, + { 0x08c1, 0x221d }, + { 0x08c2, 0x221e }, + { 0x08de, 0x2227 }, + { 0x08df, 0x2228 }, + { 0x08dc, 0x2229 }, + { 0x08dd, 0x222a }, + { 0x08bf, 0x222b }, + { 0x08c0, 0x2234 }, + { 0x08c8, 0x223c }, + { 0x08c9, 0x2243 }, + { 0x08bd, 0x2260 }, + { 0x08cf, 0x2261 }, + { 0x08bc, 0x2264 }, + { 0x08be, 0x2265 }, + { 0x08da, 0x2282 }, + { 0x08db, 0x2283 }, + { 0x0bfc, 0x22a2 }, + { 0x0bdc, 0x22a3 }, + { 0x0bce, 0x22a4 }, + { 0x0bc2, 0x22a5 }, + { 0x0bd3, 0x2308 }, + { 0x0bc4, 0x230a }, + { 0x0afa, 0x2315 }, + { 0x08a4, 0x2320 }, + { 0x08a5, 0x2321 }, + { 0x0abc, 0x2329 }, + { 0x0abe, 0x232a }, + { 0x0bcc, 0x2395 }, + { 0x08ab, 0x239b }, + { 0x08ac, 0x239d }, + { 0x08ad, 0x239e }, + { 0x08ae, 0x23a0 }, + { 0x08a7, 0x23a1 }, + { 0x08a8, 0x23a3 }, + { 0x08a9, 0x23a4 }, + { 0x08aa, 0x23a6 }, + { 0x08af, 0x23a8 }, + { 0x08b0, 0x23ac }, + { 0x08a1, 0x23b7 }, + { 0x09ef, 0x23ba }, + { 0x09f0, 0x23bb }, + { 0x09f2, 0x23bc }, + { 0x09f3, 0x23bd }, + { 0x09e2, 0x2409 }, + { 0x09e5, 0x240a }, + { 0x09e9, 0x240b }, + { 0x09e3, 0x240c }, + { 0x09e4, 0x240d }, + { 0x09e8, 0x2424 }, + { 0x09f1, 0x2500 }, + { 0x09f8, 0x2502 }, + { 0x09ec, 0x250c }, + { 0x09eb, 0x2510 }, + { 0x09ed, 0x2514 }, + { 0x09ea, 0x2518 }, + { 0x09f4, 0x251c }, + { 0x09f5, 0x2524 }, + { 0x09f7, 0x252c }, + { 0x09f6, 0x2534 }, + { 0x09ee, 0x253c }, + { 0x09e1, 0x2592 }, + { 0x0ae7, 0x25aa }, + { 0x0ae1, 0x25ab }, + { 0x0adb, 0x25ac }, + { 0x0ae2, 0x25ad }, + { 0x0adf, 0x25ae }, + { 0x0acf, 0x25af }, + { 0x0ae8, 0x25b2 }, + { 0x0ae3, 0x25b3 }, + { 0x0add, 0x25b6 }, + { 0x0acd, 0x25b7 }, + { 0x0ae9, 0x25bc }, + { 0x0ae4, 0x25bd }, + { 0x0adc, 0x25c0 }, + { 0x0acc, 0x25c1 }, + { 0x09e0, 0x25c6 }, + { 0x0ace, 0x25cb }, + { 0x0ade, 0x25cf }, + { 0x0ae0, 0x25e6 }, + { 0x0ae5, 0x2606 }, + { 0x0af9, 0x260e }, + { 0x0aca, 0x2613 }, + { 0x0aea, 0x261c }, + { 0x0aeb, 0x261e }, + { 0x0af8, 0x2640 }, + { 0x0af7, 0x2642 }, + { 0x0aec, 0x2663 }, + { 0x0aee, 0x2665 }, + { 0x0aed, 0x2666 }, + { 0x0af6, 0x266d }, + { 0x0af5, 0x266f }, + { 0x0af3, 0x2713 }, + { 0x0af4, 0x2717 }, + { 0x0ad9, 0x271d }, + { 0x0af0, 0x2720 }, + { 0x04a4, 0x3001 }, + { 0x04a1, 0x3002 }, + { 0x04a2, 0x300c }, + { 0x04a3, 0x300d }, + { 0x04de, 0x309b }, + { 0x04df, 0x309c }, + { 0x04a7, 0x30a1 }, + { 0x04b1, 0x30a2 }, + { 0x04a8, 0x30a3 }, + { 0x04b2, 0x30a4 }, + { 0x04a9, 0x30a5 }, + { 0x04b3, 0x30a6 }, + { 0x04aa, 0x30a7 }, + { 0x04b4, 0x30a8 }, + { 0x04ab, 0x30a9 }, + { 0x04b5, 0x30aa }, + { 0x04b6, 0x30ab }, + { 0x04b7, 0x30ad }, + { 0x04b8, 0x30af }, + { 0x04b9, 0x30b1 }, + { 0x04ba, 0x30b3 }, + { 0x04bb, 0x30b5 }, + { 0x04bc, 0x30b7 }, + { 0x04bd, 0x30b9 }, + { 0x04be, 0x30bb }, + { 0x04bf, 0x30bd }, + { 0x04c0, 0x30bf }, + { 0x04c1, 0x30c1 }, + { 0x04af, 0x30c3 }, + { 0x04c2, 0x30c4 }, + { 0x04c3, 0x30c6 }, + { 0x04c4, 0x30c8 }, + { 0x04c5, 0x30ca }, + { 0x04c6, 0x30cb }, + { 0x04c7, 0x30cc }, + { 0x04c8, 0x30cd }, + { 0x04c9, 0x30ce }, + { 0x04ca, 0x30cf }, + { 0x04cb, 0x30d2 }, + { 0x04cc, 0x30d5 }, + { 0x04cd, 0x30d8 }, + { 0x04ce, 0x30db }, + { 0x04cf, 0x30de }, + { 0x04d0, 0x30df }, + { 0x04d1, 0x30e0 }, + { 0x04d2, 0x30e1 }, + { 0x04d3, 0x30e2 }, + { 0x04ac, 0x30e3 }, + { 0x04d4, 0x30e4 }, + { 0x04ad, 0x30e5 }, + { 0x04d5, 0x30e6 }, + { 0x04ae, 0x30e7 }, + { 0x04d6, 0x30e8 }, + { 0x04d7, 0x30e9 }, + { 0x04d8, 0x30ea }, + { 0x04d9, 0x30eb }, + { 0x04da, 0x30ec }, + { 0x04db, 0x30ed }, + { 0x04dc, 0x30ef }, + { 0x04a6, 0x30f2 }, + { 0x04dd, 0x30f3 }, + { 0x04a5, 0x30fb }, + { 0x04b0, 0x30fc }, + { 0x0ea1, 0x3131 }, + { 0x0ea2, 0x3132 }, + { 0x0ea3, 0x3133 }, + { 0x0ea4, 0x3134 }, + { 0x0ea5, 0x3135 }, + { 0x0ea6, 0x3136 }, + { 0x0ea7, 0x3137 }, + { 0x0ea8, 0x3138 }, + { 0x0ea9, 0x3139 }, + { 0x0eaa, 0x313a }, + { 0x0eab, 0x313b }, + { 0x0eac, 0x313c }, + { 0x0ead, 0x313d }, + { 0x0eae, 0x313e }, + { 0x0eaf, 0x313f }, + { 0x0eb0, 0x3140 }, + { 0x0eb1, 0x3141 }, + { 0x0eb2, 0x3142 }, + { 0x0eb3, 0x3143 }, + { 0x0eb4, 0x3144 }, + { 0x0eb5, 0x3145 }, + { 0x0eb6, 0x3146 }, + { 0x0eb7, 0x3147 }, + { 0x0eb8, 0x3148 }, + { 0x0eb9, 0x3149 }, + { 0x0eba, 0x314a }, + { 0x0ebb, 0x314b }, + { 0x0ebc, 0x314c }, + { 0x0ebd, 0x314d }, + { 0x0ebe, 0x314e }, + { 0x0ebf, 0x314f }, + { 0x0ec0, 0x3150 }, + { 0x0ec1, 0x3151 }, + { 0x0ec2, 0x3152 }, + { 0x0ec3, 0x3153 }, + { 0x0ec4, 0x3154 }, + { 0x0ec5, 0x3155 }, + { 0x0ec6, 0x3156 }, + { 0x0ec7, 0x3157 }, + { 0x0ec8, 0x3158 }, + { 0x0ec9, 0x3159 }, + { 0x0eca, 0x315a }, + { 0x0ecb, 0x315b }, + { 0x0ecc, 0x315c }, + { 0x0ecd, 0x315d }, + { 0x0ece, 0x315e }, + { 0x0ecf, 0x315f }, + { 0x0ed0, 0x3160 }, + { 0x0ed1, 0x3161 }, + { 0x0ed2, 0x3162 }, + { 0x0ed3, 0x3163 }, + { 0x0eef, 0x316d }, + { 0x0ef0, 0x3171 }, + { 0x0ef1, 0x3178 }, + { 0x0ef2, 0x317f }, + { 0x0ef3, 0x3181 }, + { 0x0ef4, 0x3184 }, + { 0x0ef5, 0x3186 }, + { 0x0ef6, 0x318d }, + { 0x0ef7, 0x318e } + }; + + public static int translate(int unicode) { + if ((unicode >= 0x20 && unicode <= 0x7e) || + (unicode >= 0xa0 && unicode <= 0xff)) + return unicode; + + int min = 0; + int max = table.length - 1; + int mid; + + while (max >= min) { + mid = (min + max) / 2; + if (table[mid][1] < unicode) + min = mid + 1; + else if (table[mid][1] > unicode) + max = mid - 1; + else + return table[mid][0]; + } + + /* no matching Unicode value found */ + return -1; + } +} diff --git a/java/com/tigervnc/rfb/UserMsgBox.java b/java/com/tigervnc/rfb/UserMsgBox.java new file mode 100644 index 00000000..e83d9956 --- /dev/null +++ b/java/com/tigervnc/rfb/UserMsgBox.java @@ -0,0 +1,27 @@ +/* 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. + */ + +// getUserPasswd() gets the username and password. This might involve a +// dialog, getpass(), etc. The user buffer pointer can be null, in which case +// no user name will be retrieved. + +package com.tigervnc.rfb; + +public interface UserMsgBox { + public boolean showMsgBox(int flags,String title, String text); +} diff --git a/java/com/tigervnc/rfb/UserPasswdGetter.java b/java/com/tigervnc/rfb/UserPasswdGetter.java new file mode 100644 index 00000000..9796b66f --- /dev/null +++ b/java/com/tigervnc/rfb/UserPasswdGetter.java @@ -0,0 +1,27 @@ +/* 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. + */ + +// getUserPasswd() gets the username and password. This might involve a +// dialog, getpass(), etc. The user buffer pointer can be null, in which case +// no user name will be retrieved. + +package com.tigervnc.rfb; + +public interface UserPasswdGetter { + public boolean getUserPasswd(StringBuffer user, StringBuffer password); +} diff --git a/java/com/tigervnc/rfb/VncAuth.java b/java/com/tigervnc/rfb/VncAuth.java new file mode 100644 index 00000000..cce8d81c --- /dev/null +++ b/java/com/tigervnc/rfb/VncAuth.java @@ -0,0 +1,67 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class VncAuth { + + public static final int ok = 0; + public static final int failed = 1; + public static final int tooMany = 2; // deprecated + + public static final int challengeSize = 16; + + public static void encryptChallenge(byte[] challenge, String passwd) { + byte[] key = new byte[8]; + for (int i = 0; i < 8 && i < passwd.length(); i++) { + key[i] = (byte)passwd.charAt(i); + } + + DesCipher des = new DesCipher(key); + + for (int j = 0; j < challengeSize; j += 8) + des.encrypt(challenge,j,challenge,j); + } + + void obfuscatePasswd(String passwd, byte[] obfuscated) { + for (int i = 0; i < 8; i++) { + if (i < passwd.length()) + obfuscated[i] = (byte)passwd.charAt(i); + else + obfuscated[i] = 0; + } + DesCipher des = new DesCipher(obfuscationKey); + des.encrypt(obfuscated,0,obfuscated,0); + } + + String unobfuscatePasswd(byte[] obfuscated) { + DesCipher des = new DesCipher(obfuscationKey); + des.decrypt(obfuscated,0,obfuscated,0); + int len; + for (len = 0; len < 8; len++) { + if (obfuscated[len] == 0) break; + } + char[] plain = new char[len]; + for (int i = 0; i < len; i++) { + plain[i] = (char)obfuscated[i]; + } + return new String(plain); + } + + static byte[] obfuscationKey = {23,82,107,6,35,78,88,7}; +} diff --git a/java/com/tigervnc/rfb/VoidParameter.java b/java/com/tigervnc/rfb/VoidParameter.java new file mode 100644 index 00000000..f41f4c84 --- /dev/null +++ b/java/com/tigervnc/rfb/VoidParameter.java @@ -0,0 +1,41 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +abstract public class VoidParameter { + public VoidParameter(String name_, String desc_) { + name = name_; + description = desc_; + next = Configuration.head; + Configuration.head = this; + } + + final public String getName() { return name; } + final public String getDescription() { return description; } + + abstract public boolean setParam(String value); + public boolean setParam() { return false; } + abstract public String getDefaultStr(); + abstract public String getValueStr(); + public boolean isBool() { return false; } + + VoidParameter next; + protected String name; + protected String description; +} diff --git a/java/com/tigervnc/rfb/ZRLEDecoder.java b/java/com/tigervnc/rfb/ZRLEDecoder.java new file mode 100644 index 00000000..4f740f9d --- /dev/null +++ b/java/com/tigervnc/rfb/ZRLEDecoder.java @@ -0,0 +1,166 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +import com.tigervnc.rdr.*; + +public class ZRLEDecoder extends Decoder { + + public ZRLEDecoder(CMsgReader reader_) { + reader = reader_; + zis = new ZlibInStream(); + } + + public void readRect(Rect r, CMsgHandler handler) { + InStream is = reader.getInStream(); + int[] buf = reader.getImageBuf(64 * 64 * 4); + int bytesPerPixel = handler.cp.pf().bpp / 8; + boolean bigEndian = handler.cp.pf().bigEndian; + + int length = is.readU32(); + zis.setUnderlying(is, length); + Rect t = new Rect(); + + for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) { + + t.br.y = Math.min(r.br.y, t.tl.y + 64); + + for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) { + + t.br.x = Math.min(r.br.x, t.tl.x + 64); + + int mode = zis.readU8(); + boolean rle = (mode & 128) != 0; + int palSize = mode & 127; + int[] palette = new int[128]; + + if (bytesPerPixel > 1) { + zis.readPixels(palette, palSize, 3, bigEndian); + } else { + for (int i = 0; i < palSize; i++) { + palette[i] = zis.readPixel(bytesPerPixel, bigEndian); + } + } + + if (palSize == 1) { + int pix = palette[0]; + handler.fillRect(t, pix); + continue; + } + + if (!rle) { + if (palSize == 0) { + + // raw + + if (bytesPerPixel > 1) { + zis.readPixels(buf, t.area(), 3, bigEndian); + } else { + zis.readPixels(buf, t.area(), bytesPerPixel, bigEndian); + } + + } else { + + // packed pixels + int bppp = ((palSize > 16) ? 8 : + ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1))); + + int ptr = 0; + + for (int i = 0; i < t.height(); i++) { + int eol = ptr + t.width(); + int b = 0; + int nbits = 0; + + while (ptr < eol) { + if (nbits == 0) { + b = zis.readU8(); + nbits = 8; + } + nbits -= bppp; + int index = (b >> nbits) & ((1 << bppp) - 1) & 127; + buf[ptr++] = palette[index]; + } + } + } + + } else { + + if (palSize == 0) { + + // plain RLE + + int ptr = 0; + int end = ptr + t.area(); + while (ptr < end) { + int pix = (bytesPerPixel > 1 ? zis.readPixel(3, bigEndian) : + zis.readPixel(bytesPerPixel, bigEndian)); + int len = 1; + int b; + do { + b = zis.readU8(); + len += b; + } while (b == 255); + + if (!(len <= end - ptr)) + throw new Exception("ZRLEDecoder: assertion (len <= end - ptr)" + +" failed"); + + while (len-- > 0) buf[ptr++] = pix; + } + } else { + + // palette RLE + + int ptr = 0; + int end = ptr + t.area(); + while (ptr < end) { + int index = zis.readU8(); + int len = 1; + if ((index & 128) != 0) { + int b; + do { + b = zis.readU8(); + len += b; + } while (b == 255); + + if (!(len <= end - ptr)) + throw new Exception("ZRLEDecoder: assertion " + +"(len <= end - ptr) failed"); + } + + index &= 127; + + int pix = palette[index]; + + while (len-- > 0) buf[ptr++] = pix; + } + } + } + + handler.imageRect(t, buf); + } + } + + zis.reset(); + } + + CMsgReader reader; + ZlibInStream zis; +} diff --git a/java/com/tigervnc/rfb/screenTypes.java b/java/com/tigervnc/rfb/screenTypes.java new file mode 100644 index 00000000..d46741c6 --- /dev/null +++ b/java/com/tigervnc/rfb/screenTypes.java @@ -0,0 +1,36 @@ +/* Copyright 2009 Pierre Ossman for Cendio AB + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.rfb; + +public class screenTypes { + + // Reasons + public static final int reasonServer = 0; + public static final int reasonClient = 1; + public static final int reasonOtherClient = 2; + + // Result codes + public static final int resultSuccess = 0; + public static final int resultProhibited = 1; + public static final int resultNoResources = 2; + public static final int resultInvalid = 3; + + public static final int resultUnsolicited = 0xffff; // internal code used for server changes + +} diff --git a/java/com/tigervnc/vncviewer/CConn.java b/java/com/tigervnc/vncviewer/CConn.java new file mode 100644 index 00000000..7d76beed --- /dev/null +++ b/java/com/tigervnc/vncviewer/CConn.java @@ -0,0 +1,1309 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// CConn +// +// Methods on CConn are called from both the GUI thread and the thread which +// processes incoming RFB messages ("the RFB thread"). This means we need to +// be careful with synchronization here. +// +// Any access to writer() must not only be synchronized, but we must also make +// sure that the connection is in RFBSTATE_NORMAL. We are guaranteed this for +// any code called after serverInit() has been called. Since the DesktopWindow +// isn't created until then, any methods called only from DesktopWindow can +// assume that we are in RFBSTATE_NORMAL. + +package com.tigervnc.vncviewer; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.event.*; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Event; +import java.awt.Frame; +import java.awt.ScrollPane; + +import java.io.*; +import javax.net.ssl.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.File; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import javax.swing.*; +import javax.swing.filechooser.*; +import javax.swing.ImageIcon; +import java.net.URL; +import java.net.ServerSocket; +import javax.swing.border.*; +import java.util.*; + +import com.tigervnc.rdr.*; +import com.tigervnc.rfb.*; +import com.tigervnc.rfb.Exception; +import com.tigervnc.rfb.Point; +import com.tigervnc.rfb.Rect; + +class ViewportFrame extends JFrame +{ + public ViewportFrame(String name, CConn cc_) { + cc = cc_; + setTitle("TigerVNC: "+name); + setFocusable(false); + setFocusTraversalKeysEnabled(false); + addWindowFocusListener(new WindowAdapter() { + public void windowGainedFocus(WindowEvent e) { + sp.getViewport().getView().requestFocusInWindow(); + } + }); + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + cc.close(); + } + }); + addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + if (cc.options.autoScale || cc.options.fixedRatioScale) { + if (sp.getSize().width != cc.desktop.scaledWidth || + sp.getSize().height != cc.desktop.scaledHeight) { + cc.desktop.setScaledSize(); + sp.setSize(new Dimension(cc.desktop.scaledWidth, + cc.desktop.scaledHeight)); + sp.validate(); + pack(); + update(g); + if (cc.desktop.cursor != null) { + Cursor cursor = cc.desktop.cursor; + cc.setCursor(cursor.width(),cursor.height(),cursor.hotspot, + cursor.data, cursor.mask); + } + } + } + } + }); + } + + public void addChild(DesktopWindow child) { + sp = new JScrollPane(child); + child.setBackground(Color.BLACK); + child.setOpaque(true); + sp.setBorder(BorderFactory.createEmptyBorder(0,0,0,0)); + getContentPane().add(sp); + } + + public void setChild(DesktopWindow child) { + getContentPane().removeAll(); + addChild(child); + } + + public void setGeometry(int x, int y, int w, int h, boolean pack) { + if (pack) { + pack(); + } else { + setSize(w, h); + } + if (!cc.options.autoScale && !cc.options.fixedRatioScale) + setLocation(x, y); + setBackground(Color.BLACK); + } + + + CConn cc; + Graphics g; + JScrollPane sp; + static LogWriter vlog = new LogWriter("ViewportFrame"); +} + +public class CConn extends CConnection + implements UserPasswdGetter, UserMsgBox, OptionsDialogCallback +{ + //////////////////////////////////////////////////////////////////// + // The following methods are all called from the RFB thread + + public CConn(VncViewer viewer_, java.net.Socket sock_, + String vncServerName, boolean reverse) + { + serverHost = null; serverPort = 0; sock = sock_; viewer = viewer_; + currentEncoding = Encodings.encodingTight; lastServerEncoding = -1; + fullColour = viewer.fullColour.getValue(); + lowColourLevel = 2; + autoSelect = viewer.autoSelect.getValue(); + shared = viewer.shared.getValue(); formatChange = false; + encodingChange = false; sameMachine = false; + fullScreen = viewer.fullScreen.getValue(); + menuKey = Keysyms.F8; + options = new OptionsDialog(this); + options.initDialog(); + clipboardDialog = new ClipboardDialog(this); + firstUpdate = true; pendingUpdate = false; + + setShared(shared); + upg = this; + msg = this; + + String encStr = viewer.preferredEncoding.getValue(); + int encNum = Encodings.encodingNum(encStr); + if (encNum != -1) { + currentEncoding = encNum; + } + cp.supportsDesktopResize = true; + cp.supportsExtendedDesktopSize = true; + cp.supportsClientRedirect = true; + cp.supportsDesktopRename = true; + cp.supportsLocalCursor = viewer.useLocalCursor.getValue(); + cp.customCompressLevel = viewer.customCompressLevel.getValue(); + cp.compressLevel = viewer.compressLevel.getValue(); + cp.noJpeg = viewer.noJpeg.getValue(); + cp.qualityLevel = viewer.qualityLevel.getValue(); + initMenu(); + + if (sock != null) { + String name = sock.getRemoteSocketAddress()+"::"+sock.getPort(); + vlog.info("Accepted connection from "+name); + } else { + if (vncServerName != null) { + serverHost = Hostname.getHost(vncServerName); + serverPort = Hostname.getPort(vncServerName); + } else { + ServerDialog dlg = new ServerDialog(options, vncServerName, this); + if (!dlg.showDialog() || dlg.server.getSelectedItem().equals("")) { + System.exit(1); + } + vncServerName = (String)dlg.server.getSelectedItem(); + serverHost = Hostname.getHost(vncServerName); + serverPort = Hostname.getPort(vncServerName); + } + + try { + sock = new java.net.Socket(serverHost, serverPort); + } catch (java.io.IOException e) { + throw new Exception(e.toString()); + } + vlog.info("connected to host "+serverHost+" port "+serverPort); + } + + sameMachine = (sock.getLocalSocketAddress() == sock.getRemoteSocketAddress()); + try { + sock.setTcpNoDelay(true); + sock.setTrafficClass(0x10); + setServerName(serverHost); + jis = new JavaInStream(sock.getInputStream()); + jos = new JavaOutStream(sock.getOutputStream()); + } catch (java.net.SocketException e) { + throw new Exception(e.toString()); + } catch (java.io.IOException e) { + throw new Exception(e.toString()); + } + setStreams(jis, jos); + initialiseProtocol(); + } + + public boolean showMsgBox(int flags, String title, String text) + { + StringBuffer titleText = new StringBuffer("VNC Viewer: "+title); + + return true; + } + + // deleteWindow() is called when the user closes the desktop or menu windows. + + void deleteWindow() { + if (viewport != null) + viewport.dispose(); + viewport = null; + } + + // getUserPasswd() is called by the CSecurity object when it needs us to read + // a password from the user. + + public final boolean getUserPasswd(StringBuffer user, StringBuffer passwd) { + String title = ("VNC Authentication [" + +csecurity.description() + "]"); + PasswdDialog dlg; + if (user == null) { + dlg = new PasswdDialog(title, (user == null), (passwd == null)); + } else { + if ((passwd == null) && viewer.sendLocalUsername.getValue()) { + user.append((String)System.getProperties().get("user.name")); + return true; + } + dlg = new PasswdDialog(title, viewer.sendLocalUsername.getValue(), + (passwd == null)); + } + if (!dlg.showDialog()) return false; + if (user != null) { + if (viewer.sendLocalUsername.getValue()) { + user.append((String)System.getProperties().get("user.name")); + } else { + user.append(dlg.userEntry.getText()); + } + } + if (passwd != null) + passwd.append(dlg.passwdEntry.getText()); + return true; + } + + // CConnection callback methods + + // serverInit() is called when the serverInit message has been received. At + // this point we create the desktop window and display it. We also tell the + // server the pixel format and encodings to use and request the first update. + public void serverInit() { + super.serverInit(); + + // If using AutoSelect with old servers, start in FullColor + // mode. See comment in autoSelectFormatAndEncoding. + if (cp.beforeVersion(3, 8) && autoSelect) { + fullColour = true; + } + + serverPF = cp.pf(); + desktop = new DesktopWindow(cp.width, cp.height, serverPF, this); + //desktopEventHandler = desktop.setEventHandler(this); + //desktop.addEventMask(KeyPressMask | KeyReleaseMask); + fullColourPF = desktop.getPF(); + if (!serverPF.trueColour) + fullColour = true; + recreateViewport(); + formatChange = true; encodingChange = true; + requestNewUpdate(); + } + + // setDesktopSize() is called when the desktop size changes (including when + // it is set initially). + public void setDesktopSize(int w, int h) { + super.setDesktopSize(w,h); + resizeFramebuffer(); + } + + // setExtendedDesktopSize() is a more advanced version of setDesktopSize() + public void setExtendedDesktopSize(int reason, int result, int w, int h, + ScreenSet layout) { + super.setExtendedDesktopSize(reason, result, w, h, layout); + + if ((reason == screenTypes.reasonClient) && + (result != screenTypes.resultSuccess)) { + vlog.error("SetDesktopSize failed: "+result); + return; + } + + resizeFramebuffer(); + } + + // clientRedirect() migrates the client to another host/port + public void clientRedirect(int port, String host, + String x509subject) { + try { + getSocket().close(); + setServerPort(port); + sock = new java.net.Socket(host, port); + sock.setTcpNoDelay(true); + sock.setTrafficClass(0x10); + setSocket(sock); + vlog.info("Redirected to "+host+":"+port); + setStreams(new JavaInStream(sock.getInputStream()), + new JavaOutStream(sock.getOutputStream())); + initialiseProtocol(); + } catch (java.io.IOException e) { + e.printStackTrace(); + } + } + + // setName() is called when the desktop name changes + public void setName(String name) { + super.setName(name); + + if (viewport != null) { + viewport.setTitle("TigerVNC: "+name); + } + } + + // framebufferUpdateStart() is called at the beginning of an update. + // Here we try to send out a new framebuffer update request so that the + // next update can be sent out in parallel with us decoding the current + // one. We cannot do this if we're in the middle of a format change + // though. + public void framebufferUpdateStart() { + if (!formatChange) { + pendingUpdate = true; + requestNewUpdate(); + } else + pendingUpdate = false; + } + + // framebufferUpdateEnd() is called at the end of an update. + // For each rectangle, the FdInStream will have timed the speed + // of the connection, allowing us to select format and encoding + // appropriately, and then request another incremental update. + public void framebufferUpdateEnd() { + desktop.framebufferUpdateEnd(); + + if (firstUpdate) { + int width, height; + + if (cp.supportsSetDesktopSize && + viewer.desktopSize.getValue() != null && + viewer.desktopSize.getValue().split("x").length == 2) { + width = Integer.parseInt(viewer.desktopSize.getValue().split("x")[0]); + height = Integer.parseInt(viewer.desktopSize.getValue().split("x")[1]); + ScreenSet layout; + + layout = cp.screenLayout; + + if (layout.num_screens() == 0) + layout.add_screen(new Screen()); + else if (layout.num_screens() != 1) { + + while (true) { + Iterator iter = layout.screens.iterator(); + Screen screen = (Screen)iter.next(); + + if (!iter.hasNext()) + break; + + layout.remove_screen(screen.id); + } + } + + Screen screen0 = (Screen)layout.screens.iterator().next(); + screen0.dimensions.tl.x = 0; + screen0.dimensions.tl.y = 0; + screen0.dimensions.br.x = width; + screen0.dimensions.br.y = height; + + writer().writeSetDesktopSize(width, height, layout); + } + + firstUpdate = false; + } + + // A format change prevented us from sending this before the update, + // so make sure to send it now. + if (formatChange && !pendingUpdate) + requestNewUpdate(); + + // Compute new settings based on updated bandwidth values + if (autoSelect) + autoSelectFormatAndEncoding(); + } + + // The rest of the callbacks are fairly self-explanatory... + + public void setColourMapEntries(int firstColour, int nColours, int[] rgbs) { + desktop.setColourMapEntries(firstColour, nColours, rgbs); + } + + public void bell() { + if (viewer.acceptBell.getValue()) + desktop.getToolkit().beep(); + } + + public void serverCutText(String str, int len) { + if (viewer.acceptClipboard.getValue()) + clipboardDialog.serverCutText(str, len); + } + + // We start timing on beginRect and stop timing on endRect, to + // avoid skewing the bandwidth estimation as a result of the server + // being slow or the network having high latency + public void beginRect(Rect r, int encoding) { + ((JavaInStream)getInStream()).startTiming(); + if (encoding != Encodings.encodingCopyRect) { + lastServerEncoding = encoding; + } + } + + public void endRect(Rect r, int encoding) { + ((JavaInStream)getInStream()).stopTiming(); + } + + public void fillRect(Rect r, int p) { + desktop.fillRect(r.tl.x, r.tl.y, r.width(), r.height(), p); + } + public void imageRect(Rect r, int[] p) { + desktop.imageRect(r.tl.x, r.tl.y, r.width(), r.height(), p); + } + public void copyRect(Rect r, int sx, int sy) { + desktop.copyRect(r.tl.x, r.tl.y, r.width(), r.height(), sx, sy); + } + + public void setCursor(int width, int height, Point hotspot, + int[] data, byte[] mask) { + desktop.setCursor(width, height, hotspot, data, mask); + } + + private void resizeFramebuffer() + { + if ((cp.width == 0) && (cp.height == 0)) + return; + if (desktop == null) + return; + if ((desktop.width() == cp.width) && (desktop.height() == cp.height)) + return; + + desktop.resize(); + recreateViewport(); + } + + // recreateViewport() recreates our top-level window. This seems to be + // better than attempting to resize the existing window, at least with + // various X window managers. + + private void recreateViewport() + { + if (viewport != null) viewport.dispose(); + viewport = new ViewportFrame(cp.name(), this); + viewport.setUndecorated(fullScreen); + desktop.setViewport(viewport); + ClassLoader loader = this.getClass().getClassLoader(); + URL url = loader.getResource("com/tigervnc/vncviewer/tigervnc.ico"); + ImageIcon icon = null; + if (url != null) { + icon = new ImageIcon(url); + viewport.setIconImage(icon.getImage()); + } + viewport.addChild(desktop); + reconfigureViewport(); + if ((cp.width > 0) && (cp.height > 0)) + viewport.setVisible(true); + desktop.initGraphics(); + desktop.requestFocusInWindow(); + } + + private void reconfigureViewport() + { + //viewport.setMaxSize(cp.width, cp.height); + boolean pack = true; + Dimension dpySize = viewport.getToolkit().getScreenSize(); + desktop.setScaledSize(); + int w = desktop.scaledWidth; + int h = desktop.scaledHeight; + if (fullScreen) { + viewport.setExtendedState(JFrame.MAXIMIZED_BOTH); + viewport.setGeometry(0, 0, dpySize.width, dpySize.height, false); + } else { + int wmDecorationWidth = viewport.getInsets().left + viewport.getInsets().right; + int wmDecorationHeight = viewport.getInsets().top + viewport.getInsets().bottom; + if (w + wmDecorationWidth >= dpySize.width) { + w = dpySize.width - wmDecorationWidth; + pack = false; + } + if (h + wmDecorationHeight >= dpySize.height) { + h = dpySize.height - wmDecorationHeight; + pack = false; + } + + if (!pack) + viewport.setPreferredSize(new Dimension(w,h)); + + int x = (dpySize.width - w - wmDecorationWidth) / 2; + int y = (dpySize.height - h - wmDecorationHeight)/2; + + viewport.setExtendedState(JFrame.NORMAL); + viewport.setGeometry(x, y, w, h, pack); + } + viewport.update(viewport.g); + } + + // autoSelectFormatAndEncoding() chooses the format and encoding appropriate + // to the connection speed: + // + // First we wait for at least one second of bandwidth measurement. + // + // Above 16Mbps (i.e. LAN), we choose the second highest JPEG quality, + // which should be perceptually lossless. + // + // If the bandwidth is below that, we choose a more lossy JPEG quality. + // + // If the bandwidth drops below 256 Kbps, we switch to palette mode. + // + // Note: The system here is fairly arbitrary and should be replaced + // with something more intelligent at the server end. + // + private void autoSelectFormatAndEncoding() { + long kbitsPerSecond = ((JavaInStream)getInStream()).kbitsPerSecond(); + long timeWaited = ((JavaInStream)getInStream()).timeWaited(); + boolean newFullColour = fullColour; + int newQualityLevel = cp.qualityLevel; + + // Always use Tight + if (currentEncoding != Encodings.encodingTight) { + currentEncoding = Encodings.encodingTight; + encodingChange = true; + } + + // Check that we have a decent bandwidth measurement + if ((kbitsPerSecond == 0) || (timeWaited < 10000)) + return; + + // Select appropriate quality level + if (!cp.noJpeg) { + if (kbitsPerSecond > 16000) + newQualityLevel = 8; + else + newQualityLevel = 6; + + if (newQualityLevel != cp.qualityLevel) { + vlog.info("Throughput "+kbitsPerSecond+ + " kbit/s - changing to quality "+newQualityLevel); + cp.qualityLevel = newQualityLevel; + viewer.qualityLevel.setParam(Integer.toString(newQualityLevel)); + encodingChange = true; + } + } + + if (cp.beforeVersion(3, 8)) { + // Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with + // cursors "asynchronously". If this happens in the middle of a + // pixel format change, the server will encode the cursor with + // the old format, but the client will try to decode it + // according to the new format. This will lead to a + // crash. Therefore, we do not allow automatic format change for + // old servers. + return; + } + + // Select best color level + newFullColour = (kbitsPerSecond > 256); + if (newFullColour != fullColour) { + vlog.info("Throughput "+kbitsPerSecond+ + " kbit/s - full color is now "+ + (newFullColour ? "enabled" : "disabled")); + fullColour = newFullColour; + formatChange = true; + } + } + + // requestNewUpdate() requests an update from the server, having set the + // format and encoding appropriately. + private void requestNewUpdate() + { + if (formatChange) { + + /* Catch incorrect requestNewUpdate calls */ + assert(pendingUpdate == false); + + if (fullColour) { + desktop.setPF(fullColourPF); + } else { + if (lowColourLevel == 0) { + desktop.setPF(new PixelFormat(8,3,false,true,1,1,1,2,1,0)); + } else if (lowColourLevel == 1) { + desktop.setPF(new PixelFormat(8,6,false,true,3,3,3,4,2,0)); + } else { + desktop.setPF(new PixelFormat(8,8,false,true,7,7,3,0,3,6)); + } + } + String str = desktop.getPF().print(); + vlog.info("Using pixel format "+str); + cp.setPF(desktop.getPF()); + synchronized (this) { + writer().writeSetPixelFormat(cp.pf()); + } + } + checkEncodings(); + synchronized (this) { + writer().writeFramebufferUpdateRequest(new Rect(0,0,cp.width,cp.height), + !formatChange); + } + formatChange = false; + } + + + //////////////////////////////////////////////////////////////////// + // The following methods are all called from the GUI thread + + // close() closes the socket, thus waking up the RFB thread. + public void close() { + try { + shuttingDown = true; + sock.close(); + } catch (java.io.IOException e) { + e.printStackTrace(); + } + } + + // Menu callbacks. These are guaranteed only to be called after serverInit() + // has been called, since the menu is only accessible from the DesktopWindow + + private void initMenu() { + menu = new F8Menu(this); + } + + void showMenu(int x, int y) { + String os = System.getProperty("os.name"); + if (os.startsWith("Windows")) + com.sun.java.swing.plaf.windows.WindowsLookAndFeel.setMnemonicHidden(false); + menu.show(desktop, x, y); + } + + void showAbout() { + InputStream stream = cl.getResourceAsStream("com/tigervnc/vncviewer/timestamp"); + String pkgDate = ""; + String pkgTime = ""; + try { + Manifest manifest = new Manifest(stream); + Attributes attributes = manifest.getMainAttributes(); + pkgDate = attributes.getValue("Package-Date"); + pkgTime = attributes.getValue("Package-Time"); + } catch (IOException e) { } + JOptionPane.showMessageDialog((viewport != null ? viewport : null), + VncViewer.about1+" v"+VncViewer.version+" ("+VncViewer.build+")\n" + +"Built on "+pkgDate+" at "+pkgTime+"\n" + +VncViewer.about2+"\n" + +VncViewer.about3, + "About TigerVNC Viewer for Java", + JOptionPane.INFORMATION_MESSAGE, + logo); + } + + void showInfo() { + JOptionPane.showMessageDialog(viewport, + "Desktop name: "+cp.name()+"\n" + +"Host: "+serverHost+":"+sock.getPort()+"\n" + +"Size: "+cp.width+"x"+cp.height+"\n" + +"Pixel format: "+desktop.getPF().print()+"\n" + +"(server default "+serverPF.print()+")\n" + +"Requested encoding: "+Encodings.encodingName(currentEncoding)+"\n" + +"Last used encoding: "+Encodings.encodingName(lastServerEncoding)+"\n" + +"Line speed estimate: "+((JavaInStream)getInStream()).kbitsPerSecond()+" kbit/s"+"\n" + +"Protocol version: "+cp.majorVersion+"."+cp.minorVersion+"\n" + +"Security method: "+Security.secTypeName(csecurity.getType()) + +" ["+csecurity.description()+"]", + "VNC connection info", + JOptionPane.PLAIN_MESSAGE); + } + + synchronized public void refresh() { + writer().writeFramebufferUpdateRequest(new Rect(0,0,cp.width,cp.height), false); + pendingUpdate = true; + } + + + // OptionsDialogCallback. setOptions() sets the options dialog's checkboxes + // etc to reflect our flags. getOptions() sets our flags according to the + // options dialog's checkboxes. They are both called from the GUI thread. + // Some of the flags are also accessed by the RFB thread. I believe that + // reading and writing boolean and int values in java is atomic, so there is + // no need for synchronization. + + public void setOptions() { + int digit; + options.autoSelect.setSelected(autoSelect); + options.fullColour.setSelected(fullColour); + options.veryLowColour.setSelected(!fullColour && lowColourLevel == 0); + options.lowColour.setSelected(!fullColour && lowColourLevel == 1); + options.mediumColour.setSelected(!fullColour && lowColourLevel == 2); + options.tight.setSelected(currentEncoding == Encodings.encodingTight); + options.zrle.setSelected(currentEncoding == Encodings.encodingZRLE); + options.hextile.setSelected(currentEncoding == Encodings.encodingHextile); + options.raw.setSelected(currentEncoding == Encodings.encodingRaw); + + options.customCompressLevel.setSelected(viewer.customCompressLevel.getValue()); + digit = 0 + viewer.compressLevel.getValue(); + if (digit >= 0 && digit <= 9) { + options.compressLevel.setSelectedItem(digit); + } else { + options.compressLevel.setSelectedItem(Integer.parseInt(viewer.compressLevel.getDefaultStr())); + } + options.noJpeg.setSelected(!viewer.noJpeg.getValue()); + digit = 0 + viewer.qualityLevel.getValue(); + if (digit >= 0 && digit <= 9) { + options.qualityLevel.setSelectedItem(digit); + } else { + options.qualityLevel.setSelectedItem(Integer.parseInt(viewer.qualityLevel.getDefaultStr())); + } + + options.viewOnly.setSelected(viewer.viewOnly.getValue()); + options.acceptClipboard.setSelected(viewer.acceptClipboard.getValue()); + options.sendClipboard.setSelected(viewer.sendClipboard.getValue()); + options.menuKey.setSelectedIndex(menuKey-0xFFBE); + + if (state() == RFBSTATE_NORMAL) { + options.shared.setEnabled(false); + options.secVeNCrypt.setEnabled(false); + options.encNone.setEnabled(false); + options.encTLS.setEnabled(false); + options.encX509.setEnabled(false); + options.ca.setEnabled(false); + options.crl.setEnabled(false); + options.secIdent.setEnabled(false); + options.secNone.setEnabled(false); + options.secVnc.setEnabled(false); + options.secPlain.setEnabled(false); + options.sendLocalUsername.setEnabled(false); + } else { + options.shared.setSelected(shared); + + /* Process non-VeNCrypt sectypes */ + java.util.List<Integer> secTypes = new ArrayList<Integer>(); + secTypes = security.GetEnabledSecTypes(); + for (Iterator i = secTypes.iterator(); i.hasNext();) { + switch ((Integer)i.next()) { + case Security.secTypeVeNCrypt: + options.secVeNCrypt.setSelected(true); + break; + case Security.secTypeNone: + options.encNone.setSelected(true); + options.secNone.setSelected(true); + break; + case Security.secTypeVncAuth: + options.encNone.setSelected(true); + options.secVnc.setSelected(true); + break; + } + } + + /* Process VeNCrypt subtypes */ + if (options.secVeNCrypt.isSelected()) { + java.util.List<Integer> secTypesExt = new ArrayList<Integer>(); + secTypesExt = security.GetEnabledExtSecTypes(); + for (Iterator iext = secTypesExt.iterator(); iext.hasNext();) { + switch ((Integer)iext.next()) { + case Security.secTypePlain: + options.secPlain.setSelected(true); + options.sendLocalUsername.setSelected(true); + break; + case Security.secTypeIdent: + options.secIdent.setSelected(true); + options.sendLocalUsername.setSelected(true); + break; + case Security.secTypeTLSNone: + options.encTLS.setSelected(true); + options.secNone.setSelected(true); + break; + case Security.secTypeTLSVnc: + options.encTLS.setSelected(true); + options.secVnc.setSelected(true); + break; + case Security.secTypeTLSPlain: + options.encTLS.setSelected(true); + options.secPlain.setSelected(true); + options.sendLocalUsername.setSelected(true); + break; + case Security.secTypeTLSIdent: + options.encTLS.setSelected(true); + options.secIdent.setSelected(true); + options.sendLocalUsername.setSelected(true); + break; + case Security.secTypeX509None: + options.encX509.setSelected(true); + options.secNone.setSelected(true); + break; + case Security.secTypeX509Vnc: + options.encX509.setSelected(true); + options.secVnc.setSelected(true); + break; + case Security.secTypeX509Plain: + options.encX509.setSelected(true); + options.secPlain.setSelected(true); + options.sendLocalUsername.setSelected(true); + break; + case Security.secTypeX509Ident: + options.encX509.setSelected(true); + options.secIdent.setSelected(true); + options.sendLocalUsername.setSelected(true); + break; + } + } + } + options.sendLocalUsername.setEnabled(options.secPlain.isSelected()|| + options.secIdent.isSelected()); + } + + options.fullScreen.setSelected(fullScreen); + options.useLocalCursor.setSelected(viewer.useLocalCursor.getValue()); + options.fastCopyRect.setSelected(viewer.fastCopyRect.getValue()); + options.acceptBell.setSelected(viewer.acceptBell.getValue()); + options.autoScale = false; + options.fixedRatioScale = false; + String scaleString = viewer.scalingFactor.getValue(); + if (scaleString.equals("Auto")) { + options.autoScale = true; + options.scalingFactor.setSelectedItem("Auto"); + // FIXME: set scaleFactor? + } else if(scaleString.equals("FixedRatio")) { + options.fixedRatioScale = true; + options.scalingFactor.setSelectedItem("Fixed Aspect Ratio"); + // FIXME: set scaleFactor? + } else { + digit = Integer.parseInt(scaleString); + if (digit >= 1 && digit <= 1000) { + options.scalingFactor.setSelectedItem(digit+"%"); + } else { + options.scalingFactor.setSelectedItem(Integer.parseInt(viewer.scalingFactor.getDefaultStr())+"%"); + } + scaleFactor = + Integer.parseInt(scaleString.substring(0, scaleString.length())); + if (desktop != null) + desktop.setScaledSize(); + } + } + + public void getOptions() { + autoSelect = options.autoSelect.isSelected(); + if (fullColour != options.fullColour.isSelected()) + formatChange = true; + fullColour = options.fullColour.isSelected(); + if (!fullColour) { + int newLowColourLevel = (options.veryLowColour.isSelected() ? 0 : + options.lowColour.isSelected() ? 1 : 2); + if (newLowColourLevel != lowColourLevel) { + lowColourLevel = newLowColourLevel; + formatChange = true; + } + } + int newEncoding = (options.zrle.isSelected() ? Encodings.encodingZRLE : + options.hextile.isSelected() ? Encodings.encodingHextile : + options.tight.isSelected() ? Encodings.encodingTight : + Encodings.encodingRaw); + if (newEncoding != currentEncoding) { + currentEncoding = newEncoding; + encodingChange = true; + } + + viewer.customCompressLevel.setParam(options.customCompressLevel.isSelected()); + if (cp.customCompressLevel != viewer.customCompressLevel.getValue()) { + cp.customCompressLevel = viewer.customCompressLevel.getValue(); + encodingChange = true; + } + if (Integer.parseInt(options.compressLevel.getSelectedItem().toString()) >= 0 && + Integer.parseInt(options.compressLevel.getSelectedItem().toString()) <= 9) { + viewer.compressLevel.setParam(options.compressLevel.getSelectedItem().toString()); + } else { + viewer.compressLevel.setParam(viewer.compressLevel.getDefaultStr()); + } + if (cp.compressLevel != viewer.compressLevel.getValue()) { + cp.compressLevel = viewer.compressLevel.getValue(); + encodingChange = true; + } + viewer.noJpeg.setParam(!options.noJpeg.isSelected()); + if (cp.noJpeg != viewer.noJpeg.getValue()) { + cp.noJpeg = viewer.noJpeg.getValue(); + encodingChange = true; + } + viewer.qualityLevel.setParam(options.qualityLevel.getSelectedItem().toString()); + if (cp.qualityLevel != viewer.qualityLevel.getValue()) { + cp.qualityLevel = viewer.qualityLevel.getValue(); + encodingChange = true; + } + viewer.sendLocalUsername.setParam(options.sendLocalUsername.isSelected()); + + viewer.viewOnly.setParam(options.viewOnly.isSelected()); + viewer.acceptClipboard.setParam(options.acceptClipboard.isSelected()); + viewer.sendClipboard.setParam(options.sendClipboard.isSelected()); + viewer.fastCopyRect.setParam(options.fastCopyRect.isSelected()); + viewer.acceptBell.setParam(options.acceptBell.isSelected()); + if (options.autoScale) { + viewer.scalingFactor.setParam("Auto"); + scaleFactor = -1; + if (desktop != null) { + reconfigureViewport(); + viewport.update(viewport.g); + } + } else if(options.fixedRatioScale) { + viewer.scalingFactor.setParam("FixedRatio"); + scaleFactor = -1; + if (desktop != null) { + reconfigureViewport(); + viewport.update(viewport.g); + } + } else { + String scaleString = + options.scalingFactor.getSelectedItem().toString(); + viewer.scalingFactor.setParam(scaleString.substring(0, scaleString.length()-1)); + int oldScaleFactor = scaleFactor; + scaleFactor = + Integer.parseInt(scaleString.substring(0, scaleString.length()-1)); + if (oldScaleFactor != scaleFactor && desktop != null) { + reconfigureViewport(); + viewport.update(viewport.g); + } + } + + clipboardDialog.setSendingEnabled(viewer.sendClipboard.getValue()); + menuKey = (options.menuKey.getSelectedIndex()+0xFFBE); + F8Menu.f8.setText("Send F"+(menuKey-Keysyms.F1+1)); + + shared = options.shared.isSelected(); + setShared(shared); + viewer.useLocalCursor.setParam(options.useLocalCursor.isSelected()); + if (cp.supportsLocalCursor != viewer.useLocalCursor.getValue()) { + cp.supportsLocalCursor = viewer.useLocalCursor.getValue(); + encodingChange = true; + if (desktop != null) + desktop.resetLocalCursor(); + } + + checkEncodings(); + + if (state() != RFBSTATE_NORMAL) { + /* Process security types which don't use encryption */ + if (options.encNone.isSelected()) { + if (options.secNone.isSelected()) + Security.EnableSecType(Security.secTypeNone); + if (options.secVnc.isSelected()) + Security.EnableSecType(Security.secTypeVncAuth); + if (options.secPlain.isSelected()) + Security.EnableSecType(Security.secTypePlain); + if (options.secIdent.isSelected()) + Security.EnableSecType(Security.secTypeIdent); + } else { + Security.DisableSecType(Security.secTypeNone); + Security.DisableSecType(Security.secTypeVncAuth); + Security.DisableSecType(Security.secTypePlain); + Security.DisableSecType(Security.secTypeIdent); + } + + /* Process security types which use TLS encryption */ + if (options.encTLS.isSelected()) { + if (options.secNone.isSelected()) + Security.EnableSecType(Security.secTypeTLSNone); + if (options.secVnc.isSelected()) + Security.EnableSecType(Security.secTypeTLSVnc); + if (options.secPlain.isSelected()) + Security.EnableSecType(Security.secTypeTLSPlain); + if (options.secIdent.isSelected()) + Security.EnableSecType(Security.secTypeTLSIdent); + } else { + Security.DisableSecType(Security.secTypeTLSNone); + Security.DisableSecType(Security.secTypeTLSVnc); + Security.DisableSecType(Security.secTypeTLSPlain); + Security.DisableSecType(Security.secTypeTLSIdent); + } + + /* Process security types which use X509 encryption */ + if (options.encX509.isSelected()) { + if (options.secNone.isSelected()) + Security.EnableSecType(Security.secTypeX509None); + if (options.secVnc.isSelected()) + Security.EnableSecType(Security.secTypeX509Vnc); + if (options.secPlain.isSelected()) + Security.EnableSecType(Security.secTypeX509Plain); + if (options.secIdent.isSelected()) + Security.EnableSecType(Security.secTypeX509Ident); + } else { + Security.DisableSecType(Security.secTypeX509None); + Security.DisableSecType(Security.secTypeX509Vnc); + Security.DisableSecType(Security.secTypeX509Plain); + Security.DisableSecType(Security.secTypeX509Ident); + } + + /* Process *None security types */ + if (options.secNone.isSelected()) { + if (options.encNone.isSelected()) + Security.EnableSecType(Security.secTypeNone); + if (options.encTLS.isSelected()) + Security.EnableSecType(Security.secTypeTLSNone); + if (options.encX509.isSelected()) + Security.EnableSecType(Security.secTypeX509None); + } else { + Security.DisableSecType(Security.secTypeNone); + Security.DisableSecType(Security.secTypeTLSNone); + Security.DisableSecType(Security.secTypeX509None); + } + + /* Process *Vnc security types */ + if (options.secVnc.isSelected()) { + if (options.encNone.isSelected()) + Security.EnableSecType(Security.secTypeVncAuth); + if (options.encTLS.isSelected()) + Security.EnableSecType(Security.secTypeTLSVnc); + if (options.encX509.isSelected()) + Security.EnableSecType(Security.secTypeX509Vnc); + } else { + Security.DisableSecType(Security.secTypeVncAuth); + Security.DisableSecType(Security.secTypeTLSVnc); + Security.DisableSecType(Security.secTypeX509Vnc); + } + + /* Process *Plain security types */ + if (options.secPlain.isSelected()) { + if (options.encNone.isSelected()) + Security.EnableSecType(Security.secTypePlain); + if (options.encTLS.isSelected()) + Security.EnableSecType(Security.secTypeTLSPlain); + if (options.encX509.isSelected()) + Security.EnableSecType(Security.secTypeX509Plain); + } else { + Security.DisableSecType(Security.secTypePlain); + Security.DisableSecType(Security.secTypeTLSPlain); + Security.DisableSecType(Security.secTypeX509Plain); + } + + /* Process *Ident security types */ + if (options.secIdent.isSelected()) { + if (options.encNone.isSelected()) + Security.EnableSecType(Security.secTypeIdent); + if (options.encTLS.isSelected()) + Security.EnableSecType(Security.secTypeTLSIdent); + if (options.encX509.isSelected()) + Security.EnableSecType(Security.secTypeX509Ident); + } else { + Security.DisableSecType(Security.secTypeIdent); + Security.DisableSecType(Security.secTypeTLSIdent); + Security.DisableSecType(Security.secTypeX509Ident); + } + + CSecurityTLS.x509ca.setParam(options.ca.getText()); + CSecurityTLS.x509crl.setParam(options.crl.getText()); + } + } + + public void toggleFullScreen() { + fullScreen = !fullScreen; + if (!fullScreen) menu.fullScreen.setSelected(false); + recreateViewport(); + } + + // writeClientCutText() is called from the clipboard dialog + synchronized public void writeClientCutText(String str, int len) { + if (state() != RFBSTATE_NORMAL) return; + writer().writeClientCutText(str,len); + } + + synchronized public void writeKeyEvent(int keysym, boolean down) { + if (state() != RFBSTATE_NORMAL) return; + writer().writeKeyEvent(keysym, down); + } + + synchronized public void writeKeyEvent(KeyEvent ev) { + if (ev.getID() != KeyEvent.KEY_PRESSED && !ev.isActionKey()) + return; + + int keysym; + + if (!ev.isActionKey()) { + vlog.debug("key press "+ev.getKeyChar()); + if (ev.getKeyChar() < 32) { + // if the ctrl modifier key is down, send the equivalent ASCII since we + // will send the ctrl modifier anyway + + if ((ev.getModifiers() & KeyEvent.CTRL_MASK) != 0) { + if ((ev.getModifiers() & KeyEvent.SHIFT_MASK) != 0) { + keysym = ev.getKeyChar() + 64; + if (keysym == -1) + return; + } else { + keysym = ev.getKeyChar() + 96; + if (keysym == 127) keysym = 95; + } + } else { + switch (ev.getKeyCode()) { + case KeyEvent.VK_BACK_SPACE: keysym = Keysyms.BackSpace; break; + case KeyEvent.VK_TAB: keysym = Keysyms.Tab; break; + case KeyEvent.VK_ENTER: keysym = Keysyms.Return; break; + case KeyEvent.VK_ESCAPE: keysym = Keysyms.Escape; break; + default: return; + } + } + + } else if (ev.getKeyChar() == 127) { + keysym = Keysyms.Delete; + + } else { + keysym = UnicodeToKeysym.translate(ev.getKeyChar()); + if (keysym == -1) + return; + } + + } else { + // KEY_ACTION + vlog.debug("key action "+ev.getKeyCode()); + switch (ev.getKeyCode()) { + case KeyEvent.VK_HOME: keysym = Keysyms.Home; break; + case KeyEvent.VK_END: keysym = Keysyms.End; break; + case KeyEvent.VK_PAGE_UP: keysym = Keysyms.Page_Up; break; + case KeyEvent.VK_PAGE_DOWN: keysym = Keysyms.Page_Down; break; + case KeyEvent.VK_UP: keysym = Keysyms.Up; break; + case KeyEvent.VK_DOWN: keysym = Keysyms.Down; break; + case KeyEvent.VK_LEFT: keysym = Keysyms.Left; break; + case KeyEvent.VK_RIGHT: keysym = Keysyms.Right; break; + case KeyEvent.VK_F1: keysym = Keysyms.F1; break; + case KeyEvent.VK_F2: keysym = Keysyms.F2; break; + case KeyEvent.VK_F3: keysym = Keysyms.F3; break; + case KeyEvent.VK_F4: keysym = Keysyms.F4; break; + case KeyEvent.VK_F5: keysym = Keysyms.F5; break; + case KeyEvent.VK_F6: keysym = Keysyms.F6; break; + case KeyEvent.VK_F7: keysym = Keysyms.F7; break; + case KeyEvent.VK_F8: keysym = Keysyms.F8; break; + case KeyEvent.VK_F9: keysym = Keysyms.F9; break; + case KeyEvent.VK_F10: keysym = Keysyms.F10; break; + case KeyEvent.VK_F11: keysym = Keysyms.F11; break; + case KeyEvent.VK_F12: keysym = Keysyms.F12; break; + case KeyEvent.VK_PRINTSCREEN: keysym = Keysyms.Print; break; + case KeyEvent.VK_PAUSE: keysym = Keysyms.Pause; break; + case KeyEvent.VK_INSERT: keysym = Keysyms.Insert; break; + default: return; + } + } + + writeModifiers(ev.getModifiers()); + writeKeyEvent(keysym, true); + writeKeyEvent(keysym, false); + writeModifiers(0); + } + + + synchronized public void writePointerEvent(MouseEvent ev) { + if (state() != RFBSTATE_NORMAL) return; + int x, y; + + switch (ev.getID()) { + case MouseEvent.MOUSE_PRESSED: + buttonMask = 1; + if ((ev.getModifiers() & KeyEvent.ALT_MASK) != 0) buttonMask = 2; + if ((ev.getModifiers() & KeyEvent.META_MASK) != 0) buttonMask = 4; + break; + case MouseEvent.MOUSE_RELEASED: + buttonMask = 0; + break; + } + + writeModifiers(ev.getModifiers() & ~KeyEvent.ALT_MASK & ~KeyEvent.META_MASK); + + if (cp.width != desktop.scaledWidth || + cp.height != desktop.scaledHeight) { + int sx = (desktop.scaleWidthRatio == 1.00) + ? ev.getX() : (int)Math.floor(ev.getX()/desktop.scaleWidthRatio); + int sy = (desktop.scaleHeightRatio == 1.00) + ? ev.getY() : (int)Math.floor(ev.getY()/desktop.scaleHeightRatio); + ev.translatePoint(sx - ev.getX(), sy - ev.getY()); + writer().writePointerEvent(new Point(ev.getX(),ev.getY()), buttonMask); + } else { + writer().writePointerEvent(new Point(ev.getX(),ev.getY()), buttonMask); + } + + if (buttonMask == 0) writeModifiers(0); + } + + + synchronized public void writeWheelEvent(MouseWheelEvent ev) { + if (state() != RFBSTATE_NORMAL) return; + int x, y; + int clicks = ev.getWheelRotation(); + if (clicks < 0) { + buttonMask = 8; + } else { + buttonMask = 16; + } + writeModifiers(ev.getModifiers() & ~KeyEvent.ALT_MASK & ~KeyEvent.META_MASK); + for (int i=0;i<Math.abs(clicks);i++) { + x = ev.getX(); + y = ev.getY(); + writer().writePointerEvent(new Point(x, y), buttonMask); + buttonMask = 0; + writer().writePointerEvent(new Point(x, y), buttonMask); + } + writeModifiers(0); + + } + + + void writeModifiers(int m) { + if ((m & Event.SHIFT_MASK) != (pressedModifiers & Event.SHIFT_MASK)) + writeKeyEvent(Keysyms.Shift_L, (m & Event.SHIFT_MASK) != 0); + if ((m & Event.CTRL_MASK) != (pressedModifiers & Event.CTRL_MASK)) + writeKeyEvent(Keysyms.Control_L, (m & Event.CTRL_MASK) != 0); + if ((m & Event.ALT_MASK) != (pressedModifiers & Event.ALT_MASK)) + writeKeyEvent(Keysyms.Alt_L, (m & Event.ALT_MASK) != 0); + if ((m & Event.META_MASK) != (pressedModifiers & Event.META_MASK)) + writeKeyEvent(Keysyms.Meta_L, (m & Event.META_MASK) != 0); + pressedModifiers = m; + } + + + //////////////////////////////////////////////////////////////////// + // The following methods are called from both RFB and GUI threads + + // checkEncodings() sends a setEncodings message if one is needed. + synchronized private void checkEncodings() { + if (encodingChange && state() == RFBSTATE_NORMAL) { + vlog.info("Using "+Encodings.encodingName(currentEncoding)+" encoding"); + writer().writeSetEncodings(currentEncoding, true); + encodingChange = false; + } + } + + // the following never change so need no synchronization: + JavaInStream jis; + JavaOutStream jos; + + + // viewer object is only ever accessed by the GUI thread so needs no + // synchronization (except for one test in DesktopWindow - see comment + // there). + VncViewer viewer; + + // access to desktop by different threads is specified in DesktopWindow + + // the following need no synchronization: + + ClassLoader cl = this.getClass().getClassLoader(); + ImageIcon logo = new ImageIcon(cl.getResource("com/tigervnc/vncviewer/tigervnc.png")); + public static UserPasswdGetter upg; + public UserMsgBox msg; + + // shuttingDown is set by the GUI thread and only ever tested by the RFB + // thread after the window has been destroyed. + boolean shuttingDown; + + // reading and writing int and boolean is atomic in java, so no + // synchronization of the following flags is needed: + int currentEncoding, lastServerEncoding; + + int lowColourLevel; + + + // All menu, options, about and info stuff is done in the GUI thread (apart + // from when constructed). + F8Menu menu; + OptionsDialog options; + + // clipboard sync issues? + ClipboardDialog clipboardDialog; + + // the following are only ever accessed by the GUI thread: + int buttonMask; + int pressedModifiers; + + public String serverHost; + public int serverPort; + public int menuKey; + PixelFormat serverPF; + ViewportFrame viewport; + DesktopWindow desktop; + PixelFormat fullColourPF; + boolean fullColour; + boolean autoSelect; + boolean shared; + boolean formatChange; + boolean encodingChange; + boolean sameMachine; + boolean fullScreen; + boolean reverseConnection; + boolean firstUpdate; + boolean pendingUpdate; + + int scaleFactor = 100; + + static LogWriter vlog = new LogWriter("CConn"); +} diff --git a/java/com/tigervnc/vncviewer/ClipboardDialog.java b/java/com/tigervnc/vncviewer/ClipboardDialog.java new file mode 100644 index 00000000..dca85f25 --- /dev/null +++ b/java/com/tigervnc/vncviewer/ClipboardDialog.java @@ -0,0 +1,107 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.vncviewer; + +import java.awt.*; +import java.awt.event.*; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; +import javax.swing.*; +import com.tigervnc.rfb.LogWriter; + +class ClipboardDialog extends Dialog implements ActionListener { + + public ClipboardDialog(CConn cc_) { + super(false); + cc = cc_; + setTitle("VNC clipboard"); + textArea = new JTextArea(5,50); + getContentPane().add("Center", textArea); + + JPanel pb = new JPanel(); + clearButton = new JButton("Clear"); + pb.add(clearButton); + clearButton.addActionListener(this); + sendButton = new JButton("Send to VNC server"); + pb.add(sendButton); + sendButton.addActionListener(this); + cancelButton = new JButton("Cancel"); + pb.add(cancelButton); + cancelButton.addActionListener(this); + getContentPane().add("South", pb); + + pack(); + } + + public void initDialog() { + textArea.setText(current); + textArea.selectAll(); + } + + public void setContents(String str) { + current = str; + textArea.setText(str); + textArea.selectAll(); + } + + public void serverCutText(String str, int len) { + setContents(str); + SecurityManager sm = System.getSecurityManager(); + try { + if (sm != null) sm.checkSystemClipboardAccess(); + Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard(); + if (cb != null) { + StringSelection ss = new StringSelection(str); + try { + cb.setContents(ss, ss); + } catch(Exception e) { + vlog.debug(e.toString()); + } + } + } catch(SecurityException e) { + System.err.println("Cannot access the system clipboard"); + } + } + + public void setSendingEnabled(boolean b) { + sendButton.setEnabled(b); + } + + public void actionPerformed(ActionEvent e) { + Object s = e.getSource(); + if (s instanceof JButton && (JButton)s == clearButton) { + current = ""; + textArea.setText(current); + } else if (s instanceof JButton && (JButton)s == sendButton) { + ok = true; + current = textArea.getText(); + cc.writeClientCutText(current, current.length()); + endDialog(); + } else if (s instanceof JButton && (JButton)s == cancelButton) { + ok = false; + endDialog(); + } + } + + CConn cc; + String current; + JTextArea textArea; + JButton clearButton, sendButton, cancelButton; + static LogWriter vlog = new LogWriter("ClipboardDialog"); +} diff --git a/java/com/tigervnc/vncviewer/DesktopWindow.java b/java/com/tigervnc/vncviewer/DesktopWindow.java new file mode 100644 index 00000000..f5acfff1 --- /dev/null +++ b/java/com/tigervnc/vncviewer/DesktopWindow.java @@ -0,0 +1,553 @@ +/* Copyright (C) 2010 D. R. Commander. All Rights Reserved. + * Copyright (C) 2009 Paul Donohue. All Rights Reserved. + * Copyright (C) 2006 Constantin Kaplinsky. All Rights Reserved. + * Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// DesktopWindow is an AWT Canvas representing a VNC desktop. +// +// Methods on DesktopWindow are called from both the GUI thread and the thread +// which processes incoming RFB messages ("the RFB thread"). This means we +// need to be careful with synchronization here. +// + +package com.tigervnc.vncviewer; +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.Clipboard; +import javax.swing.*; + +import com.tigervnc.rfb.*; +import com.tigervnc.rfb.Cursor; +import com.tigervnc.rfb.Exception; +import com.tigervnc.rfb.Point; + +class DesktopWindow extends JPanel implements + Runnable, + MouseListener, + MouseMotionListener, + MouseWheelListener, + KeyListener +{ + + //////////////////////////////////////////////////////////////////// + // The following methods are all called from the RFB thread + + public DesktopWindow(int width, int height, PixelFormat serverPF, CConn cc_) { + cc = cc_; + setSize(width, height); + im = new PixelBufferImage(width, height, cc, this); + + cursor = new Cursor(); + cursorBacking = new ManagedPixelBuffer(); + addMouseListener(this); + addMouseWheelListener(this); + addMouseMotionListener(this); + addKeyListener(this); + addFocusListener(new FocusAdapter() { + public void focusGained(FocusEvent e) { + checkClipboard(); + } + }); + setFocusTraversalKeysEnabled(false); + setFocusable(true); + setDoubleBuffered(true); + } + + public int width() { + return getWidth(); + } + + public int height() { + return getHeight(); + } + + // initGraphics() is needed because for some reason you can't call + // getGraphics() on a newly-created awt Component. It is called when the + // DesktopWindow has actually been made visible so that getGraphics() ought + // to work. + + public void initGraphics() { + cc.viewport.g = cc.viewport.getGraphics(); + graphics = getComponentGraphics(cc.viewport.g); + prepareImage(im.image, scaledWidth, scaledHeight, this); + } + + final public PixelFormat getPF() { return im.getPF(); } + + synchronized public void setPF(PixelFormat pf) { + im.setPF(pf); + } + + public void setViewport(ViewportFrame viewport) + { + viewport.setChild(this); + } + + // Methods called from the RFB thread - these need to be synchronized + // wherever they access data shared with the GUI thread. + + public void setCursor(int w, int h, Point hotspot, + int[] data, byte[] mask) { + // strictly we should use a mutex around this test since useLocalCursor + // might be being altered by the GUI thread. However it's only a single + // boolean and it doesn't matter if we get the wrong value anyway. + + synchronized(this) { + if (!cc.viewer.useLocalCursor.getValue()) return; + + hideLocalCursor(); + + cursor.hotspot = hotspot; + + Dimension bsc = tk.getBestCursorSize(w, h); + + cursor.setSize(((int)bsc.getWidth() > w ? (int)bsc.getWidth() : w), + ((int)bsc.getHeight() > h ? (int)bsc.getHeight() : h)); + cursor.setPF(getPF()); + + cursorBacking.setSize(cursor.width(), cursor.height()); + cursorBacking.setPF(getPF()); + + cursor.data = new int[cursor.width() * cursor.height()]; + cursor.mask = new byte[cursor.maskLen()]; + + // set the masked pixels of the cursor transparent by using an extra bit in + // the colormap. We'll OR this into the data based on the values in the mask. + if (cursor.getPF().bpp == 8) { + cursor.cm = new DirectColorModel(9, 7, (7 << 3), (3 << 6), (1 << 8)); + } + + int maskBytesPerRow = (w + 7) / 8; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int byte_ = y * maskBytesPerRow + x / 8; + int bit = 7 - x % 8; + if ((mask[byte_] & (1 << bit)) > 0) { + cursor.data[y * cursor.width() + x] = (cursor.getPF().bpp == 8) ? + data[y * w + x] | (1 << 8) : data[y * w + x]; + } + } + System.arraycopy(mask, y * maskBytesPerRow, cursor.mask, + y * ((cursor.width() + 7) / 8), maskBytesPerRow); + } + + MemoryImageSource bitmap = + new MemoryImageSource(cursor.width(), cursor.height(), cursor.cm, + cursor.data, 0, cursor.width()); + int cw = (int)Math.floor((float)cursor.width() * scaleWidthRatio); + int ch = (int)Math.floor((float)cursor.height() * scaleHeightRatio); + int hint = java.awt.Image.SCALE_DEFAULT; + hotspot = new Point((int)Math.floor((float)hotspot.x * scaleWidthRatio), + (int)Math.floor((float)hotspot.y * scaleHeightRatio)); + Image cursorImage = (cw <= 0 || ch <= 0) ? tk.createImage(bitmap) : + tk.createImage(bitmap).getScaledInstance(cw,ch,hint); + softCursor = tk.createCustomCursor(cursorImage, + new java.awt.Point(hotspot.x,hotspot.y), "Cursor"); + } + + if (softCursor != null) { + setCursor(softCursor); + cursorAvailable = true; + return; + } + + if (!cursorAvailable) { + cursorAvailable = true; + } + + showLocalCursor(); + return; + } + + // setColourMapEntries() changes some of the entries in the colourmap. + // Unfortunately these messages are often sent one at a time, so we delay the + // settings taking effect unless the whole colourmap has changed. This is + // because getting java to recalculate its internal translation table and + // redraw the screen is expensive. + + synchronized public void setColourMapEntries(int firstColour, int nColours, + int[] rgbs) { + im.setColourMapEntries(firstColour, nColours, rgbs); + if (nColours <= 256) { + im.updateColourMap(); + im.put(0, 0, im.width(), im.height(), graphics); + } else { + if (setColourMapEntriesTimerThread == null) { + setColourMapEntriesTimerThread = new Thread(this); + setColourMapEntriesTimerThread.start(); + } + } + } + +// Update the actual window with the changed parts of the framebuffer. + + public void framebufferUpdateEnd() + { + drawInvalidRect(); + } + + // resize() is called when the desktop has changed size + synchronized public void resize() { + int w = cc.cp.width; + int h = cc.cp.height; + hideLocalCursor(); + setSize(w, h); + im.resize(w, h); + } + + final void drawInvalidRect() { + if (!invalidRect) return; + int x = invalidLeft; + int w = invalidRight - x; + int y = invalidTop; + int h = invalidBottom - y; + invalidRect = false; + + synchronized (this) { + im.put(x, y, w, h, graphics); + } + } + + final void invalidate(int x, int y, int w, int h) { + if (invalidRect) { + if (x < invalidLeft) invalidLeft = x; + if (x + w > invalidRight) invalidRight = x + w; + if (y < invalidTop) invalidTop = y; + if (y + h > invalidBottom) invalidBottom = y + h; + } else { + invalidLeft = x; + invalidRight = x + w; + invalidTop = y; + invalidBottom = y + h; + invalidRect = true; + } + + if ((invalidRight - invalidLeft) * (invalidBottom - invalidTop) > 100000) + drawInvalidRect(); + } + + public void beginRect(int x, int y, int w, int h, int encoding) { + invalidRect = false; + } + + public void endRect(int x, int y, int w, int h, int encoding) { + drawInvalidRect(); + } + + synchronized final public void fillRect(int x, int y, int w, int h, int pix) + { + if (overlapsCursor(x, y, w, h)) hideLocalCursor(); + im.fillRect(x, y, w, h, pix); + invalidate(x, y, w, h); + if (softCursor == null) + showLocalCursor(); + } + + synchronized final public void imageRect(int x, int y, int w, int h, + int[] pix) { + if (overlapsCursor(x, y, w, h)) hideLocalCursor(); + im.imageRect(x, y, w, h, pix); + invalidate(x, y, w, h); + if (softCursor == null) + showLocalCursor(); + } + + synchronized final public void copyRect(int x, int y, int w, int h, + int srcX, int srcY) { + if (overlapsCursor(x, y, w, h) || overlapsCursor(srcX, srcY, w, h)) + hideLocalCursor(); + im.copyRect(x, y, w, h, srcX, srcY); + if (!cc.viewer.fastCopyRect.getValue()) { + invalidate(x, y, w, h); + } + } + + + // mutex MUST be held when overlapsCursor() is called + final boolean overlapsCursor(int x, int y, int w, int h) { + return (x < cursorBackingX + cursorBacking.width() && + y < cursorBackingY + cursorBacking.height() && + x+w > cursorBackingX && y+h > cursorBackingY); + } + + + //////////////////////////////////////////////////////////////////// + // The following methods are all called from the GUI thread + + synchronized void resetLocalCursor() { + hideLocalCursor(); + cursorAvailable = false; + } + + // + // 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); + } + + public void setScaledSize() { + if (!cc.options.autoScale && !cc.options.fixedRatioScale) { + scaledWidth = (int)Math.floor((float)cc.cp.width * (float)cc.scaleFactor/100.0); + scaledHeight = (int)Math.floor((float)cc.cp.height * (float)cc.scaleFactor/100.0); + } else { + if (cc.viewport == null) { + scaledWidth = cc.cp.width; + scaledHeight = cc.cp.height; + } else { + Dimension vpSize = cc.viewport.getSize(); + Insets vpInsets = cc.viewport.getInsets(); + Dimension availableSize = + new Dimension(vpSize.width - vpInsets.left - vpInsets.right, + vpSize.height - vpInsets.top - vpInsets.bottom); + if (availableSize.width == 0 || availableSize.height == 0) + availableSize = new Dimension(cc.cp.width, cc.cp.height); + if (cc.options.fixedRatioScale) { + float widthRatio = (float)availableSize.width / (float)cc.cp.width; + float heightRatio = (float)availableSize.height / (float)cc.cp.height; + float ratio = Math.min(widthRatio, heightRatio); + scaledWidth = (int)Math.floor(cc.cp.width * ratio); + scaledHeight = (int)Math.floor(cc.cp.height * ratio); + } else { + scaledWidth = availableSize.width; + scaledHeight = availableSize.height; + } + } + } + scaleWidthRatio = (float)scaledWidth / (float)cc.cp.width; + scaleHeightRatio = (float)scaledHeight / (float)cc.cp.height; + } + + synchronized public void paintComponent(Graphics g) { + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.setRenderingHint(RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY); + if (cc.cp.width == scaledWidth && cc.cp.height == scaledHeight) { + g2.drawImage(im.image, 0, 0, null); + } else { + g2.drawImage(im.image, 0, 0, scaledWidth, scaledHeight, null); + } + } + + String oldContents = ""; + + synchronized public void checkClipboard() { + SecurityManager sm = System.getSecurityManager(); + try { + if (sm != null) sm.checkSystemClipboardAccess(); + Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard(); + if (cb != null && cc.viewer.sendClipboard.getValue()) { + Transferable t = cb.getContents(null); + if ((t != null) && t.isDataFlavorSupported(DataFlavor.stringFlavor)) { + try { + String newContents = (String)t.getTransferData(DataFlavor.stringFlavor); + if (newContents != null && !newContents.equals(oldContents)) { + cc.writeClientCutText(newContents, newContents.length()); + oldContents = newContents; + cc.clipboardDialog.setContents(newContents); + } + } catch (java.lang.Exception e) { + System.out.println("Exception getting clipboard data: " + e.getMessage()); + } + } + } + } catch(SecurityException e) { + System.err.println("Cannot access the system clipboard"); + } + } + + /** Mouse-Motion callback function */ + private void mouseMotionCB(MouseEvent e) { + if (!cc.viewer.viewOnly.getValue()) + cc.writePointerEvent(e); + // - If local cursor rendering is enabled then use it + synchronized(this) { + if (cursorAvailable) { + // - Render the cursor! + if (e.getX() != cursorPosX || e.getY() != cursorPosY) { + hideLocalCursor(); + if (e.getX() >= 0 && e.getX() < im.width() && + e.getY() >= 0 && e.getY() < im.height()) { + cursorPosX = e.getX(); + cursorPosY = e.getY(); + if (softCursor == null) + showLocalCursor(); + } + } + } + } + lastX = e.getX(); + lastY = e.getY(); + } + public void mouseDragged(MouseEvent e) { mouseMotionCB(e);} + public void mouseMoved(MouseEvent e) { mouseMotionCB(e);} + + /** Mouse callback function */ + private void mouseCB(MouseEvent e) { + if (!cc.viewer.viewOnly.getValue()) + cc.writePointerEvent(e); + lastX = e.getX(); + lastY = e.getY(); + } + public void mouseReleased(MouseEvent e){ mouseCB(e);} + public void mousePressed(MouseEvent e) { mouseCB(e);} + public void mouseClicked(MouseEvent e){} + public void mouseEntered(MouseEvent e){} + public void mouseExited(MouseEvent e){} + + /** MouseWheel callback function */ + private void mouseWheelCB(MouseWheelEvent e) { + if (!cc.viewer.viewOnly.getValue()) + cc.writeWheelEvent(e); + } + public void mouseWheelMoved(MouseWheelEvent e){ + mouseWheelCB(e); + } + + /** Handle the key-typed event. */ + public void keyTyped(KeyEvent e) {} + /** Handle the key-released event. */ + public void keyReleased(KeyEvent e) {} + /** Handle the key-pressed event. */ + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == + (KeyEvent.VK_F1+cc.menuKey-Keysyms.F1)) { + int sx = (scaleWidthRatio == 1.00) + ? lastX : (int)Math.floor(lastX*scaleWidthRatio); + int sy = (scaleHeightRatio == 1.00) + ? lastY : (int)Math.floor(lastY*scaleHeightRatio); + java.awt.Point ev = new java.awt.Point(lastX, lastY); + ev.translate(sx - lastX, sy - lastY); + cc.showMenu((int)ev.getX(), (int)ev.getY()); + return; + } + if (!cc.viewer.viewOnly.getValue()) + cc.writeKeyEvent(e); + } + + //////////////////////////////////////////////////////////////////// + // The following methods are called from both RFB and GUI threads + + // Note that mutex MUST be held when hideLocalCursor() and showLocalCursor() + // are called. + + private void hideLocalCursor() { + // - Blit the cursor backing store over the cursor + if (cursorVisible) { + cursorVisible = false; + im.imageRect(cursorBackingX, cursorBackingY, cursorBacking.width(), + cursorBacking.height(), cursorBacking.data); + im.put(cursorBackingX, cursorBackingY, cursorBacking.width(), + cursorBacking.height(), graphics); + } + } + + private void showLocalCursor() { + if (cursorAvailable && !cursorVisible) { + if (!im.getPF().equal(cursor.getPF()) || + cursor.width() == 0 || cursor.height() == 0) { + vlog.debug("attempting to render invalid local cursor"); + cursorAvailable = false; + return; + } + cursorVisible = true; + if (softCursor != null) return; + + int cursorLeft = cursor.hotspot.x; + int cursorTop = cursor.hotspot.y; + int cursorRight = cursorLeft + cursor.width(); + int cursorBottom = cursorTop + cursor.height(); + + int x = (cursorLeft >= 0 ? cursorLeft : 0); + int y = (cursorTop >= 0 ? cursorTop : 0); + int w = ((cursorRight < im.width() ? cursorRight : im.width()) - x); + int h = ((cursorBottom < im.height() ? cursorBottom : im.height()) - y); + + cursorBackingX = x; + cursorBackingY = y; + cursorBacking.setSize(w, h); + + for (int j = 0; j < h; j++) + System.arraycopy(im.data, (y+j) * im.width() + x, + cursorBacking.data, j*w, w); + + im.maskRect(cursorLeft, cursorTop, cursor.width(), cursor.height(), + cursor.data, cursor.mask); + im.put(x, y, w, h, graphics); + } + } + + + // run() is executed by the setColourMapEntriesTimerThread - it sleeps for + // 100ms before actually updating the colourmap. + public void run() { + try { + Thread.sleep(100); + } catch (InterruptedException e) {} + synchronized (this) { + im.updateColourMap(); + im.put(0, 0, im.width(), im.height(), graphics); + setColourMapEntriesTimerThread = null; + } + } + + // access to cc by different threads is specified in CConn + CConn cc; + + // access to the following must be synchronized: + PixelBufferImage im; + Graphics graphics; + Thread setColourMapEntriesTimerThread; + + Cursor cursor; + boolean cursorVisible; // Is cursor currently rendered? + boolean cursorAvailable; // Is cursor available for rendering? + int cursorPosX, cursorPosY; + ManagedPixelBuffer cursorBacking; + int cursorBackingX, cursorBackingY; + java.awt.Cursor softCursor; + static Toolkit tk = Toolkit.getDefaultToolkit(); + + public int scaledWidth = 0, scaledHeight = 0; + float scaleWidthRatio, scaleHeightRatio; + + // the following are only ever accessed by the RFB thread: + boolean invalidRect; + int invalidLeft, invalidRight, invalidTop, invalidBottom; + + // the following are only ever accessed by the GUI thread: + int lastX, lastY; + + static LogWriter vlog = new LogWriter("DesktopWindow"); +} diff --git a/java/com/tigervnc/vncviewer/Dialog.java b/java/com/tigervnc/vncviewer/Dialog.java new file mode 100644 index 00000000..6a11fb3a --- /dev/null +++ b/java/com/tigervnc/vncviewer/Dialog.java @@ -0,0 +1,143 @@ +/* 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. + */ + +// +// This Dialog class implements a pop-up dialog. This is needed because +// apparently you can't use the standard AWT Dialog from within an applet. The +// dialog can be made visible by calling its showDialog() method. Dialogs can +// be modal or non-modal. For a modal dialog box, the showDialog() method must +// be called from a thread other than the GUI thread, and it only returns when +// the dialog box has been dismissed. For a non-modal dialog box, the +// showDialog() method returns immediately. + +package com.tigervnc.vncviewer; + +import java.io.*; +import java.net.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.image.*; +import javax.swing.*; +import javax.swing.filechooser.*; + +//class Dialog extends JFrame implements WindowListener { +class Dialog extends JFrame { + + protected boolean ok, done; + boolean modal; + + public Dialog(boolean modal_) { + modal = modal_; + //addWindowListener(this); + } + + public boolean showDialog() { + ok = false; + done = false; + initDialog(); + Dimension dpySize = getToolkit().getScreenSize(); + Dimension mySize = getSize(); + int x = (dpySize.width - mySize.width) / 2; + int y = (dpySize.height - mySize.height) / 2; + setLocation(x, y); + ClassLoader cl = this.getClass().getClassLoader(); + ImageIcon icon = new ImageIcon(cl.getResource("com/tigervnc/vncviewer/tigervnc.ico")); + setIconImage(icon.getImage()); + //setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + //setFont(new Font("SansSerif", Font.PLAIN, 11)); + + setVisible(true); + setFocusable(true); + if (!modal) return true; + synchronized(this) { + try { + while (!done) + wait(); + } catch (InterruptedException e) { + } + } + return ok; + } + + public void endDialog() { + done = true; + setVisible(false); + setFocusable(false); + if (modal) { + synchronized (this) { + notify(); + } + } + } + + // initDialog() can be overridden in a derived class. Typically it is used + // to make sure that checkboxes have the right state, etc. + public void initDialog() { + } + + //------------------------------------------------------------------ + // implemented blank methods + //public void windowClosed(WindowEvent event){} + //public void windowDeiconified(WindowEvent event){} + //public void windowIconified(WindowEvent event){} + //public void windowActivated(WindowEvent event){} + //public void windowDeactivated(WindowEvent event){} + //public void windowOpened(WindowEvent event){} + + //------------------------------------------------------------------ + + // method to check which window was closing + //public void windowClosing(WindowEvent event) { + // ok = false; + // endDialog(); + //} + + public void addGBComponent(JComponent c, JComponent cp, + int gx, int gy, + int gw, int gh, + int gipx, int gipy, + double gwx, double gwy, + int fill, int anchor, + Insets insets) + { + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = anchor; + gbc.fill = fill; + gbc.gridx = gx; + gbc.gridy = gy; + gbc.gridwidth = gw; + gbc.gridheight = gh; + gbc.insets = insets; + gbc.ipadx = gipx; + gbc.ipady = gipy; + gbc.weightx = gwx; + gbc.weighty = gwy; + cp.add(c, gbc); + } + + final public String getFileSeperator() { + String seperator = System.getProperties().get("file.separator").toString(); + return seperator; + } + + final public String getUserName() { + String userName = (String)System.getProperties().get("user.name"); + return userName; + } + +} diff --git a/java/com/tigervnc/vncviewer/F8Menu.java b/java/com/tigervnc/vncviewer/F8Menu.java new file mode 100644 index 00000000..9b617857 --- /dev/null +++ b/java/com/tigervnc/vncviewer/F8Menu.java @@ -0,0 +1,133 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.vncviewer; + +import java.awt.Cursor; +import java.awt.event.*; +import javax.swing.JFrame; +import javax.swing.JPopupMenu; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JCheckBoxMenuItem; + +import com.tigervnc.rfb.*; + +public class F8Menu extends JPopupMenu implements ActionListener { + public F8Menu(CConn cc_) { + super("VNC Menu"); + setLightWeightPopupEnabled(false); + cc = cc_; + restore = addMenuItem("Restore",KeyEvent.VK_R); + move = addMenuItem("Move"); + move.setEnabled(false); + size = addMenuItem("Size"); + size.setEnabled(false); + minimize = addMenuItem("Minimize", KeyEvent.VK_N); + maximize = addMenuItem("Maximize", KeyEvent.VK_X); + addSeparator(); + exit = addMenuItem("Close Viewer", KeyEvent.VK_C); + addSeparator(); + fullScreen = new JCheckBoxMenuItem("Full Screen"); + fullScreen.setMnemonic(KeyEvent.VK_F); + fullScreen.addActionListener(this); + add(fullScreen); + addSeparator(); + clipboard = addMenuItem("Clipboard..."); + addSeparator(); + f8 = addMenuItem("Send F"+(cc.menuKey-Keysyms.F1+1)); + ctrlAltDel = addMenuItem("Send Ctrl-Alt-Del"); + addSeparator(); + refresh = addMenuItem("Refresh Screen", KeyEvent.VK_H); + addSeparator(); + newConn = addMenuItem("New connection...", KeyEvent.VK_W); + options = addMenuItem("Options...", KeyEvent.VK_O); + info = addMenuItem("Connection info...", KeyEvent.VK_I); + about = addMenuItem("About VncViewer...", KeyEvent.VK_A); + addSeparator(); + dismiss = addMenuItem("Dismiss menu"); + setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + + JMenuItem addMenuItem(String str, int mnemonic) { + JMenuItem item = new JMenuItem(str, mnemonic); + item.addActionListener(this); + add(item); + return item; + } + + JMenuItem addMenuItem(String str) { + JMenuItem item = new JMenuItem(str); + item.addActionListener(this); + add(item); + return item; + } + + boolean actionMatch(ActionEvent ev, JMenuItem item) { + return ev.getActionCommand().equals(item.getActionCommand()); + } + + public void actionPerformed(ActionEvent ev) { + if (actionMatch(ev, exit)) { + cc.close(); + } else if (actionMatch(ev, fullScreen)) { + cc.toggleFullScreen(); + } else if (actionMatch(ev, restore)) { + if (cc.fullScreen) cc.toggleFullScreen(); + cc.viewport.setExtendedState(JFrame.NORMAL); + } else if (actionMatch(ev, minimize)) { + if (cc.fullScreen) cc.toggleFullScreen(); + cc.viewport.setExtendedState(JFrame.ICONIFIED); + } else if (actionMatch(ev, maximize)) { + if (cc.fullScreen) cc.toggleFullScreen(); + cc.viewport.setExtendedState(JFrame.MAXIMIZED_BOTH); + } else if (actionMatch(ev, clipboard)) { + cc.clipboardDialog.showDialog(); + } else if (actionMatch(ev, f8)) { + cc.writeKeyEvent(cc.menuKey, true); + cc.writeKeyEvent(cc.menuKey, false); + } else if (actionMatch(ev, ctrlAltDel)) { + cc.writeKeyEvent(Keysyms.Control_L, true); + cc.writeKeyEvent(Keysyms.Alt_L, true); + cc.writeKeyEvent(Keysyms.Delete, true); + cc.writeKeyEvent(Keysyms.Delete, false); + cc.writeKeyEvent(Keysyms.Alt_L, false); + cc.writeKeyEvent(Keysyms.Control_L, false); + } else if (actionMatch(ev, refresh)) { + cc.refresh(); + } else if (actionMatch(ev, newConn)) { + VncViewer.newViewer(cc.viewer); + } else if (actionMatch(ev, options)) { + cc.options.showDialog(); + } else if (actionMatch(ev, info)) { + cc.showInfo(); + } else if (actionMatch(ev, about)) { + cc.showAbout(); + } else if (actionMatch(ev, dismiss)) { + firePopupMenuCanceled(); + } + } + + CConn cc; + JMenuItem restore, move, size, minimize, maximize; + JMenuItem exit, clipboard, ctrlAltDel, refresh; + JMenuItem newConn, options, info, about, dismiss; + static JMenuItem f8; + JCheckBoxMenuItem fullScreen; + static LogWriter vlog = new LogWriter("F8Menu"); +} diff --git a/java/com/tigervnc/vncviewer/LICENCE.TXT b/java/com/tigervnc/vncviewer/LICENCE.TXT new file mode 100644 index 00000000..ae3b5319 --- /dev/null +++ b/java/com/tigervnc/vncviewer/LICENCE.TXT @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/java/com/tigervnc/vncviewer/MANIFEST.MF b/java/com/tigervnc/vncviewer/MANIFEST.MF new file mode 100644 index 00000000..b3574fe2 --- /dev/null +++ b/java/com/tigervnc/vncviewer/MANIFEST.MF @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +Main-Class: com.tigervnc.vncviewer.VncViewer diff --git a/java/com/tigervnc/vncviewer/OptionsDialog.java b/java/com/tigervnc/vncviewer/OptionsDialog.java new file mode 100644 index 00000000..9664dc99 --- /dev/null +++ b/java/com/tigervnc/vncviewer/OptionsDialog.java @@ -0,0 +1,413 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.vncviewer; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.border.*; +import javax.swing.filechooser.*; +import javax.swing.ImageIcon; +import java.net.URL; +import java.io.IOException; + +import com.tigervnc.rfb.*; +import com.tigervnc.rfb.Exception; + +class OptionsDialog extends Dialog implements + ActionListener, + ItemListener +{ + + // Constants + // Static variables + static LogWriter vlog = new LogWriter("OptionsDialog"); + + OptionsDialogCallback cb; + JPanel FormatPanel, InputsPanel, MiscPanel, DefaultsPanel, SecPanel; + JCheckBox autoSelect, customCompressLevel, noJpeg; + JComboBox menuKey, compressLevel, qualityLevel, scalingFactor; + ButtonGroup encodingGroup, colourGroup; + JRadioButton zrle, hextile, tight, raw; + JRadioButton fullColour, mediumColour, lowColour, veryLowColour; + JCheckBox viewOnly, acceptClipboard, sendClipboard, acceptBell; + JCheckBox fullScreen, shared, useLocalCursor, fastCopyRect; + JCheckBox secVeNCrypt, encNone, encTLS, encX509; + JCheckBox secNone, secVnc, secPlain, secIdent, sendLocalUsername; + JButton okButton, cancelButton; + JButton ca, crl; + JButton defSaveButton; + UserPrefs defaults; + + boolean autoScale = false; + boolean fixedRatioScale = false; + + public OptionsDialog(OptionsDialogCallback cb_) { + super(false); + cb = cb_; + setResizable(false); + setTitle("VNC Viewer Options"); + defaults = new UserPrefs("vncviewer"); + + getContentPane().setLayout( + new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS)); + + JTabbedPane tabPane = new JTabbedPane(); + + ButtonGroup encodingGroup = new ButtonGroup(); + ButtonGroup colourGroup = new ButtonGroup(); + + // Colour & Encoding tab + FormatPanel=new JPanel(new GridBagLayout()); + + autoSelect = new JCheckBox("Auto Select"); + autoSelect.addItemListener(this); + + JPanel encodingPanel = new JPanel(new GridBagLayout()); + encodingPanel.setBorder(BorderFactory.createTitledBorder("Preferred encoding")); + zrle = addRadioCheckbox("ZRLE", encodingGroup, encodingPanel); + hextile = addRadioCheckbox("Hextile", encodingGroup, encodingPanel); + tight = addRadioCheckbox("Tight", encodingGroup, encodingPanel); + raw = addRadioCheckbox("Raw", encodingGroup, encodingPanel); + + JPanel tightPanel = new JPanel(new GridBagLayout()); + customCompressLevel = new JCheckBox("Custom Compression Level"); + customCompressLevel.addItemListener(this); + Object[] compressionLevels = { 1, 2, 3, 4, 5, 6 }; + compressLevel = new JComboBox(compressionLevels); + compressLevel.setEditable(true); + JLabel compressionLabel = new JLabel("Level (1=fast, 6=best [4-6 are rarely useful])"); + noJpeg = new JCheckBox("Allow JPEG Compression"); + noJpeg.addItemListener(this); + Object[] qualityLevels = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + qualityLevel = new JComboBox(qualityLevels); + JLabel qualityLabel = new JLabel("Level (0=poor, 9=best)"); + compressLevel.setPreferredSize(qualityLevel.getPreferredSize()); + // Hack to set the left inset on editable JComboBox + if (UIManager.getLookAndFeel().getID() == "Windows") { + compressLevel.setBorder(BorderFactory.createCompoundBorder(compressLevel.getBorder(), + BorderFactory.createEmptyBorder(0,1,0,0))); + } else { + ComboBoxEditor editor = compressLevel.getEditor(); + JTextField jtf = (JTextField)editor.getEditorComponent(); + jtf.setBorder(new CompoundBorder(jtf.getBorder(), new EmptyBorder(0,1,0,0))); + } + addGBComponent(customCompressLevel, tightPanel, 0, 0, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,0)); + addGBComponent(compressLevel, tightPanel, 0, 1, 1, 1, 2, 2, 0, 0, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(0,20,0,0)); + addGBComponent(compressionLabel, tightPanel, 1, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,5,0,0)); + addGBComponent(noJpeg, tightPanel, 0, 2, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,0)); + addGBComponent(qualityLevel, tightPanel, 0, 3, 1, 1, 2, 2, 0, 0, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(0,20,0,0)); + addGBComponent(qualityLabel, tightPanel, 1, 3, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,5,0,0)); + + + JPanel colourPanel = new JPanel(new GridBagLayout()); + colourPanel.setBorder(BorderFactory.createTitledBorder("Colour level")); + fullColour = addRadioCheckbox("Full (all available colours)", colourGroup, colourPanel); + mediumColour = addRadioCheckbox("Medium (256 colours)", colourGroup, colourPanel); + lowColour = addRadioCheckbox("Low (64 colours)", colourGroup, colourPanel); + veryLowColour = addRadioCheckbox("Very low(8 colours)", colourGroup, colourPanel); + + addGBComponent(autoSelect,FormatPanel, 0, 0, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,0)); + addGBComponent(encodingPanel,FormatPanel, 0, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,2,0,0)); + addGBComponent(colourPanel,FormatPanel, 1, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_END, new Insets(0,2,0,0)); + addGBComponent(tightPanel,FormatPanel, 0, 2, 2, GridBagConstraints.REMAINDER, 2, 2, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,0)); + + // Inputs tab + InputsPanel=new JPanel(new GridBagLayout()); + + viewOnly = new JCheckBox("View Only (ignore mouse & keyboard)"); + viewOnly.addItemListener(this); + acceptClipboard = new JCheckBox("Accept clipboard from server"); + acceptClipboard.addItemListener(this); + sendClipboard = new JCheckBox("Send clipboard to server"); + sendClipboard.addItemListener(this); + JLabel menuKeyLabel = new JLabel("Menu Key"); + String[] menuKeys = + { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12" }; + menuKey = new JComboBox(menuKeys); + menuKey.addItemListener(this); + addGBComponent(viewOnly,InputsPanel, 0, 0, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4)); + addGBComponent(acceptClipboard,InputsPanel, 0, 1, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4)); + addGBComponent(sendClipboard,InputsPanel, 0, 2, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4)); + addGBComponent(menuKeyLabel,InputsPanel, 0, 3, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(8,8,0,4)); + addGBComponent(menuKey,InputsPanel, 1, 3, 1, GridBagConstraints.REMAINDER, 0, 0, 25, 1, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(4,4,0,4)); + + // Misc tab + MiscPanel=new JPanel(new GridBagLayout()); + + fullScreen = new JCheckBox("Full-screen mode"); + fullScreen.addItemListener(this); + shared = new JCheckBox("Shared connection (do not disconnect other viewers)"); + shared.addItemListener(this); + useLocalCursor = new JCheckBox("Render cursor locally"); + useLocalCursor.addItemListener(this); + fastCopyRect = new JCheckBox("Fast CopyRect"); + fastCopyRect.addItemListener(this); + acceptBell = new JCheckBox("Beep when requested by the server"); + acceptBell.addItemListener(this); + JLabel scalingFactorLabel = new JLabel("Scaling Factor"); + Object[] scalingFactors = { + "Auto", "Fixed Aspect Ratio", "50%", "75%", "95%", "100%", "105%", + "125%", "150%", "175%", "200%", "250%", "300%", "350%", "400%" }; + scalingFactor = new JComboBox(scalingFactors); + scalingFactor.setEditable(true); + scalingFactor.addItemListener(this); + addGBComponent(fullScreen,MiscPanel, 0, 0, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4)); + addGBComponent(shared,MiscPanel, 0, 1, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4)); + addGBComponent(useLocalCursor,MiscPanel, 0, 2, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4)); + addGBComponent(fastCopyRect,MiscPanel, 0, 3, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4)); + addGBComponent(acceptBell,MiscPanel, 0, 4, 2, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(4,4,0,4)); + addGBComponent(scalingFactorLabel,MiscPanel, 0, 5, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(8,8,0,4)); + addGBComponent(scalingFactor,MiscPanel, 1, 5, 1, GridBagConstraints.REMAINDER, 0, 0, 25, 1, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(4,4,0,4)); + + // load/save tab + DefaultsPanel=new JPanel(new GridBagLayout()); + + JPanel configPanel = new JPanel(new GridBagLayout()); + configPanel.setBorder(BorderFactory.createTitledBorder("Configuration File")); + JButton cfReloadButton = new JButton("Reload"); + cfReloadButton.addActionListener(this); + addGBComponent(cfReloadButton,configPanel, 0, 0, 1, 1, 0, 0, 0, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8)); + JButton cfSaveButton = new JButton("Save"); + cfSaveButton.addActionListener(this); + addGBComponent(cfSaveButton,configPanel, 0, 1, 1, 1, 0, 0, 0, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8)); + JButton cfSaveAsButton = new JButton("Save As..."); + cfSaveAsButton.addActionListener(this); + addGBComponent(cfSaveAsButton,configPanel, 0, 2, 1, 1, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8)); + cfReloadButton.setEnabled(false); + cfSaveButton.setEnabled(false); + //cfSaveAsButton.setEnabled(!applet); + + JPanel defaultsPanel = new JPanel(new GridBagLayout()); + defaultsPanel.setBorder(BorderFactory.createTitledBorder("Defaults")); + JButton defReloadButton = new JButton("Reload"); + defReloadButton.addActionListener(this); + addGBComponent(defReloadButton,defaultsPanel, 0, 0, 1, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8)); + defSaveButton = new JButton("Save"); + defSaveButton.addActionListener(this); + addGBComponent(defSaveButton,defaultsPanel, 0, 1, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8)); + + addGBComponent(configPanel,DefaultsPanel, 0, 0, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.PAGE_START, new Insets(4,4,4,4)); + addGBComponent(defaultsPanel,DefaultsPanel, 1, 0, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.PAGE_START, new Insets(4,4,4,4)); + //defReloadButton.setEnabled(!applet); + //defSaveButton.setEnabled(!applet); + + // security tab + SecPanel=new JPanel(new GridBagLayout()); + + JPanel encryptionPanel = new JPanel(new GridBagLayout()); + encryptionPanel.setBorder(BorderFactory.createTitledBorder("Session Encryption")); + encNone = addCheckbox("None", null, encryptionPanel); + encTLS = addCheckbox("Anonymous TLS", null, encryptionPanel); + encX509 = addJCheckBox("TLS with X.509 certificates", null, encryptionPanel, new GridBagConstraints(0,2,1,1,1,1,GridBagConstraints.LINE_START,GridBagConstraints.REMAINDER,new Insets(0,0,0,60),0,0)); + + JPanel x509Panel = new JPanel(new GridBagLayout()); + x509Panel.setBorder(BorderFactory.createTitledBorder("X.509 certificates")); + ca = new JButton("Load CA certificate"); + ca.setPreferredSize(new Dimension(145,25)); + ca.addActionListener(this); + crl = new JButton("Load CRL certificate"); + crl.setPreferredSize(new Dimension(145,25)); + crl.addActionListener(this); + addGBComponent(ca, x509Panel, 0, 0, 1, 1, 2, 2, 0, 1, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(2,2,2,2)); + addGBComponent(crl, x509Panel, 1, 0, 1, 1, 2, 2, 1, 1, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(2,2,2,2)); + + JPanel authPanel = new JPanel(new GridBagLayout()); + authPanel.setBorder(BorderFactory.createTitledBorder("Authentication")); + secNone = addCheckbox("None", null, authPanel); + secVnc = addCheckbox("Standard VNC", null, authPanel); + secPlain = addJCheckBox("Plaintext", null, authPanel, new GridBagConstraints(0,2,1,1,1,1,GridBagConstraints.LINE_START,GridBagConstraints.NONE,new Insets(0,0,0,5),0,0)); + secIdent = addJCheckBox("Ident", null, authPanel, new GridBagConstraints(0,3,1,1,1,1,GridBagConstraints.LINE_START,GridBagConstraints.NONE,new Insets(0,0,0,5),0,0)); + sendLocalUsername = new JCheckBox("Send Local Username"); + sendLocalUsername.addItemListener(this); + addGBComponent(sendLocalUsername, authPanel, 1, 2, 1, 2, 0, 0, 2, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,20,0,0)); + + secVeNCrypt = new JCheckBox("Extended encryption and authentication methods (VeNCrypt)"); + secVeNCrypt.addItemListener(this); + addGBComponent(secVeNCrypt,SecPanel, 0, 0, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,2,0,20)); + addGBComponent(encryptionPanel,SecPanel, 0, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(0,4,2,4)); + addGBComponent(x509Panel,SecPanel, 0, 2, 1, 1, 2, 2, 1, 0, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(2,4,2,4)); + addGBComponent(authPanel,SecPanel, 0, 3, 1, 1, 2, 2, 1, 1, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(2,4,2,4)); + + tabPane.add(FormatPanel); + tabPane.add(InputsPanel); + tabPane.add(MiscPanel); + tabPane.add(DefaultsPanel); + tabPane.add(SecPanel); + tabPane.addTab("Colour & Encoding", FormatPanel); + tabPane.addTab("Inputs", InputsPanel); + tabPane.addTab("Misc", MiscPanel); + tabPane.addTab("Load / Save", DefaultsPanel); + tabPane.addTab("Security", SecPanel); + tabPane.setBorder(BorderFactory.createEmptyBorder(4,4,0,4)); + + okButton = new JButton("OK"); + okButton.setPreferredSize(new Dimension(90,30)); + okButton.addActionListener(this); + cancelButton = new JButton("Cancel"); + cancelButton.setPreferredSize(new Dimension(90,30)); + cancelButton.addActionListener(this); + + JPanel buttonPane = new JPanel(); + buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS)); + buttonPane.setBorder(BorderFactory.createEmptyBorder(4,0,0,0)); + buttonPane.add(Box.createHorizontalGlue()); + buttonPane.add(okButton); + buttonPane.add(Box.createRigidArea(new Dimension(4,0))); + buttonPane.add(cancelButton); + buttonPane.add(Box.createRigidArea(new Dimension(4,0))); + + this.getContentPane().add(tabPane); + this.getContentPane().add(buttonPane); + + pack(); + + } + + public void initDialog() { + if (cb != null) cb.setOptions(); + zrle.setEnabled(!autoSelect.isSelected()); + hextile.setEnabled(!autoSelect.isSelected()); + tight.setEnabled(!autoSelect.isSelected()); + raw.setEnabled(!autoSelect.isSelected()); + fullColour.setEnabled(!autoSelect.isSelected()); + mediumColour.setEnabled(!autoSelect.isSelected()); + lowColour.setEnabled(!autoSelect.isSelected()); + veryLowColour.setEnabled(!autoSelect.isSelected()); + compressLevel.setEnabled(customCompressLevel.isSelected()); + qualityLevel.setEnabled(noJpeg.isSelected()); + autoScale = (scalingFactor.getSelectedItem().equals("Auto")); + fixedRatioScale = + (scalingFactor.getSelectedItem().equals("Fixed Aspect Ratio")); + sendLocalUsername.setEnabled(secVeNCrypt.isEnabled()&& + (secPlain.isSelected()||secIdent.isSelected())); + } + + JRadioButton addRadioCheckbox(String str, ButtonGroup group, JPanel panel) { + JRadioButton c = new JRadioButton(str); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.LINE_START; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.weightx = 1; + gbc.weighty = 1; + panel.add(c,gbc); + group.add(c); + c.addItemListener(this); + return c; + } + + JCheckBox addCheckbox(String str, ButtonGroup group, JPanel panel) { + JCheckBox c = new JCheckBox(str); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.LINE_START; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.weightx = 1; + gbc.weighty = 1; + panel.add(c,gbc); + if (group != null) + group.add(c); + c.addItemListener(this); + return c; + } + + JCheckBox addJCheckBox(String str, ButtonGroup group, JPanel panel, + GridBagConstraints gbc) { + JCheckBox c = new JCheckBox(str); + panel.add(c,gbc); + if (group != null) + group.add(c); + c.addItemListener(this); + + return c; + } + + public void actionPerformed(ActionEvent e) { + Object s = e.getSource(); + if (s instanceof JButton && (JButton)s == okButton) { + autoScale = (scalingFactor.getSelectedItem().equals("Auto")); + fixedRatioScale = + (scalingFactor.getSelectedItem().equals("Fixed Aspect Ratio")); + ok = true; + if (cb != null) cb.getOptions(); + endDialog(); + } else if (s instanceof JButton && (JButton)s == cancelButton) { + ok = false; + endDialog(); + } else if (s instanceof JButton && (JButton)s == defSaveButton) { + try { + defaults.Save(); + } catch (java.lang.Exception x) { } + } else if (s instanceof JButton && (JButton)s == ca) { + JFileChooser fc = new JFileChooser(); + fc.setDialogTitle("Path to X509 CA certificate"); + int ret = fc.showOpenDialog(this); + if (ret == JFileChooser.APPROVE_OPTION) + CSecurityTLS.x509ca.setParam(fc.getSelectedFile().toString()); + } else if (s instanceof JButton && (JButton)s == crl) { + JFileChooser fc = new JFileChooser(); + fc.setDialogTitle("Path to X509 CRL file"); + int ret = fc.showOpenDialog(this); + if (ret == JFileChooser.APPROVE_OPTION) + CSecurityTLS.x509crl.setParam(fc.getSelectedFile().toString()); + } + } + + public void itemStateChanged(ItemEvent e) { + Object s = e.getSource(); + if (s instanceof JCheckBox && (JCheckBox)s == autoSelect) { + zrle.setEnabled(!autoSelect.isSelected()); + hextile.setEnabled(!autoSelect.isSelected()); + tight.setEnabled(!autoSelect.isSelected()); + raw.setEnabled(!autoSelect.isSelected()); + fullColour.setEnabled(!autoSelect.isSelected()); + mediumColour.setEnabled(!autoSelect.isSelected()); + lowColour.setEnabled(!autoSelect.isSelected()); + veryLowColour.setEnabled(!autoSelect.isSelected()); + defaults.setPref("autoSelect",(autoSelect.isSelected()) ? "on" : "off"); + } + if (s instanceof JCheckBox && (JCheckBox)s == customCompressLevel) { + compressLevel.setEnabled(customCompressLevel.isSelected()); + defaults.setPref("customCompressLevel",(customCompressLevel.isSelected()) ? "on" : "off"); + } + if (s instanceof JCheckBox && (JCheckBox)s == noJpeg) { + qualityLevel.setEnabled(noJpeg.isSelected()); + defaults.setPref("noJpeg",(noJpeg.isSelected()) ? "on" : "off"); + } + if (s instanceof JCheckBox && (JCheckBox)s == sendLocalUsername) { + defaults.setPref("sendLocalUsername",(sendLocalUsername.isSelected()) ? "on" : "off"); + } + if (s instanceof JCheckBox && (JCheckBox)s == secVeNCrypt) { + encNone.setEnabled(secVeNCrypt.isSelected()); + encTLS.setEnabled(secVeNCrypt.isSelected()); + encX509.setEnabled(secVeNCrypt.isSelected()); + ca.setEnabled(secVeNCrypt.isSelected()); + crl.setEnabled(secVeNCrypt.isSelected()); + secIdent.setEnabled(secVeNCrypt.isSelected()); + secNone.setEnabled(secVeNCrypt.isSelected()); + secVnc.setEnabled(secVeNCrypt.isSelected()); + secPlain.setEnabled(secVeNCrypt.isSelected()); + sendLocalUsername.setEnabled(secVeNCrypt.isSelected()); + } + if (s instanceof JCheckBox && (JCheckBox)s == secIdent || + s instanceof JCheckBox && (JCheckBox)s == secPlain) { + sendLocalUsername.setEnabled(secIdent.isSelected()||secPlain.isSelected()); + } + } + +} diff --git a/java/com/tigervnc/vncviewer/OptionsDialogCallback.java b/java/com/tigervnc/vncviewer/OptionsDialogCallback.java new file mode 100644 index 00000000..f6897e24 --- /dev/null +++ b/java/com/tigervnc/vncviewer/OptionsDialogCallback.java @@ -0,0 +1,24 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.vncviewer; + +public interface OptionsDialogCallback { + public void setOptions(); + public void getOptions(); +} diff --git a/java/com/tigervnc/vncviewer/PasswdDialog.java b/java/com/tigervnc/vncviewer/PasswdDialog.java new file mode 100644 index 00000000..eaace698 --- /dev/null +++ b/java/com/tigervnc/vncviewer/PasswdDialog.java @@ -0,0 +1,88 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.vncviewer; + +import java.awt.*; +import java.awt.event.*; +import javax.swing.*; +import java.net.URL; + +class PasswdDialog extends Dialog implements KeyListener{ + + public PasswdDialog(String title, boolean userDisabled, boolean passwdDisabled) { + super(true); + setResizable(false); + setTitle(title); + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + + JPanel p1 = new JPanel(); + userLabel = new JLabel("Username:"); + p1.add(userLabel); + userEntry = new JTextField(30); + userEntry.setEnabled(!userDisabled); + userLabel.setEnabled(!userDisabled); + p1.add(userEntry); + userEntry.addKeyListener(this); + + JPanel p2 = new JPanel(); + passwdLabel = new JLabel("Password:"); + passwdLabel.setPreferredSize(userLabel.getPreferredSize()); + p2.add(passwdLabel); + passwdEntry = new JPasswordField(30); + passwdEntry.setEnabled(!passwdDisabled); + passwdLabel.setEnabled(!passwdDisabled); + p2.add(passwdEntry); + passwdEntry.addKeyListener(this); + + getContentPane().setLayout(new BoxLayout(getContentPane(),BoxLayout.Y_AXIS)); + getContentPane().add(p1); + getContentPane().add(p2); + pack(); + if (userEntry.isEnabled()) { + userEntry.requestFocus(); + } else { + passwdEntry.requestFocus(); + } + } + + /** Handle the key-typed event. */ + public void keyTyped(KeyEvent event) { } + /** Handle the key-released event. */ + public void keyReleased(KeyEvent event) { } + /** Handle the key-pressed event. */ + public void keyPressed(KeyEvent event) { + Object s = event.getSource(); + if (s instanceof JTextField && (JTextField)s == userEntry) { + if (event.getKeyCode() == KeyEvent.VK_ENTER) { + ok = true; + endDialog(); + } + } else if (s instanceof JPasswordField && (JPasswordField)s == passwdEntry) { + if (event.getKeyCode() == KeyEvent.VK_ENTER) { + ok = true; + endDialog(); + } + } + } + + JLabel userLabel; + JTextField userEntry; + JLabel passwdLabel; + JTextField passwdEntry; +} diff --git a/java/com/tigervnc/vncviewer/PixelBufferImage.java b/java/com/tigervnc/vncviewer/PixelBufferImage.java new file mode 100644 index 00000000..7e5e7174 --- /dev/null +++ b/java/com/tigervnc/vncviewer/PixelBufferImage.java @@ -0,0 +1,185 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// PixelBufferImage is an PixelBuffer which also acts as an ImageProducer. +// Currently it only supports 8-bit colourmapped pixel format. +// + +package com.tigervnc.vncviewer; + +import java.awt.*; +import java.awt.image.*; +import java.nio.ByteOrder; +import javax.swing.JScrollPane; + +import com.tigervnc.rfb.*; + +public class PixelBufferImage extends PixelBuffer implements ImageProducer +{ + public PixelBufferImage(int w, int h, CConn cc_, DesktopWindow desktop_) { + cc = cc_; + desktop = desktop_; + PixelFormat nativePF = getNativePF(); + switch ((nativePF.depth > cc.serverPF.depth) ? cc.serverPF.depth : nativePF.depth) { + case 8: + setPF(new PixelFormat(8,8,false,true,7,7,3,0,3,6)); + break; + case 16: + setPF(new PixelFormat(16,16,false,true,0xF800,0x07C0,0x003E,0,0,0)); + break; + case 24: + setPF(new PixelFormat(32,24,false,true,0xff,0xff,0xff,16,8,0)); + break; + default: + setPF(new PixelFormat(8,8,false,true,7,7,3,0,3,6)); + vlog.debug("Unsupported native PF, defaulting to depth 8"); + } + resize(w, h); + } + + // resize() resizes the image, preserving the image data where possible. + public void resize(int w, int h) { + if (w == width() && h == height()) return; + + int rowsToCopy = h < height() ? h : height(); + int copyWidth = w < width() ? w : width(); + int oldWidth = width(); + int[] oldData = data; + + width_ = w; + height_ = h; + image = desktop.createImage(this); + //image.setAccelerationPriority(1); + + data = new int[width() * height()]; + + for (int i = 0; i < rowsToCopy; i++) + System.arraycopy(oldData, copyWidth * i, + data, width() * i, copyWidth); + } + + private PixelFormat getNativePF() { + PixelFormat pf; + cm = java.awt.Toolkit.getDefaultToolkit().getColorModel(); + if (cm.getColorSpace().getType() == java.awt.color.ColorSpace.TYPE_RGB) { + int depth = cm.getPixelSize(); + int bpp = (depth > 16 ? 32 : (depth > 8 ? 16 : 8)); + ByteOrder byteOrder = ByteOrder.nativeOrder(); + boolean bigEndian = (byteOrder == ByteOrder.BIG_ENDIAN ? true : false); + boolean trueColour = (depth > 8 ? true : false); + int redShift = cm.getComponentSize()[0] + cm.getComponentSize()[1]; + int greenShift = cm.getComponentSize()[0]; + int blueShift = 0; + pf = new PixelFormat(bpp, depth, bigEndian, trueColour, + (depth > 8 ? 0xff : 0), + (depth > 8 ? 0xff : 0), + (depth > 8 ? 0xff : 0), + (depth > 8 ? redShift : 0), + (depth > 8 ? greenShift : 0), + (depth > 8 ? blueShift : 0)); + } else { + pf = new PixelFormat(8, 8, false, false, 7, 7, 3, 0, 3, 6); + } + vlog.debug("Native pixel format is "+pf.print()); + return pf; + } + + // put() causes the given rectangle to be drawn using the given graphics + // context. + public void put(int x, int y, int w, int h, Graphics g) { + if (ic != null) { + ic.setPixels(x, y, w, h, cm, data, width() * y + x, width()); + ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE); + } + } + + // fillRect(), imageRect(), maskRect() are inherited from PixelBuffer. For + // copyRect() we also need to tell the ImageConsumer that the pixels have + // changed (this is done in the put() call for the others). + + public void copyRect(int x, int y, int w, int h, int srcX, int srcY) { + super.copyRect(x, y, w, h, srcX, srcY); + if (ic == null) return; + ic.setPixels(x, y, w, h, cm, data, width() * y + x, width()); + ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE); + } + + // setColourMapEntries() changes some of the entries in the colourmap. + // However these settings won't take effect until updateColourMap() is + // called. This is because getting java to recalculate its internal + // translation table and redraw the screen is expensive. + + public void setColourMapEntries(int firstColour, int nColours_, + int[] rgbs) { + nColours = nColours_; + reds = new byte[nColours]; + blues = new byte[nColours]; + greens = new byte[nColours]; + for (int i = 0; i < nColours; i++) { + reds[firstColour+i] = (byte)(rgbs[i*3] >> 8); + greens[firstColour+i] = (byte)(rgbs[i*3+1] >> 8); + blues[firstColour+i] = (byte)(rgbs[i*3+2] >> 8); + } + } + + // ImageProducer methods + + public void updateColourMap() { + cm = new IndexColorModel(8, nColours, reds, greens, blues); + } + + public void addConsumer(ImageConsumer c) { + if (ic == c) return; + + vlog.debug("adding consumer "+c); + + if (ic != null) + vlog.error("Only one ImageConsumer allowed - discarding old one"); + + ic = c; + ic.setDimensions(width(), height()); + ic.setHints(ImageConsumer.RANDOMPIXELORDER); + // Calling ic.setColorModel(cm) seemed to help in some earlier versions of + // the JDK, but it shouldn't be necessary because we pass the ColorModel + // with each setPixels() call. + ic.setPixels(0, 0, width(), height(), cm, data, 0, width()); + ic.imageComplete(ImageConsumer.SINGLEFRAMEDONE); + } + + public void removeConsumer(ImageConsumer c) { + System.err.println("removeConsumer "+c); + if (ic == c) ic = null; + } + + public boolean isConsumer(ImageConsumer c) { return ic == c; } + public void requestTopDownLeftRightResend(ImageConsumer c) {} + public void startProduction(ImageConsumer c) { addConsumer(c); } + + Image image; + ImageConsumer ic; + + int nColours; + byte[] reds; + byte[] greens; + byte[] blues; + + CConn cc; + DesktopWindow desktop; + static LogWriter vlog = new LogWriter("PixelBufferImage"); +} diff --git a/java/com/tigervnc/vncviewer/README b/java/com/tigervnc/vncviewer/README new file mode 100644 index 00000000..9d825684 --- /dev/null +++ b/java/com/tigervnc/vncviewer/README @@ -0,0 +1,174 @@ + +TigerVNC Java Viewer +==================== + +This distribution is based on the standard VNC source and includes new +TigerVNC-specific features and fixes, such as additional low-bandwidth +optimizations, major GUI improvements, and more. + + Copyright (C) 2000-2003 Constantin Kaplinsky + Copyright (C) 2003 Sun Microsystems, Inc. + Copyright (C) 2002-2005 RealVNC Ltd. + Copyright (C) 2004-2005 Cendio AB + Copyright (C) 2005 Martin Koegler + Copyright (C) 2009 Pierre Ossman for Cendio AB + Copyright (C) 2009-2011 TigerVNC Team + All rights reserved. + +This software is distributed under the GNU General Public Licence as +published by the Free Software Foundation. See the file LICENCE.TXT for the +conditions under which this software is made available. TigerVNC also +contains code from other sources. See the Acknowledgements section below, and +the individual files for details of the conditions under which they are made +available. + + +Installation +============ + +There are three basic ways to use the TigerVNC Java Viewer: + + 1. Running the applet as part of a TigerVNC Server installation. + + Both the Unix and Windows versions of the TigerVNC Server include a small + built-in HTTP server that can serve the TigerVNC Java Viewer to web + clients. This enables easy access to the shared desktop without the need + to install any software on the client machine. + + The Unix TigerVNC Server (Xvnc) is able to serve up any set of files that + are present in a particular directory, which is specified in the -httpd + argument to Xvnc. The default version of the vncserver script will look + for a directory called vnc/classes, one level up from the directory + containing the vncserver script, then it will look for a directory called + /usr/share/vnc/classes, then /usr/local/vnc/classes. It will set the + -httpd argument to Xvnc to the first one of these VNC classes directories + it finds. Thus, one can easily deploy a modified version of the TigerVNC + Java Viewer by simply copying a new version of VncViewer.jar and/or + index.vnc into the VNC classes directory. + + In the case of the Windows TigerVNC Server, VncViewer.jar and index.vnc + are embedded as resources in the WinVNC executable, so deploying a + modified version of the TigerVNC Java Viewer on a Windows server requires + rebuilding WinVNC. + + 2. Running the applet from a standalone web server. + + Another possibility for using the TigerVNC Java Viewer is to install it + under a fully-functional HTTP server, such as Apache or IIS. Due to Java + security restrictions, the applet must be signed in order for it to + connect to a VNC server running on a different machine from the HTTP + server. + + One can install the TigerVNC Java Viewer by simply copying the .class and + .jar files into a directory that is under the control of the HTTP server. + Also, an HTML page should be created to act as a the base document for the + TigerVNC Java Viewer applet (an example named index.html is provided in + this directory. Modify this file to suit your specific needs.) + + 3. Running the viewer as a standalone application. + + Finally, the TigerVNC Java Viewer can be executed locally on the client + machine, but this method requires installation of either a JRE (Java + Runtime Environment) or a JDK (Java Development Kit). If VncViewer.jar is + in the current directory, then the TigerVNC Java Viewer can be launched + with the following command line: + + java -jar VncViewer.jar [parameters] + + Add an argument of -? to the above command line to print a list of + optional parameters supported by VncViewer. + + +Parameters +========== + +The TigerVNC Java Viewer accepts a number of optional parameters, allowing you +to customize its behavior. + +Parameters can be specified in one of the two ways, depending on how the +TigerVNC Java Viewer is used: + + 1. When the TigerVNC Java Viewer is run as an applet (embedded within an HTML + document), parameters should be specified using the <PARAM> HTML tags + within the appropriate <APPLET> section. Example: + + <APPLET CODE=com.tigervnc.vncviewer.VncViewer ARCHIVE=VncViewer.jar + WIDTH=400 HEIGHT=300> + <PARAM NAME="PORT" VALUE=5901> + <PARAM NAME="ScalingFactor" VALUE=50> + </APPLET> + + 2. When run as a standalone application, the TigerVNC Java Viewer reads + parameters from the command line. Example: + + java -jar VncViewer.jar Port=5901 ScalingFactor=50 + +Both parameter names and their values are case-insensitive. + +For a complete list of parameters and their descriptions, run: + + java -jar VncViewer.jar -? + + +HINTS +===== + +--> To refresh the remote desktop in the view-only mode, press "r" or "R" + on the keyboard. + + +ACKNOWLEDGEMENTS +================ + +This distribution contains Java DES software by Dave Zimmerman +<dzimm@widget.com> and Jef Poskanzer <jef@acme.com>. This is: + + Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved. + + Permission to use, copy, modify, and distribute this software and its + documentation for NON-COMMERCIAL or COMMERCIAL purposes and without fee + is hereby granted, provided that this copyright notice is kept intact. + + WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE + SUITABILITY OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT + NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE + LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, + MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. + + THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE + CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE + PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT + NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE + SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE + SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE + PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET + WORKSHOP SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF + FITNESS FOR HIGH RISK ACTIVITIES. + + Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights + reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Visit the ACME Labs Java page for up-to-date versions of this and other + fine Java utilities: http://www.acme.com/java/ diff --git a/java/com/tigervnc/vncviewer/ServerDialog.java b/java/com/tigervnc/vncviewer/ServerDialog.java new file mode 100644 index 00000000..00a7e183 --- /dev/null +++ b/java/com/tigervnc/vncviewer/ServerDialog.java @@ -0,0 +1,179 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.vncviewer; + +import java.awt.*; +import java.awt.image.*; +import java.awt.event.*; +import javax.swing.*; +import javax.swing.border.*; +import java.net.URL; +import java.io.File; +import java.util.*; + +import com.tigervnc.rfb.*; +import com.tigervnc.rfb.Exception; + +class ServerDialog extends Dialog implements + ActionListener, + ItemListener +{ + + public ServerDialog(OptionsDialog options_, + String defaultServerName, CConn cc_) { + + super(true); + cc = cc_; + setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); + setResizable(false); + setSize(new Dimension(340, 135)); + setTitle("VNC Viewer : Connection Details"); + + options = options_; + getContentPane().setLayout(new GridBagLayout()); + + JLabel serverLabel = new JLabel("Server:", JLabel.RIGHT); + if (options.defaults.getString("server") != null) { + server = new JComboBox(options.defaults.getString("server").split(",")); + } else { + server = new JComboBox(); + } + + // Hack to set the left inset on editable JComboBox + if (UIManager.getLookAndFeel().getID() == "Windows") { + server.setBorder(BorderFactory.createCompoundBorder(server.getBorder(), + BorderFactory.createEmptyBorder(0,2,0,0))); + } else { + ComboBoxEditor editor = server.getEditor(); + JTextField jtf = (JTextField)editor.getEditorComponent(); + jtf.setBorder(new CompoundBorder(jtf.getBorder(), new EmptyBorder(0,2,0,0))); + } + + server.setEditable(true); + editor = server.getEditor(); + JLabel encryptionLabel = new JLabel("Encryption:"); + encryption = new JComboBox(); + serverLabel.setPreferredSize(encryptionLabel.getPreferredSize()); + + JPanel topPanel = new JPanel(new GridBagLayout()); + + addGBComponent(new JLabel(cc.logo),topPanel, 0, 0, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(5,5,5,15)); + addGBComponent(serverLabel,topPanel, 1, 0, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_END, new Insets(10,0,5,5)); + addGBComponent(server,topPanel, 2, 0, 1, 1, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(10,0,5,40)); + + optionsButton = new JButton("Options..."); + aboutButton = new JButton("About..."); + okButton = new JButton("OK"); + cancelButton = new JButton("Cancel"); + JPanel buttonPanel = new JPanel(new GridBagLayout()); + buttonPanel.setPreferredSize(new Dimension(340, 40)); + addGBComponent(aboutButton,buttonPanel, 0, 3, 1, 1, 0, 0, 0.2, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5)); + addGBComponent(optionsButton,buttonPanel, 1, 3, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5)); + addGBComponent(okButton,buttonPanel, 2, 3, 1, 1, 0, 0, 0.8, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5)); + addGBComponent(cancelButton,buttonPanel, 3, 3, 1, 1, 0, 0, 0.5, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5)); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.LINE_START; + gbc.fill = GridBagConstraints.BOTH; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridheight = 1; + gbc.insets = new Insets(0,0,0,0); + gbc.ipadx = 0; + gbc.ipady = 0; + gbc.weightx = 1; + gbc.weighty = 1; + getContentPane().add(topPanel,gbc); + getContentPane().add(buttonPanel); + + server.addActionListener(this); + optionsButton.addActionListener(this); + aboutButton.addActionListener(this); + okButton.addActionListener(this); + cancelButton.addActionListener(this); + + pack(); + } + + public void itemStateChanged(ItemEvent e) { + Object s = e.getSource(); + } + + public void actionPerformed(ActionEvent e) { + Object s = e.getSource(); + if (s instanceof JButton && (JButton)s == okButton) { + ok = true; + endDialog(); + } else if (s instanceof JButton && (JButton)s == cancelButton) { + ok = false; + endDialog(); + } else if (s instanceof JButton && (JButton)s == optionsButton) { + options.showDialog(); + } else if (s instanceof JButton && (JButton)s == aboutButton) { + cc.showAbout(); + } else if (s instanceof JComboBox && (JComboBox)s == server) { + if (e.getActionCommand().equals("comboBoxEdited")) { + server.insertItemAt(editor.getItem(), 0); + server.setSelectedIndex(0); + ok = true; + endDialog(); + } + } + } + + public void endDialog() { + if (ok) { + try { + options.defaults.setPref("encryption",(encryption.getSelectedIndex()==1) ? "off" : "on"); + if (!server.getSelectedItem().toString().equals("")) { + String t = (options.defaults.getString("server")==null) ? "" : options.defaults.getString("server"); + StringTokenizer st = new StringTokenizer(t, ","); + StringBuffer sb = new StringBuffer().append((String)server.getSelectedItem()); + while (st.hasMoreTokens()) { + String s = st.nextToken(); + if (!s.equals((String)server.getSelectedItem()) && !s.equals("")) { + sb.append(','); + sb.append(s); + } + } + options.defaults.setPref("server", sb.toString()); + } + options.defaults.Save(); + } catch (java.io.IOException e) { + System.out.println(e.toString()); + } catch(java.security.AccessControlException e) { + System.out.println(e.toString()); + } + } + done = true; + if (modal) { + synchronized (this) { + notify(); + } + } + this.dispose(); + } + + CConn cc; + JComboBox encryption, server; + ComboBoxEditor editor; + JButton aboutButton, optionsButton, okButton, cancelButton; + OptionsDialog options; + static LogWriter vlog = new LogWriter("ServerDialog"); + +} diff --git a/java/com/tigervnc/vncviewer/UserPrefs.java b/java/com/tigervnc/vncviewer/UserPrefs.java new file mode 100644 index 00000000..bc2cf0f1 --- /dev/null +++ b/java/com/tigervnc/vncviewer/UserPrefs.java @@ -0,0 +1,286 @@ +/* Copyright (C) 2011 TigerVNC Team. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +package com.tigervnc.vncviewer; + +import java.util.Properties; +import java.io.FileOutputStream; +import javax.swing.filechooser.*; + +/** + * Simple, generic class to load up or save properties for an application. + * (eg user preferences) + * + * While reading in values, it will automatically convert from string format, + * to your choice of int or boolean, if desired. + * + * We assume that you want properties stored in a file named + * $HOME/.yourappname, where $HOME represents the users "home directory" + * + * Feel free to email comments or suggestions to the author + * + * @version 1.0, 09/16/1997 + * @author Philip Brown phil@bolthole.com + */ + +public class UserPrefs extends Properties { + String userhome=null; //This will have fileseparator on end if it + String prefFile; + String appName; + + Properties systemprops; + + + /** + * We try to read in a preferences file, from the user's "HOME" + * directory. We base the name of the file, on the name of the + * application we are in. + * Use the getHomeDir() call if you want to know what directory + * this is in. + * + * @param appName_ name of application calling this class + */ + public UserPrefs(String appName_) { + appName = appName_; + + try { + systemprops=System.getProperties(); + } catch(java.security.AccessControlException e) { + System.out.println("Cannot access system properties"); + } + // This is guaranteed as always being some valid directory, + // according to spec. + prefFile= getHomeDir()+getFileSeparator()+ + "."+appName; + try { + load(new java.io.FileInputStream(prefFile)); + } catch(java.security.AccessControlException e) { + System.out.println("Cannot access system properties"); + } catch (Exception err) { + if(err instanceof java.io.FileNotFoundException) { + try { + store(new FileOutputStream(prefFile), appName+" preferences"); + } catch (Exception e) { /* FIXME */ } + } else { + // FIXME - should be a dialog + System.out.println("Error opening prefs file:"+err.getMessage()); + } + } + + } + + // Strip off any comments + String trimValue(String prop) { + if(prop==null) + return null; + + int lastpos; + lastpos=prop.indexOf('#'); + if(lastpos==-1) + lastpos=prop.length()-1; + while((prop.charAt(lastpos)==' ') || + (prop.charAt(lastpos)=='\t')) { + lastpos--; + if(lastpos==0) + break; + } + + return prop.substring(0, lastpos+1); + } + + /** + * The java spec guarantees that a "home" directory be + * specified. We look it up for our own uses at initialization + * If you want to do other things in the user's home dir, + * this routine is an easy way to find out where it is. + * + * This returns string that will have trailing fileseparator, eg "/") + * so you can slap together a filename directly after it, and + * not worry about that sort of junk. + */ + final public static String getHomeDir() { + String homeDir = null; + try { + String os = System.getProperty("os.name"); + try { + if (os.startsWith("Windows")) { + // JRE prior to 1.5 cannot reliably determine USERPROFILE + // return user.home and hope it's right... + if (Integer.parseInt(System.getProperty("java.version").split("\\.")[1]) < 5) { + try { + homeDir = System.getProperty("user.home"); + } catch(java.security.AccessControlException e) { + System.out.println("Cannot access user.home system property"); + } + } else { + homeDir = System.getenv("USERPROFILE"); + } + } else { + try { + homeDir = FileSystemView.getFileSystemView(). + getDefaultDirectory().getCanonicalPath(); + } catch(java.security.AccessControlException e) { + System.out.println("Cannot access system property"); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } catch(java.security.AccessControlException e) { + System.out.println("Cannot access os.name system property"); + } + return homeDir; + } + + final private String getUserName() { + String userName = null; + try { + userName = (String) System.getProperties().get("user.name"); + } catch(java.security.AccessControlException e) { + System.out.println("Cannot access user.name system property"); + } + return userName; + } + + final public static String getFileSeparator() { + String separator = null; + try { + separator = Character.toString(java.io.File.separatorChar); + } catch(java.security.AccessControlException e) { + System.out.println("Cannot access file.separator system property"); + } + return separator; + } + + /** + * way to directly set a preference. You'll have to + * do your own type conversion. + * @param prefname name of property + * @param value string value of property + */ + public void setPref(String prefname, String value) { + setProperty(prefname, value); + } + + public void setPref(String prefname, int value) { + systemprops.put(prefname, java.lang.Integer.toString(value)); + } + + public void setPref(String prefname, boolean value) { + put(prefname, (value ? "true" : "false")); + } + + /** + * Gets named resource, as a string value. + * returns null if no such resource defined. + * @param name name of property + */ + public String getString(String name) { + return trimValue(getProperty(name)); + } + /** + * Gets named resource, as a string value. + * + * @param name name of property + * @param defval default value to remember and return , if + * no existing property name found. + */ + public String getString(String name, String defstr) { + String val = trimValue(getProperty(name)); + if(val==null) { + setPref(name, defstr); + return defstr; + } + return val; + } + + /** + * look up property that is an int value + * @param name name of property + */ + public int getInt(String name) { + String strint = trimValue(getProperty(name)); + int val=0; + try { + val = Integer.parseInt(strint); + } catch (NumberFormatException err) { + //we dont care + } + return val; + } + + /** + * look up property that is an int value + * @param name name of property + * @param defval default value to remember and return , if + * no existing property name found. + */ + public int getInt(String name, int defval) { + String strint = trimValue(getProperty(name)); + if(strint==null) { + setPref(name, String.valueOf(defval)); + return defval; + } + int val=0; + try { + val = Integer.parseInt(strint); + } catch (NumberFormatException err) { + //we dont care + } + return val; + } + + /** + * look up property that is a boolean value + * @param name name of property + * @param defval default value to remember and return , if + * no existing property name found. + */ + public boolean getBool(String name) { + String strval = trimValue(getProperty(name)); + if(strval.equals("true")) + return true; + + return false; + } + + /** + * @param name name of property + * @param defval default value to remember and return , if + * no existing property name found. + */ + public boolean getBool(String name, boolean defval) { + String strval = trimValue(getProperty(name)); + if(strval==null) { + setPref(name, String.valueOf(defval)); + return defval; + } + + if(strval.equals("true")) + return true; + + return false; + } + + /** + * save user preferences to default file. Duh. + */ + public void Save() throws java.io.IOException { + store(new FileOutputStream(prefFile), appName+" preferences"); + } +} diff --git a/java/com/tigervnc/vncviewer/VncViewer.java b/java/com/tigervnc/vncviewer/VncViewer.java new file mode 100644 index 00000000..bea486b0 --- /dev/null +++ b/java/com/tigervnc/vncviewer/VncViewer.java @@ -0,0 +1,314 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +// +// VncViewer - the VNC viewer applet. It can also be run from the +// command-line, when it behaves as much as possibly like the windows and unix +// viewers. +// +// Unfortunately, because of the way Java classes are loaded on demand, only +// configuration parameters defined in this file can be set from the command +// line or in applet parameters. + +package com.tigervnc.vncviewer; + +import java.awt.*; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.image.*; +import java.awt.Label; +import java.io.InputStream; +import java.util.jar.Attributes; +import java.util.jar.Manifest; +import javax.swing.*; +import java.net.URL; + +import com.tigervnc.rdr.*; +import com.tigervnc.rfb.*; +import com.tigervnc.rfb.Exception; + +public class VncViewer extends java.applet.Applet implements Runnable +{ + public static final String about1 = "TigerVNC Viewer for Java"; + public static final String about2 = "Copyright (C) 1998-2011 "+ + "TigerVNC Team and many others (see README)"; + public static final String about3 = "Visit http://www.tigervnc.org "+ + "for information on TigerVNC."; + public static String version = null; + public static String build = null; + + public static void main(String[] argv) { + try { + String os = System.getProperty("os.name"); + if (os.startsWith("Windows")) { + String laf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"; + UIManager.setLookAndFeel(laf); + } else { + UIManager.put("swing.boldMetal", Boolean.FALSE); + javax.swing.plaf.FontUIResource f = new + javax.swing.plaf.FontUIResource("SansSerif", Font.PLAIN, 11); + java.util.Enumeration keys = UIManager.getDefaults().keys(); + while (keys.hasMoreElements()) { + Object key = keys.nextElement(); + Object value = UIManager.get (key); + if (value instanceof javax.swing.plaf.FontUIResource) + UIManager.put(key, f); + } + } + UIManager.put("TitledBorder.titleColor",Color.blue); + } catch (java.lang.Exception exc) { } + VncViewer viewer = new VncViewer(argv); + viewer.start(); + } + + + public VncViewer(String[] argv) { + applet = false; + + // Override defaults with command-line options + for (int i = 0; i < argv.length; i++) { + if (argv[i].equalsIgnoreCase("-log")) { + if (++i >= argv.length) usage(); + System.err.println("Log setting: "+argv[i]); + LogWriter.setLogParams(argv[i]); + continue; + } + + if (Configuration.setParam(argv[i])) + continue; + + if (argv[i].charAt(0) == '-') { + if (i+1 < argv.length) { + if (Configuration.setParam(argv[i].substring(1), argv[i+1])) { + i++; + continue; + } + } + usage(); + } + + if (vncServerName.getValue() != null) + usage(); + vncServerName.setParam(argv[i]); + } + } + + public static void usage() { + String usage = ("\nusage: vncviewer [options/parameters] "+ + "[host:displayNum] [options/parameters]\n"+ + //" vncviewer [options/parameters] -listen [port] "+ + //"[options/parameters]\n"+ + "\n"+ + "Options:\n"+ + " -log <level> configure logging level\n"+ + "\n"+ + "Parameters can be turned on with -<param> or off with "+ + "-<param>=0\n"+ + "Parameters which take a value can be specified as "+ + "-<param> <value>\n"+ + "Other valid forms are <param>=<value> -<param>=<value> "+ + "--<param>=<value>\n"+ + "Parameter names are case-insensitive. The parameters "+ + "are:\n\n"+ + Configuration.listParams()); + System.err.print(usage); + System.exit(1); + } + + public VncViewer() { + applet = true; + firstApplet = true; + } + + public static void newViewer(VncViewer oldViewer) { + VncViewer viewer = new VncViewer(); + viewer.applet = oldViewer.applet; + viewer.firstApplet = false; + viewer.start(); + } + + + public void init() { + vlog.debug("init called"); + setBackground(Color.white); + ClassLoader cl = this.getClass().getClassLoader(); + ImageIcon icon = new ImageIcon(cl.getResource("com/tigervnc/vncviewer/tigervnc.png")); + logo = icon.getImage(); + } + + public void start() { + vlog.debug("start called"); + if (version == null || build == null) { + ClassLoader cl = this.getClass().getClassLoader(); + InputStream stream = cl.getResourceAsStream("com/tigervnc/vncviewer/timestamp"); + try { + Manifest manifest = new Manifest(stream); + Attributes attributes = manifest.getMainAttributes(); + version = attributes.getValue("Version"); + build = attributes.getValue("Build"); + } catch (java.io.IOException e) { } + } + nViewers++; + if (firstApplet) { + alwaysShowServerDialog.setParam(true); + Configuration.readAppletParams(this); + String host = getCodeBase().getHost(); + if (vncServerName.getValue() == null && vncServerPort.getValue() != 0) { + int port = vncServerPort.getValue(); + vncServerName.setParam(host + ((port >= 5900 && port <= 5999) + ? (":"+(port-5900)) + : ("::"+port))); + } + } + thread = new Thread(this); + thread.start(); + } + + public void paint(Graphics g) { + g.drawImage(logo, 0, 0, this); + int h = logo.getHeight(this)+20; + g.drawString(about1+" v"+version+" ("+build+")", 0, h); + h += g.getFontMetrics().getHeight(); + g.drawString(about2, 0, h); + h += g.getFontMetrics().getHeight(); + g.drawString(about3, 0, h); + } + + public void run() { + CConn cc = null; + try { + cc = new CConn(this, null, vncServerName.getValue(), false); + while (true) + cc.processMsg(); + } catch (EndOfStream e) { + vlog.info(e.toString()); + } catch (java.lang.Exception e) { + if (cc != null) cc.deleteWindow(); + if (cc == null || !cc.shuttingDown) { + e.printStackTrace(); + JOptionPane.showMessageDialog(null, + e.toString(), + "VNC Viewer : Error", + JOptionPane.ERROR_MESSAGE); + } + } + if (cc != null) cc.deleteWindow(); + nViewers--; + if (!applet && nViewers == 0) { + System.exit(0); + } + } + + BoolParameter fastCopyRect + = new BoolParameter("FastCopyRect", + "Use fast CopyRect - turn this off if you get "+ + "screen corruption when copying from off-screen", + true); + BoolParameter useLocalCursor + = new BoolParameter("UseLocalCursor", + "Render the mouse cursor locally", true); + BoolParameter sendLocalUsername + = new BoolParameter("SendLocalUsername", + "Send the local username for SecurityTypes "+ + "such as Plain rather than prompting", true); + BoolParameter autoSelect + = new BoolParameter("AutoSelect", + "Auto select pixel format and encoding", true); + BoolParameter fullColour + = new BoolParameter("FullColour", + "Use full colour - otherwise 6-bit colour is used "+ + "until AutoSelect decides the link is fast enough", + true); + AliasParameter fullColor + = new AliasParameter("FullColor", "Alias for FullColour", fullColour); + StringParameter preferredEncoding + = new StringParameter("PreferredEncoding", + "Preferred encoding to use (Tight, ZRLE, hextile or"+ + " raw) - implies AutoSelect=0", "Tight"); + BoolParameter viewOnly + = new BoolParameter("ViewOnly", "Don't send any mouse or keyboard "+ + "events to the server", false); + BoolParameter shared + = new BoolParameter("Shared", "Don't disconnect other viewers upon "+ + "connection - share the desktop instead", false); + BoolParameter fullScreen + = new BoolParameter("FullScreen", "Full Screen Mode", false); + BoolParameter acceptClipboard + = new BoolParameter("AcceptClipboard", + "Accept clipboard changes from the server", true); + BoolParameter sendClipboard + = new BoolParameter("SendClipboard", + "Send clipboard changes to the server", true); + StringParameter desktopSize + = new StringParameter("DesktopSize", + "Reconfigure desktop size on the server on "+ + "connect (if possible)", ""); + StringParameter scalingFactor + = new StringParameter("ScalingFactor", + "Reduce or enlarge the remote desktop image. "+ + "The value is interpreted as a scaling factor "+ + "in percent. If the parameter is set to "+ + "\"Auto\", then automatic scaling is "+ + "performed. Auto-scaling tries to choose a "+ + "scaling factor in such a way that the whole "+ + "remote desktop will fit on the local screen. "+ + "If the parameter is set to \"FixedRatio\", "+ + "then automatic scaling is performed, but the "+ + "original aspect ratio is preserved.", "100"); + BoolParameter alwaysShowServerDialog + = new BoolParameter("AlwaysShowServerDialog", + "Always show the server dialog even if a server "+ + "has been specified in an applet parameter or on "+ + "the command line", false); + StringParameter vncServerName + = new StringParameter("Server", + "The VNC server <host>[:<dpyNum>] or "+ + "<host>::<port>", null); + IntParameter vncServerPort + = new IntParameter("Port", + "The VNC server's port number, assuming it is on "+ + "the host from which the applet was downloaded", 0); + BoolParameter acceptBell + = new BoolParameter("AcceptBell", + "Produce a system beep when requested to by the server.", + true); + BoolParameter customCompressLevel + = new BoolParameter("CustomCompressLevel", + "Use custom compression level. "+ + "Default if CompressLevel is specified.", false); + IntParameter compressLevel + = new IntParameter("CompressLevel", + "Use specified compression level "+ + "0 = Low, 6 = High", + 1); + BoolParameter noJpeg + = new BoolParameter("NoJPEG", + "Disable lossy JPEG compression in Tight encoding.", false); + IntParameter qualityLevel + = new IntParameter("QualityLevel", + "JPEG quality level. "+ + "0 = Low, 9 = High", + 8); + + Thread thread; + boolean applet, firstApplet; + Image logo; + static int nViewers; + static LogWriter vlog = new LogWriter("main"); +} diff --git a/java/com/tigervnc/vncviewer/index.html b/java/com/tigervnc/vncviewer/index.html new file mode 100644 index 00000000..ba00e26b --- /dev/null +++ b/java/com/tigervnc/vncviewer/index.html @@ -0,0 +1,20 @@ +<!-- + index.html - an example HTML page for the TigerVNC Java Viewer applet, to + be used with a standalone web server. Before using this example, please + MAKE SURE to check the following: + + * the CODE and ARCHIVE attributes of the <APPLET> tag should point to + the correct directory (this example assumes that this page is in the + same directory as VncViewer.jar); +--> + +<HTML> +<TITLE> +TigerVNC desktop +</TITLE> +<APPLET CODE="com.tigervnc.vncviewer.VncViewer" ARCHIVE="VncViewer.jar" + WIDTH=500> +</APPLET> +<BR> +<A href="http://www.tigervnc.org/">TigerVNC site</A> +</HTML> diff --git a/java/com/tigervnc/vncviewer/index.vnc b/java/com/tigervnc/vncviewer/index.vnc new file mode 100644 index 00000000..e8899875 --- /dev/null +++ b/java/com/tigervnc/vncviewer/index.vnc @@ -0,0 +1,20 @@ +<!-- + index.vnc - default HTML page for TigerVNC Java viewer applet, to be + used with Xvnc. On any file ending in .vnc, the HTTP server embedded in + Xvnc will substitute the following variables when preceded by a dollar: + USER, DESKTOP, DISPLAY, APPLETWIDTH, APPLETHEIGHT, WIDTH, HEIGHT, PORT, + Use two dollar signs ($$) to get a dollar sign in the generated + HTML page. +--> + +<HTML> +<TITLE> +$USER's $DESKTOP desktop ($DISPLAY) +</TITLE> +<APPLET CODE=com.tigervnc.vncviewer.VncViewer ARCHIVE=VncViewer.jar + WIDTH=$APPLETWIDTH HEIGHT=$APPLETHEIGHT> +<param name=PORT value=$PORT> +</APPLET> +<BR> +<A href="http://www.tigervnc.org/">TigerVNC site</A> +</HTML> diff --git a/java/com/tigervnc/vncviewer/tigervnc.ico b/java/com/tigervnc/vncviewer/tigervnc.ico Binary files differnew file mode 100644 index 00000000..50b90f00 --- /dev/null +++ b/java/com/tigervnc/vncviewer/tigervnc.ico diff --git a/java/com/tigervnc/vncviewer/tigervnc.png b/java/com/tigervnc/vncviewer/tigervnc.png Binary files differnew file mode 100644 index 00000000..8ac883ef --- /dev/null +++ b/java/com/tigervnc/vncviewer/tigervnc.png diff --git a/java/com/tigervnc/vncviewer/timestamp.in b/java/com/tigervnc/vncviewer/timestamp.in new file mode 100644 index 00000000..b666e7c0 --- /dev/null +++ b/java/com/tigervnc/vncviewer/timestamp.in @@ -0,0 +1,6 @@ +Manifest-Version: 1.0 +Main-Class: com.tigervnc.vncviewer.VncViewer +Package-Date: @JAVA_DATE@ +Package-Time: @JAVA_TIME@ +Version: @VERSION@ +Build: @BUILD@ |