diff options
author | DRC <dcommander@users.sourceforge.net> | 2011-05-13 21:42:14 +0000 |
---|---|---|
committer | DRC <dcommander@users.sourceforge.net> | 2011-05-13 21:42:14 +0000 |
commit | c5dc03809027dfac6b88153304bf30d4adb5ec8a (patch) | |
tree | 9125624d0d913dc1cfd6610b0a3ca88eef8c15a7 | |
parent | 561ff0cba91b23fcb5c19793764fc2510fc4d366 (diff) | |
download | tigervnc-c5dc03809027dfac6b88153304bf30d4adb5ec8a.tar.gz tigervnc-c5dc03809027dfac6b88153304bf30d4adb5ec8a.zip |
Completely reworked Java viewer (contributed by Brian Hinz)
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@4413 3789f03b-4d11-0410-bbf8-ca57d06f2519
106 files changed, 9200 insertions, 7166 deletions
diff --git a/java/src/com/tigervnc/decoder/CoRREDecoder.java b/java/src/com/tigervnc/decoder/CoRREDecoder.java deleted file mode 100644 index dd509253..00000000 --- a/java/src/com/tigervnc/decoder/CoRREDecoder.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.tigervnc.decoder; - -import com.tigervnc.vncviewer.RfbInputStream; -import java.awt.Graphics; -import java.awt.Color; -import java.io.IOException; - -// -// Class that used for decoding CoRRE encoded data. -// - -public class CoRREDecoder extends RawDecoder { - - final static int EncodingCoRRE = 4; - - public CoRREDecoder(Graphics g, RfbInputStream is) { - super(g, is); - } - - public CoRREDecoder(Graphics g, RfbInputStream is, int frameBufferW, - int frameBufferH) { - super(g, is, frameBufferW, frameBufferH); - } - - // - // Override handleRect method to decode CoRRE encoded data insted of - // raw pixel data. - // - - public void handleRect(int x, int y, int w, int h) throws IOException { - - // - // Write encoding ID to record output stream - // - - if (dos != null) { - dos.writeInt(CoRREDecoder.EncodingCoRRE); - } - - int nSubrects = rfbis.readU32(); - - byte[] bg_buf = new byte[bytesPerPixel]; - rfbis.readFully(bg_buf); - Color pixel; - if (bytesPerPixel == 1) { - pixel = getColor256()[bg_buf[0] & 0xFF]; - } else { - pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, bg_buf[0] & 0xFF); - } - graphics.setColor(pixel); - graphics.fillRect(x, y, w, h); - - byte[] buf = new byte[nSubrects * (bytesPerPixel + 4)]; - rfbis.readFully(buf); - - // - // Save decoded data to data output stream - // - - if (dos != null) { - dos.writeInt(nSubrects); - dos.write(bg_buf); - dos.write(buf); - } - - int sx, sy, sw, sh; - int i = 0; - - for (int j = 0; j < nSubrects; j++) { - if (bytesPerPixel == 1) { - pixel = getColor256()[buf[i++] & 0xFF]; - } else { - pixel = new Color(buf[i+2] & 0xFF, buf[i+1] & 0xFF, buf[i] & 0xFF); - i += 4; - } - sx = x + (buf[i++] & 0xFF); - sy = y + (buf[i++] & 0xFF); - sw = buf[i++] & 0xFF; - sh = buf[i++] & 0xFF; - - graphics.setColor(pixel); - graphics.fillRect(sx, sy, sw, sh); - } - } -} diff --git a/java/src/com/tigervnc/decoder/CopyRectDecoder.java b/java/src/com/tigervnc/decoder/CopyRectDecoder.java deleted file mode 100644 index c3f1c8cb..00000000 --- a/java/src/com/tigervnc/decoder/CopyRectDecoder.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.tigervnc.decoder; - -import com.tigervnc.vncviewer.RfbInputStream; -import java.awt.Graphics; -import java.io.IOException; - -// -// Class that used for decoding CopyRect encoded data. -// - -public class CopyRectDecoder extends RawDecoder { - - final static int EncodingCopyRect = 1; - - public CopyRectDecoder(Graphics g, RfbInputStream is) { - super(g, is); - } - - public CopyRectDecoder(Graphics g, RfbInputStream is, int frameBufferW, - int frameBufferH) { - super(g, is, frameBufferW, frameBufferH); - } - - // - // Override handleRect method handle CopyRect - // - - public void handleRect(int x, int y, int w, int h) throws IOException { - - // - // Write encoding ID to record output stream - // - - if (dos != null) { - dos.writeInt(CopyRectDecoder.EncodingCopyRect); - } - - int copyRectSrcX = rfbis.readU16(); - int copyRectSrcY = rfbis.readU16(); - - // If the session is being recorded: - if (dos != null) { - dos.writeShort(copyRectSrcX); - dos.writeShort(copyRectSrcY); - } - - graphics.copyArea(copyRectSrcX, copyRectSrcY, w, h, - x - copyRectSrcX, y - copyRectSrcY); - } -} diff --git a/java/src/com/tigervnc/decoder/HextileDecoder.java b/java/src/com/tigervnc/decoder/HextileDecoder.java deleted file mode 100644 index 7052ff41..00000000 --- a/java/src/com/tigervnc/decoder/HextileDecoder.java +++ /dev/null @@ -1,228 +0,0 @@ -package com.tigervnc.decoder; - -import com.tigervnc.decoder.common.Repaintable; -import com.tigervnc.vncviewer.RfbInputStream; -import java.awt.Color; -import java.awt.Graphics; -import java.io.IOException; - -// -// Class that used for decoding hextile encoded data. -// - -public class HextileDecoder extends RawDecoder { - - final static int EncodingHextile = 5; - - // Contstants used in the Hextile decoder - final static int - HextileRaw = 1, - HextileBackgroundSpecified = 2, - HextileForegroundSpecified = 4, - HextileAnySubrects = 8, - HextileSubrectsColoured = 16; - - public HextileDecoder(Graphics g, RfbInputStream is) { - super(g, is); - } - - public HextileDecoder(Graphics g, RfbInputStream is, int frameBufferW, - int frameBufferH) { - super(g, is, frameBufferW, frameBufferH); - } - - // - // Set private members methods - // - - public void setRepainableControl(Repaintable r) { - repainableControl = r; - } - - // - // Override handleRect method to decode Hextile encoded data insted of - // raw pixel data. - // - - public void handleRect(int x, int y, int w, int h) throws IOException, - Exception { - - // - // Write encoding ID to record output stream - // - - if (dos != null) { - dos.writeInt(HextileDecoder.EncodingHextile); - } - - hextile_bg = new Color(0); - hextile_fg = new Color(0); - - for (int ty = y; ty < y + h; ty += 16) { - int th = 16; - if (y + h - ty < 16) - th = y + h - ty; - - for (int tx = x; tx < x + w; tx += 16) { - int tw = 16; - if (x + w - tx < 16) - tw = x + w - tx; - - handleHextileSubrect(tx, ty, tw, th); - } - if (repainableControl != null) - repainableControl.scheduleRepaint(x, y, w, h); - } - if (repainableControl != null) - repainableControl.scheduleRepaint(x, y, w, h); - } - - // - // Handle one tile in the Hextile-encoded data. - // - - private void handleHextileSubrect(int tx, int ty, int tw, int th) - throws IOException, Exception { - - int subencoding = rfbis.readU8(); - - // - // Save decoded data to data output stream - // - - if (dos != null) { - dos.writeByte((byte)subencoding); - } - - // Is it a raw-encoded sub-rectangle? - if ((subencoding & HextileRaw) != 0) { - // - // Disable encoding id writting to record stream - // in super (RawDecoder) class, cause we write subencoding ID - // in this class (see code above). - // - - super.enableEncodingRecordWritting(false); - super.handleRect(tx, ty, tw, th); - super.handleUpdatedPixels(tx, ty, tw, th); - super.enableEncodingRecordWritting(true); - return; - } - - // Read and draw the background if specified. - byte[] cbuf = new byte[bytesPerPixel]; - if ((subencoding & HextileBackgroundSpecified) != 0) { - rfbis.readFully(cbuf); - if (bytesPerPixel == 1) { - hextile_bg = getColor256()[cbuf[0] & 0xFF]; - } else { - hextile_bg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, cbuf[0] & 0xFF); - } - - // - // Save decoded data to data output stream - // - - if (dos != null) { - dos.write(cbuf); - } - } - graphics.setColor(hextile_bg); - graphics.fillRect(tx, ty, tw, th); - - // Read the foreground color if specified. - if ((subencoding & HextileForegroundSpecified) != 0) { - rfbis.readFully(cbuf); - if (bytesPerPixel == 1) { - hextile_fg = getColor256()[cbuf[0] & 0xFF]; - } else { - hextile_fg = new Color(cbuf[2] & 0xFF, cbuf[1] & 0xFF, cbuf[0] & 0xFF); - } - - // - // Save decoded data to data output stream - // - - if (dos != null) { - dos.write(cbuf); - } - } - - // Done with this tile if there is no sub-rectangles. - if ((subencoding & HextileAnySubrects) == 0) - return; - - int nSubrects = rfbis.readU8(); - int bufsize = nSubrects * 2; - if ((subencoding & HextileSubrectsColoured) != 0) { - bufsize += nSubrects * bytesPerPixel; - } - byte[] buf = new byte[bufsize]; - rfbis.readFully(buf); - - // - // Save decoded data to data output stream - // - - if (dos != null) { - dos.writeByte((byte)nSubrects); - dos.write(buf); - } - - int b1, b2, sx, sy, sw, sh; - int i = 0; - - if ((subencoding & HextileSubrectsColoured) == 0) { - - // Sub-rectangles are all of the same color. - graphics.setColor(hextile_fg); - for (int j = 0; j < nSubrects; j++) { - b1 = buf[i++] & 0xFF; - b2 = buf[i++] & 0xFF; - sx = tx + (b1 >> 4); - sy = ty + (b1 & 0xf); - sw = (b2 >> 4) + 1; - sh = (b2 & 0xf) + 1; - graphics.fillRect(sx, sy, sw, sh); - } - } else if (bytesPerPixel == 1) { - - // BGR233 (8-bit color) version for colored sub-rectangles. - for (int j = 0; j < nSubrects; j++) { - hextile_fg = getColor256()[buf[i++] & 0xFF]; - b1 = buf[i++] & 0xFF; - b2 = buf[i++] & 0xFF; - sx = tx + (b1 >> 4); - sy = ty + (b1 & 0xf); - sw = (b2 >> 4) + 1; - sh = (b2 & 0xf) + 1; - graphics.setColor(hextile_fg); - graphics.fillRect(sx, sy, sw, sh); - } - - } else { - - // Full-color (24-bit) version for colored sub-rectangles. - for (int j = 0; j < nSubrects; j++) { - hextile_fg = new Color(buf[i+2] & 0xFF, - buf[i+1] & 0xFF, - buf[i] & 0xFF); - i += 4; - b1 = buf[i++] & 0xFF; - b2 = buf[i++] & 0xFF; - sx = tx + (b1 >> 4); - sy = ty + (b1 & 0xf); - sw = (b2 >> 4) + 1; - sh = (b2 & 0xf) + 1; - graphics.setColor(hextile_fg); - graphics.fillRect(sx, sy, sw, sh); - } - - } - } - - // These colors should be kept between handleHextileSubrect() calls. - private Color hextile_bg, hextile_fg; - // Repaitable object - private Repaintable repainableControl = null; -} diff --git a/java/src/com/tigervnc/decoder/RREDecoder.java b/java/src/com/tigervnc/decoder/RREDecoder.java deleted file mode 100644 index f6a67aec..00000000 --- a/java/src/com/tigervnc/decoder/RREDecoder.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.tigervnc.decoder; - -import com.tigervnc.vncviewer.RfbInputStream; -import java.awt.Graphics; -import java.awt.Color; -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.IOException; - -// -// Class that used for decoding RRE encoded data. -// - -public class RREDecoder extends RawDecoder { - - final static int EncodingRRE = 2; - - public RREDecoder(Graphics g, RfbInputStream is) { - super(g, is); - } - - public RREDecoder(Graphics g, RfbInputStream is, int frameBufferW, - int frameBufferH) { - super(g, is, frameBufferW, frameBufferH); - } - - // - // Override handleRect method to decode RRE encoded data insted of - // raw pixel data. - // - - public void handleRect(int x, int y, int w, int h) throws IOException { - - // - // Write encoding ID to record output stream - // - - if (dos != null) { - dos.writeInt(RREDecoder.EncodingRRE); - } - - int nSubrects = rfbis.readU32(); - byte[] bg_buf = new byte[bytesPerPixel]; - rfbis.readFully(bg_buf); - Color pixel; - if (bytesPerPixel == 1) { - pixel = getColor256()[bg_buf[0] & 0xFF]; - } else { - pixel = new Color(bg_buf[2] & 0xFF, bg_buf[1] & 0xFF, bg_buf[0] & 0xFF); - } - graphics.setColor(pixel); - graphics.fillRect(x, y, w, h); - byte[] buf = new byte[nSubrects * (bytesPerPixel + 8)]; - rfbis.readFully(buf); - DataInputStream ds = new DataInputStream(new ByteArrayInputStream(buf)); - - // - // Save decoded data to data output stream - // - if (dos != null) { - dos.writeInt(nSubrects); - dos.write(bg_buf); - dos.write(buf); - } - - int sx, sy, sw, sh; - for (int j = 0; j < nSubrects; j++) { - if (bytesPerPixel == 1) { - pixel = getColor256()[ds.readUnsignedByte()]; - } else { - ds.skip(4); - pixel = new Color(buf[j*12+2] & 0xFF, - buf[j*12+1] & 0xFF, - buf[j*12] & 0xFF); - } - sx = x + ds.readUnsignedShort(); - sy = y + ds.readUnsignedShort(); - sw = ds.readUnsignedShort(); - sh = ds.readUnsignedShort(); - - graphics.setColor(pixel); - graphics.fillRect(sx, sy, sw, sh); - } - } -} diff --git a/java/src/com/tigervnc/decoder/RawDecoder.java b/java/src/com/tigervnc/decoder/RawDecoder.java deleted file mode 100644 index 478a668e..00000000 --- a/java/src/com/tigervnc/decoder/RawDecoder.java +++ /dev/null @@ -1,223 +0,0 @@ -package com.tigervnc.decoder; - -import com.tigervnc.vncviewer.RfbInputStream; -import java.io.IOException; -import java.io.DataOutput; -import java.awt.Graphics; -import java.awt.Image; -import java.awt.image.ColorModel; -import java.awt.image.DirectColorModel; -import java.awt.image.MemoryImageSource; -import java.awt.Color; -import java.awt.Toolkit; - -// -// This is base decoder class. -// Other classes will be childs of RawDecoder. -// - -public class RawDecoder { - final static int EncodingRaw = 0; - - public RawDecoder(Graphics g, RfbInputStream is) { - setGraphics(g); - setRfbInputStream(is); - } - - public RawDecoder(Graphics g, RfbInputStream is, int frameBufferW, - int frameBufferH) { - setGraphics(g); - setRfbInputStream(is); - setFrameBufferSize(frameBufferW, frameBufferH); - // FIXME: cm24 created in getColorModel24. - // Remove if no bugs - cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); - } - - // - // Set methods to set value of non-static protected members of class - // - - public void setRfbInputStream(RfbInputStream is) { - rfbis = is; - } - - public void setGraphics(Graphics g) { - graphics = g; - } - - public void setBPP(int bpp) { - bytesPerPixel = bpp; - } - - public void setFrameBufferSize(int w, int h) { - framebufferWidth = w; - framebufferHeight = h; - } - - // - // FIXME: Rename this method after we don't need RecordInterface - // in RawDecoder class to record session - // - - public void setDataOutputStream(DataOutput os) { - dos = os; - } - - // - // Decodes Raw Pixels data and draw it into graphics - // - - public void handleRect(int x, int y, int w, int h) throws IOException, Exception { - - // - // Write encoding ID to record output stream - // - - if ((dos != null) && (enableEncodingRecordWritting)) { - dos.writeInt(RawDecoder.EncodingRaw); - } - - if (bytesPerPixel == 1) { - for (int dy = y; dy < y + h; dy++) { - if (pixels8 != null) { - rfbis.readFully(pixels8, dy * framebufferWidth + x, w); - } - // - // Save decoded data to record output stream - // - if (dos != null) { - dos.write(pixels8, dy * framebufferWidth + x, w); - } - } - } else { - byte[] buf = new byte[w * 4]; - int i, offset; - for (int dy = y; dy < y + h; dy++) { - rfbis.readFully(buf); - // - // Save decoded data to record output stream - // - if (dos != null) { - dos.write(buf); - } - offset = dy * framebufferWidth + x; - if (pixels24 != null) { - for (i = 0; i < w; i++) { - pixels24[offset + i] = - (buf[i * 4 + 2] & 0xFF) << 16 | - (buf[i * 4 + 1] & 0xFF) << 8 | - (buf[i * 4] & 0xFF); - } //for - } // if - } // for - } // else - handleUpdatedPixels(x, y, w, h); - } // void - - // - // Display newly updated area of pixels. - // - - protected void handleUpdatedPixels(int x, int y, int w, int h) { - // Draw updated pixels of the off-screen image. - pixelsSource.newPixels(x, y, w, h); - graphics.setClip(x, y, w, h); - graphics.drawImage(rawPixelsImage, 0, 0, null); - graphics.setClip(0, 0, framebufferWidth, framebufferHeight); - } - - // - // Updates pixels data. - // This method must be called when framebuffer is resized - // or BPP is changed. - // - - public void update() { - // Images with raw pixels should be re-allocated on every change - // of geometry or pixel format. - int fbWidth = framebufferWidth; - int fbHeight = framebufferHeight; - - if (bytesPerPixel == 1) { - pixels24 = null; - pixels8 = new byte[fbWidth * fbHeight]; - pixelsSource = new MemoryImageSource(fbWidth, fbHeight, getColorModel8(), - pixels8, 0, fbWidth); - } else { - pixels8 = null; - pixels24 = new int[fbWidth * fbHeight]; - pixelsSource = - new MemoryImageSource(fbWidth, fbHeight, cm24, pixels24, 0, fbWidth); - } - pixelsSource.setAnimated(true); - rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource); - } - - // - // Private static members access methods - // - - protected ColorModel getColorModel8() { - if (cm8 == null) { - cm8 = cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); - } - return cm8; - } - - protected ColorModel getColorModel24() { - if (cm24 == null) { - cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); - } - return cm24; - } - - protected Color[]getColor256() { - if (color256 == null) { - color256 = new Color[256]; - for (int i = 0; i < 256; i++) - color256[i] = new Color(cm8.getRGB(i)); - } - return color256; - } - - // - // This method will be used by HextileDecoder to disable - // double writting encoding id to record stream. - // - // FIXME: Try to find better solution than this. - // - - protected void enableEncodingRecordWritting(boolean enable) { - enableEncodingRecordWritting = enable; - } - - // - // Unique data for every decoder (? maybe not ?) - // - - protected int bytesPerPixel = 4; - protected int framebufferWidth = 0; - protected int framebufferHeight = 0; - protected RfbInputStream rfbis = null; - protected Graphics graphics = null; - protected DataOutput dos = null; - protected boolean enableEncodingRecordWritting = true; - - // - // This data must be shared between decoders - // - - protected static byte []pixels8 = null; - protected static int []pixels24 = null; - protected static MemoryImageSource pixelsSource = null; - protected static Image rawPixelsImage = null; - - // - // Access to this static members only though protected methods - // - - private static ColorModel cm8 = null; - private static ColorModel cm24 = null; - private static Color []color256 = null; -} diff --git a/java/src/com/tigervnc/decoder/TightDecoder.java b/java/src/com/tigervnc/decoder/TightDecoder.java deleted file mode 100644 index 4e9969c7..00000000 --- a/java/src/com/tigervnc/decoder/TightDecoder.java +++ /dev/null @@ -1,525 +0,0 @@ -package com.tigervnc.decoder; - -import com.tigervnc.decoder.common.Repaintable; -import com.tigervnc.vncviewer.RfbInputStream; -import java.awt.Graphics; -import java.awt.Color; -import java.awt.Image; -import java.awt.Rectangle; -import java.awt.Toolkit; -import java.awt.image.ImageObserver; -import java.io.IOException; -import java.util.zip.Deflater; -import java.util.zip.Inflater; - -// -// Class that used for decoding Tight encoded data. -// - -public class TightDecoder extends RawDecoder implements ImageObserver { - - final static int EncodingTight = 7; - - // - // Tight decoder constants - // - - final static int TightExplicitFilter = 0x04; - final static int TightFill = 0x08; - final static int TightJpeg = 0x09; - final static int TightMaxSubencoding = 0x09; - final static int TightFilterCopy = 0x00; - final static int TightFilterPalette = 0x01; - final static int TightFilterGradient = 0x02; - final static int TightMinToCompress = 12; - - // Tight encoder's data. - final static int tightZlibBufferSize = 512; - - public TightDecoder(Graphics g, RfbInputStream is) { - super(g, is); - tightInflaters = new Inflater[4]; - } - - public TightDecoder(Graphics g, RfbInputStream is, int frameBufferW, - int frameBufferH) { - super(g, is, frameBufferW, frameBufferH); - tightInflaters = new Inflater[4]; - } - - // - // Set and get methods for private TightDecoder - // - - public void setRepainableControl(Repaintable r) { - repainatableControl = r; - } - - // - // JPEG processing statistic methods - // - - public long getNumJPEGRects() { - return statNumRectsTightJPEG; - } - - public void setNumJPEGRects(int v) { - statNumRectsTightJPEG = v; - } - - // - // Tight processing statistic methods - // - - public long getNumTightRects() { - return statNumRectsTight; - } - - public void setNumTightRects(int v) { - statNumRectsTight = v; - } - - // - // Handle a Tight-encoded rectangle. - // - - public void handleRect(int x, int y, int w, int h) throws Exception { - - // - // Write encoding ID to record output stream - // - - if (dos != null) { - dos.writeInt(TightDecoder.EncodingTight); - } - - int comp_ctl = rfbis.readU8(); - - if (dos != null) { - // Tell the decoder to flush each of the four zlib streams. - dos.writeByte(comp_ctl | 0x0F); - } - - // Flush zlib streams if we are told by the server to do so. - for (int stream_id = 0; stream_id < 4; stream_id++) { - if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) { - tightInflaters[stream_id] = null; - } - comp_ctl >>= 1; - } - - // Check correctness of subencoding value. - if (comp_ctl > TightDecoder.TightMaxSubencoding) { - throw new Exception("Incorrect tight subencoding: " + comp_ctl); - } - - // Handle solid-color rectangles. - if (comp_ctl == TightDecoder.TightFill) { - - if (bytesPerPixel == 1) { - int idx = rfbis.readU8(); - graphics.setColor(getColor256()[idx]); - if (dos != null) { - dos.writeByte(idx); - } - } else { - byte[] buf = new byte[3]; - rfbis.readFully(buf); - if (dos != null) { - dos.write(buf); - } - Color bg = new Color(0xFF000000 | (buf[0] & 0xFF) << 16 | - (buf[1] & 0xFF) << 8 | (buf[2] & 0xFF)); - graphics.setColor(bg); - } - graphics.fillRect(x, y, w, h); - repainatableControl.scheduleRepaint(x, y, w, h); - return; - - } - - if (comp_ctl == TightDecoder.TightJpeg) { - - statNumRectsTightJPEG++; - - // Read JPEG data. - byte[] jpegData = new byte[rfbis.readCompactLen()]; - rfbis.readFully(jpegData); - if (dos != null) { - recordCompactLen(jpegData.length); - dos.write(jpegData); - } - - // Create an Image object from the JPEG data. - Image jpegImage = Toolkit.getDefaultToolkit().createImage(jpegData); - - // Remember the rectangle where the image should be drawn. - jpegRect = new Rectangle(x, y, w, h); - - // Let the imageUpdate() method do the actual drawing, here just - // wait until the image is fully loaded and drawn. - synchronized(jpegRect) { - Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1, this); - try { - // Wait no longer than three seconds. - jpegRect.wait(3000); - } catch (InterruptedException e) { - throw new Exception("Interrupted while decoding JPEG image"); - } - } - - // Done, jpegRect is not needed any more. - jpegRect = null; - return; - - } else { - statNumRectsTight++; - } - - // Read filter id and parameters. - int numColors = 0, rowSize = w; - byte[] palette8 = new byte[2]; - int[] palette24 = new int[256]; - boolean useGradient = false; - if ((comp_ctl & TightDecoder.TightExplicitFilter) != 0) { - int filter_id = rfbis.readU8(); - if (dos != null) { - dos.writeByte(filter_id); - } - if (filter_id == TightDecoder.TightFilterPalette) { - numColors = rfbis.readU8() + 1; - if (dos != null) { - dos.writeByte((numColors - 1)); - } - if (bytesPerPixel == 1) { - if (numColors != 2) { - throw new Exception("Incorrect tight palette size: " + numColors); - } - rfbis.readFully(palette8); - if (dos != null) { - dos.write(palette8); - } - } else { - byte[] buf = new byte[numColors * 3]; - rfbis.readFully(buf); - if (dos != null) { - dos.write(buf); - } - for (int i = 0; i < numColors; i++) { - palette24[i] = ((buf[i * 3] & 0xFF) << 16 | - (buf[i * 3 + 1] & 0xFF) << 8 | - (buf[i * 3 + 2] & 0xFF)); - } - } - if (numColors == 2) { - rowSize = (w + 7) / 8; - } - } else if (filter_id == TightDecoder.TightFilterGradient) { - useGradient = true; - } else if (filter_id != TightDecoder.TightFilterCopy) { - throw new Exception("Incorrect tight filter id: " + filter_id); - } - } - if (numColors == 0 && bytesPerPixel == 4) - rowSize *= 3; - - // Read, optionally uncompress and decode data. - int dataSize = h * rowSize; - if (dataSize < TightDecoder.TightMinToCompress) { - // Data size is small - not compressed with zlib. - if (numColors != 0) { - // Indexed colors. - byte[] indexedData = new byte[dataSize]; - rfbis.readFully(indexedData); - if (dos != null) { - dos.write(indexedData); - } - if (numColors == 2) { - // Two colors. - if (bytesPerPixel == 1) { - decodeMonoData(x, y, w, h, indexedData, palette8); - } else { - decodeMonoData(x, y, w, h, indexedData, palette24); - } - } else { - // 3..255 colors (assuming bytesPixel == 4). - int i = 0; - for (int dy = y; dy < y + h; dy++) { - for (int dx = x; dx < x + w; dx++) { - pixels24[dy * framebufferWidth + dx] = - palette24[indexedData[i++] & 0xFF]; - } - } - } - } else if (useGradient) { - // "Gradient"-processed data - byte[] buf = new byte[w * h * 3]; - rfbis.readFully(buf); - if (dos != null) { - dos.write(buf); - } - decodeGradientData(x, y, w, h, buf); - } else { - // Raw truecolor data. - if (bytesPerPixel == 1) { - for (int dy = y; dy < y + h; dy++) { - rfbis.readFully(pixels8, dy * framebufferWidth + x, w); - if (dos != null) { - dos.write(pixels8, dy * framebufferWidth + x, w); - } - } - } else { - byte[] buf = new byte[w * 3]; - int i, offset; - for (int dy = y; dy < y + h; dy++) { - rfbis.readFully(buf); - if (dos != null) { - dos.write(buf); - } - offset = dy * framebufferWidth + x; - for (i = 0; i < w; i++) { - pixels24[offset + i] = - (buf[i * 3] & 0xFF) << 16 | - (buf[i * 3 + 1] & 0xFF) << 8 | - (buf[i * 3 + 2] & 0xFF); - } - } - } - } - } else { - // Data was compressed with zlib. - int zlibDataLen = rfbis.readCompactLen(); - byte[] zlibData = new byte[zlibDataLen]; - rfbis.readFully(zlibData); - int stream_id = comp_ctl & 0x03; - if (tightInflaters[stream_id] == null) { - tightInflaters[stream_id] = new Inflater(); - } - Inflater myInflater = tightInflaters[stream_id]; - myInflater.setInput(zlibData); - byte[] buf = new byte[dataSize]; - myInflater.inflate(buf); - if (dos != null) { - recordCompressedData(buf); - } - - if (numColors != 0) { - // Indexed colors. - if (numColors == 2) { - // Two colors. - if (bytesPerPixel == 1) { - decodeMonoData(x, y, w, h, buf, palette8); - } else { - decodeMonoData(x, y, w, h, buf, palette24); - } - } else { - // More than two colors (assuming bytesPixel == 4). - int i = 0; - for (int dy = y; dy < y + h; dy++) { - for (int dx = x; dx < x + w; dx++) { - pixels24[dy * framebufferWidth + dx] = - palette24[buf[i++] & 0xFF]; - } - } - } - } else if (useGradient) { - // Compressed "Gradient"-filtered data (assuming bytesPixel == 4). - decodeGradientData(x, y, w, h, buf); - } else { - // Compressed truecolor data. - if (bytesPerPixel == 1) { - int destOffset = y * framebufferWidth + x; - for (int dy = 0; dy < h; dy++) { - System.arraycopy(buf, dy * w, pixels8, destOffset, w); - destOffset += framebufferWidth; - } - } else { - int srcOffset = 0; - int destOffset, i; - for (int dy = 0; dy < h; dy++) { - myInflater.inflate(buf); - destOffset = (y + dy) * framebufferWidth + x; - for (i = 0; i < w; i++) { - RawDecoder.pixels24[destOffset + i] = - (buf[srcOffset] & 0xFF) << 16 | - (buf[srcOffset + 1] & 0xFF) << 8 | - (buf[srcOffset + 2] & 0xFF); - srcOffset += 3; - } - } - } - } - } - handleUpdatedPixels(x, y, w, h); - } - - // - // Decode 1bpp-encoded bi-color rectangle (8-bit and 24-bit versions). - // - - private void decodeMonoData(int x, int y, int w, int h, byte[] src, byte[] palette) { - - int dx, dy, n; - int i = y * framebufferWidth + x; - int rowBytes = (w + 7) / 8; - byte b; - - for (dy = 0; dy < h; dy++) { - for (dx = 0; dx < w / 8; dx++) { - b = src[dy*rowBytes+dx]; - for (n = 7; n >= 0; n--) - pixels8[i++] = palette[b >> n & 1]; - } - for (n = 7; n >= 8 - w % 8; n--) { - pixels8[i++] = palette[src[dy*rowBytes+dx] >> n & 1]; - } - i += (framebufferWidth - w); - } - } - - private void decodeMonoData(int x, int y, int w, int h, byte[] src, int[] palette) { - - int dx, dy, n; - int i = y * framebufferWidth + x; - int rowBytes = (w + 7) / 8; - byte b; - - for (dy = 0; dy < h; dy++) { - for (dx = 0; dx < w / 8; dx++) { - b = src[dy*rowBytes+dx]; - for (n = 7; n >= 0; n--) - pixels24[i++] = palette[b >> n & 1]; - } - for (n = 7; n >= 8 - w % 8; n--) { - pixels24[i++] = palette[src[dy*rowBytes+dx] >> n & 1]; - } - i += (framebufferWidth - w); - } - } - - // - // Decode data processed with the "Gradient" filter. - // - - private void decodeGradientData (int x, int y, int w, int h, byte[] buf) { - - int dx, dy, c; - byte[] prevRow = new byte[w * 3]; - byte[] thisRow = new byte[w * 3]; - byte[] pix = new byte[3]; - int[] est = new int[3]; - - int offset = y * framebufferWidth + x; - - for (dy = 0; dy < h; dy++) { - - /* First pixel in a row */ - for (c = 0; c < 3; c++) { - pix[c] = (byte)(prevRow[c] + buf[dy * w * 3 + c]); - thisRow[c] = pix[c]; - } - pixels24[offset++] = - (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF); - - /* Remaining pixels of a row */ - for (dx = 1; dx < w; dx++) { - for (c = 0; c < 3; c++) { - est[c] = ((prevRow[dx * 3 + c] & 0xFF) + (pix[c] & 0xFF) - - (prevRow[(dx-1) * 3 + c] & 0xFF)); - if (est[c] > 0xFF) { - est[c] = 0xFF; - } else if (est[c] < 0x00) { - est[c] = 0x00; - } - pix[c] = (byte)(est[c] + buf[(dy * w + dx) * 3 + c]); - thisRow[dx * 3 + c] = pix[c]; - } - pixels24[offset++] = - (pix[0] & 0xFF) << 16 | (pix[1] & 0xFF) << 8 | (pix[2] & 0xFF); - } - - System.arraycopy(thisRow, 0, prevRow, 0, w * 3); - offset += (framebufferWidth - w); - } - } - - // - // Override the ImageObserver interface method to handle drawing of - // JPEG-encoded data. - // - - public boolean imageUpdate(Image img, int infoflags, - int x, int y, int width, int height) { - if ((infoflags & (ALLBITS | ABORT)) == 0) { - return true; // We need more image data. - } else { - // If the whole image is available, draw it now. - if ((infoflags & ALLBITS) != 0) { - if (jpegRect != null) { - synchronized(jpegRect) { - graphics.drawImage(img, jpegRect.x, jpegRect.y, null); - repainatableControl.scheduleRepaint(jpegRect.x, jpegRect.y, - jpegRect.width, jpegRect.height); - jpegRect.notify(); - } - } - } - return false; // All image data was processed. - } - } - - // - // Write an integer in compact representation (1..3 bytes) into the - // recorded session file. - // - - void recordCompactLen(int len) throws IOException { - byte[] buf = new byte[3]; - int bytes = 0; - buf[bytes++] = (byte)(len & 0x7F); - if (len > 0x7F) { - buf[bytes-1] |= 0x80; - buf[bytes++] = (byte)(len >> 7 & 0x7F); - if (len > 0x3FFF) { - buf[bytes-1] |= 0x80; - buf[bytes++] = (byte)(len >> 14 & 0xFF); - } - } - if (dos != null) dos.write(buf, 0, bytes); - } - - // - // Compress and write the data into the recorded session file. - // - - void recordCompressedData(byte[] data, int off, int len) throws IOException { - Deflater deflater = new Deflater(); - deflater.setInput(data, off, len); - int bufSize = len + len / 100 + 12; - byte[] buf = new byte[bufSize]; - deflater.finish(); - int compressedSize = deflater.deflate(buf); - recordCompactLen(compressedSize); - if (dos != null) dos.write(buf, 0, compressedSize); - } - - void recordCompressedData(byte[] data) throws IOException { - recordCompressedData(data, 0, data.length); - } - - // - // Private members - // - - private Inflater[] tightInflaters; - // Since JPEG images are loaded asynchronously, we have to remember - // their position in the framebuffer. Also, this jpegRect object is - // used for synchronization between the rfbThread and a JVM's thread - // which decodes and loads JPEG images. - private Rectangle jpegRect; - private Repaintable repainatableControl = null; - // Jpeg decoding statistics - private long statNumRectsTightJPEG = 0; - // Tight decoding statistics - private long statNumRectsTight = 0; -} diff --git a/java/src/com/tigervnc/decoder/ZRLEDecoder.java b/java/src/com/tigervnc/decoder/ZRLEDecoder.java deleted file mode 100644 index 91a9f9de..00000000 --- a/java/src/com/tigervnc/decoder/ZRLEDecoder.java +++ /dev/null @@ -1,310 +0,0 @@ -package com.tigervnc.decoder; - -import com.tigervnc.vncviewer.InStream; -import com.tigervnc.vncviewer.RfbInputStream; -import com.tigervnc.vncviewer.ZlibInStream; -import java.awt.Graphics; -import com.tigervnc.vncviewer.MemInStream; -import java.awt.Color; -import java.awt.Toolkit; -import java.awt.image.MemoryImageSource; -import java.io.IOException; - -// -// Class that used for decoding ZRLE encoded data. -// - -public class ZRLEDecoder extends RawDecoder { - - final static int EncodingZRLE = 16; - - public ZRLEDecoder(Graphics g, RfbInputStream is) { - super(g, is); - } - - public ZRLEDecoder(Graphics g, RfbInputStream is, int frameBufferW, - int frameBufferH) { - super(g, is, frameBufferW, frameBufferH); - } - - // - // Handle a ZRLE-encoded rectangle. - // - // FIXME: Currently, session recording is not fully supported for ZRLE. - // - - public void handleRect(int x, int y, int w, int h) throws IOException, Exception { - - // - // Write encoding ID to record output stream - // - - if (dos != null) { - dos.writeInt(ZRLEDecoder.EncodingZRLE); - } - - if (zrleInStream == null) - zrleInStream = new ZlibInStream(); - - int nBytes = rfbis.readU32(); - if (nBytes > 64 * 1024 * 1024) - throw new Exception("ZRLE decoder: illegal compressed data size"); - - if (zrleBuf == null || zrleBufLen < nBytes) { - zrleBufLen = nBytes + 4096; - zrleBuf = new byte[zrleBufLen]; - } - - // FIXME: Do not wait for all the data before decompression. - rfbis.readFully(zrleBuf, 0, nBytes); - - // - // Override handleRect method to decode RRE encoded data insted of - // raw pixel data. - // - - if (dos != null) { - if (!zrleRecWarningShown) { - System.out.println("Warning: ZRLE session can be recorded" + - " only from the beginning"); - System.out.println("Warning: Recorded file may be corrupted"); - zrleRecWarningShown = true; - } - } - - zrleInStream.setUnderlying(new MemInStream(zrleBuf, 0, nBytes), nBytes); - - for (int ty = y; ty < y+h; ty += 64) { - - int th = Math.min(y+h-ty, 64); - - for (int tx = x; tx < x+w; tx += 64) { - - int tw = Math.min(x+w-tx, 64); - - int mode = zrleInStream.readU8(); - boolean rle = (mode & 128) != 0; - int palSize = mode & 127; - int[] palette = new int[128]; - - readZrlePalette(palette, palSize); - - if (palSize == 1) { - int pix = palette[0]; - Color c = (bytesPerPixel == 1) ? - getColor256()[pix] : new Color(0xFF000000 | pix); - graphics.setColor(c); - graphics.fillRect(tx, ty, tw, th); - continue; - } - - if (!rle) { - if (palSize == 0) { - readZrleRawPixels(tw, th); - } else { - readZrlePackedPixels(tw, th, palette, palSize); - } - } else { - if (palSize == 0) { - readZrlePlainRLEPixels(tw, th); - } else { - readZrlePackedRLEPixels(tw, th, palette); - } - } - handleUpdatedZrleTile(tx, ty, tw, th); - } - } - zrleInStream.reset(); - } - - // - // Override update() method cause we have own data that - // must be updated when framebuffer is resized or BPP is changed - // - - public void update() { - // Images with raw pixels should be re-allocated on every change - // of geometry or pixel format. - int fbWidth = framebufferWidth; - int fbHeight = framebufferHeight; - - if (bytesPerPixel == 1) { - RawDecoder.pixels24 = null; - RawDecoder.pixels8 = new byte[fbWidth * fbHeight]; - RawDecoder.pixelsSource = new MemoryImageSource(fbWidth, fbHeight, getColorModel8(), pixels8, 0, fbWidth); - zrleTilePixels24 = null; - zrleTilePixels8 = new byte[64 * 64]; - } else { - RawDecoder.pixels8 = null; - RawDecoder.pixels24 = new int[fbWidth * fbHeight]; - RawDecoder.pixelsSource = - new MemoryImageSource(fbWidth, fbHeight, getColorModel24(), pixels24, 0, fbWidth); - zrleTilePixels8 = null; - zrleTilePixels24 = new int[64 * 64]; - } - RawDecoder.pixelsSource.setAnimated(true); - RawDecoder.rawPixelsImage = Toolkit.getDefaultToolkit().createImage(pixelsSource); - } - - // - // Copy pixels from zrleTilePixels8 or zrleTilePixels24, then update. - // - - private void handleUpdatedZrleTile(int x, int y, int w, int h) { - Object src, dst; - if (bytesPerPixel == 1) { - src = zrleTilePixels8; dst = pixels8; - } else { - src = zrleTilePixels24; dst = pixels24; - } - int offsetSrc = 0; - int offsetDst = (y * framebufferWidth + x); - for (int j = 0; j < h; j++) { - System.arraycopy(src, offsetSrc, dst, offsetDst, w); - offsetSrc += w; - offsetDst += framebufferWidth; - } - handleUpdatedPixels(x, y, w, h); - } - - // - // Private methods for reading ZRLE data - // - - private int readPixel(InStream is) throws Exception { - int pix; - if (bytesPerPixel == 1) { - pix = is.readU8(); - } else { - int p1 = is.readU8(); - int p2 = is.readU8(); - int p3 = is.readU8(); - pix = (p3 & 0xFF) << 16 | (p2 & 0xFF) << 8 | (p1 & 0xFF); - } - return pix; - } - - private void readPixels(InStream is, int[] dst, int count) throws Exception { - if (bytesPerPixel == 1) { - byte[] buf = new byte[count]; - is.readBytes(buf, 0, count); - for (int i = 0; i < count; i++) { - dst[i] = (int)buf[i] & 0xFF; - } - } else { - byte[] buf = new byte[count * 3]; - is.readBytes(buf, 0, count * 3); - for (int i = 0; i < count; i++) { - dst[i] = ((buf[i*3+2] & 0xFF) << 16 | - (buf[i*3+1] & 0xFF) << 8 | - (buf[i*3] & 0xFF)); - } - } - } - - private void readZrlePalette(int[] palette, int palSize) throws Exception { - readPixels(zrleInStream, palette, palSize); - } - - private void readZrleRawPixels(int tw, int th) throws Exception { - if (bytesPerPixel == 1) { - zrleInStream.readBytes(zrleTilePixels8, 0, tw * th); - } else { - readPixels(zrleInStream, zrleTilePixels24, tw * th); /// - } - } - - private void readZrlePackedPixels(int tw, int th, int[] palette, int palSize) - throws Exception { - - int bppp = ((palSize > 16) ? 8 : - ((palSize > 4) ? 4 : ((palSize > 2) ? 2 : 1))); - int ptr = 0; - - for (int i = 0; i < th; i++) { - int eol = ptr + tw; - int b = 0; - int nbits = 0; - - while (ptr < eol) { - if (nbits == 0) { - b = zrleInStream.readU8(); - nbits = 8; - } - nbits -= bppp; - int index = (b >> nbits) & ((1 << bppp) - 1) & 127; - if (bytesPerPixel == 1) { - zrleTilePixels8[ptr++] = (byte)palette[index]; - } else { - zrleTilePixels24[ptr++] = palette[index]; - } - } - } - } - - private void readZrlePlainRLEPixels(int tw, int th) throws Exception { - int ptr = 0; - int end = ptr + tw * th; - while (ptr < end) { - int pix = readPixel(zrleInStream); - int len = 1; - int b; - do { - b = zrleInStream.readU8(); - len += b; - } while (b == 255); - - if (!(len <= end - ptr)) - throw new Exception("ZRLE decoder: assertion failed" + - " (len <= end-ptr)"); - - if (bytesPerPixel == 1) { - while (len-- > 0) zrleTilePixels8[ptr++] = (byte)pix; - } else { - while (len-- > 0) zrleTilePixels24[ptr++] = pix; - } - } - } - - private void readZrlePackedRLEPixels(int tw, int th, int[] palette) - throws Exception { - - int ptr = 0; - int end = ptr + tw * th; - while (ptr < end) { - int index = zrleInStream.readU8(); - int len = 1; - if ((index & 128) != 0) { - int b; - do { - b = zrleInStream.readU8(); - len += b; - } while (b == 255); - - if (!(len <= end - ptr)) - throw new Exception("ZRLE decoder: assertion failed" + - " (len <= end - ptr)"); - } - - index &= 127; - int pix = palette[index]; - - if (bytesPerPixel == 1) { - while (len-- > 0) zrleTilePixels8[ptr++] = (byte)pix; - } else { - while (len-- > 0) zrleTilePixels24[ptr++] = pix; - } - } - } - - // - // ZRLE encoder's data. - // - - private byte[] zrleBuf; - private int zrleBufLen = 0; - private byte[] zrleTilePixels8; - private int[] zrleTilePixels24; - private ZlibInStream zrleInStream; - private boolean zrleRecWarningShown = false; -} diff --git a/java/src/com/tigervnc/decoder/ZlibDecoder.java b/java/src/com/tigervnc/decoder/ZlibDecoder.java deleted file mode 100644 index 0eeaef5f..00000000 --- a/java/src/com/tigervnc/decoder/ZlibDecoder.java +++ /dev/null @@ -1,103 +0,0 @@ -package com.tigervnc.decoder; - -import com.tigervnc.vncviewer.RfbInputStream; -import java.awt.Graphics; -import java.io.IOException; -import java.util.zip.DataFormatException; -import java.util.zip.Inflater; - -// -// Class that used for decoding ZLib encoded data. -// - -public class ZlibDecoder extends RawDecoder { - - final static int EncodingZlib = 6; - - public ZlibDecoder(Graphics g, RfbInputStream is) { - super(g, is); - } - - public ZlibDecoder(Graphics g, RfbInputStream is, int frameBufferW, - int frameBufferH) { - super(g, is, frameBufferW, frameBufferH); - } - - // - // Override handleRect method to decode ZLib encoded data insted of - // raw pixel data. - // - - public void handleRect(int x, int y, int w, int h) throws IOException { - - // - // Write encoding ID to record output stream. - // Remark: we forced changed encoding from zlib to raw - // cause at this moment we cannot save data in zlib encoding. - // - - if (dos != null) { - dos.writeInt(RawDecoder.EncodingRaw); - } - - int nBytes = rfbis.readU32(); - - if (zlibBuf == null || zlibBufLen < nBytes) { - zlibBufLen = nBytes * 2; - zlibBuf = new byte[zlibBufLen]; - } - - rfbis.readFully(zlibBuf, 0, nBytes); - - if (zlibInflater == null) { - zlibInflater = new Inflater(); - } - zlibInflater.setInput(zlibBuf, 0, nBytes); - - try { - if (bytesPerPixel == 1) { - for (int dy = y; dy < y + h; dy++) { - zlibInflater.inflate(pixels8, dy * framebufferWidth + x, w); - - // - // Save decoded raw data to data output stream - // - - if (dos != null) - dos.write(pixels8, dy * framebufferWidth + x, w); - } - } else { - byte[] buf = new byte[w * 4]; - int i, offset; - for (int dy = y; dy < y + h; dy++) { - zlibInflater.inflate(buf); - offset = dy * framebufferWidth + x; - for (i = 0; i < w; i++) { - RawDecoder.pixels24[offset + i] = - (buf[i * 4 + 2] & 0xFF) << 16 | - (buf[i * 4 + 1] & 0xFF) << 8 | - (buf[i * 4] & 0xFF); - } - - // - // Save decoded raw data to data output stream - // - - if (dos != null) - dos.write(buf); - } - } - } catch (DataFormatException ex) { - ex.printStackTrace(); - } - handleUpdatedPixels(x, y, w, h); - } - - // - // Zlib encoder's data. - // - - protected byte[] zlibBuf; - protected int zlibBufLen = 0; - protected Inflater zlibInflater; -} diff --git a/java/src/com/tigervnc/decoder/common/Repaintable.java b/java/src/com/tigervnc/decoder/common/Repaintable.java deleted file mode 100644 index 896f09fe..00000000 --- a/java/src/com/tigervnc/decoder/common/Repaintable.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.tigervnc.decoder.common; - -public interface Repaintable { - - public void scheduleRepaint(int x, int y, int w, int h); - -} diff --git a/java/src/com/tigervnc/rdr/EndOfStream.java b/java/src/com/tigervnc/rdr/EndOfStream.java new file mode 100644 index 00000000..bdcf7c27 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rdr/Exception.java b/java/src/com/tigervnc/rdr/Exception.java new file mode 100644 index 00000000..a5fe938d --- /dev/null +++ b/java/src/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/src/com/tigervnc/rdr/IOException.java b/java/src/com/tigervnc/rdr/IOException.java new file mode 100644 index 00000000..6343d7a4 --- /dev/null +++ b/java/src/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/src/com/tigervnc/vncviewer/InStream.java b/java/src/com/tigervnc/rdr/InStream.java index 75ff91aa..ec2d6d7c 100644 --- a/java/src/com/tigervnc/vncviewer/InStream.java +++ b/java/src/com/tigervnc/rdr/InStream.java @@ -21,7 +21,7 @@ // Representation). // -package com.tigervnc.vncviewer; +package com.tigervnc.rdr; abstract public class InStream { @@ -29,7 +29,7 @@ abstract public class InStream { // itemSize bytes. Returns the number of items in the buffer (up to a // maximum of nItems). - public final int check(int itemSize, int nItems) throws Exception { + public int check(int itemSize, int nItems) { if (ptr + itemSize * nItems > end) { if (ptr + itemSize > end) return overrun(itemSize, nItems); @@ -39,45 +39,36 @@ abstract public class InStream { return nItems; } - public final void check(int itemSize) throws Exception { + public final int check(int itemSize) { if (ptr + itemSize > end) - overrun(itemSize, 1); + return overrun(itemSize, 1); + return 1; } - // readU/SN() methods read unsigned and signed N-bit integers. - - public final int readS8() throws Exception { - check(1); return b[ptr++]; - } + // 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 int readS16() throws Exception { - check(2); int b0 = b[ptr++]; - int b1 = b[ptr++] & 0xff; return b0 << 8 | b1; - } + public final boolean checkNoWait(int length) { return check(length, 1)!=0; } - public final int readS32() throws Exception { - check(4); int b0 = b[ptr++]; - int b1 = b[ptr++] & 0xff; - int b2 = b[ptr++] & 0xff; - int b3 = b[ptr++] & 0xff; - return b0 << 24 | b1 << 16 | b2 << 8 | b3; - } - - public final int readU8() throws Exception { - return readS8() & 0xff; - } + // readU/SN() methods read unsigned and signed N-bit integers. - public final int readU16() throws Exception { - return readS16() & 0xffff; - } + 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 readU32() throws Exception { - return readS32() & 0xffffffff; - } + 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() throws Exception { + public final String readString() { int len = readU32(); if (len > maxStringLength) throw new Exception("InStream max string length exceeded"); @@ -99,7 +90,7 @@ abstract public class InStream { public static int maxStringLength = 65535; - public final void skip(int bytes) throws Exception { + public final void skip(int bytes) { while (bytes > 0) { int n = check(1, bytes); ptr += n; @@ -109,7 +100,7 @@ abstract public class InStream { // readBytes() reads an exact number of bytes into an array at an offset. - public void readBytes(byte[] data, int offset, int length) throws Exception { + public void readBytes(byte[] data, int offset, int length) { int offsetEnd = offset + length; while (offset < offsetEnd) { int n = check(1, offsetEnd - offset); @@ -119,33 +110,59 @@ abstract public class InStream { } } - // readOpaqueN() reads a quantity "without byte-swapping". Because java has - // no byte-ordering, we just use big-endian. - - public final int readOpaque8() throws Exception { - return readU8(); + public void readBytes(int[] data, int offset, int length) { + int offsetEnd = offset + length; + while (offset < offsetEnd) { + int n = check(1, offsetEnd - offset); + System.arraycopy(b, ptr, data, offset, n); + ptr += n; + offset += n; + } } - public final int readOpaque16() throws Exception { - return readU16(); - } + // readOpaqueN() reads a quantity "without byte-swapping". Because java has + // no byte-ordering, we just use big-endian. - public final int readOpaque32() throws Exception { - return readU32(); + 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 int readOpaque24A() throws Exception { - check(3); int b0 = b[ptr++]; - int b1 = b[ptr++]; int b2 = b[ptr++]; - return b0 << 24 | b1 << 16 | b2 << 8; + public final 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 readOpaque24B() throws Exception { - check(3); int b0 = b[ptr++]; - int b1 = b[ptr++]; int b2 = b[ptr++]; - return b0 << 16 | b1 << 8 | b2; + 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(); @@ -170,7 +187,7 @@ abstract public class InStream { // the number of items in the buffer (up to a maximum of nItems). itemSize // is supposed to be "small" (a few bytes). - abstract protected int overrun(int itemSize, int nItems) throws Exception; + abstract protected int overrun(int itemSize, int nItems); protected InStream() {} protected byte[] b; diff --git a/java/src/com/tigervnc/rdr/JavaInStream.java b/java/src/com/tigervnc/rdr/JavaInStream.java new file mode 100644 index 00000000..426a0e76 --- /dev/null +++ b/java/src/com/tigervnc/rdr/JavaInStream.java @@ -0,0 +1,147 @@ +/* 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 = ptrOffset = 0; + timeWaitedIn100us = 5; + timedKbits = 0; + } + + public JavaInStream(java.io.InputStream jis_) { this(jis_, defaultBufSize); } + + public void readBytes(byte[] data, int offset, int length) { + if (length < minBulkSize) { + super.readBytes(data, offset, length); + return; + } + + int n = end - ptr; + if (n > length) n = length; + + System.arraycopy(b, ptr, data, offset, n); + offset += n; + length -= n; + ptr += n; + + while (length > 0) { + n = read(data, offset, length); + offset += n; + length -= n; + ptrOffset += n; + } + } + + public int pos() { return ptrOffset + 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) { + if (itemSize > bufSize) + throw new Exception("JavaInStream overrun: max itemSize exceeded"); + + if (end - ptr != 0) + System.arraycopy(b, ptr, b, 0, end - ptr); + + ptrOffset += ptr; + end -= ptr; + ptr = 0; + + while (end < itemSize) { + int n = read(b, end, bufSize - end); + end += n; + } + + if (itemSize * nItems > end) + nItems = end / itemSize; + + return nItems; + } + + private int read(byte[] buf, int offset, int len) { + try { + long before = 0; + if (timing) + before = System.currentTimeMillis(); + + int n = jis.read(buf, offset, 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 java.io.InputStream jis; + private int ptrOffset; + private int bufSize; + + boolean timing; + long timeWaitedIn100us; + long timedKbits; +} diff --git a/java/src/com/tigervnc/rdr/JavaOutStream.java b/java/src/com/tigervnc/rdr/JavaOutStream.java new file mode 100644 index 00000000..94791b9d --- /dev/null +++ b/java/src/com/tigervnc/rdr/JavaOutStream.java @@ -0,0 +1,82 @@ +/* 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 = 0; + end = bufSize; + } + + public JavaOutStream(java.io.OutputStream jos) { this(jos, defaultBufSize); } + + public void writeBytes(byte[] data, int offset, int length) { + if (length < minBulkSize) { + super.writeBytes(data, offset, length); + return; + } + + flush(); + try { + jos.write(data, offset, length); + } catch (java.io.IOException e) { + throw new IOException(e); + } + ptrOffset += length; + } + + public void flush() { + try { + jos.write(b, 0, ptr); + } catch (java.io.IOException e) { + throw new IOException(e); + } + ptrOffset += ptr; + ptr = 0; + } + + public int length() { return ptrOffset + ptr; } + + protected int overrun(int itemSize, int nItems) { + if (itemSize > bufSize) + throw new Exception("JavaOutStream overrun: max itemSize exceeded"); + + flush(); + + if (itemSize * nItems > end) + nItems = end / itemSize; + + return nItems; + } + + private java.io.OutputStream jos; + private int ptrOffset; + private int bufSize; +} diff --git a/java/src/com/tigervnc/vncviewer/MemInStream.java b/java/src/com/tigervnc/rdr/MemInStream.java index 41a4fc01..ce4f91e3 100644 --- a/java/src/com/tigervnc/vncviewer/MemInStream.java +++ b/java/src/com/tigervnc/rdr/MemInStream.java @@ -16,7 +16,7 @@ * USA. */ -package com.tigervnc.vncviewer; +package com.tigervnc.rdr; public class MemInStream extends InStream { @@ -28,7 +28,7 @@ public class MemInStream extends InStream { public int pos() { return ptr; } - protected int overrun(int itemSize, int nItems) throws Exception { - throw new Exception("MemInStream overrun: end of stream"); + protected int overrun(int itemSize, int nItems) { + throw new EndOfStream(); } } diff --git a/java/src/com/tigervnc/rdr/MemOutStream.java b/java/src/com/tigervnc/rdr/MemOutStream.java new file mode 100644 index 00000000..b3040793 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rdr/OutStream.java b/java/src/com/tigervnc/rdr/OutStream.java new file mode 100644 index 00000000..7b4869e0 --- /dev/null +++ b/java/src/com/tigervnc/rdr/OutStream.java @@ -0,0 +1,141 @@ +/* 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); } + + // writeString() writes a string - a U32 length followed by the data. + + public final void writeString(String str) { + int len = str.length(); + writeU32(len); + for (int i = 0; i < len;) { + int j = i + check(1, len - i); + while (i < j) { + b[ptr++] = (byte)str.charAt(i++); + } + } + } + + 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 offset, int length) { + int offsetEnd = offset + length; + while (offset < offsetEnd) { + int n = check(1, offsetEnd - offset); + System.arraycopy(data, offset, b, ptr, n); + ptr += n; + offset += 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/src/com/tigervnc/vncviewer/ZlibInStream.java b/java/src/com/tigervnc/rdr/ZlibInStream.java index 2f2a3c07..64de00a1 100644 --- a/java/src/com/tigervnc/vncviewer/ZlibInStream.java +++ b/java/src/com/tigervnc/rdr/ZlibInStream.java @@ -20,7 +20,7 @@ // A ZlibInStream reads from a zlib.io.InputStream // -package com.tigervnc.vncviewer; +package com.tigervnc.rdr; public class ZlibInStream extends InStream { @@ -41,7 +41,7 @@ public class ZlibInStream extends InStream { ptr = end = 0; } - public void reset() throws Exception { + public void reset() { ptr = end = 0; if (underlying == null) return; @@ -54,7 +54,7 @@ public class ZlibInStream extends InStream { public int pos() { return ptrOffset + ptr; } - protected int overrun(int itemSize, int nItems) throws Exception { + protected int overrun(int itemSize, int nItems) { if (itemSize > bufSize) throw new Exception("ZlibInStream overrun: max itemSize exceeded"); if (underlying == null) @@ -82,7 +82,7 @@ public class ZlibInStream extends InStream { // data. Returns false if wait is false and we would block on the underlying // stream. - private void decompress() throws Exception { + private void decompress() { try { underlying.check(1); int avail_in = underlying.getend() - underlying.getptr(); diff --git a/java/src/com/tigervnc/rfb/AliasParameter.java b/java/src/com/tigervnc/rfb/AliasParameter.java new file mode 100644 index 00000000..2570b877 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/AuthFailureException.java b/java/src/com/tigervnc/rfb/AuthFailureException.java new file mode 100644 index 00000000..35fabef0 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/BoolParameter.java b/java/src/com/tigervnc/rfb/BoolParameter.java new file mode 100644 index 00000000..06c6ed79 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/CConnection.java b/java/src/com/tigervnc/rfb/CConnection.java new file mode 100644 index 00000000..bca2e9df --- /dev/null +++ b/java/src/com/tigervnc/rfb/CConnection.java @@ -0,0 +1,370 @@ +/* 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 = true; + if (!cp.readVersion(is, done)) { + state_ = RFBSTATE_INVALID; + throw new Exception("reading version failed: not an RFB server?"); + } + if (!done) 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 setEncryptionType(String type) { + // encryptionType = type; + //} + + public void initSecTypes() { + nSecTypes = 0; + } + + // addSecType() should be called once for each security type which the + // client supports. The order in which they're added is such that the + // first one is most preferred. +/* + public void addSecType(int secType) { + if (nSecTypes == maxSecTypes) + throw new Exception("too many security types"); + secTypes.set(nSecTypes++,secType); + } +*/ + + // 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 CMsgReader reader() { return reader_; } + public CMsgWriter 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; + CMsgReader reader_ = null; + CMsgWriter 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/src/com/tigervnc/rfb/CMsgHandler.java b/java/src/com/tigervnc/rfb/CMsgHandler.java new file mode 100644 index 00000000..11d26815 --- /dev/null +++ b/java/src/com/tigervnc/rfb/CMsgHandler.java @@ -0,0 +1,85 @@ +/* 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 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/src/com/tigervnc/rfb/CMsgReader.java b/java/src/com/tigervnc/rfb/CMsgReader.java new file mode 100644 index 00000000..66b9d174 --- /dev/null +++ b/java/src/com/tigervnc/rfb/CMsgReader.java @@ -0,0 +1,173 @@ +/* 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); + handler.serverCutText(new String(buf), 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/src/com/tigervnc/rfb/CMsgReaderV3.java b/java/src/com/tigervnc/rfb/CMsgReaderV3.java new file mode 100644 index 00000000..b865a6c7 --- /dev/null +++ b/java/src/com/tigervnc/rfb/CMsgReaderV3.java @@ -0,0 +1,139 @@ +/* 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.readU32(); + + 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; + 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); + } + + int nUpdateRectsLeft; + + static LogWriter vlog = new LogWriter("CMsgReaderV3"); +} diff --git a/java/src/com/tigervnc/rfb/CMsgWriter.java b/java/src/com/tigervnc/rfb/CMsgWriter.java new file mode 100644 index 00000000..79f5ee99 --- /dev/null +++ b/java/src/com/tigervnc/rfb/CMsgWriter.java @@ -0,0 +1,164 @@ +/* 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 (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); + os.writeBytes(str.getBytes(), 0, len); + 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/src/com/tigervnc/rfb/CMsgWriterV3.java b/java/src/com/tigervnc/rfb/CMsgWriterV3.java new file mode 100644 index 00000000..24d47567 --- /dev/null +++ b/java/src/com/tigervnc/rfb/CMsgWriterV3.java @@ -0,0 +1,39 @@ +/* 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 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(); + } +} diff --git a/java/src/com/tigervnc/rfb/CSecurity.java b/java/src/com/tigervnc/rfb/CSecurity.java new file mode 100644 index 00000000..e5b300f6 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/CSecurityManaged.java b/java/src/com/tigervnc/rfb/CSecurityManaged.java new file mode 100644 index 00000000..3502289f --- /dev/null +++ b/java/src/com/tigervnc/rfb/CSecurityManaged.java @@ -0,0 +1,71 @@ +/* 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()); + os.writeBytes(username.toString().getBytes(), 0, username.length()); + 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/src/com/tigervnc/rfb/CSecurityNone.java b/java/src/com/tigervnc/rfb/CSecurityNone.java new file mode 100644 index 00000000..e31056da --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/CSecurityPlain.java b/java/src/com/tigervnc/rfb/CSecurityPlain.java new file mode 100644 index 00000000..c790852b --- /dev/null +++ b/java/src/com/tigervnc/rfb/CSecurityPlain.java @@ -0,0 +1,50 @@ +/* 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()); + os.writeBytes(username.toString().getBytes(), 0, username.length()); + os.writeBytes(password.toString().getBytes(), 0, password.length()); + 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/src/com/tigervnc/rfb/CSecurityStack.java b/java/src/com/tigervnc/rfb/CSecurityStack.java new file mode 100644 index 00000000..5886268e --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/CSecurityTLS.java b/java/src/com/tigervnc/rfb/CSecurityTLS.java new file mode 100644 index 00000000..977987ea --- /dev/null +++ b/java/src/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 enabled = new ArrayList();
+
+ supported = ssl.getSupportedCipherSuites();
+
+ for (int i = 0; i < supported.length; i++)
+ if (supported[i].matches("TLS_DH_anon.*"))
+ enabled.add(supported[i]);
+
+ ssl.setEnabledCipherSuites((String[])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.getFileSeperator()+".vnc"+
+ UserPrefs.getFileSeperator();
+ 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/src/com/tigervnc/rfb/CSecurityVeNCrypt.java b/java/src/com/tigervnc/rfb/CSecurityVeNCrypt.java new file mode 100644 index 00000000..ae758e7f --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/CSecurityVncAuth.java b/java/src/com/tigervnc/rfb/CSecurityVncAuth.java new file mode 100644 index 00000000..f047f530 --- /dev/null +++ b/java/src/com/tigervnc/rfb/CSecurityVncAuth.java @@ -0,0 +1,60 @@ +/* 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();
+ for (int i=0; i<8; i++)
+ key[i] = i<pwdLen ? (byte)passwd.charAt(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/src/com/tigervnc/rfb/Configuration.java b/java/src/com/tigervnc/rfb/Configuration.java new file mode 100644 index 00000000..bc676087 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/ConnFailedException.java b/java/src/com/tigervnc/rfb/ConnFailedException.java new file mode 100644 index 00000000..d1ddcb4e --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/ConnParams.java b/java/src/com/tigervnc/rfb/ConnParams.java new file mode 100644 index 00000000..77acea0e --- /dev/null +++ b/java/src/com/tigervnc/rfb/ConnParams.java @@ -0,0 +1,172 @@ +/* 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; + customCompressLevel = false; compressLevel = 6; + noJpeg = false; qualityLevel = -1; + name_ = null; nEncodings_ = 0; encodings_ = null; + currentEncoding_ = Encodings.encodingRaw; verStrPos = 0; + + setName(""); + } + + public boolean readVersion(InStream is, boolean done) { + if (verStrPos >= 12) return false; + byte[] verStr = new byte[13]; + while (verStrPos < 12) { + verStr[verStrPos++] = (byte)is.readU8(); + } + + if (verStrPos < 12) { + done = false; + return true; + } + done = true; + verStr[12] = 0; + majorVersion = (verStr[4] - '0') * 100 + (verStr[5] - '0') * 10 + (verStr[6] - '0'); + minorVersion = (verStr[8] - '0') * 100 + (verStr[9] - '0') * 10 + (verStr[10] - '0'); + verStrPos = 0; + return true; + } + + public void writeVersion(OutStream os) { + byte[] b = new byte[12]; + b[0] = (byte)'R'; b[1] = (byte)'F'; b[2] = (byte)'B'; b[3] = (byte)' '; + b[4] = (byte)('0' + (majorVersion / 100) % 10); + b[5] = (byte)('0' + (majorVersion / 10) % 10); + b[6] = (byte)('0' + majorVersion % 10); + b[7] = (byte)'.'; + b[8] = (byte)('0' + (minorVersion / 100) % 10); + b[9] = (byte)('0' + (minorVersion / 10) % 10); + b[10] = (byte)('0' + minorVersion % 10); + b[11] = (byte)'\n'; + os.writeBytes(b, 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.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 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 String verStr; + private int verStrPos; +} diff --git a/java/src/com/tigervnc/rfb/Cursor.java b/java/src/com/tigervnc/rfb/Cursor.java new file mode 100644 index 00000000..420eb82a --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/Decoder.java b/java/src/com/tigervnc/rfb/Decoder.java new file mode 100644 index 00000000..8d42ea5e --- /dev/null +++ b/java/src/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/src/com/tigervnc/vncviewer/DesCipher.java b/java/src/com/tigervnc/rfb/DesCipher.java index 25362fee..f7ae9db9 100644 --- a/java/src/com/tigervnc/vncviewer/DesCipher.java +++ b/java/src/com/tigervnc/rfb/DesCipher.java @@ -70,10 +70,6 @@ // fine Java utilities: http://www.acme.com/java/ -package com.tigervnc.vncviewer; - -import java.io.*; - /// The DES encryption method. // <P> // This is surprisingly fast, for pure Java. On a SPARC 20, wrapped @@ -90,6 +86,8 @@ import java.io.*; // @see EncryptedOutputStream // @see EncryptedInputStream +package com.tigervnc.rfb; + public class DesCipher { diff --git a/java/src/com/tigervnc/rfb/Encoder.java b/java/src/com/tigervnc/rfb/Encoder.java new file mode 100644 index 00000000..0964f88e --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/Encodings.java b/java/src/com/tigervnc/rfb/Encodings.java new file mode 100644 index 00000000..215178dc --- /dev/null +++ b/java/src/com/tigervnc/rfb/Encodings.java @@ -0,0 +1,69 @@ +/* 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; + + // 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/src/com/tigervnc/rfb/Exception.java b/java/src/com/tigervnc/rfb/Exception.java new file mode 100644 index 00000000..26ac355b --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/Hextile.java b/java/src/com/tigervnc/rfb/Hextile.java new file mode 100644 index 00000000..9c05b729 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/HextileDecoder.java b/java/src/com/tigervnc/rfb/HextileDecoder.java new file mode 100644 index 00000000..4c32b52c --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/Hostname.java b/java/src/com/tigervnc/rfb/Hostname.java new file mode 100644 index 00000000..42fda537 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/IntParameter.java b/java/src/com/tigervnc/rfb/IntParameter.java new file mode 100644 index 00000000..877063e7 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/Keysyms.java b/java/src/com/tigervnc/rfb/Keysyms.java new file mode 100644 index 00000000..6bfafeaf --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/LogWriter.java b/java/src/com/tigervnc/rfb/LogWriter.java new file mode 100644 index 00000000..c5730531 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/ManagedPixelBuffer.java b/java/src/com/tigervnc/rfb/ManagedPixelBuffer.java new file mode 100644 index 00000000..46b5acf7 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/MsgTypes.java b/java/src/com/tigervnc/rfb/MsgTypes.java new file mode 100644 index 00000000..a009b396 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/PixelBuffer.java b/java/src/com/tigervnc/rfb/PixelBuffer.java new file mode 100644 index 00000000..f87fead8 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/PixelFormat.java b/java/src/com/tigervnc/rfb/PixelFormat.java new file mode 100644 index 00000000..a8ab5f11 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/Point.java b/java/src/com/tigervnc/rfb/Point.java new file mode 100644 index 00000000..25de8c20 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/RREDecoder.java b/java/src/com/tigervnc/rfb/RREDecoder.java new file mode 100644 index 00000000..e0ff5cfe --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/RawDecoder.java b/java/src/com/tigervnc/rfb/RawDecoder.java new file mode 100644 index 00000000..79db8260 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/Rect.java b/java/src/com/tigervnc/rfb/Rect.java new file mode 100644 index 00000000..fab4f5dd --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/Screen.java b/java/src/com/tigervnc/rfb/Screen.java new file mode 100644 index 00000000..90b22b6d --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/ScreenSet.java b/java/src/com/tigervnc/rfb/ScreenSet.java new file mode 100644 index 00000000..071282ff --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/Security.java b/java/src/com/tigervnc/rfb/Security.java new file mode 100644 index 00000000..5e572e3f --- /dev/null +++ b/java/src/com/tigervnc/rfb/Security.java @@ -0,0 +1,189 @@ +/* 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; + public static final int secTypeManaged = 20; + + /* 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; + + // 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(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; + if (name.equalsIgnoreCase("Managed")) return secTypeManaged; + + /* VeNCrypt subtypes */ + if (name.equalsIgnoreCase("Plain")) return secTypePlain; + if (name.equalsIgnoreCase("TLSNone")) return secTypeTLSNone; + if (name.equalsIgnoreCase("TLSVnc")) return secTypeTLSVnc; + if (name.equalsIgnoreCase("TLSPlain")) return secTypeTLSPlain; + if (name.equalsIgnoreCase("X509None")) return secTypeX509None; + if (name.equalsIgnoreCase("X509Vnc")) return secTypeX509Vnc; + if (name.equalsIgnoreCase("X509Plain")) return secTypeX509Plain; + + 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"; + case secTypeManaged: return "Managed"; + + /* VeNCrypt subtypes */ + case secTypePlain: return "Plain"; + case secTypeTLSNone: return "TLSNone"; + case secTypeTLSVnc: return "TLSVnc"; + case secTypeTLSPlain: return "TLSPlain"; + case secTypeX509None: return "X509None"; + case secTypeX509Vnc: return "X509Vnc"; + case secTypeX509Plain: return "X509Plain"; + 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/src/com/tigervnc/rfb/SecurityClient.java b/java/src/com/tigervnc/rfb/SecurityClient.java new file mode 100644 index 00000000..90c35d81 --- /dev/null +++ b/java/src/com/tigervnc/rfb/SecurityClient.java @@ -0,0 +1,77 @@ +/* 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; + +public class SecurityClient extends Security { + + public SecurityClient() { super(secTypes); } + + public CSecurity GetCSecurity(int secType) + { + //assert (CSecurity::upg != NULL); /* (upg == NULL) means bug in the viewer */ + //assert (CSecurityTLS::msg != NULL); + + if (!IsSupported(secType)) + throw new Exception("Security type not supported"); + + switch (secType) { + case Security.secTypeManaged: return (new CSecurityManaged()); + 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.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.secTypeX509None: + return (new CSecurityStack(secTypeX509None, "X509 with no password", + new CSecurityTLS(false), null)); + case Security.secTypeX509Vnc: + return (new CSecurityStack(secTypeX509None, "X509 with VNCAuth", + new CSecurityTLS(false), new CSecurityVncAuth())); + case Security.secTypeX509Plain: + return (new CSecurityStack(secTypeX509Plain, "X509 with Username/Password", + new CSecurityTLS(false), new CSecurityPlain())); + 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)", + "Managed,X509Plain,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone,VncAuth,None"); + +} diff --git a/java/src/com/tigervnc/rfb/StringParameter.java b/java/src/com/tigervnc/rfb/StringParameter.java new file mode 100644 index 00000000..4f704d9b --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/TightDecoder.java b/java/src/com/tigervnc/rfb/TightDecoder.java new file mode 100644 index 00000000..1065feb6 --- /dev/null +++ b/java/src/com/tigervnc/rfb/TightDecoder.java @@ -0,0 +1,427 @@ +/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved. + * Copyright (C) 2004-2005 Cendio AB. 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.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.width() * r.height(); 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 i = 0; i < r.width() * r.height(); i++) { + buf[i] = input.readU8(); + } + } + } + } else { + int ptr = 0; + int bits; + if (palSize <= 2) { + // 2-color palette + int dw = (r.width() + 7) / 8; + for (int dy = 0; dy < r.height(); dy++) { + for (int dx = 0; dx < r.width() / 8; dx++) { + bits = input.readU8(); + for(int b = 7; b >= 0; b--) { + buf[ptr++] = palette[bits >> b & 1]; + } + } + if (r.width() % 8 != 0) { + bits = input.readU8(); + for (int b = 7; b >= 8 - r.width() % 8; b--) { + buf[ptr++] = palette[bits >> b & 1]; + } + } + } + } else { + // 256-color palette + for (int dy = 0; dy < r.height(); dy++) { + for (int dx = 0; dx < r.width(); dx++) { + 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 integers = new ArrayList(); + 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] = ((Integer)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/src/com/tigervnc/rfb/UnicodeToKeysym.java b/java/src/com/tigervnc/rfb/UnicodeToKeysym.java new file mode 100644 index 00000000..44f61ba6 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/UserMsgBox.java b/java/src/com/tigervnc/rfb/UserMsgBox.java new file mode 100644 index 00000000..e83d9956 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/UserPasswdGetter.java b/java/src/com/tigervnc/rfb/UserPasswdGetter.java new file mode 100644 index 00000000..9796b66f --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/VncAuth.java b/java/src/com/tigervnc/rfb/VncAuth.java new file mode 100644 index 00000000..cce8d81c --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/VoidParameter.java b/java/src/com/tigervnc/rfb/VoidParameter.java new file mode 100644 index 00000000..f41f4c84 --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/ZRLEDecoder.java b/java/src/com/tigervnc/rfb/ZRLEDecoder.java new file mode 100644 index 00000000..4f740f9d --- /dev/null +++ b/java/src/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/src/com/tigervnc/rfb/screenTypes.java b/java/src/com/tigervnc/rfb/screenTypes.java new file mode 100644 index 00000000..d46741c6 --- /dev/null +++ b/java/src/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/src/com/tigervnc/vncviewer/AuthPanel.java b/java/src/com/tigervnc/vncviewer/AuthPanel.java deleted file mode 100644 index a23758e4..00000000 --- a/java/src/com/tigervnc/vncviewer/AuthPanel.java +++ /dev/null @@ -1,125 +0,0 @@ -// -// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. -// Copyright (C) 2002-2006 Constantin Kaplinsky. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -package com.tigervnc.vncviewer; - -import java.awt.*; -import java.awt.event.*; - -// -// The panel which implements the user authentication scheme -// - -class AuthPanel extends Panel implements ActionListener { - - TextField passwordField; - Button okButton; - boolean AskPassword; - - // - // Constructor. - // - - public AuthPanel(VncViewer viewer, boolean askpassword) - { - AskPassword = askpassword; - Label titleLabel = new Label("VNC Authentication", Label.CENTER); - titleLabel.setFont(new Font("Helvetica", Font.BOLD, 18)); - - Label promptLabel; - if (AskPassword) - promptLabel = new Label("Password:", Label.CENTER); - else - promptLabel = new Label("User:", Label.CENTER); - - passwordField = new TextField(10); - passwordField.setForeground(Color.black); - passwordField.setBackground(Color.white); - if (AskPassword) - passwordField.setEchoChar('*'); - - okButton = new Button("OK"); - - GridBagLayout gridbag = new GridBagLayout(); - GridBagConstraints gbc = new GridBagConstraints(); - - setLayout(gridbag); - - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbc.insets = new Insets(0,0,20,0); - gridbag.setConstraints(titleLabel,gbc); - add(titleLabel); - - gbc.fill = GridBagConstraints.NONE; - gbc.gridwidth = 1; - gbc.insets = new Insets(0,0,0,0); - gridbag.setConstraints(promptLabel,gbc); - add(promptLabel); - - gridbag.setConstraints(passwordField,gbc); - add(passwordField); - passwordField.addActionListener(this); - - // gbc.ipady = 10; - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(0,20,0,0); - gbc.ipadx = 30; - gridbag.setConstraints(okButton,gbc); - add(okButton); - okButton.addActionListener(this); - } - - // - // Move keyboard focus to the default object, that is, the password - // text field. - // - - public void moveFocusToDefaultField() - { - passwordField.requestFocus(); - } - - // - // This method is called when a button is pressed or return is - // pressed in the password text field. - // - - public synchronized void actionPerformed(ActionEvent evt) - { - if (evt.getSource() == passwordField || evt.getSource() == okButton) { - passwordField.setEnabled(false); - notify(); - } - } - - // - // Wait for user entering a password, and return it as String. - // - - public synchronized String getPassword() throws Exception - { - try { - wait(); - } catch (InterruptedException e) { } - return passwordField.getText(); - } - -} diff --git a/java/src/com/tigervnc/vncviewer/ButtonPanel.java b/java/src/com/tigervnc/vncviewer/ButtonPanel.java deleted file mode 100644 index 1ef64124..00000000 --- a/java/src/com/tigervnc/vncviewer/ButtonPanel.java +++ /dev/null @@ -1,154 +0,0 @@ -// -// Copyright (C) 2001,2002 HorizonLive.com, Inc. All Rights Reserved. -// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -// -// ButtonPanel class implements panel with four buttons in the -// VNCViewer desktop window. -// - -package com.tigervnc.vncviewer; - -import java.awt.*; -import java.awt.event.*; -import java.io.*; - -class ButtonPanel extends Panel implements ActionListener { - - VncViewer viewer; - Button disconnectButton; - Button optionsButton; - Button recordButton; - Button clipboardButton; - Button ctrlAltDelButton; - Button refreshButton; - - ButtonPanel(VncViewer v) { - viewer = v; - - setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); - disconnectButton = new Button("Disconnect"); - disconnectButton.setEnabled(false); - add(disconnectButton); - disconnectButton.addActionListener(this); - optionsButton = new Button("Options"); - add(optionsButton); - optionsButton.addActionListener(this); - clipboardButton = new Button("Clipboard"); - clipboardButton.setEnabled(false); - add(clipboardButton); - clipboardButton.addActionListener(this); - if (viewer.rec != null) { - recordButton = new Button("Record"); - add(recordButton); - recordButton.addActionListener(this); - } - ctrlAltDelButton = new Button("Send Ctrl-Alt-Del"); - ctrlAltDelButton.setEnabled(false); - add(ctrlAltDelButton); - ctrlAltDelButton.addActionListener(this); - refreshButton = new Button("Refresh"); - refreshButton.setEnabled(false); - add(refreshButton); - refreshButton.addActionListener(this); - } - - // - // Enable buttons on successful connection. - // - - public void enableButtons() { - disconnectButton.setEnabled(true); - clipboardButton.setEnabled(true); - refreshButton.setEnabled(true); - } - - // - // Disable all buttons on disconnect. - // - - public void disableButtonsOnDisconnect() { - remove(disconnectButton); - disconnectButton = new Button("Hide desktop"); - disconnectButton.setEnabled(true); - add(disconnectButton, 0); - disconnectButton.addActionListener(this); - - optionsButton.setEnabled(false); - clipboardButton.setEnabled(false); - ctrlAltDelButton.setEnabled(false); - refreshButton.setEnabled(false); - } - - // - // Enable/disable controls that should not be available in view-only - // mode. - // - - public void enableRemoteAccessControls(boolean enable) { - ctrlAltDelButton.setEnabled(enable); - } - - // - // Event processing. - // - - public void actionPerformed(ActionEvent evt) { - - viewer.moveFocusToDesktop(); - - if (evt.getSource() == disconnectButton) { - viewer.disconnect(); - - } else if (evt.getSource() == optionsButton) { - viewer.options.setVisible(!viewer.options.isVisible()); - - } else if (evt.getSource() == recordButton) { - viewer.rec.setVisible(!viewer.rec.isVisible()); - - } else if (evt.getSource() == clipboardButton) { - viewer.clipboard.setVisible(!viewer.clipboard.isVisible()); - - } else if (evt.getSource() == ctrlAltDelButton) { - try { - final int modifiers = InputEvent.CTRL_MASK | InputEvent.ALT_MASK; - - KeyEvent ctrlAltDelEvent = - new KeyEvent(this, KeyEvent.KEY_PRESSED, 0, modifiers, 127); - viewer.rfb.writeKeyEvent(ctrlAltDelEvent); - - ctrlAltDelEvent = - new KeyEvent(this, KeyEvent.KEY_RELEASED, 0, modifiers, 127); - viewer.rfb.writeKeyEvent(ctrlAltDelEvent); - - } catch (IOException e) { - e.printStackTrace(); - } - } else if (evt.getSource() == refreshButton) { - try { - RfbProto rfb = viewer.rfb; - rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, - rfb.framebufferHeight, false); - } catch (IOException e) { - e.printStackTrace(); - } - } - } -} - diff --git a/java/src/com/tigervnc/vncviewer/CConn.java b/java/src/com/tigervnc/vncviewer/CConn.java new file mode 100644 index 00000000..e4da1697 --- /dev/null +++ b/java/src/com/tigervnc/vncviewer/CConn.java @@ -0,0 +1,1137 @@ +/* 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.*;
+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();
+ }
+ });
+ }
+
+ 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 setGeometry(int x, int y, int w, int h) {
+ pack();
+ if (cc.fullScreen) setSize(w, h);
+ 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);
+ 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.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();
+ }
+
+ // 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 = (int)viewer.desktopSize.getValue().split("x")[0];
+ //int height = (int)viewer.desktopSize.getValue().split("x")[1];
+
+/*
+ if (cp.supportsSetDesktopSize &&
+ ((width != 0) && (height != 0))) {
+ 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();
+ iter.next();
+
+ if (!iter.hasNext())
+ break;
+
+ layout.remove_screen(iter.id);
+ }
+ }
+
+ layout.screens.iterator().dimensions.tl.x = 0;
+ layout.screens.iterator().dimensions.tl.y = 0;
+ layout.screens.iterator().dimensions.by.x = width;
+ layout.screens.iterator().dimensions.by.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() { 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 (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);
+ ClassLoader loader = this.getClass().getClassLoader();
+ URL url = loader.getResource("tigervnc.ico");
+ ImageIcon icon = null;
+ if (url != null) {
+ icon = new ImageIcon(url);
+ viewport.setIconImage(icon.getImage());
+ }
+ viewport.addChild(desktop);
+ reconfigureViewport();
+ viewport.setVisible(true);
+ desktop.initGraphics();
+ desktop.requestFocusInWindow();
+ }
+
+ private void reconfigureViewport()
+ {
+ //viewport->setMaxSize(cp.width, cp.height);
+ int w = cp.width;
+ int h = cp.height;
+ Dimension dpySize = viewport.getToolkit().getScreenSize();
+ int wmDecorationWidth = 0;
+ int wmDecorationHeight = 24;
+ if (w + wmDecorationWidth >= dpySize.width)
+ w = dpySize.width - wmDecorationWidth;
+ if (h + wmDecorationHeight >= dpySize.height)
+ h = dpySize.height - wmDecorationHeight;
+
+ int x = (dpySize.width - w - wmDecorationWidth) / 2;
+ int y = (dpySize.height - h - wmDecorationHeight)/2;
+
+ if (fullScreen) {
+ viewport.setExtendedState(JFrame.MAXIMIZED_BOTH);
+ viewport.setGeometry(0, 0, dpySize.width, dpySize.height);
+ } else {
+ viewport.setExtendedState(JFrame.NORMAL);
+ viewport.setGeometry(x, y, w, h);
+ }
+ }
+
+ // 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) {
+ 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("manifest");
+ String pkgVersion = "";
+ String pkgDate = "";
+ String pkgTime = "";
+ try {
+ Manifest manifest = new Manifest(stream);
+ Attributes attributes = manifest.getMainAttributes();
+ pkgVersion = attributes.getValue("Package-Version");
+ pkgDate = attributes.getValue("Package-Date");
+ pkgTime = attributes.getValue("Package-Time");
+ } catch (IOException e) { }
+ JOptionPane.showMessageDialog((viewport != null ? viewport : null),
+ VncViewer.about1+"\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);
+ }
+
+
+ // 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 = -1 + viewer.compressLevel.getValue();
+ options.compressLevel.setSelectedIndex(digit);
+ options.noJpeg.setSelected(!viewer.noJpeg.getValue());
+ digit = -1 + viewer.qualityLevel.getValue();
+ options.qualityLevel.setSelectedIndex(digit);
+
+ 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.secManaged.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.secTypeManaged:
+ options.encNone.setSelected(true);
+ options.secManaged.setSelected(true);
+ options.sendLocalUsername.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.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.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;
+ }
+ }
+ }
+ options.sendLocalUsername.setEnabled(options.secPlain.isSelected()||
+ options.secManaged.isSelected());
+ }
+
+ options.fullScreen.setSelected(fullScreen);
+ options.useLocalCursor.setSelected(viewer.useLocalCursor.getValue());
+ options.fastCopyRect.setSelected(viewer.fastCopyRect.getValue());
+ }
+
+ 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;
+ }
+ viewer.compressLevel.setParam(Integer.toString(options.compressLevel.getSelectedIndex()));
+ 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(Integer.toString(options.qualityLevel.getSelectedIndex()));
+ 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());
+ clipboardDialog.setSendingEnabled(viewer.sendClipboard.getValue());
+ menuKey = (int)(options.menuKey.getSelectedIndex()+0xFFBE);
+ F8Menu.f8.setLabel("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.secManaged.isSelected())
+ Security.EnableSecType(Security.secTypeManaged);
+ if (options.secNone.isSelected())
+ Security.EnableSecType(Security.secTypeNone);
+ if (options.secVnc.isSelected())
+ Security.EnableSecType(Security.secTypeVncAuth);
+ if (options.secPlain.isSelected())
+ Security.EnableSecType(Security.secTypePlain);
+ } else {
+ Security.DisableSecType(Security.secTypeManaged);
+ Security.DisableSecType(Security.secTypeNone);
+ Security.DisableSecType(Security.secTypeVncAuth);
+ Security.DisableSecType(Security.secTypePlain);
+ }
+
+ /* 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);
+ } else {
+ Security.DisableSecType(Security.secTypeTLSNone);
+ Security.DisableSecType(Security.secTypeTLSVnc);
+ Security.DisableSecType(Security.secTypeTLSPlain);
+ }
+
+ /* 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);
+ } else {
+ Security.DisableSecType(Security.secTypeX509None);
+ Security.DisableSecType(Security.secTypeX509Vnc);
+ Security.DisableSecType(Security.secTypeX509Plain);
+ }
+
+ /* 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);
+ }
+
+ 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);
+
+ x = ev.getX();
+ y = ev.getY();
+ if (x < 0) x = 0;
+ if (x > cp.width-1) x = cp.width-1;
+ if (y < 0) y = 0;
+ if (y > cp.height-1) y = cp.height-1;
+
+ writer().writePointerEvent(new Point(x, y), 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<java.lang.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;
+
+ static LogWriter vlog = new LogWriter("CConn");
+}
diff --git a/java/src/com/tigervnc/vncviewer/ClipboardDialog.java b/java/src/com/tigervnc/vncviewer/ClipboardDialog.java new file mode 100644 index 00000000..9753b152 --- /dev/null +++ b/java/src/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();
+ }
+
+ static Clipboard systemClipboard;
+ static {
+ try {
+ systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
+ } catch (Exception e) { }
+ }
+
+ 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);
+ if (systemClipboard != null) {
+ StringSelection ss = new StringSelection(str);
+ try {
+ systemClipboard.setContents(ss, ss);
+ } catch(Exception e) {
+ vlog.debug(e.toString());
+ }
+ }
+ }
+
+ 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/src/com/tigervnc/vncviewer/ClipboardFrame.java b/java/src/com/tigervnc/vncviewer/ClipboardFrame.java deleted file mode 100644 index a09519cd..00000000 --- a/java/src/com/tigervnc/vncviewer/ClipboardFrame.java +++ /dev/null @@ -1,135 +0,0 @@ -// -// Copyright (C) 2001 HorizonLive.com, Inc. All Rights Reserved. -// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -// -// Clipboard frame. -// - -package com.tigervnc.vncviewer; - -import java.awt.*; -import java.awt.event.*; - -class ClipboardFrame extends Frame - implements WindowListener, ActionListener { - - TextArea textArea; - Button clearButton, closeButton; - String selection; - VncViewer viewer; - - // - // Constructor. - // - - ClipboardFrame(VncViewer v) { - super("TigerVNC Clipboard"); - - viewer = v; - - GridBagLayout gridbag = new GridBagLayout(); - setLayout(gridbag); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbc.fill = GridBagConstraints.BOTH; - gbc.weighty = 1.0; - - textArea = new TextArea(5, 40); - gridbag.setConstraints(textArea, gbc); - add(textArea); - - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.weightx = 1.0; - gbc.weighty = 0.0; - gbc.gridwidth = 1; - - clearButton = new Button("Clear"); - gridbag.setConstraints(clearButton, gbc); - add(clearButton); - clearButton.addActionListener(this); - - closeButton = new Button("Close"); - gridbag.setConstraints(closeButton, gbc); - add(closeButton); - closeButton.addActionListener(this); - - pack(); - - addWindowListener(this); - } - - - // - // Set the cut text from the RFB server. - // - - void setCutText(String text) { - selection = text; - textArea.setText(text); - if (isVisible()) { - textArea.selectAll(); - } - } - - - // - // When the focus leaves the window, see if we have new cut text and - // if so send it to the RFB server. - // - - public void windowDeactivated (WindowEvent evt) { - if (selection != null && !selection.equals(textArea.getText())) { - selection = textArea.getText(); - viewer.setCutText(selection); - } - } - - // - // Close our window properly. - // - - public void windowClosing(WindowEvent evt) { - setVisible(false); - } - - // - // Ignore window events we're not interested in. - // - - public void windowActivated(WindowEvent evt) {} - public void windowOpened(WindowEvent evt) {} - public void windowClosed(WindowEvent evt) {} - public void windowIconified(WindowEvent evt) {} - public void windowDeiconified(WindowEvent evt) {} - - - // - // Respond to button presses - // - - public void actionPerformed(ActionEvent evt) { - if (evt.getSource() == clearButton) { - textArea.setText(""); - } else if (evt.getSource() == closeButton) { - setVisible(false); - } - } -} diff --git a/java/src/com/tigervnc/vncviewer/DesktopWindow.java b/java/src/com/tigervnc/vncviewer/DesktopWindow.java new file mode 100644 index 00000000..4fd80d31 --- /dev/null +++ b/java/src/com/tigervnc/vncviewer/DesktopWindow.java @@ -0,0 +1,480 @@ +/* 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 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, -1, -1, this);
+ }
+
+ final public PixelFormat getPF() { return im.getPF(); }
+
+ synchronized public void setPF(PixelFormat pf) {
+ im.setPF(pf);
+ }
+
+ // 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());
+ softCursor =
+ tk.createCustomCursor(tk.createImage(bitmap), 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() {
+ vlog.debug("DesktopWindow.resize() called");
+ 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;
+ }
+
+ synchronized public Dimension getPreferredSize() {
+ return new Dimension(im.width(), im.height());
+ }
+
+ synchronized public Dimension getMinimumSize() {
+ return new Dimension(im.width(), im.height());
+ }
+
+ public void update(Graphics g) {
+ //repaint();
+ }
+
+ synchronized public void paintComponent(Graphics g) {
+ Graphics2D g2 = (Graphics2D) g;
+ g2.drawImage(im.image, 0, 0, this);
+ }
+
+
+ String oldContents = "";
+
+ synchronized public void checkClipboard() {
+ if (ClipboardDialog.systemClipboard != null &&
+ cc.viewer.sendClipboard.getValue()) {
+ Transferable t = ClipboardDialog.systemClipboard.getContents(this);
+ 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());
+ }
+ }
+ }
+ }
+
+ /** 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)) {
+ cc.showMenu(lastX, lastY);
+ 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 = (int)cursor.hotspot.x;
+ int cursorTop = (int)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();
+
+ // 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/src/com/tigervnc/vncviewer/Dialog.java b/java/src/com/tigervnc/vncviewer/Dialog.java index 9572b9f1..6a11fb3a 100644 --- a/java/src/com/tigervnc/vncviewer/Dialog.java +++ b/java/src/com/tigervnc/vncviewer/Dialog.java @@ -1,5 +1,4 @@ /* 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 @@ -16,6 +15,7 @@ * 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 @@ -27,11 +27,24 @@ 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 Frame { +//class Dialog extends JFrame implements WindowListener { +class Dialog extends JFrame { - public Dialog(boolean modal_) { modal = modal_; } + protected boolean ok, done; + boolean modal; + + public Dialog(boolean modal_) { + modal = modal_; + //addWindowListener(this); + } public boolean showDialog() { ok = false; @@ -42,7 +55,14 @@ class Dialog extends Frame { int x = (dpySize.width - mySize.width) / 2; int y = (dpySize.height - mySize.height) / 2; setLocation(x, y); - show(); + 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 { @@ -56,7 +76,8 @@ class Dialog extends Frame { public void endDialog() { done = true; - hide(); + setVisible(false); + setFocusable(false); if (modal) { synchronized (this) { notify(); @@ -66,16 +87,57 @@ class Dialog extends Frame { // 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() {} - - public boolean handleEvent(Event event) { - if (event.id == Event.WINDOW_DESTROY) { - ok = false; - endDialog(); - } - return super.handleEvent(event); + 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; } - protected boolean ok, done; - boolean modal; } diff --git a/java/src/com/tigervnc/vncviewer/F8Menu.java b/java/src/com/tigervnc/vncviewer/F8Menu.java new file mode 100644 index 00000000..5838b38c --- /dev/null +++ b/java/src/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/src/com/tigervnc/vncviewer/HTTPConnectSocket.java b/java/src/com/tigervnc/vncviewer/HTTPConnectSocket.java deleted file mode 100644 index 2a66b25d..00000000 --- a/java/src/com/tigervnc/vncviewer/HTTPConnectSocket.java +++ /dev/null @@ -1,61 +0,0 @@ -// -// Copyright (C) 2002 Constantin Kaplinsky, Inc. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -// -// HTTPConnectSocket.java together with HTTPConnectSocketFactory.java -// implement an alternate way to connect to VNC servers via one or two -// HTTP proxies supporting the HTTP CONNECT method. -// - -package com.tigervnc.vncviewer; - -import java.net.*; -import java.io.*; - -class HTTPConnectSocket extends Socket { - - public HTTPConnectSocket(String host, int port, - String proxyHost, int proxyPort) - throws IOException { - - // Connect to the specified HTTP proxy - super(proxyHost, proxyPort); - - // Send the CONNECT request - getOutputStream().write(("CONNECT " + host + ":" + port + - " HTTP/1.0\r\n\r\n").getBytes()); - - // Read the first line of the response - DataInputStream is = new DataInputStream(getInputStream()); - String str = is.readLine(); - - // Check the HTTP error code -- it should be "200" on success - if (!str.startsWith("HTTP/1.0 200 ")) { - if (str.startsWith("HTTP/1.0 ")) - str = str.substring(9); - throw new IOException("Proxy reports \"" + str + "\""); - } - - // Success -- skip remaining HTTP headers - do { - str = is.readLine(); - } while (str.length() != 0); - } -} - diff --git a/java/src/com/tigervnc/vncviewer/HTTPConnectSocketFactory.java b/java/src/com/tigervnc/vncviewer/HTTPConnectSocketFactory.java deleted file mode 100644 index 56c8b97c..00000000 --- a/java/src/com/tigervnc/vncviewer/HTTPConnectSocketFactory.java +++ /dev/null @@ -1,88 +0,0 @@ -// -// Copyright (C) 2002 Constantin Kaplinsky, Inc. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -// -// HTTPConnectSocketFactory.java together with HTTPConnectSocket.java -// implement an alternate way to connect to VNC servers via one or two -// HTTP proxies supporting the HTTP CONNECT method. -// - -package com.tigervnc.vncviewer; - -import java.applet.*; -import java.net.*; -import java.io.*; - -class HTTPConnectSocketFactory implements SocketFactory { - - public Socket createSocket(String host, int port, Applet applet) - throws IOException { - - return createSocket(host, port, - applet.getParameter("PROXYHOST1"), - applet.getParameter("PROXYPORT1")); - } - - public Socket createSocket(String host, int port, String[] args) - throws IOException { - - return createSocket(host, port, - readArg(args, "PROXYHOST1"), - readArg(args, "PROXYPORT1")); - } - - public Socket createSocket(String host, int port, - String proxyHost, String proxyPortStr) - throws IOException { - - int proxyPort = 0; - if (proxyPortStr != null) { - try { - proxyPort = Integer.parseInt(proxyPortStr); - } catch (NumberFormatException e) { } - } - - if (proxyHost == null || proxyPort == 0) { - System.out.println("Incomplete parameter list for HTTPConnectSocket"); - return new Socket(host, port); - } - - System.out.println("HTTP CONNECT via proxy " + proxyHost + - " port " + proxyPort); - HTTPConnectSocket s = - new HTTPConnectSocket(host, port, proxyHost, proxyPort); - - return (Socket)s; - } - - private String readArg(String[] args, String name) { - - for (int i = 0; i < args.length; i += 2) { - if (args[i].equalsIgnoreCase(name)) { - try { - return args[i+1]; - } catch (Exception e) { - return null; - } - } - } - return null; - } -} - diff --git a/java/src/com/tigervnc/vncviewer/Makefile b/java/src/com/tigervnc/vncviewer/Makefile index 39ccbb76..99c04ac4 100644 --- a/java/src/com/tigervnc/vncviewer/Makefile +++ b/java/src/com/tigervnc/vncviewer/Makefile @@ -11,25 +11,15 @@ MANIFEST = MANIFEST.MF PAGES = index.vnc INSTALL_DIR = /usr/local/vnc/classes -CLASSES = VncViewer.class RfbProto.class AuthPanel.class VncCanvas.class \ - VncCanvas2.class \ - OptionsFrame.class ClipboardFrame.class ButtonPanel.class \ - DesCipher.class \ - RecordingFrame.class SessionRecorder.class \ - SocketFactory.class HTTPConnectSocketFactory.class \ - HTTPConnectSocket.class ReloginPanel.class \ - InStream.class MemInStream.class ZlibInStream.class \ - TLSTunnelBase.class TLSTunnel.class X509Tunnel.class Dialog.class MessageBox.class - -SOURCES = VncViewer.java RfbProto.java AuthPanel.java VncCanvas.java \ - VncCanvas2.java \ - OptionsFrame.java ClipboardFrame.java ButtonPanel.java \ - DesCipher.java \ - RecordingFrame.java SessionRecorder.java \ - SocketFactory.java HTTPConnectSocketFactory.java \ - HTTPConnectSocket.java ReloginPanel.java \ - InStream.java MemInStream.java ZlibInStream.java \ - TLSTunnelBase.java TLSTunnel.java X509Tunnel.java Dialog.java MessageBox.java +CLASSES = CConn.class OptionsDialogCallback.class ClipboardDialog.class \ + PasswdDialog.class DesktopWindow.class PixelBufferImage.class \ + Dialog.class ServerDialog.class F8Menu.class UserPrefs.class \ + OptionsDialog.class VncViewer.class + +SOURCES = CConn.java OptionsDialogCallback.java ClipboardDialog.java \ + PasswdDialog.java DesktopWindow.java PixelBufferImage.java \ + Dialog.java ServerDialog.java F8Menu.java UserPrefs.java \ + OptionsDialog.java VncViewer.java all: $(CLASSES) $(ARCHIVE) @@ -41,8 +31,10 @@ $(ARCHIVE): $(CLASSES) $(MANIFEST) $(JAR) cfm com/tigervnc/vncviewer/$(ARCHIVE) \ com/tigervnc/vncviewer/$(MANIFEST) \ com/tigervnc/vncviewer/*.class \ - com/tigervnc/decoder/*.class \ - com/tigervnc/decoder/common/*.class + com/tigervnc/rfb/*.class \ + com/tigervnc/rdr/*.class \ + com/tigervnc/vncviewer/tigervnc.png \ + com/tigervnc/vncviewer/tigervnc.ico install: $(CLASSES) $(ARCHIVE) $(CP) $(CLASSES) $(ARCHIVE) $(PAGES) $(INSTALL_DIR) @@ -51,4 +43,4 @@ export:: $(CLASSES) $(ARCHIVE) $(PAGES) @$(ExportJavaClasses) clean:: - $(RM) *.class *.jar ../decoder/*.class ../decoder/common/*.class + $(RM) *.class *.jar ../rfb/*.class ../rdr/*.class diff --git a/java/src/com/tigervnc/vncviewer/MessageBox.java b/java/src/com/tigervnc/vncviewer/MessageBox.java deleted file mode 100644 index feac5ceb..00000000 --- a/java/src/com/tigervnc/vncviewer/MessageBox.java +++ /dev/null @@ -1,83 +0,0 @@ -/* 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.vncviewer; - -import java.awt.*; - -public class MessageBox extends com.tigervnc.vncviewer.Dialog { - - public static final int MB_OK = 0; - public static final int MB_OKAYCANCEL = 1; - public static final int MB_YESNO = 2; - - public MessageBox(String msg, int flags) { - super(true); - GridLayout g = new GridLayout(0,1); - setLayout(g); - while (true) { - int i = msg.indexOf('\n'); - int j = (i==-1) ? msg.length() : i; - add(new Label(msg.substring(0, j))); - if (i==-1) break; - msg = msg.substring(j+1); - } - Panel p2 = new Panel(); - switch (flags & 3) { - case MB_OKAYCANCEL: - cancelButton = new Button("Cancel"); - // No break - case MB_OK: - okButton = new Button("OK"); - break; - case MB_YESNO: - okButton = new Button("Yes"); - cancelButton = new Button("No"); - break; - } - if (okButton != null) p2.add(okButton); - if (cancelButton != null) p2.add(cancelButton); - add("South", p2); - pack(); - showDialog(); - } - - public MessageBox(String msg) { - this(msg, MB_OK); - } - - - public boolean action(Event event, Object arg) { - if (event.target == okButton) { - ok = true; - endDialog(); - } else if (event.target == cancelButton) { - ok = false; - endDialog(); - } - return true; - } - - Button okButton, cancelButton; - - public boolean result() { - return ok; - } - -} diff --git a/java/src/com/tigervnc/vncviewer/OptionsDialog.java b/java/src/com/tigervnc/vncviewer/OptionsDialog.java new file mode 100644 index 00000000..7e1828e7 --- /dev/null +++ b/java/src/com/tigervnc/vncviewer/OptionsDialog.java @@ -0,0 +1,380 @@ +/* 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 ;
+ ButtonGroup encodingGroup, colourGroup;
+ JRadioButton zrle, hextile, tight, raw;
+ JRadioButton fullColour, mediumColour, lowColour, veryLowColour;
+ JCheckBox viewOnly, acceptClipboard, sendClipboard;
+ JCheckBox fullScreen, shared, useLocalCursor, fastCopyRect;
+ JCheckBox secVeNCrypt, encNone, encTLS, encX509;
+ JCheckBox secNone, secVnc, secPlain, secManaged, sendLocalUsername;
+ JButton okButton, cancelButton;
+ JButton ca, crl;
+ JButton defSaveButton;
+ boolean encryption = true;
+ UserPrefs defaults;
+
+ 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);
+ String[] compressionLevels = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+ compressLevel = new JComboBox(compressionLevels);
+ JLabel compressionLabel = new JLabel("Level (1=fast, 9=best)");
+ noJpeg = new JCheckBox("Allow JPEG Compression");
+ noJpeg.addItemListener(this);
+ String[] qualityLevels = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+ qualityLevel = new JComboBox(qualityLevels);
+ JLabel qualityLabel = new JLabel("Level (1=poor, 9=best)");
+ 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.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(8,10,0,4));
+ addGBComponent(menuKey,InputsPanel, 1, 3, 1, GridBagConstraints.REMAINDER, 0, 0, 2, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(4,4,0,125));
+ //((javax.swing.plaf.basic.BasicComboBoxRenderer)menuKey.getRenderer()).setBorder(new EmptyBorder(0,3,0,3));
+
+ // 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);
+ addGBComponent(fullScreen,MiscPanel, 0, 0, 1, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));
+ addGBComponent(shared,MiscPanel, 0, 1, 1, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));
+ addGBComponent(useLocalCursor,MiscPanel, 0, 2, 1, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,4,0,4));
+ addGBComponent(fastCopyRect,MiscPanel, 0, 3, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, 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);
+
+ 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));
+
+ // 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));
+ secManaged = addJCheckBox("Managed", 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());
+ sendLocalUsername.setEnabled(secVeNCrypt.isEnabled()&&
+ (secPlain.isSelected()||secManaged.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) {
+ 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());
+ secManaged.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 == secManaged ||
+ s instanceof JCheckBox && (JCheckBox)s == secPlain) {
+ sendLocalUsername.setEnabled(secManaged.isSelected()||secPlain.isSelected());
+ }
+ }
+
+}
diff --git a/java/src/com/tigervnc/vncviewer/TLSTunnel.java b/java/src/com/tigervnc/vncviewer/OptionsDialogCallback.java index 00cfb4ab..f6897e24 100644 --- a/java/src/com/tigervnc/vncviewer/TLSTunnel.java +++ b/java/src/com/tigervnc/vncviewer/OptionsDialogCallback.java @@ -1,18 +1,15 @@ -/* - * Copyright (C) 2003 Sun Microsystems, Inc. - * Copyright (C) 2003-2010 Martin Koegler - * Copyright (C) 2006 OCCAM Financial Technology - * +/* 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, @@ -21,31 +18,7 @@ package com.tigervnc.vncviewer; -import java.util.*; -import java.net.*; -import javax.net.ssl.*; - -public class TLSTunnel extends TLSTunnelBase -{ - - public TLSTunnel (Socket sock_) - { - super (sock_); - } - - - protected void setParam (SSLSocket sock) - { - String[]supported; - ArrayList enabled = new ArrayList (); - - supported = sock.getSupportedCipherSuites (); - - for (int i = 0; i < supported.length; i++) - if (supported[i].matches (".*DH_anon.*")) - enabled.add (supported[i]); - - sock.setEnabledCipherSuites ((String[])enabled.toArray (new String[0])); - } - +public interface OptionsDialogCallback { + public void setOptions(); + public void getOptions(); } diff --git a/java/src/com/tigervnc/vncviewer/OptionsFrame.java b/java/src/com/tigervnc/vncviewer/OptionsFrame.java deleted file mode 100644 index 573f21d9..00000000 --- a/java/src/com/tigervnc/vncviewer/OptionsFrame.java +++ /dev/null @@ -1,441 +0,0 @@ -// -// Copyright (C) 2001 HorizonLive.com, Inc. All Rights Reserved. -// Copyright (C) 2001 Constantin Kaplinsky. All Rights Reserved. -// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. -// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -// -// Options frame. -// -// This deals with all the options the user can play with. -// It sets the encodings array and some booleans. -// - -package com.tigervnc.vncviewer; - -import java.awt.*; -import java.awt.event.*; - -class OptionsFrame extends Frame - implements WindowListener, ActionListener, ItemListener { - - static String[] names = { - "Encoding", - "Compression level", - "JPEG image quality", - "Cursor shape updates", - "Use CopyRect", - "Restricted colors", - "Mouse buttons 2 and 3", - "View only", - "Scaling factor", - "Scale remote cursor", - "Share desktop" - }; - - static String[][] values = { - { "Auto", "Raw", "RRE", "CoRRE", "Hextile", "Zlib", "Tight", "ZRLE" }, - { "Default", "1", "2", "3", "4", "5", "6", "7", "8", "9" }, - { "JPEG off", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }, - { "Enable", "Ignore", "Disable" }, - { "Yes", "No" }, - { "Yes", "No" }, - { "Normal", "Reversed" }, - { "Yes", "No" }, - { "Auto", "1%", "5%", "10%", "20%", "25%", "50%", "75%", "100%"}, - { "No", "50%", "75%", "125%", "150%" }, - { "Yes", "No" } - }; - - final int - encodingIndex = 0, - compressLevelIndex = 1, - jpegQualityIndex = 2, - cursorUpdatesIndex = 3, - useCopyRectIndex = 4, - eightBitColorsIndex = 5, - mouseButtonIndex = 6, - viewOnlyIndex = 7, - scalingFactorIndex = 8, - scaleCursorIndex = 9, - shareDesktopIndex = 10; - - Label[] labels = new Label[names.length]; - Choice[] choices = new Choice[names.length]; - Button closeButton; - VncViewer viewer; - - - // - // The actual data which other classes look at: - // - - int preferredEncoding; - int compressLevel; - int jpegQuality; - boolean useCopyRect; - boolean requestCursorUpdates; - boolean ignoreCursorUpdates; - - boolean eightBitColors; - - boolean reverseMouseButtons2And3; - boolean shareDesktop; - boolean viewOnly; - int scaleCursor; - - boolean autoScale; - int scalingFactor; - - // - // Constructor. Set up the labels and choices from the names and values - // arrays. - // - - OptionsFrame(VncViewer v) { - super("TigerVNC Options"); - - viewer = v; - - GridBagLayout gridbag = new GridBagLayout(); - setLayout(gridbag); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.fill = GridBagConstraints.BOTH; - - for (int i = 0; i < names.length; i++) { - labels[i] = new Label(names[i]); - gbc.gridwidth = 1; - gridbag.setConstraints(labels[i],gbc); - add(labels[i]); - - choices[i] = new Choice(); - gbc.gridwidth = GridBagConstraints.REMAINDER; - gridbag.setConstraints(choices[i],gbc); - add(choices[i]); - choices[i].addItemListener(this); - - for (int j = 0; j < values[i].length; j++) { - choices[i].addItem(values[i][j]); - } - } - - closeButton = new Button("Close"); - gbc.gridwidth = GridBagConstraints.REMAINDER; - gridbag.setConstraints(closeButton, gbc); - add(closeButton); - closeButton.addActionListener(this); - - pack(); - - addWindowListener(this); - - // Set up defaults - - choices[encodingIndex].select("Auto"); - choices[compressLevelIndex].select("Default"); - choices[jpegQualityIndex].select("6"); - choices[cursorUpdatesIndex].select("Enable"); - choices[useCopyRectIndex].select("Yes"); - choices[eightBitColorsIndex].select("No"); - choices[mouseButtonIndex].select("Normal"); - choices[viewOnlyIndex].select("No"); - choices[scaleCursorIndex].select("No"); - choices[shareDesktopIndex].select("Yes"); - - // But let them be overridden by parameters - - for (int i = 0; i < names.length; i++) { - String s = viewer.readParameter(names[i], false); - if (s != null) { - for (int j = 0; j < values[i].length; j++) { - if (s.equalsIgnoreCase(values[i][j])) { - choices[i].select(j); - } - } - } - } - - // Get scaling factor from parameters and set it - // to gui and class member scalingFactor - - String s = viewer.readParameter("Scaling Factor", false); - if (s == null) s = "100%"; - setScalingFactor(s); - if (autoScale) { - choices[scalingFactorIndex].select("Auto"); - } else { - choices[scalingFactorIndex].select(s); - } - - // Make the booleans and encodings array correspond to the state of the GUI - - setEncodings(); - setColorFormat(); - setOtherOptions(); - } - - // - // Set scaling factor class member value - // - - void setScalingFactor(int sf) { - setScalingFactor(((Integer)sf).toString()); - } - - void setScalingFactor(String s) { - autoScale = false; - scalingFactor = 100; - if (s != null) { - if (s.equalsIgnoreCase("Auto")) { - autoScale = true; - } else { - // Remove the '%' char at the end of string if present. - if (s.charAt(s.length() - 1) == '%') { - s = s.substring(0, s.length() - 1); - } - // Convert to an integer. - try { - scalingFactor = Integer.parseInt(s); - } - catch (NumberFormatException e) { - scalingFactor = 100; - } - // Make sure scalingFactor is in the range of [1..1000]. - if (scalingFactor < 1) { - scalingFactor = 1; - } else if (scalingFactor > 1000) { - scalingFactor = 1000; - } - } - } - } - - - // - // Disable the shareDesktop option - // - - void disableShareDesktop() { - labels[shareDesktopIndex].setEnabled(false); - choices[shareDesktopIndex].setEnabled(false); - } - - // - // setEncodings looks at the encoding, compression level, JPEG - // quality level, cursor shape updates and copyRect choices and sets - // corresponding variables properly. Then it calls the VncViewer's - // setEncodings method to send a SetEncodings message to the RFB - // server. - // - - void setEncodings() { - useCopyRect = choices[useCopyRectIndex].getSelectedItem().equals("Yes"); - - preferredEncoding = RfbProto.EncodingRaw; - boolean enableCompressLevel = false; - boolean enableQualityLevel = false; - - if (choices[encodingIndex].getSelectedItem().equals("RRE")) { - preferredEncoding = RfbProto.EncodingRRE; - } else if (choices[encodingIndex].getSelectedItem().equals("CoRRE")) { - preferredEncoding = RfbProto.EncodingCoRRE; - } else if (choices[encodingIndex].getSelectedItem().equals("Hextile")) { - preferredEncoding = RfbProto.EncodingHextile; - } else if (choices[encodingIndex].getSelectedItem().equals("ZRLE")) { - preferredEncoding = RfbProto.EncodingZRLE; - } else if (choices[encodingIndex].getSelectedItem().equals("Zlib")) { - preferredEncoding = RfbProto.EncodingZlib; - enableCompressLevel = true; - } else if (choices[encodingIndex].getSelectedItem().equals("Tight")) { - preferredEncoding = RfbProto.EncodingTight; - enableCompressLevel = true; - enableQualityLevel = !eightBitColors; - } else if (choices[encodingIndex].getSelectedItem().equals("Auto")) { - preferredEncoding = -1; - enableQualityLevel = !eightBitColors; - } - - // Handle compression level setting. - - try { - compressLevel = - Integer.parseInt(choices[compressLevelIndex].getSelectedItem()); - } - catch (NumberFormatException e) { - compressLevel = -1; - } - if (compressLevel < 1 || compressLevel > 9) { - compressLevel = -1; - } - labels[compressLevelIndex].setEnabled(enableCompressLevel); - choices[compressLevelIndex].setEnabled(enableCompressLevel); - - // Handle JPEG quality setting. - - try { - jpegQuality = - Integer.parseInt(choices[jpegQualityIndex].getSelectedItem()); - } - catch (NumberFormatException e) { - jpegQuality = -1; - } - if (jpegQuality < 0 || jpegQuality > 9) { - jpegQuality = -1; - } - labels[jpegQualityIndex].setEnabled(enableQualityLevel); - choices[jpegQualityIndex].setEnabled(enableQualityLevel); - - // Request cursor shape updates if necessary. - - requestCursorUpdates = - !choices[cursorUpdatesIndex].getSelectedItem().equals("Disable"); - - if (requestCursorUpdates) { - ignoreCursorUpdates = - choices[cursorUpdatesIndex].getSelectedItem().equals("Ignore"); - } - - viewer.setEncodings(); - } - - // - // setColorFormat sets eightBitColors variable depending on the GUI - // setting, causing switches between 8-bit and 24-bit colors mode if - // necessary. - // - - void setColorFormat() { - - eightBitColors = - choices[eightBitColorsIndex].getSelectedItem().equals("Yes"); - - boolean enableJPEG = !eightBitColors && - (choices[encodingIndex].getSelectedItem().equals("Tight") || - choices[encodingIndex].getSelectedItem().equals("Auto")); - - labels[jpegQualityIndex].setEnabled(enableJPEG); - choices[jpegQualityIndex].setEnabled(enableJPEG); - } - - // - // setOtherOptions looks at the "other" choices (ones that do not - // cause sending any protocol messages) and sets the boolean flags - // appropriately. - // - - void setOtherOptions() { - - reverseMouseButtons2And3 - = choices[mouseButtonIndex].getSelectedItem().equals("Reversed"); - - viewOnly - = choices[viewOnlyIndex].getSelectedItem().equals("Yes"); - if (viewer.vc != null) - viewer.vc.enableInput(!viewOnly); - - shareDesktop - = choices[shareDesktopIndex].getSelectedItem().equals("Yes"); - - String scaleString = choices[scaleCursorIndex].getSelectedItem(); - if (scaleString.endsWith("%")) - scaleString = scaleString.substring(0, scaleString.length() - 1); - try { - scaleCursor = Integer.parseInt(scaleString); - } - catch (NumberFormatException e) { - scaleCursor = 0; - } - if (scaleCursor < 10 || scaleCursor > 500) { - scaleCursor = 0; - } - if (requestCursorUpdates && !ignoreCursorUpdates && !viewOnly) { - labels[scaleCursorIndex].setEnabled(true); - choices[scaleCursorIndex].setEnabled(true); - } else { - labels[scaleCursorIndex].setEnabled(false); - choices[scaleCursorIndex].setEnabled(false); - } - if (viewer.vc != null) - viewer.vc.createSoftCursor(); // update cursor scaling - } - - - // - // Respond to actions on Choice controls - // - - public void itemStateChanged(ItemEvent evt) { - Object source = evt.getSource(); - - if (source == choices[encodingIndex] || - source == choices[compressLevelIndex] || - source == choices[jpegQualityIndex] || - source == choices[cursorUpdatesIndex] || - source == choices[useCopyRectIndex]) { - - setEncodings(); - - if (source == choices[cursorUpdatesIndex]) { - setOtherOptions(); // update scaleCursor state - } - - } else if (source == choices[eightBitColorsIndex]) { - - setColorFormat(); - - } else if (source == choices[mouseButtonIndex] || - source == choices[shareDesktopIndex] || - source == choices[viewOnlyIndex] || - source == choices[scaleCursorIndex]) { - - setOtherOptions(); - - } else if (source == choices[scalingFactorIndex]){ - // Tell VNC canvas that scaling factor has changed - setScalingFactor(choices[scalingFactorIndex].getSelectedItem()); - if (viewer.vc != null) - viewer.vc.setScalingFactor(scalingFactor); - } - } - - // - // Respond to button press - // - - public void actionPerformed(ActionEvent evt) { - if (evt.getSource() == closeButton) - setVisible(false); - } - - // - // Respond to window events - // - - public void windowClosing(WindowEvent evt) { - setVisible(false); - } - - public void windowActivated(WindowEvent evt) {} - public void windowDeactivated(WindowEvent evt) {} - public void windowOpened(WindowEvent evt) {} - public void windowClosed(WindowEvent evt) {} - public void windowIconified(WindowEvent evt) {} - public void windowDeiconified(WindowEvent evt) {} -} diff --git a/java/src/com/tigervnc/vncviewer/PasswdDialog.java b/java/src/com/tigervnc/vncviewer/PasswdDialog.java new file mode 100644 index 00000000..eaace698 --- /dev/null +++ b/java/src/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/src/com/tigervnc/vncviewer/PixelBufferImage.java b/java/src/com/tigervnc/vncviewer/PixelBufferImage.java new file mode 100644 index 00000000..7e5e7174 --- /dev/null +++ b/java/src/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/src/com/tigervnc/vncviewer/README b/java/src/com/tigervnc/vncviewer/README index 39ba825f..c6949d59 100644 --- a/java/src/com/tigervnc/vncviewer/README +++ b/java/src/com/tigervnc/vncviewer/README @@ -9,7 +9,7 @@ optimizations, major GUI improvements, and more. Copyright (C) 1999 AT&T Laboratories Cambridge. Copyright (C) 2000 Tridia Corp. - Copyright (C) 2002-2003 RealVNC Ltd. + Copyright (C) 2002-2005 RealVNC Ltd. Copyright (C) 2001-2004 HorizonLive.com, Inc. Copyright (C) 2000-2007 Constantin Kaplinsky Copyright (C) 2000-2007 TightVNC Group diff --git a/java/src/com/tigervnc/vncviewer/RecordOutputStream.java b/java/src/com/tigervnc/vncviewer/RecordOutputStream.java deleted file mode 100644 index 7f132492..00000000 --- a/java/src/com/tigervnc/vncviewer/RecordOutputStream.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.tigervnc.vncviewer; - -import java.io.DataOutput; -import java.io.IOException; - -public class RecordOutputStream implements DataOutput { - - public RecordOutputStream(RfbProto rfbproto) { - rfb = rfbproto; - } - - private boolean canWrite() { - return ((rfb != null) && (rfb.rec != null)); - } - - public void write(byte[] b) throws IOException { - if (canWrite()) - rfb.rec.write(b); - } - - public void write(byte[] b, int off, int len) throws IOException { - if (canWrite()) - rfb.rec.write(b, off, len); - } - - public void write(int b) throws IOException { - if (canWrite()) - rfb.rec.writeIntBE(b); - } - - public void writeBoolean(boolean v) { } - - public void writeByte(int v) throws IOException { - if (canWrite()) { - rfb.rec.writeByte(v); - } - } - - public void writeBytes(String s) { } - public void writeChar(int v) { } - public void writeChars(String s) { } - public void writeDouble(double v) { } - public void writeFloat(float v) { } - - public void writeInt(int v) throws IOException { - if (canWrite()) - rfb.rec.writeIntBE(v); - } - - public void writeLong(long v) { } - - public void writeShort(int v) throws IOException { - if (canWrite()) - rfb.rec.writeShortBE(v); - } - - public void writeUTF(String str) { } - - private RfbProto rfb = null; -} diff --git a/java/src/com/tigervnc/vncviewer/RecordingFrame.java b/java/src/com/tigervnc/vncviewer/RecordingFrame.java deleted file mode 100644 index 6bf40ea0..00000000 --- a/java/src/com/tigervnc/vncviewer/RecordingFrame.java +++ /dev/null @@ -1,313 +0,0 @@ -// -// Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -// -// Recording frame. It allows to control recording RFB sessions into -// FBS (FrameBuffer Stream) files. -// - -package com.tigervnc.vncviewer; - -import java.io.*; -import java.awt.*; -import java.awt.event.*; - -class RecordingFrame extends Frame - implements WindowListener, ActionListener { - - boolean recording; - - TextField fnameField; - Button browseButton; - - Label statusLabel; - - Button recordButton, nextButton, closeButton; - VncViewer viewer; - - // - // Check if current security manager allows to create a - // RecordingFrame object. - // - - public static boolean checkSecurity() { - SecurityManager security = System.getSecurityManager(); - if (security != null) { - try { - security.checkPropertyAccess("user.dir"); - security.checkPropertyAccess("file.separator"); - // Work around (rare) checkPropertyAccess bug - System.getProperty("user.dir"); - } catch (SecurityException e) { - System.out.println("SecurityManager restricts session recording."); - return false; - } - } - return true; - } - - // - // Constructor. - // - - RecordingFrame(VncViewer v) { - super("TigerVNC Session Recording"); - - viewer = v; - - // Determine initial filename for next saved session. - // FIXME: Check SecurityManager. - - String fname = nextNewFilename(System.getProperty("user.dir") + - System.getProperty("file.separator") + - "vncsession.fbs"); - - // Construct new panel with file name field and "Browse" button. - - Panel fnamePanel = new Panel(); - GridBagLayout fnameGridbag = new GridBagLayout(); - fnamePanel.setLayout(fnameGridbag); - - GridBagConstraints fnameConstraints = new GridBagConstraints(); - fnameConstraints.gridwidth = GridBagConstraints.RELATIVE; - fnameConstraints.fill = GridBagConstraints.BOTH; - fnameConstraints.weightx = 4.0; - - fnameField = new TextField(fname, 64); - fnameGridbag.setConstraints(fnameField, fnameConstraints); - fnamePanel.add(fnameField); - fnameField.addActionListener(this); - - fnameConstraints.gridwidth = GridBagConstraints.REMAINDER; - fnameConstraints.weightx = 1.0; - - browseButton = new Button("Browse"); - fnameGridbag.setConstraints(browseButton, fnameConstraints); - fnamePanel.add(browseButton); - browseButton.addActionListener(this); - - // Construct the frame. - - GridBagLayout gridbag = new GridBagLayout(); - setLayout(gridbag); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbc.fill = GridBagConstraints.BOTH; - gbc.weighty = 1.0; - gbc.insets = new Insets(10, 0, 0, 0); - - Label helpLabel = - new Label("File name to save next recorded session in:", Label.CENTER); - gridbag.setConstraints(helpLabel, gbc); - add(helpLabel); - - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.weighty = 0.0; - gbc.insets = new Insets(0, 0, 0, 0); - - gridbag.setConstraints(fnamePanel, gbc); - add(fnamePanel); - - gbc.fill = GridBagConstraints.BOTH; - gbc.weighty = 1.0; - gbc.insets = new Insets(10, 0, 10, 0); - - statusLabel = new Label("", Label.CENTER); - gridbag.setConstraints(statusLabel, gbc); - add(statusLabel); - - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.weightx = 1.0; - gbc.weighty = 0.0; - gbc.gridwidth = 1; - gbc.insets = new Insets(0, 0, 0, 0); - - recordButton = new Button("Record"); - gridbag.setConstraints(recordButton, gbc); - add(recordButton); - recordButton.addActionListener(this); - - nextButton = new Button("Next file"); - gridbag.setConstraints(nextButton, gbc); - add(nextButton); - nextButton.addActionListener(this); - - closeButton = new Button("Close"); - gridbag.setConstraints(closeButton, gbc); - add(closeButton); - closeButton.addActionListener(this); - - // Set correct text, font and color for the statusLabel. - stopRecording(); - - pack(); - - addWindowListener(this); - } - - // - // If the given string ends with ".NNN" where NNN is a decimal - // number, increase this number by one. Otherwise, append ".001" - // to the given string. - // - - protected String nextFilename(String fname) { - int len = fname.length(); - int suffixPos = len; - int suffixNum = 1; - - if (len > 4 && fname.charAt(len - 4) == '.') { - try { - suffixNum = Integer.parseInt(fname.substring(len - 3, len)) + 1; - suffixPos = len - 4; - } catch (NumberFormatException e) { } - } - - char[] zeroes = {'0', '0', '0'}; - String suffix = String.valueOf(suffixNum); - if (suffix.length() < 3) { - suffix = new String(zeroes, 0, 3 - suffix.length()) + suffix; - } - - return fname.substring(0, suffixPos) + '.' + suffix; - } - - // - // Find next name of a file which does not exist yet. - // - - protected String nextNewFilename(String fname) { - String newName = fname; - File f; - try { - do { - newName = nextFilename(newName); - f = new File(newName); - } while (f.exists()); - } catch (SecurityException e) { } - - return newName; - } - - // - // Let the user choose a file name showing a FileDialog. - // - - protected boolean browseFile() { - File currentFile = new File(fnameField.getText()); - - FileDialog fd = - new FileDialog(this, "Save next session as...", FileDialog.SAVE); - fd.setDirectory(currentFile.getParent()); - fd.setVisible(true); - if (fd.getFile() != null) { - String newDir = fd.getDirectory(); - String sep = System.getProperty("file.separator"); - if (newDir.length() > 0) { - if (!sep.equals(newDir.substring(newDir.length() - sep.length()))) - newDir += sep; - } - String newFname = newDir + fd.getFile(); - if (newFname.equals(fnameField.getText())) { - fnameField.setText(newFname); - return true; - } - } - return false; - } - - // - // Start recording. - // - - public void startRecording() { - statusLabel.setText("Status: Recording..."); - statusLabel.setFont(new Font("Helvetica", Font.BOLD, 12)); - statusLabel.setForeground(Color.red); - recordButton.setLabel("Stop recording"); - - recording = true; - - viewer.setRecordingStatus(fnameField.getText()); - } - - // - // Stop recording. - // - - public void stopRecording() { - statusLabel.setText("Status: Not recording."); - statusLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); - statusLabel.setForeground(Color.black); - recordButton.setLabel("Record"); - - recording = false; - - viewer.setRecordingStatus(null); - } - - // - // Close our window properly. - // - - public void windowClosing(WindowEvent evt) { - setVisible(false); - } - - // - // Ignore window events we're not interested in. - // - - public void windowActivated(WindowEvent evt) {} - public void windowDeactivated (WindowEvent evt) {} - public void windowOpened(WindowEvent evt) {} - public void windowClosed(WindowEvent evt) {} - public void windowIconified(WindowEvent evt) {} - public void windowDeiconified(WindowEvent evt) {} - - - // - // Respond to button presses - // - - public void actionPerformed(ActionEvent evt) { - if (evt.getSource() == browseButton) { - if (browseFile() && recording) - startRecording(); - - } else if (evt.getSource() == recordButton) { - if (!recording) { - startRecording(); - } else { - stopRecording(); - fnameField.setText(nextNewFilename(fnameField.getText())); - } - - } else if (evt.getSource() == nextButton) { - fnameField.setText(nextNewFilename(fnameField.getText())); - if (recording) - startRecording(); - - } else if (evt.getSource() == closeButton) { - setVisible(false); - - } - } -} diff --git a/java/src/com/tigervnc/vncviewer/ReloginPanel.java b/java/src/com/tigervnc/vncviewer/ReloginPanel.java deleted file mode 100644 index 51d9d210..00000000 --- a/java/src/com/tigervnc/vncviewer/ReloginPanel.java +++ /dev/null @@ -1,66 +0,0 @@ -// -// Copyright (C) 2002 Cendio Systems. All Rights Reserved. -// Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -// -// ReloginPanel class implements panel with a button for logging in again, -// after fatal errors or disconnect -// - -package com.tigervnc.vncviewer; - -import java.awt.*; -import java.awt.event.*; -import java.applet.*; - -// -// The panel which implements the Relogin button -// - -class ReloginPanel extends Panel implements ActionListener { - Button reloginButton; - Button closeButton; - VncViewer viewer; - - // - // Constructor. - // - public ReloginPanel(VncViewer v) { - viewer = v; - setLayout(new FlowLayout(FlowLayout.CENTER)); - reloginButton = new Button("Login again"); - add(reloginButton); - reloginButton.addActionListener(this); - if (viewer.inSeparateFrame) { - closeButton = new Button("Close window"); - add(closeButton); - closeButton.addActionListener(this); - } - } - - // - // This method is called when a button is pressed. - // - public synchronized void actionPerformed(ActionEvent evt) { - if (viewer.inSeparateFrame) - viewer.vncFrame.dispose(); - if (evt.getSource() == reloginButton) - viewer.getAppletContext().showDocument(viewer.getDocumentBase()); - } -} diff --git a/java/src/com/tigervnc/vncviewer/RfbInputStream.java b/java/src/com/tigervnc/vncviewer/RfbInputStream.java deleted file mode 100644 index cac3ec77..00000000 --- a/java/src/com/tigervnc/vncviewer/RfbInputStream.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.tigervnc.vncviewer; - -import java.io.IOException; - -// -// This class is layer between data of private RfbProto class -// and classes in other packages. -// -// For now this class is used by com.tigervnc.decoder.RawDecoder -// -public class RfbInputStream { - RfbInputStream(RfbProto rfbProto) { - rfb = rfbProto; - } - - // - // Read data methods - // - - public void readFully(byte b[]) throws IOException { - readFully(b, 0, b.length); - } - - public void readFully(byte b[], int off, int len) throws IOException { - rfb.readFully(b, off, len); - } - - public int readU32() throws IOException { - return rfb.readU32(); - } - - public int readU8() throws IOException { - return rfb.readU8(); - } - - public int readCompactLen() throws IOException { - return rfb.readCompactLen(); - } - - public int readU16() throws IOException { - return rfb.readU16(); - } - - private RfbProto rfb = null; -} diff --git a/java/src/com/tigervnc/vncviewer/RfbProto.java b/java/src/com/tigervnc/vncviewer/RfbProto.java deleted file mode 100644 index da54c56b..00000000 --- a/java/src/com/tigervnc/vncviewer/RfbProto.java +++ /dev/null @@ -1,1202 +0,0 @@ -// -// Copyright (C) 2001-2004 HorizonLive.com, Inc. All Rights Reserved. -// Copyright (C) 2001-2006 Constantin Kaplinsky. All Rights Reserved. -// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. -// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -// -// RfbProto.java -// - -package com.tigervnc.vncviewer; - -import java.io.*; -import java.awt.*; -import java.awt.event.*; -import java.net.Socket; -import java.util.zip.*; - -class RfbProto { - - final static String - versionMsg_3_3 = "RFB 003.003\n", - versionMsg_3_7 = "RFB 003.007\n", - versionMsg_3_8 = "RFB 003.008\n"; - - // Security types - final static int - SecTypeInvalid = 0, - SecTypeNone = 1, - SecTypeVncAuth = 2, - SecTypeTight = 16, - SecTypeVeNCrypt = 19, - SecTypePlain = 256, - SecTypeTLSNone = 257, - SecTypeTLSVnc = 258, - SecTypeTLSPlain = 259, - SecTypeX509None = 260, - SecTypeX509Vnc = 261, - SecTypeX509Plain = 262; - - // VNC authentication results - final static int - VncAuthOK = 0, - VncAuthFailed = 1, - VncAuthTooMany = 2; - - // Standard server-to-client messages - final static int - FramebufferUpdate = 0, - SetColourMapEntries = 1, - Bell = 2, - ServerCutText = 3; - - // Standard client-to-server messages - final static int - SetPixelFormat = 0, - FixColourMapEntries = 1, - SetEncodings = 2, - FramebufferUpdateRequest = 3, - KeyboardEvent = 4, - PointerEvent = 5, - ClientCutText = 6; - - // Supported encodings and pseudo-encodings - final static int - EncodingRaw = 0, - EncodingCopyRect = 1, - EncodingRRE = 2, - EncodingCoRRE = 4, - EncodingHextile = 5, - EncodingZlib = 6, - EncodingTight = 7, - EncodingZRLE = 16, - EncodingCompressLevel0 = 0xFFFFFF00, - EncodingQualityLevel0 = 0xFFFFFFE0, - EncodingXCursor = 0xFFFFFF10, - EncodingRichCursor = 0xFFFFFF11, - EncodingPointerPos = 0xFFFFFF18, - EncodingLastRect = 0xFFFFFF20, - EncodingNewFBSize = 0xFFFFFF21; - - final static int MaxNormalEncoding = 255; - - // Contstants used in the Hextile decoder - final static int - HextileRaw = 1, - HextileBackgroundSpecified = 2, - HextileForegroundSpecified = 4, - HextileAnySubrects = 8, - HextileSubrectsColoured = 16; - - // Contstants used in the Tight decoder - final static int TightMinToCompress = 12; - final static int - TightExplicitFilter = 0x04, - TightFill = 0x08, - TightJpeg = 0x09, - TightMaxSubencoding = 0x09, - TightFilterCopy = 0x00, - TightFilterPalette = 0x01, - TightFilterGradient = 0x02; - - - String host; - int port; - Socket sock; - OutputStream os; - SessionRecorder rec; - boolean inNormalProtocol = false; - VncViewer viewer; - - // Input stream is declared private to make sure it can be accessed - // only via RfbProto methods. We have to do this because we want to - // count how many bytes were read. - private DataInputStream is; - private long numBytesRead = 0; - public long getNumBytesRead() { return numBytesRead; } - - // Java on UNIX does not call keyPressed() on some keys, for example - // swedish keys To prevent our workaround to produce duplicate - // keypresses on JVMs that actually works, keep track of if - // keyPressed() for a "broken" key was called or not. - boolean brokenKeyPressed = false; - - // This will be set to true on the first framebuffer update - // containing Zlib-, ZRLE- or Tight-encoded data. - boolean wereZlibUpdates = false; - - // This fields are needed to show warnings about inefficiently saved - // sessions only once per each saved session file. - boolean zlibWarningShown; - boolean tightWarningShown; - - // Before starting to record each saved session, we set this field - // to 0, and increment on each framebuffer update. We don't flush - // the SessionRecorder data into the file before the second update. - // This allows us to write initial framebuffer update with zero - // timestamp, to let the player show initial desktop before - // playback. - int numUpdatesInSession; - - // Measuring network throughput. - boolean timing; - long timeWaitedIn100us; - long timedKbits; - - // Protocol version and TightVNC-specific protocol options. - int serverMajor, serverMinor; - int clientMajor, clientMinor; - - // If true, informs that the RFB socket was closed. - private boolean closed; - - // - // Constructor. Make TCP connection to RFB server. - // - - RfbProto(String h, int p, VncViewer v) throws IOException { - viewer = v; - host = h; - port = p; - - if (viewer.socketFactory == null) { - sock = new Socket(host, port); - sock.setTcpNoDelay(true); - } else { - try { - Class factoryClass = Class.forName(viewer.socketFactory); - SocketFactory factory = (SocketFactory)factoryClass.newInstance(); - if (viewer.inAnApplet) - sock = factory.createSocket(host, port, viewer); - else - sock = factory.createSocket(host, port, viewer.mainArgs); - } catch(Exception e) { - e.printStackTrace(); - throw new IOException(e.getMessage()); - } - } - is = new DataInputStream(new BufferedInputStream(sock.getInputStream(), - 16384)); - os = sock.getOutputStream(); - - timing = false; - timeWaitedIn100us = 5; - timedKbits = 0; - } - - - synchronized void close() { - try { - sock.close(); - closed = true; - System.out.println("RFB socket closed"); - if (rec != null) { - rec.close(); - rec = null; - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - synchronized boolean closed() { - return closed; - } - - // - // Read server's protocol version message - // - - void readVersionMsg() throws Exception { - - byte[] b = new byte[12]; - - readFully(b); - - if ((b[0] != 'R') || (b[1] != 'F') || (b[2] != 'B') || (b[3] != ' ') - || (b[4] < '0') || (b[4] > '9') || (b[5] < '0') || (b[5] > '9') - || (b[6] < '0') || (b[6] > '9') || (b[7] != '.') - || (b[8] < '0') || (b[8] > '9') || (b[9] < '0') || (b[9] > '9') - || (b[10] < '0') || (b[10] > '9') || (b[11] != '\n')) - { - throw new Exception("Host " + host + " port " + port + - " is not an RFB server"); - } - - serverMajor = (b[4] - '0') * 100 + (b[5] - '0') * 10 + (b[6] - '0'); - serverMinor = (b[8] - '0') * 100 + (b[9] - '0') * 10 + (b[10] - '0'); - - if (serverMajor < 3) { - throw new Exception("RFB server does not support protocol version 3"); - } - } - - - // - // Write our protocol version message - // - - void writeVersionMsg() throws IOException { - clientMajor = 3; - if (serverMajor > 3 || serverMinor >= 8) { - clientMinor = 8; - os.write(versionMsg_3_8.getBytes()); - } else if (serverMinor >= 7) { - clientMinor = 7; - os.write(versionMsg_3_7.getBytes()); - } else { - clientMinor = 3; - os.write(versionMsg_3_3.getBytes()); - } - } - - - // - // Negotiate the authentication scheme. - // - - int negotiateSecurity() throws Exception { - return (clientMinor >= 7) ? - selectSecurityType() : readSecurityType(); - } - - // - // Read security type from the server (protocol version 3.3). - // - - int readSecurityType() throws Exception { - int secType = readU32(); - - switch (secType) { - case SecTypeInvalid: - readConnFailedReason(); - return SecTypeInvalid; // should never be executed - case SecTypeNone: - case SecTypeVncAuth: - return secType; - default: - throw new Exception("Unknown security type from RFB server: " + secType); - } - } - - // - // Select security type from the server's list (protocol versions 3.7/3.8). - // - - int selectSecurityType() throws Exception { - int secType = SecTypeInvalid; - - // Read the list of secutiry types. - int nSecTypes = readU8(); - if (nSecTypes == 0) { - readConnFailedReason(); - return SecTypeInvalid; // should never be executed - } - byte[] secTypes = new byte[nSecTypes]; - readFully(secTypes); - - // Find first supported security type. - for (int i = 0; i < nSecTypes; i++) { - if (secTypes[i] == SecTypeNone || secTypes[i] == SecTypeVncAuth - || secTypes[i] == SecTypeVeNCrypt) { - secType = secTypes[i]; - break; - } - } - - if (secType == SecTypeInvalid) { - throw new Exception("Server did not offer supported security type"); - } else { - os.write(secType); - } - - return secType; - } - - int authenticateVeNCrypt() throws Exception { - int majorVersion = readU8(); - int minorVersion = readU8(); - int Version = (majorVersion << 8) | minorVersion; - if (Version < 0x0002) { - os.write(0); - os.write(0); - throw new Exception("Server reported an unsupported VeNCrypt version"); - } - os.write(0); - os.write(2); - if (readU8() != 0) - throw new Exception("Server reported it could not support the VeNCrypt version"); - int nSecTypes = readU8(); - int[] secTypes = new int[nSecTypes]; - for(int i = 0; i < nSecTypes; i++) - secTypes[i] = readU32(); - - for(int i = 0; i < nSecTypes; i++) - switch(secTypes[i]) - { - case SecTypeNone: - case SecTypeVncAuth: - case SecTypePlain: - case SecTypeTLSNone: - case SecTypeTLSVnc: - case SecTypeTLSPlain: - case SecTypeX509None: - case SecTypeX509Vnc: - case SecTypeX509Plain: - writeInt(secTypes[i]); - return secTypes[i]; - } - - throw new Exception("No valid VeNCrypt sub-type"); - } - - // - // Perform "no authentication". - // - - void authenticateNone() throws Exception { - if (clientMinor >= 8) - readSecurityResult("No authentication"); - } - - // - // Perform standard VNC Authentication. - // - - void authenticateVNC(String pw) throws Exception { - byte[] challenge = new byte[16]; - readFully(challenge); - - if (pw.length() > 8) - pw = pw.substring(0, 8); // Truncate to 8 chars - - // Truncate password on the first zero byte. - int firstZero = pw.indexOf(0); - if (firstZero != -1) - pw = pw.substring(0, firstZero); - - byte[] key = {0, 0, 0, 0, 0, 0, 0, 0}; - System.arraycopy(pw.getBytes(), 0, key, 0, pw.length()); - - DesCipher des = new DesCipher(key); - - des.encrypt(challenge, 0, challenge, 0); - des.encrypt(challenge, 8, challenge, 8); - - os.write(challenge); - - readSecurityResult("VNC authentication"); - } - - void authenticateTLS() throws Exception { - TLSTunnel tunnel = new TLSTunnel(sock); - tunnel.setup (this); - } - - void authenticateX509() throws Exception { - X509Tunnel tunnel = new X509Tunnel(sock); - tunnel.setup (this); - } - - void authenticatePlain(String User, String Password) throws Exception { - byte[] user=User.getBytes(); - byte[] password=Password.getBytes(); - writeInt(user.length); - writeInt(password.length); - os.write(user); - os.write(password); - - readSecurityResult("Plain authentication"); - } - - // - // Read security result. - // Throws an exception on authentication failure. - // - - void readSecurityResult(String authType) throws Exception { - int securityResult = readU32(); - - switch (securityResult) { - case VncAuthOK: - System.out.println(authType + ": success"); - break; - case VncAuthFailed: - if (clientMinor >= 8) - readConnFailedReason(); - throw new Exception(authType + ": failed"); - case VncAuthTooMany: - throw new Exception(authType + ": failed, too many tries"); - default: - throw new Exception(authType + ": unknown result " + securityResult); - } - } - - // - // Read the string describing the reason for a connection failure, - // and throw an exception. - // - - void readConnFailedReason() throws Exception { - int reasonLen = readU32(); - byte[] reason = new byte[reasonLen]; - readFully(reason); - throw new Exception(new String(reason)); - } - - // - // Write a 32-bit integer into the output stream. - // - - void writeInt(int value) throws IOException { - byte[] b = new byte[4]; - b[0] = (byte) ((value >> 24) & 0xff); - b[1] = (byte) ((value >> 16) & 0xff); - b[2] = (byte) ((value >> 8) & 0xff); - b[3] = (byte) (value & 0xff); - os.write(b); - } - - // - // Write the client initialisation message - // - - void writeClientInit() throws IOException { - if (viewer.options.shareDesktop) { - os.write(1); - } else { - os.write(0); - } - viewer.options.disableShareDesktop(); - } - - - // - // Read the server initialisation message - // - - String desktopName; - int framebufferWidth, framebufferHeight; - int bitsPerPixel, depth; - boolean bigEndian, trueColour; - int redMax, greenMax, blueMax, redShift, greenShift, blueShift; - - void readServerInit() throws IOException { - framebufferWidth = readU16(); - framebufferHeight = readU16(); - bitsPerPixel = readU8(); - depth = readU8(); - bigEndian = (readU8() != 0); - trueColour = (readU8() != 0); - redMax = readU16(); - greenMax = readU16(); - blueMax = readU16(); - redShift = readU8(); - greenShift = readU8(); - blueShift = readU8(); - byte[] pad = new byte[3]; - readFully(pad); - int nameLength = readU32(); - byte[] name = new byte[nameLength]; - readFully(name); - desktopName = new String(name); - inNormalProtocol = true; - } - - - // - // Create session file and write initial protocol messages into it. - // - - void startSession(String fname) throws IOException { - rec = new SessionRecorder(fname); - rec.writeHeader(); - rec.write(versionMsg_3_3.getBytes()); - rec.writeIntBE(SecTypeNone); - rec.writeShortBE(framebufferWidth); - rec.writeShortBE(framebufferHeight); - byte[] fbsServerInitMsg = { - 32, 24, 0, 1, 0, - (byte)0xFF, 0, (byte)0xFF, 0, (byte)0xFF, - 16, 8, 0, 0, 0, 0 - }; - rec.write(fbsServerInitMsg); - rec.writeIntBE(desktopName.length()); - rec.write(desktopName.getBytes()); - numUpdatesInSession = 0; - - // FIXME: If there were e.g. ZRLE updates only, that should not - // affect recording of Zlib and Tight updates. So, actually - // we should maintain separate flags for Zlib, ZRLE and - // Tight, instead of one ``wereZlibUpdates'' variable. - // - - zlibWarningShown = false; - tightWarningShown = false; - } - - // - // Close session file. - // - - void closeSession() throws IOException { - if (rec != null) { - rec.close(); - rec = null; - } - } - - - // - // Set new framebuffer size - // - - void setFramebufferSize(int width, int height) { - framebufferWidth = width; - framebufferHeight = height; - } - - - // - // Read the server message type - // - - int readServerMessageType() throws IOException { - int msgType = readU8(); - - // If the session is being recorded: - if (rec != null) { - if (msgType == Bell) { // Save Bell messages in session files. - rec.writeByte(msgType); - if (numUpdatesInSession > 0) - rec.flush(); - } - } - - return msgType; - } - - - // - // Read a FramebufferUpdate message - // - - int updateNRects; - - void readFramebufferUpdate() throws IOException { - skipBytes(1); - updateNRects = readU16(); - - // If the session is being recorded: - if (rec != null) { - rec.writeByte(FramebufferUpdate); - rec.writeByte(0); - rec.writeShortBE(updateNRects); - } - - numUpdatesInSession++; - } - - // - // Returns true if encoding is not pseudo - // - // FIXME: Find better way to differ pseudo and real encodings - // - - boolean isRealDecoderEncoding(int encoding) { - if ((encoding >= 1) && (encoding <= 16)) { - return true; - } - return false; - } - - // Read a FramebufferUpdate rectangle header - - int updateRectX, updateRectY, updateRectW, updateRectH, updateRectEncoding; - - void readFramebufferUpdateRectHdr() throws Exception { - updateRectX = readU16(); - updateRectY = readU16(); - updateRectW = readU16(); - updateRectH = readU16(); - updateRectEncoding = readU32(); - - if (updateRectEncoding == EncodingZlib || - updateRectEncoding == EncodingZRLE || - updateRectEncoding == EncodingTight) - wereZlibUpdates = true; - - // If the session is being recorded: - if (rec != null) { - if (numUpdatesInSession > 1) - rec.flush(); // Flush the output on each rectangle. - rec.writeShortBE(updateRectX); - rec.writeShortBE(updateRectY); - rec.writeShortBE(updateRectW); - rec.writeShortBE(updateRectH); - - // - // If this is pseudo encoding or CopyRect that write encoding ID - // in this place. All real encoding ID will be written to record stream - // in decoder classes. - - if (((!isRealDecoderEncoding(updateRectEncoding))) && (rec != null)) { - rec.writeIntBE(updateRectEncoding); - } - } - - if (updateRectEncoding < 0 || updateRectEncoding > MaxNormalEncoding) - return; - - if (updateRectX + updateRectW > framebufferWidth || - updateRectY + updateRectH > framebufferHeight) { - throw new Exception("Framebuffer update rectangle too large: " + - updateRectW + "x" + updateRectH + " at (" + - updateRectX + "," + updateRectY + ")"); - } - } - - // - // Read a ServerCutText message - // - - String readServerCutText() throws IOException { - skipBytes(3); - int len = readU32(); - byte[] text = new byte[len]; - readFully(text); - return new String(text); - } - - - // - // Read an integer in compact representation (1..3 bytes). - // Such format is used as a part of the Tight encoding. - // Also, this method records data if session recording is active and - // the viewer's recordFromBeginning variable is set to true. - // - - int readCompactLen() throws IOException { - int[] portion = new int[3]; - portion[0] = readU8(); - int byteCount = 1; - int len = portion[0] & 0x7F; - if ((portion[0] & 0x80) != 0) { - portion[1] = readU8(); - byteCount++; - len |= (portion[1] & 0x7F) << 7; - if ((portion[1] & 0x80) != 0) { - portion[2] = readU8(); - byteCount++; - len |= (portion[2] & 0xFF) << 14; - } - } - - return len; - } - - - // - // Write a FramebufferUpdateRequest message - // - - void writeFramebufferUpdateRequest(int x, int y, int w, int h, - boolean incremental) - throws IOException - { - byte[] b = new byte[10]; - - b[0] = (byte) FramebufferUpdateRequest; - b[1] = (byte) (incremental ? 1 : 0); - b[2] = (byte) ((x >> 8) & 0xff); - b[3] = (byte) (x & 0xff); - b[4] = (byte) ((y >> 8) & 0xff); - b[5] = (byte) (y & 0xff); - b[6] = (byte) ((w >> 8) & 0xff); - b[7] = (byte) (w & 0xff); - b[8] = (byte) ((h >> 8) & 0xff); - b[9] = (byte) (h & 0xff); - - os.write(b); - } - - - // - // Write a SetPixelFormat message - // - - void writeSetPixelFormat(int bitsPerPixel, int depth, boolean bigEndian, - boolean trueColour, - int redMax, int greenMax, int blueMax, - int redShift, int greenShift, int blueShift) - throws IOException - { - byte[] b = new byte[20]; - - b[0] = (byte) SetPixelFormat; - b[4] = (byte) bitsPerPixel; - b[5] = (byte) depth; - b[6] = (byte) (bigEndian ? 1 : 0); - b[7] = (byte) (trueColour ? 1 : 0); - b[8] = (byte) ((redMax >> 8) & 0xff); - b[9] = (byte) (redMax & 0xff); - b[10] = (byte) ((greenMax >> 8) & 0xff); - b[11] = (byte) (greenMax & 0xff); - b[12] = (byte) ((blueMax >> 8) & 0xff); - b[13] = (byte) (blueMax & 0xff); - b[14] = (byte) redShift; - b[15] = (byte) greenShift; - b[16] = (byte) blueShift; - - os.write(b); - } - - - // - // Write a FixColourMapEntries message. The values in the red, green and - // blue arrays are from 0 to 65535. - // - - void writeFixColourMapEntries(int firstColour, int nColours, - int[] red, int[] green, int[] blue) - throws IOException - { - byte[] b = new byte[6 + nColours * 6]; - - b[0] = (byte) FixColourMapEntries; - b[2] = (byte) ((firstColour >> 8) & 0xff); - b[3] = (byte) (firstColour & 0xff); - b[4] = (byte) ((nColours >> 8) & 0xff); - b[5] = (byte) (nColours & 0xff); - - for (int i = 0; i < nColours; i++) { - b[6 + i * 6] = (byte) ((red[i] >> 8) & 0xff); - b[6 + i * 6 + 1] = (byte) (red[i] & 0xff); - b[6 + i * 6 + 2] = (byte) ((green[i] >> 8) & 0xff); - b[6 + i * 6 + 3] = (byte) (green[i] & 0xff); - b[6 + i * 6 + 4] = (byte) ((blue[i] >> 8) & 0xff); - b[6 + i * 6 + 5] = (byte) (blue[i] & 0xff); - } - - os.write(b); - } - - - // - // Write a SetEncodings message - // - - void writeSetEncodings(int[] encs, int len) throws IOException { - byte[] b = new byte[4 + 4 * len]; - - b[0] = (byte) SetEncodings; - b[2] = (byte) ((len >> 8) & 0xff); - b[3] = (byte) (len & 0xff); - - for (int i = 0; i < len; i++) { - b[4 + 4 * i] = (byte) ((encs[i] >> 24) & 0xff); - b[5 + 4 * i] = (byte) ((encs[i] >> 16) & 0xff); - b[6 + 4 * i] = (byte) ((encs[i] >> 8) & 0xff); - b[7 + 4 * i] = (byte) (encs[i] & 0xff); - } - - os.write(b); - } - - - // - // Write a ClientCutText message - // - - void writeClientCutText(String text) throws IOException { - byte[] b = new byte[8 + text.length()]; - - b[0] = (byte) ClientCutText; - b[4] = (byte) ((text.length() >> 24) & 0xff); - b[5] = (byte) ((text.length() >> 16) & 0xff); - b[6] = (byte) ((text.length() >> 8) & 0xff); - b[7] = (byte) (text.length() & 0xff); - - System.arraycopy(text.getBytes(), 0, b, 8, text.length()); - - os.write(b); - } - - - // - // A buffer for putting pointer and keyboard events before being sent. This - // is to ensure that multiple RFB events generated from a single Java Event - // will all be sent in a single network packet. The maximum possible - // length is 4 modifier down events, a single key event followed by 4 - // modifier up events i.e. 9 key events or 72 bytes. - // - - byte[] eventBuf = new byte[72]; - int eventBufLen; - - - // Useful shortcuts for modifier masks. - - final static int CTRL_MASK = InputEvent.CTRL_MASK; - final static int SHIFT_MASK = InputEvent.SHIFT_MASK; - final static int META_MASK = InputEvent.META_MASK; - final static int ALT_MASK = InputEvent.ALT_MASK; - - - // - // Write a pointer event message. We may need to send modifier key events - // around it to set the correct modifier state. - // - - int pointerMask = 0; - - void writePointerEvent(MouseEvent evt) throws IOException { - int modifiers = evt.getModifiers(); - - int mask2 = 2; - int mask3 = 4; - if (viewer.options.reverseMouseButtons2And3) { - mask2 = 4; - mask3 = 2; - } - - // Note: For some reason, AWT does not set BUTTON1_MASK on left - // button presses. Here we think that it was the left button if - // modifiers do not include BUTTON2_MASK or BUTTON3_MASK. - - if (evt.getID() == MouseEvent.MOUSE_PRESSED) { - if ((modifiers & InputEvent.BUTTON2_MASK) != 0) { - pointerMask = mask2; - modifiers &= ~ALT_MASK; - } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) { - pointerMask = mask3; - modifiers &= ~META_MASK; - } else { - pointerMask = 1; - } - } else if (evt.getID() == MouseEvent.MOUSE_RELEASED) { - pointerMask = 0; - if ((modifiers & InputEvent.BUTTON2_MASK) != 0) { - modifiers &= ~ALT_MASK; - } else if ((modifiers & InputEvent.BUTTON3_MASK) != 0) { - modifiers &= ~META_MASK; - } - } - - eventBufLen = 0; - writeModifierKeyEvents(modifiers); - - int x = evt.getX(); - int y = evt.getY(); - - if (x < 0) x = 0; - if (y < 0) y = 0; - - eventBuf[eventBufLen++] = (byte) PointerEvent; - eventBuf[eventBufLen++] = (byte) pointerMask; - eventBuf[eventBufLen++] = (byte) ((x >> 8) & 0xff); - eventBuf[eventBufLen++] = (byte) (x & 0xff); - eventBuf[eventBufLen++] = (byte) ((y >> 8) & 0xff); - eventBuf[eventBufLen++] = (byte) (y & 0xff); - - // - // Always release all modifiers after an "up" event - // - - if (pointerMask == 0) { - writeModifierKeyEvents(0); - } - - os.write(eventBuf, 0, eventBufLen); - } - - - // - // Write a key event message. We may need to send modifier key events - // around it to set the correct modifier state. Also we need to translate - // from the Java key values to the X keysym values used by the RFB protocol. - // - - void writeKeyEvent(KeyEvent evt) throws IOException { - - int keyChar = evt.getKeyChar(); - - // - // Ignore event if only modifiers were pressed. - // - - // Some JVMs return 0 instead of CHAR_UNDEFINED in getKeyChar(). - if (keyChar == 0) - keyChar = KeyEvent.CHAR_UNDEFINED; - - if (keyChar == KeyEvent.CHAR_UNDEFINED) { - int code = evt.getKeyCode(); - if (code == KeyEvent.VK_CONTROL || code == KeyEvent.VK_SHIFT || - code == KeyEvent.VK_META || code == KeyEvent.VK_ALT) - return; - } - - // - // Key press or key release? - // - - boolean down = (evt.getID() == KeyEvent.KEY_PRESSED); - - int key; - if (evt.isActionKey()) { - - // - // An action key should be one of the following. - // If not then just ignore the event. - // - - switch(evt.getKeyCode()) { - case KeyEvent.VK_HOME: key = 0xff50; break; - case KeyEvent.VK_LEFT: key = 0xff51; break; - case KeyEvent.VK_UP: key = 0xff52; break; - case KeyEvent.VK_RIGHT: key = 0xff53; break; - case KeyEvent.VK_DOWN: key = 0xff54; break; - case KeyEvent.VK_PAGE_UP: key = 0xff55; break; - case KeyEvent.VK_PAGE_DOWN: key = 0xff56; break; - case KeyEvent.VK_END: key = 0xff57; break; - case KeyEvent.VK_INSERT: key = 0xff63; break; - case KeyEvent.VK_F1: key = 0xffbe; break; - case KeyEvent.VK_F2: key = 0xffbf; break; - case KeyEvent.VK_F3: key = 0xffc0; break; - case KeyEvent.VK_F4: key = 0xffc1; break; - case KeyEvent.VK_F5: key = 0xffc2; break; - case KeyEvent.VK_F6: key = 0xffc3; break; - case KeyEvent.VK_F7: key = 0xffc4; break; - case KeyEvent.VK_F8: key = 0xffc5; break; - case KeyEvent.VK_F9: key = 0xffc6; break; - case KeyEvent.VK_F10: key = 0xffc7; break; - case KeyEvent.VK_F11: key = 0xffc8; break; - case KeyEvent.VK_F12: key = 0xffc9; break; - default: - return; - } - - } else { - - // - // A "normal" key press. Ordinary ASCII characters go straight through. - // For CTRL-<letter>, CTRL is sent separately so just send <letter>. - // Backspace, tab, return, escape and delete have special keysyms. - // Anything else we ignore. - // - - key = keyChar; - - if (key < 0x20) { - if (evt.isControlDown()) { - key += 0x60; - } else { - switch(key) { - case KeyEvent.VK_BACK_SPACE: key = 0xff08; break; - case KeyEvent.VK_TAB: key = 0xff09; break; - case KeyEvent.VK_ENTER: key = 0xff0d; break; - case KeyEvent.VK_ESCAPE: key = 0xff1b; break; - } - } - } else if (key == 0x7f) { - // Delete - key = 0xffff; - } else if (key > 0xff) { - // JDK1.1 on X incorrectly passes some keysyms straight through, - // so we do too. JDK1.1.4 seems to have fixed this. - // The keysyms passed are 0xff00 .. XK_BackSpace .. XK_Delete - // Also, we pass through foreign currency keysyms (0x20a0..0x20af). - if ((key < 0xff00 || key > 0xffff) && - !(key >= 0x20a0 && key <= 0x20af)) - return; - } - } - - // Fake keyPresses for keys that only generates keyRelease events - if ((key == 0xe5) || (key == 0xc5) || // XK_aring / XK_Aring - (key == 0xe4) || (key == 0xc4) || // XK_adiaeresis / XK_Adiaeresis - (key == 0xf6) || (key == 0xd6) || // XK_odiaeresis / XK_Odiaeresis - (key == 0xa7) || (key == 0xbd) || // XK_section / XK_onehalf - (key == 0xa3)) { // XK_sterling - // Make sure we do not send keypress events twice on platforms - // with correct JVMs (those that actually report KeyPress for all - // keys) - if (down) - brokenKeyPressed = true; - - if (!down && !brokenKeyPressed) { - // We've got a release event for this key, but haven't received - // a press. Fake it. - eventBufLen = 0; - writeModifierKeyEvents(evt.getModifiers()); - writeKeyEvent(key, true); - os.write(eventBuf, 0, eventBufLen); - } - - if (!down) - brokenKeyPressed = false; - } - - eventBufLen = 0; - writeModifierKeyEvents(evt.getModifiers()); - writeKeyEvent(key, down); - - // Always release all modifiers after an "up" event - if (!down) - writeModifierKeyEvents(0); - - os.write(eventBuf, 0, eventBufLen); - } - - - // - // Add a raw key event with the given X keysym to eventBuf. - // - - void writeKeyEvent(int keysym, boolean down) { - eventBuf[eventBufLen++] = (byte) KeyboardEvent; - eventBuf[eventBufLen++] = (byte) (down ? 1 : 0); - eventBuf[eventBufLen++] = (byte) 0; - eventBuf[eventBufLen++] = (byte) 0; - eventBuf[eventBufLen++] = (byte) ((keysym >> 24) & 0xff); - eventBuf[eventBufLen++] = (byte) ((keysym >> 16) & 0xff); - eventBuf[eventBufLen++] = (byte) ((keysym >> 8) & 0xff); - eventBuf[eventBufLen++] = (byte) (keysym & 0xff); - } - - - // - // Write key events to set the correct modifier state. - // - - int oldModifiers = 0; - - void writeModifierKeyEvents(int newModifiers) { - if ((newModifiers & CTRL_MASK) != (oldModifiers & CTRL_MASK)) - writeKeyEvent(0xffe3, (newModifiers & CTRL_MASK) != 0); - - if ((newModifiers & SHIFT_MASK) != (oldModifiers & SHIFT_MASK)) - writeKeyEvent(0xffe1, (newModifiers & SHIFT_MASK) != 0); - - if ((newModifiers & META_MASK) != (oldModifiers & META_MASK)) - writeKeyEvent(0xffe7, (newModifiers & META_MASK) != 0); - - if ((newModifiers & ALT_MASK) != (oldModifiers & ALT_MASK)) - writeKeyEvent(0xffe9, (newModifiers & ALT_MASK) != 0); - - oldModifiers = newModifiers; - } - - - public void startTiming() { - timing = true; - - // Carry over up to 1s worth of previous rate for smoothing. - - if (timeWaitedIn100us > 10000) { - timedKbits = timedKbits * 10000 / timeWaitedIn100us; - timeWaitedIn100us = 10000; - } - } - - public void stopTiming() { - timing = false; - if (timeWaitedIn100us < timedKbits/2) - timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s - } - - public long kbitsPerSecond() { - return timedKbits * 10000 / timeWaitedIn100us; - } - - public long timeWaited() { - return timeWaitedIn100us; - } - - // - // Methods for reading data via our DataInputStream member variable (is). - // - // In addition to reading data, the readFully() methods updates variables - // used to estimate data throughput. - // - - public void readFully(byte b[]) throws IOException { - readFully(b, 0, b.length); - } - - public void readFully(byte b[], int off, int len) throws IOException { - long before = 0; - if (timing) - before = System.currentTimeMillis(); - - is.readFully(b, off, len); - - if (timing) { - long after = System.currentTimeMillis(); - long newTimeWaited = (after - before) * 10; - int newKbits = len * 8 / 1000; - - // limit rate to between 10kbit/s and 40Mbit/s - - if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000; - if (newTimeWaited < newKbits/4) newTimeWaited = newKbits/4; - - timeWaitedIn100us += newTimeWaited; - timedKbits += newKbits; - } - - numBytesRead += len; - } - - final int available() throws IOException { - return is.available(); - } - - // FIXME: DataInputStream::skipBytes() is not guaranteed to skip - // exactly n bytes. Probably we don't want to use this method. - final int skipBytes(int n) throws IOException { - int r = is.skipBytes(n); - numBytesRead += r; - return r; - } - - final int readU8() throws IOException { - int r = is.readUnsignedByte(); - numBytesRead++; - return r; - } - - final int readU16() throws IOException { - int r = is.readUnsignedShort(); - numBytesRead += 2; - return r; - } - - final int readU32() throws IOException { - int r = is.readInt(); - numBytesRead += 4; - return r; - } - - public void setStreams(InputStream is_, OutputStream os_) { - is = new DataInputStream(is_); - os = os_; - } -} diff --git a/java/src/com/tigervnc/vncviewer/ServerDialog.java b/java/src/com/tigervnc/vncviewer/ServerDialog.java new file mode 100644 index 00000000..86160f80 --- /dev/null +++ b/java/src/com/tigervnc/vncviewer/ServerDialog.java @@ -0,0 +1,178 @@ +/* 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();
+ if (s instanceof JComboBox && (JComboBox)s == encryption) {
+ options.encryption=(encryption.getSelectedIndex()==1) ? false : true;
+ }
+ }
+
+ 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) {
+ 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());
+ }
+ try {
+ options.defaults.Save();
+ } catch (java.lang.Exception x) { }
+ }
+ 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/src/com/tigervnc/vncviewer/SessionRecorder.java b/java/src/com/tigervnc/vncviewer/SessionRecorder.java deleted file mode 100644 index 2ae70798..00000000 --- a/java/src/com/tigervnc/vncviewer/SessionRecorder.java +++ /dev/null @@ -1,195 +0,0 @@ -// -// Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -// -// SessionRecorder is a class to write FBS (FrameBuffer Stream) files. -// FBS files are used to save RFB sessions for later playback. -// - -package com.tigervnc.vncviewer; - -import java.io.*; - -class SessionRecorder { - - protected FileOutputStream f; - protected DataOutputStream df; - protected long startTime, lastTimeOffset; - - protected byte[] buffer; - protected int bufferSize; - protected int bufferBytes; - - public SessionRecorder(String name, int bufsize) throws IOException { - f = new FileOutputStream(name); - df = new DataOutputStream(f); - startTime = System.currentTimeMillis(); - lastTimeOffset = 0; - - bufferSize = bufsize; - bufferBytes = 0; - buffer = new byte[bufferSize]; - } - - public SessionRecorder(String name) throws IOException { - this(name, 65536); - } - - // - // Close the file, free resources. - // - - public void close() throws IOException { - try { - flush(); - } catch (IOException e) { - } - - df = null; - f.close(); - f = null; - buffer = null; - } - - // - // Write the FBS file header as defined in the rfbproxy utility. - // - - public void writeHeader() throws IOException { - df.write("FBS 001.000\n".getBytes()); - } - - // - // Write one byte. - // - - public void writeByte(int b) throws IOException { - prepareWriting(); - buffer[bufferBytes++] = (byte)b; - } - - // - // Write 16-bit value, big-endian. - // - - public void writeShortBE(int v) throws IOException { - prepareWriting(); - buffer[bufferBytes++] = (byte)(v >> 8); - buffer[bufferBytes++] = (byte)v; - } - - // - // Write 32-bit value, big-endian. - // - - public void writeIntBE(int v) throws IOException { - prepareWriting(); - buffer[bufferBytes] = (byte)(v >> 24); - buffer[bufferBytes + 1] = (byte)(v >> 16); - buffer[bufferBytes + 2] = (byte)(v >> 8); - buffer[bufferBytes + 3] = (byte)v; - bufferBytes += 4; - } - - // - // Write 16-bit value, little-endian. - // - - public void writeShortLE(int v) throws IOException { - prepareWriting(); - buffer[bufferBytes++] = (byte)v; - buffer[bufferBytes++] = (byte)(v >> 8); - } - - // - // Write 32-bit value, little-endian. - // - - public void writeIntLE(int v) throws IOException { - prepareWriting(); - buffer[bufferBytes] = (byte)v; - buffer[bufferBytes + 1] = (byte)(v >> 8); - buffer[bufferBytes + 2] = (byte)(v >> 16); - buffer[bufferBytes + 3] = (byte)(v >> 24); - bufferBytes += 4; - } - - // - // Write byte arrays. - // - - public void write(byte b[], int off, int len) throws IOException { - prepareWriting(); - while (len > 0) { - if (bufferBytes > bufferSize - 4) - flush(false); - - int partLen; - if (bufferBytes + len > bufferSize) { - partLen = bufferSize - bufferBytes; - } else { - partLen = len; - } - System.arraycopy(b, off, buffer, bufferBytes, partLen); - bufferBytes += partLen; - off += partLen; - len -= partLen; - } - } - - public void write(byte b[]) throws IOException { - write(b, 0, b.length); - } - - // - // Flush the output. This method saves buffered data in the - // underlying file object adding data sizes and timestamps. If the - // updateTimeOffset is set to false, then the current time offset - // will not be changed for next write operation. - // - - public void flush(boolean updateTimeOffset) throws IOException { - if (bufferBytes > 0) { - df.writeInt(bufferBytes); - df.write(buffer, 0, (bufferBytes + 3) & 0x7FFFFFFC); - df.writeInt((int)lastTimeOffset); - bufferBytes = 0; - if (updateTimeOffset) - lastTimeOffset = -1; - } - } - - public void flush() throws IOException { - flush(true); - } - - // - // Before writing any data, remember time offset and flush the - // buffer before it becomes full. - // - - protected void prepareWriting() throws IOException { - if (lastTimeOffset == -1) - lastTimeOffset = System.currentTimeMillis() - startTime; - if (bufferBytes > bufferSize - 4) - flush(false); - } - -} - diff --git a/java/src/com/tigervnc/vncviewer/SocketFactory.java b/java/src/com/tigervnc/vncviewer/SocketFactory.java deleted file mode 100644 index 30460dce..00000000 --- a/java/src/com/tigervnc/vncviewer/SocketFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (C) 2002 HorizonLive.com, Inc. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -// -// SocketFactory.java describes an interface used to substitute the -// standard Socket class by its alternative implementations. -// - -package com.tigervnc.vncviewer; - -import java.applet.*; -import java.net.*; -import java.io.*; - -public interface SocketFactory { - - public Socket createSocket(String host, int port, Applet applet) - throws IOException; - - public Socket createSocket(String host, int port, String[] args) - throws IOException; -} diff --git a/java/src/com/tigervnc/vncviewer/TLSTunnelBase.java b/java/src/com/tigervnc/vncviewer/TLSTunnelBase.java deleted file mode 100644 index 922e8374..00000000 --- a/java/src/com/tigervnc/vncviewer/TLSTunnelBase.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2003 Sun Microsystems, Inc. - * Copyright (C) 2003-2010 Martin Koegler - * - * 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.ArrayList; -import java.net.*; -import javax.net.ssl.*; - -public abstract class TLSTunnelBase -{ - - public TLSTunnelBase (Socket sock_) - { - sock = sock_; - } - - protected void initContext (SSLContext sc) throws java.security. - GeneralSecurityException - { - sc.init (null, null, null); - } - - public void setup (RfbProto cc) throws Exception - { - if (cc.readU8 () == 0) - throw new Exception("Setup on the server failed"); - try - { - SSLSocketFactory sslfactory; - SSLSocket sslsock; - SSLContext sc = SSLContext.getInstance ("TLS"); - System.out.println("Generating TLS context"); - initContext (sc); - System.out.println("Doing TLS handshake"); - sslfactory = sc.getSocketFactory (); - sslsock = (SSLSocket) sslfactory.createSocket (sock, - sock.getInetAddress (). - getHostName (), - sock.getPort (), true); - - setParam (sslsock); - - /* Not neccessary - just ensures that we know what cipher - * suite we are using for the output of toString() - */ - sslsock.startHandshake (); - - System.out.println("TLS done"); - - cc.setStreams (sslsock.getInputStream (), - sslsock.getOutputStream ()); - } - catch (java.io.IOException e) - { - throw new Exception("TLS handshake failed " + e.toString ()); - } - catch (java.security.GeneralSecurityException e) - { - throw new Exception("TLS handshake failed " + e.toString ()); - } - } - - - protected abstract void setParam (SSLSocket sock); - - Socket sock; - -} diff --git a/java/src/com/tigervnc/vncviewer/UserPrefs.java b/java/src/com/tigervnc/vncviewer/UserPrefs.java new file mode 100644 index 00000000..51b8b904 --- /dev/null +++ b/java/src/com/tigervnc/vncviewer/UserPrefs.java @@ -0,0 +1,258 @@ +/* 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 fileseperator 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_; + + systemprops=System.getProperties(); + // This is guaranteed as always being some valid directory, + // according to spec. + prefFile= getHomeDir()+getFileSeperator()+ + "."+appName; + try { + load(new java.io.FileInputStream(prefFile)); + } 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; + 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) { + homeDir = System.getProperty("user.home"); + } else { + homeDir = System.getenv("USERPROFILE"); + } + } else { + homeDir = FileSystemView.getFileSystemView(). + getDefaultDirectory().getCanonicalPath(); + } + } catch (Exception e) { + e.printStackTrace(); + } + return homeDir; + } + + final private String getUserName() { + String userName = (String) System.getProperties().get("user.name"); + return userName; + } + + final public static String getFileSeperator() { + String seperator = System.getProperties().get("file.separator").toString(); + return seperator; + } + + /** + * 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/src/com/tigervnc/vncviewer/VncCanvas.java b/java/src/com/tigervnc/vncviewer/VncCanvas.java deleted file mode 100644 index b7764819..00000000 --- a/java/src/com/tigervnc/vncviewer/VncCanvas.java +++ /dev/null @@ -1,1092 +0,0 @@ -// -// Copyright (C) 2004 Horizon Wimba. All Rights Reserved. -// Copyright (C) 2001-2003 HorizonLive.com, Inc. All Rights Reserved. -// Copyright (C) 2001,2002 Constantin Kaplinsky. All Rights Reserved. -// Copyright (C) 2000 Tridia Corporation. All Rights Reserved. -// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -package com.tigervnc.vncviewer; - -import com.tigervnc.decoder.CoRREDecoder; -import com.tigervnc.decoder.CopyRectDecoder; -import com.tigervnc.decoder.HextileDecoder; -import com.tigervnc.decoder.RREDecoder; -import com.tigervnc.decoder.RawDecoder; -import com.tigervnc.decoder.TightDecoder; -import com.tigervnc.decoder.ZRLEDecoder; -import com.tigervnc.decoder.ZlibDecoder; -import com.tigervnc.decoder.common.Repaintable; -import java.awt.*; -import java.awt.event.*; -import java.awt.image.*; -import java.io.*; -import java.lang.*; -import java.util.zip.*; - - -// -// VncCanvas is a subclass of Canvas which draws a VNC desktop on it. -// - -class VncCanvas extends Canvas - implements KeyListener, MouseListener, MouseMotionListener, Repaintable, Runnable { - - VncViewer viewer; - RfbProto rfb; - ColorModel cm8, cm24; - int bytesPixel; - - int maxWidth = 0, maxHeight = 0; - int scalingFactor; - int scaledWidth, scaledHeight; - - Image memImage; - Graphics memGraphics; - - // - // Decoders - // - - RawDecoder rawDecoder; - RREDecoder rreDecoder; - CoRREDecoder correDecoder; - ZlibDecoder zlibDecoder; - HextileDecoder hextileDecoder; - ZRLEDecoder zrleDecoder; - TightDecoder tightDecoder; - CopyRectDecoder copyRectDecoder; - - // Base decoder decoders array - RawDecoder []decoders = null; - - // Update statistics. - long statStartTime; // time on first framebufferUpdateRequest - long statNumUpdates; // counter for FramebufferUpdate messages - long statNumTotalRects; // rectangles in FramebufferUpdate messages - long statNumPixelRects; // the same, but excluding pseudo-rectangles - long statNumRectsTight; // Tight-encoded rectangles (including JPEG) - long statNumRectsTightJPEG; // JPEG-compressed Tight-encoded rectangles - long statNumRectsZRLE; // ZRLE-encoded rectangles - long statNumRectsHextile; // Hextile-encoded rectangles - long statNumRectsRaw; // Raw-encoded rectangles - long statNumRectsCopy; // CopyRect rectangles - long statNumBytesEncoded; // number of bytes in updates, as received - long statNumBytesDecoded; // number of bytes, as if Raw encoding was used - - // True if we process keyboard and mouse events. - boolean inputEnabled; - - // True if was no one auto resize of canvas - boolean isFirstSizeAutoUpdate = true; - - // Members for limiting sending mouse events to server - long lastMouseEventSendTime = System.currentTimeMillis(); - long mouseMaxFreq = 20; - - // - // The constructors. - // - - public VncCanvas(VncViewer v, int maxWidth_, int maxHeight_) - throws IOException { - - viewer = v; - maxWidth = maxWidth_; - maxHeight = maxHeight_; - - rfb = viewer.rfb; - scalingFactor = viewer.options.scalingFactor; - - cm8 = new DirectColorModel(8, 7, (7 << 3), (3 << 6)); - cm24 = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0x0000FF); - - // - // Create decoders - // - - // Input stream for decoders - RfbInputStream rfbis = new RfbInputStream(rfb); - // Create output stream for session recording - RecordOutputStream ros = new RecordOutputStream(rfb); - - rawDecoder = new RawDecoder(memGraphics, rfbis); - rreDecoder = new RREDecoder(memGraphics, rfbis); - correDecoder = new CoRREDecoder(memGraphics, rfbis); - hextileDecoder = new HextileDecoder(memGraphics, rfbis); - tightDecoder = new TightDecoder(memGraphics, rfbis); - zlibDecoder = new ZlibDecoder(memGraphics, rfbis); - zrleDecoder = new ZRLEDecoder(memGraphics, rfbis); - copyRectDecoder = new CopyRectDecoder(memGraphics, rfbis); - - // - // Set data for decoders that needs extra parameters - // - - hextileDecoder.setRepainableControl(this); - tightDecoder.setRepainableControl(this); - - // - // Create array that contains our decoders - // - - decoders = new RawDecoder[8]; - decoders[0] = rawDecoder; - decoders[1] = rreDecoder; - decoders[2] = correDecoder; - decoders[3] = hextileDecoder; - decoders[4] = zlibDecoder; - decoders[5] = tightDecoder; - decoders[6] = zrleDecoder; - decoders[7] = copyRectDecoder; - - // - // Set session recorder for decoders - // - - for (int i = 0; i < decoders.length; i++) { - decoders[i].setDataOutputStream(ros); - } - - setPixelFormat(); - - inputEnabled = false; - if (!viewer.options.viewOnly) - enableInput(true); - - // Enable mouse and keyboard event listeners. - addKeyListener(this); - addMouseListener(this); - addMouseMotionListener(this); - - // Create thread, that will send mouse movement events - // to VNC server. - Thread mouseThread = new Thread(this); - mouseThread.start(); - } - - public VncCanvas(VncViewer v) throws IOException { - this(v, 0, 0); - } - - // - // Callback methods to determine geometry of our Component. - // - - public Dimension getPreferredSize() { - return new Dimension(scaledWidth, scaledHeight); - } - - public Dimension getMinimumSize() { - return new Dimension(scaledWidth, scaledHeight); - } - - public Dimension getMaximumSize() { - return new Dimension(scaledWidth, scaledHeight); - } - - // - // All painting is performed here. - // - - public void update(Graphics g) { - paint(g); - } - - public void paint(Graphics g) { - synchronized(memImage) { - if (rfb.framebufferWidth == scaledWidth) { - g.drawImage(memImage, 0, 0, null); - } else { - paintScaledFrameBuffer(g); - } - } - if (showSoftCursor) { - int x0 = cursorX - hotX, y0 = cursorY - hotY; - Rectangle r = new Rectangle(x0, y0, cursorWidth, cursorHeight); - if (r.intersects(g.getClipBounds())) { - g.drawImage(softCursor, x0, y0, null); - } - } - } - - public void paintScaledFrameBuffer(Graphics g) { - g.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null); - } - - // - // Start/stop receiving mouse events. Keyboard events are received - // even in view-only mode, because we want to map the 'r' key to the - // screen refreshing function. - // - - public synchronized void enableInput(boolean enable) { - if (enable && !inputEnabled) { - inputEnabled = true; - if (viewer.showControls) { - viewer.buttonPanel.enableRemoteAccessControls(true); - } - createSoftCursor(); // scaled cursor - } else if (!enable && inputEnabled) { - inputEnabled = false; - if (viewer.showControls) { - viewer.buttonPanel.enableRemoteAccessControls(false); - } - createSoftCursor(); // non-scaled cursor - } - } - - public void setPixelFormat() throws IOException { - if (viewer.options.eightBitColors) { - rfb.writeSetPixelFormat(8, 8, false, true, 7, 7, 3, 0, 3, 6); - bytesPixel = 1; - } else { - rfb.writeSetPixelFormat(32, 24, false, true, 255, 255, 255, 16, 8, 0); - bytesPixel = 4; - } - updateFramebufferSize(); - } - - void setScalingFactor(int sf) { - scalingFactor = sf; - updateFramebufferSize(); - invalidate(); - } - - void updateFramebufferSize() { - - // Useful shortcuts. - int fbWidth = rfb.framebufferWidth; - int fbHeight = rfb.framebufferHeight; - - // FIXME: This part of code must be in VncViewer i think - if (viewer.options.autoScale) { - if (viewer.inAnApplet) { - maxWidth = viewer.getWidth(); - maxHeight = viewer.getHeight(); - } else { - if (viewer.vncFrame != null) { - if (isFirstSizeAutoUpdate) { - isFirstSizeAutoUpdate = false; - Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize(); - maxWidth = (int)screenSize.getWidth() - 100; - maxHeight = (int)screenSize.getHeight() - 100; - viewer.vncFrame.setSize(maxWidth, maxHeight); - } else { - viewer.desktopScrollPane.doLayout(); - maxWidth = viewer.desktopScrollPane.getWidth(); - maxHeight = viewer.desktopScrollPane.getHeight(); - } - } else { - maxWidth = fbWidth; - maxHeight = fbHeight; - } - } - int f1 = maxWidth * 100 / fbWidth; - int f2 = maxHeight * 100 / fbHeight; - scalingFactor = Math.min(f1, f2); - if (scalingFactor > 100) - scalingFactor = 100; - System.out.println("Scaling desktop at " + scalingFactor + "%"); - } - - // Update scaled framebuffer geometry. - scaledWidth = (fbWidth * scalingFactor + 50) / 100; - scaledHeight = (fbHeight * scalingFactor + 50) / 100; - - // Create new off-screen image either if it does not exist, or if - // its geometry should be changed. It's not necessary to replace - // existing image if only pixel format should be changed. - if (memImage == null) { - memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); - memGraphics = memImage.getGraphics(); - } else if (memImage.getWidth(null) != fbWidth || - memImage.getHeight(null) != fbHeight) { - synchronized(memImage) { - memImage = viewer.vncContainer.createImage(fbWidth, fbHeight); - memGraphics = memImage.getGraphics(); - } - } - - // - // Update decoders - // - - // - // FIXME: Why decoders can be null here? - // - - if (decoders != null) { - for (int i = 0; i < decoders.length; i++) { - // - // Set changes to every decoder that we can use - // - - decoders[i].setBPP(bytesPixel); - decoders[i].setFrameBufferSize(fbWidth, fbHeight); - decoders[i].setGraphics(memGraphics); - - // - // Update decoder - // - - decoders[i].update(); - } - } - - // FIXME: This part of code must be in VncViewer i think - // Update the size of desktop containers. - if (viewer.inSeparateFrame) { - if (viewer.desktopScrollPane != null) { - if (!viewer.options.autoScale) { - resizeDesktopFrame(); - } else { - setSize(scaledWidth, scaledHeight); - viewer.desktopScrollPane.setSize(maxWidth + 200, - maxHeight + 200); - } - } - } else { - setSize(scaledWidth, scaledHeight); - } - viewer.moveFocusToDesktop(); - } - - void resizeDesktopFrame() { - setSize(scaledWidth, scaledHeight); - - // FIXME: Find a better way to determine correct size of a - // ScrollPane. -- const - Insets insets = viewer.desktopScrollPane.getInsets(); - viewer.desktopScrollPane.setSize(scaledWidth + - 2 * Math.min(insets.left, insets.right), - scaledHeight + - 2 * Math.min(insets.top, insets.bottom)); - - viewer.vncFrame.pack(); - - // Try to limit the frame size to the screen size. - - Dimension screenSize = viewer.vncFrame.getToolkit().getScreenSize(); - Dimension frameSize = viewer.vncFrame.getSize(); - Dimension newSize = frameSize; - - // Reduce Screen Size by 30 pixels in each direction; - // This is a (poor) attempt to account for - // 1) Menu bar on Macintosh (should really also account for - // Dock on OSX). Usually 22px on top of screen. - // 2) Taxkbar on Windows (usually about 28 px on bottom) - // 3) Other obstructions. - - screenSize.height -= 30; - screenSize.width -= 30; - - boolean needToResizeFrame = false; - if (frameSize.height > screenSize.height) { - newSize.height = screenSize.height; - needToResizeFrame = true; - } - if (frameSize.width > screenSize.width) { - newSize.width = screenSize.width; - needToResizeFrame = true; - } - if (needToResizeFrame) { - viewer.vncFrame.setSize(newSize); - } - - viewer.desktopScrollPane.doLayout(); - } - - // - // processNormalProtocol() - executed by the rfbThread to deal with the - // RFB socket. - // - - public void processNormalProtocol() throws Exception { - - // Start/stop session recording if necessary. - viewer.checkRecordingStatus(); - - rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, - rfb.framebufferHeight, false); - - resetStats(); - boolean statsRestarted = false; - - // - // main dispatch loop - // - - while (true) { - - // Read message type from the server. - int msgType = rfb.readServerMessageType(); - - // Process the message depending on its type. - switch (msgType) { - case RfbProto.FramebufferUpdate: - - if (statNumUpdates == viewer.debugStatsExcludeUpdates && - !statsRestarted) { - resetStats(); - statsRestarted = true; - } else if (statNumUpdates == viewer.debugStatsMeasureUpdates && - statsRestarted) { - viewer.disconnect(); - } - - rfb.readFramebufferUpdate(); - statNumUpdates++; - - boolean cursorPosReceived = false; - - for (int i = 0; i < rfb.updateNRects; i++) { - - rfb.readFramebufferUpdateRectHdr(); - statNumTotalRects++; - int rx = rfb.updateRectX, ry = rfb.updateRectY; - int rw = rfb.updateRectW, rh = rfb.updateRectH; - - if (rfb.updateRectEncoding == rfb.EncodingLastRect) - break; - - if (rfb.updateRectEncoding == rfb.EncodingNewFBSize) { - rfb.setFramebufferSize(rw, rh); - updateFramebufferSize(); - break; - } - - if (rfb.updateRectEncoding == rfb.EncodingXCursor || - rfb.updateRectEncoding == rfb.EncodingRichCursor) { - handleCursorShapeUpdate(rfb.updateRectEncoding, rx, ry, rw, rh); - continue; - } - - if (rfb.updateRectEncoding == rfb.EncodingPointerPos) { - softCursorMove(rx, ry); - cursorPosReceived = true; - continue; - } - - long numBytesReadBefore = rfb.getNumBytesRead(); - - rfb.startTiming(); - - switch (rfb.updateRectEncoding) { - case RfbProto.EncodingRaw: - statNumRectsRaw++; - handleRawRect(rx, ry, rw, rh); - break; - case RfbProto.EncodingCopyRect: - statNumRectsCopy++; - handleCopyRect(rx, ry, rw, rh); - break; - case RfbProto.EncodingRRE: - handleRRERect(rx, ry, rw, rh); - break; - case RfbProto.EncodingCoRRE: - handleCoRRERect(rx, ry, rw, rh); - break; - case RfbProto.EncodingHextile: - statNumRectsHextile++; - handleHextileRect(rx, ry, rw, rh); - break; - case RfbProto.EncodingZRLE: - statNumRectsZRLE++; - handleZRLERect(rx, ry, rw, rh); - break; - case RfbProto.EncodingZlib: - handleZlibRect(rx, ry, rw, rh); - break; - case RfbProto.EncodingTight: - if (tightDecoder != null) { - statNumRectsTightJPEG = tightDecoder.getNumJPEGRects(); - //statNumRectsTight = tightDecoder.getNumTightRects(); - } - statNumRectsTight++; - handleTightRect(rx, ry, rw, rh); - break; - default: - throw new Exception("Unknown RFB rectangle encoding " + - rfb.updateRectEncoding); - } - - rfb.stopTiming(); - - statNumPixelRects++; - statNumBytesDecoded += rw * rh * bytesPixel; - statNumBytesEncoded += - (int)(rfb.getNumBytesRead() - numBytesReadBefore); - } - - boolean fullUpdateNeeded = false; - - // Start/stop session recording if necessary. Request full - // update if a new session file was opened. - if (viewer.checkRecordingStatus()) - fullUpdateNeeded = true; - - // Defer framebuffer update request if necessary. But wake up - // immediately on keyboard or mouse event. Also, don't sleep - // if there is some data to receive, or if the last update - // included a PointerPos message. - if (viewer.deferUpdateRequests > 0 && - rfb.available() == 0 && !cursorPosReceived) { - synchronized(rfb) { - try { - rfb.wait(viewer.deferUpdateRequests); - } catch (InterruptedException e) { - } - } - } - - viewer.autoSelectEncodings(); - - // Before requesting framebuffer update, check if the pixel - // format should be changed. - if (viewer.options.eightBitColors != (bytesPixel == 1)) { - // Pixel format should be changed. - setPixelFormat(); - fullUpdateNeeded = true; - } - - // Finally, request framebuffer update if needed. - if (fullUpdateNeeded) { - rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, - rfb.framebufferHeight, false); - } else { - rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, - rfb.framebufferHeight, true); - } - - break; - - case RfbProto.SetColourMapEntries: - throw new Exception("Can't handle SetColourMapEntries message"); - - case RfbProto.Bell: - Toolkit.getDefaultToolkit().beep(); - break; - - case RfbProto.ServerCutText: - String s = rfb.readServerCutText(); - viewer.clipboard.setCutText(s); - break; - - default: - throw new Exception("Unknown RFB message type " + msgType); - } - } - } - - // - // Handle a raw rectangle. The second form with paint==false is used - // by the Hextile decoder for raw-encoded tiles. - // - - void handleRawRect(int x, int y, int w, int h) throws IOException, Exception { - handleRawRect(x, y, w, h, true); - } - - void handleRawRect(int x, int y, int w, int h, boolean paint) - throws IOException , Exception{ - rawDecoder.handleRect(x, y, w, h); - if (paint) - scheduleRepaint(x, y, w, h); - } - - // - // Handle a CopyRect rectangle. - // - - void handleCopyRect(int x, int y, int w, int h) throws IOException { - copyRectDecoder.handleRect(x, y, w, h); - scheduleRepaint(x, y, w, h); - } - - // - // Handle an RRE-encoded rectangle. - // - - void handleRRERect(int x, int y, int w, int h) throws IOException { - rreDecoder.handleRect(x, y, w, h); - scheduleRepaint(x, y, w, h); - } - - // - // Handle a CoRRE-encoded rectangle. - // - - void handleCoRRERect(int x, int y, int w, int h) throws IOException { - correDecoder.handleRect(x, y, w, h); - scheduleRepaint(x, y, w, h); - } - - // - // Handle a Hextile-encoded rectangle. - // - - void handleHextileRect(int x, int y, int w, int h) throws IOException, - Exception { - hextileDecoder.handleRect(x, y, w, h); - } - - // - // Handle a ZRLE-encoded rectangle. - // - // FIXME: Currently, session recording is not fully supported for ZRLE. - // - - void handleZRLERect(int x, int y, int w, int h) throws Exception { - zrleDecoder.handleRect(x, y, w, h); - scheduleRepaint(x, y, w, h); - } - - // - // Handle a Zlib-encoded rectangle. - // - - void handleZlibRect(int x, int y, int w, int h) throws Exception { - zlibDecoder.handleRect(x, y, w, h); - scheduleRepaint(x, y, w, h); - } - - // - // Handle a Tight-encoded rectangle. - // - - void handleTightRect(int x, int y, int w, int h) throws Exception { - tightDecoder.handleRect(x, y, w, h); - scheduleRepaint(x, y, w, h); - } - - // - // Tell JVM to repaint specified desktop area. - // - - public void scheduleRepaint(int x, int y, int w, int h) { - // Request repaint, deferred if necessary. - if (rfb.framebufferWidth == scaledWidth) { - repaint(viewer.deferScreenUpdates, x, y, w, h); - } else { - int sx = x * scalingFactor / 100; - int sy = y * scalingFactor / 100; - int sw = ((x + w) * scalingFactor + 49) / 100 - sx + 1; - int sh = ((y + h) * scalingFactor + 49) / 100 - sy + 1; - repaint(viewer.deferScreenUpdates, sx, sy, sw, sh); - } - } - - // - // Handle events. - // - - public void keyPressed(KeyEvent evt) { - processLocalKeyEvent(evt); - } - public void keyReleased(KeyEvent evt) { - processLocalKeyEvent(evt); - } - public void keyTyped(KeyEvent evt) { - evt.consume(); - } - - public void mousePressed(MouseEvent evt) { - processLocalMouseEvent(evt, false); - } - public void mouseReleased(MouseEvent evt) { - processLocalMouseEvent(evt, false); - } - public void mouseMoved(MouseEvent evt) { - processLocalMouseEvent(evt, true); - } - public void mouseDragged(MouseEvent evt) { - processLocalMouseEvent(evt, true); - } - - private synchronized void trySendPointerEvent() { - if ((needToSendMouseEvent) && (mouseEvent!=null)) { - sendMouseEvent(mouseEvent, false); - needToSendMouseEvent = false; - lastMouseEventSendTime = System.currentTimeMillis(); - } - } - - public void run() { - while (true) { - // Send mouse movement if we have it - trySendPointerEvent(); - // Sleep for some time - try { - Thread.sleep(1000 / mouseMaxFreq); - } catch (InterruptedException ex) { - } - } - } - - // - // Ignored events. - // - - public void mouseClicked(MouseEvent evt) {} - public void mouseEntered(MouseEvent evt) {} - public void mouseExited(MouseEvent evt) {} - - // - // Actual event processing. - // - - private void processLocalKeyEvent(KeyEvent evt) { - if (viewer.rfb != null && rfb.inNormalProtocol) { - if (!inputEnabled) { - if ((evt.getKeyChar() == 'r' || evt.getKeyChar() == 'R') && - evt.getID() == KeyEvent.KEY_PRESSED ) { - // Request screen update. - try { - rfb.writeFramebufferUpdateRequest(0, 0, rfb.framebufferWidth, - rfb.framebufferHeight, false); - } catch (IOException e) { - e.printStackTrace(); - } - } - } else { - // Input enabled. - synchronized(rfb) { - try { - rfb.writeKeyEvent(evt); - } catch (Exception e) { - e.printStackTrace(); - } - rfb.notify(); - } - } - } - // Don't ever pass keyboard events to AWT for default processing. - // Otherwise, pressing Tab would switch focus to ButtonPanel etc. - evt.consume(); - } - - private void processLocalMouseEvent(MouseEvent evt, boolean moved) { - if (viewer.rfb != null && rfb.inNormalProtocol) { - if (inputEnabled) { - // If mouse not moved, but it's click event then - // send it to server immideanlty. - // Else, it's mouse movement - we can send it in - // our thread later. - if (!moved) { - sendMouseEvent(evt, moved); - } else { - mouseEvent = evt; - needToSendMouseEvent = true; - } - } - } - } - - private void sendMouseEvent(MouseEvent evt, boolean moved) { - if (moved) { - softCursorMove(evt.getX(), evt.getY()); - } - if (rfb.framebufferWidth != scaledWidth) { - int sx = (evt.getX() * 100 + scalingFactor/2) / scalingFactor; - int sy = (evt.getY() * 100 + scalingFactor/2) / scalingFactor; - evt.translatePoint(sx - evt.getX(), sy - evt.getY()); - } - synchronized(rfb) { - try { - rfb.writePointerEvent(evt); - } catch (Exception e) { - e.printStackTrace(); - } - rfb.notify(); - lastMouseEventSendTime = System.currentTimeMillis(); - } - } - - // - // Reset update statistics. - // - - void resetStats() { - statStartTime = System.currentTimeMillis(); - statNumUpdates = 0; - statNumTotalRects = 0; - statNumPixelRects = 0; - statNumRectsTight = 0; - statNumRectsTightJPEG = 0; - statNumRectsZRLE = 0; - statNumRectsHextile = 0; - statNumRectsRaw = 0; - statNumRectsCopy = 0; - statNumBytesEncoded = 0; - statNumBytesDecoded = 0; - if (tightDecoder != null) { - tightDecoder.setNumJPEGRects(0); - tightDecoder.setNumTightRects(0); - } - } - - ////////////////////////////////////////////////////////////////// - // - // Handle cursor shape updates (XCursor and RichCursor encodings). - // - - boolean showSoftCursor = false; - - MemoryImageSource softCursorSource; - Image softCursor; - MouseEvent mouseEvent = null; - boolean needToSendMouseEvent = false; - int cursorX = 0, cursorY = 0; - int cursorWidth, cursorHeight; - int origCursorWidth, origCursorHeight; - int hotX, hotY; - int origHotX, origHotY; - - // - // Handle cursor shape update (XCursor and RichCursor encodings). - // - - synchronized void - handleCursorShapeUpdate(int encodingType, - int xhot, int yhot, int width, int height) - throws IOException { - - softCursorFree(); - - if (width * height == 0) - return; - - // Ignore cursor shape data if requested by user. - if (viewer.options.ignoreCursorUpdates) { - int bytesPerRow = (width + 7) / 8; - int bytesMaskData = bytesPerRow * height; - - if (encodingType == rfb.EncodingXCursor) { - rfb.skipBytes(6 + bytesMaskData * 2); - } else { - // rfb.EncodingRichCursor - rfb.skipBytes(width * height + bytesMaskData); - } - return; - } - - // Decode cursor pixel data. - softCursorSource = decodeCursorShape(encodingType, width, height); - - // Set original (non-scaled) cursor dimensions. - origCursorWidth = width; - origCursorHeight = height; - origHotX = xhot; - origHotY = yhot; - - // Create off-screen cursor image. - createSoftCursor(); - - // Show the cursor. - showSoftCursor = true; - repaint(viewer.deferCursorUpdates, - cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight); - } - - // - // decodeCursorShape(). Decode cursor pixel data and return - // corresponding MemoryImageSource instance. - // - - synchronized MemoryImageSource - decodeCursorShape(int encodingType, int width, int height) - throws IOException { - - int bytesPerRow = (width + 7) / 8; - int bytesMaskData = bytesPerRow * height; - - int[] softCursorPixels = new int[width * height]; - - if (encodingType == rfb.EncodingXCursor) { - - // Read foreground and background colors of the cursor. - byte[] rgb = new byte[6]; - rfb.readFully(rgb); - int[] colors = { (0xFF000000 | (rgb[3] & 0xFF) << 16 | - (rgb[4] & 0xFF) << 8 | (rgb[5] & 0xFF)), - (0xFF000000 | (rgb[0] & 0xFF) << 16 | - (rgb[1] & 0xFF) << 8 | (rgb[2] & 0xFF)) }; - - // Read pixel and mask data. - byte[] pixBuf = new byte[bytesMaskData]; - rfb.readFully(pixBuf); - byte[] maskBuf = new byte[bytesMaskData]; - rfb.readFully(maskBuf); - - // Decode pixel data into softCursorPixels[]. - byte pixByte, maskByte; - int x, y, n, result; - int i = 0; - for (y = 0; y < height; y++) { - for (x = 0; x < width / 8; x++) { - pixByte = pixBuf[y * bytesPerRow + x]; - maskByte = maskBuf[y * bytesPerRow + x]; - for (n = 7; n >= 0; n--) { - if ((maskByte >> n & 1) != 0) { - result = colors[pixByte >> n & 1]; - } else { - result = 0; // Transparent pixel - } - softCursorPixels[i++] = result; - } - } - for (n = 7; n >= 8 - width % 8; n--) { - if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { - result = colors[pixBuf[y * bytesPerRow + x] >> n & 1]; - } else { - result = 0; // Transparent pixel - } - softCursorPixels[i++] = result; - } - } - - } else { - // encodingType == rfb.EncodingRichCursor - - // Read pixel and mask data. - byte[] pixBuf = new byte[width * height * bytesPixel]; - rfb.readFully(pixBuf); - byte[] maskBuf = new byte[bytesMaskData]; - rfb.readFully(maskBuf); - - // Decode pixel data into softCursorPixels[]. - byte maskByte; - int x, y, n, result; - int i = 0; - for (y = 0; y < height; y++) { - for (x = 0; x < width / 8; x++) { - maskByte = maskBuf[y * bytesPerRow + x]; - for (n = 7; n >= 0; n--) { - if ((maskByte >> n & 1) != 0) { - if (bytesPixel == 1) { - result = cm8.getRGB(pixBuf[i]); - } else { - result = 0xFF000000 | - (pixBuf[i * 4 + 2] & 0xFF) << 16 | - (pixBuf[i * 4 + 1] & 0xFF) << 8 | - (pixBuf[i * 4] & 0xFF); - } - } else { - result = 0; // Transparent pixel - } - softCursorPixels[i++] = result; - } - } - for (n = 7; n >= 8 - width % 8; n--) { - if ((maskBuf[y * bytesPerRow + x] >> n & 1) != 0) { - if (bytesPixel == 1) { - result = cm8.getRGB(pixBuf[i]); - } else { - result = 0xFF000000 | - (pixBuf[i * 4 + 2] & 0xFF) << 16 | - (pixBuf[i * 4 + 1] & 0xFF) << 8 | - (pixBuf[i * 4] & 0xFF); - } - } else { - result = 0; // Transparent pixel - } - softCursorPixels[i++] = result; - } - } - - } - - return new MemoryImageSource(width, height, softCursorPixels, 0, width); - } - - // - // createSoftCursor(). Assign softCursor new Image (scaled if necessary). - // Uses softCursorSource as a source for new cursor image. - // - - synchronized void - createSoftCursor() { - - if (softCursorSource == null) - return; - - int scaleCursor = viewer.options.scaleCursor; - if (scaleCursor == 0 || !inputEnabled) - scaleCursor = 100; - - // Save original cursor coordinates. - int x = cursorX - hotX; - int y = cursorY - hotY; - int w = cursorWidth; - int h = cursorHeight; - - cursorWidth = (origCursorWidth * scaleCursor + 50) / 100; - cursorHeight = (origCursorHeight * scaleCursor + 50) / 100; - hotX = (origHotX * scaleCursor + 50) / 100; - hotY = (origHotY * scaleCursor + 50) / 100; - softCursor = Toolkit.getDefaultToolkit().createImage(softCursorSource); - - if (scaleCursor != 100) { - softCursor = softCursor.getScaledInstance(cursorWidth, cursorHeight, - Image.SCALE_SMOOTH); - } - - if (showSoftCursor) { - // Compute screen area to update. - x = Math.min(x, cursorX - hotX); - y = Math.min(y, cursorY - hotY); - w = Math.max(w, cursorWidth); - h = Math.max(h, cursorHeight); - - repaint(viewer.deferCursorUpdates, x, y, w, h); - } - } - - // - // softCursorMove(). Moves soft cursor into a particular location. - // - - synchronized void softCursorMove(int x, int y) { - int oldX = cursorX; - int oldY = cursorY; - cursorX = x; - cursorY = y; - if (showSoftCursor) { - repaint(viewer.deferCursorUpdates, - oldX - hotX, oldY - hotY, cursorWidth, cursorHeight); - repaint(viewer.deferCursorUpdates, - cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight); - } - } - - // - // softCursorFree(). Remove soft cursor, dispose resources. - // - - synchronized void softCursorFree() { - if (showSoftCursor) { - showSoftCursor = false; - softCursor = null; - softCursorSource = null; - - repaint(viewer.deferCursorUpdates, - cursorX - hotX, cursorY - hotY, cursorWidth, cursorHeight); - } - } -} diff --git a/java/src/com/tigervnc/vncviewer/VncCanvas2.java b/java/src/com/tigervnc/vncviewer/VncCanvas2.java deleted file mode 100644 index c39d4a52..00000000 --- a/java/src/com/tigervnc/vncviewer/VncCanvas2.java +++ /dev/null @@ -1,65 +0,0 @@ -// -// Copyright (C) 2006 Constantin Kaplinsky. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// - -package com.tigervnc.vncviewer; - -import java.awt.*; -import java.io.*; - -// -// VncCanvas2 is a special version of VncCanvas which may use Java 2 API. -// - -class VncCanvas2 extends VncCanvas { - - public VncCanvas2(VncViewer v) throws IOException { - super(v); - disableFocusTraversalKeys(); - } - - public VncCanvas2(VncViewer v, int maxWidth_, int maxHeight_) - throws IOException { - - super(v, maxWidth_, maxHeight_); - disableFocusTraversalKeys(); - } - - public void paintScaledFrameBuffer(Graphics g) { - Graphics2D g2d = (Graphics2D)g; - g2d.setRenderingHint(RenderingHints.KEY_RENDERING, - RenderingHints.VALUE_RENDER_QUALITY); - g2d.drawImage(memImage, 0, 0, scaledWidth, scaledHeight, null); - } - - // - // Try to disable focus traversal keys (JVMs 1.4 and higher). - // - - private void disableFocusTraversalKeys() { - try { - Class[] argClasses = { Boolean.TYPE }; - java.lang.reflect.Method method = - getClass().getMethod("setFocusTraversalKeysEnabled", argClasses); - Object[] argObjects = { new Boolean(false) }; - method.invoke(this, argObjects); - } catch (Exception e) {} - } - -} - diff --git a/java/src/com/tigervnc/vncviewer/VncViewer.java b/java/src/com/tigervnc/vncviewer/VncViewer.java index 3a9f4c62..ba549186 100644 --- a/java/src/com/tigervnc/vncviewer/VncViewer.java +++ b/java/src/com/tigervnc/vncviewer/VncViewer.java @@ -1,1106 +1,268 @@ -// -// Copyright (C) 2001-2004 HorizonLive.com, Inc. All Rights Reserved. -// Copyright (C) 2002 Constantin Kaplinsky. All Rights Reserved. -// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. -// -// This is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This software is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this software; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, -// USA. -// +/* 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.java - the VNC viewer applet. This class mainly just sets up the -// user interface, leaving it to the VncCanvas to do the actual rendering of -// a VNC desktop. +// 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.event.*; -import java.io.*; -import java.net.*; - -public class VncViewer extends java.applet.Applet - implements java.lang.Runnable, WindowListener, ComponentListener { - - boolean inAnApplet = true; - boolean inSeparateFrame = false; - - // - // main() is called when run as a java program from the command line. - // It simply runs the applet inside a newly-created frame. - // +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.image.*; +import java.awt.Label; +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 version = "1.0.90"; + public static final String about1 = "TigerVNC Viewer for Java "+version; + public static final String about2 = "Copyright (C) 1998-2010 "+ + "[many holders]"; + public static final String about3 = "Visit www.tigervnc.org "+ + "for information on TigerVNC."; public static void main(String[] argv) { - VncViewer v = new VncViewer(); - v.mainArgs = argv; - v.inAnApplet = false; - v.inSeparateFrame = true; - - v.init(); - v.start(); - } - - String[] mainArgs; - - RfbProto rfb; - Thread rfbThread; - - Frame vncFrame; - Container vncContainer; - ScrollPane desktopScrollPane; - GridBagLayout gridbag; - ButtonPanel buttonPanel; - Label connStatusLabel; - VncCanvas vc; - OptionsFrame options; - ClipboardFrame clipboard; - RecordingFrame rec; - - // Control session recording. - Object recordingSync; - String sessionFileName; - boolean recordingActive; - boolean recordingStatusChanged; - String cursorUpdatesDef; - String eightBitColorsDef; - - // Variables read from parameter values. - String socketFactory; - String host; - int port; - String passwordParam; - boolean showControls; - boolean offerRelogin; - boolean showOfflineDesktop; - int deferScreenUpdates; - int deferCursorUpdates; - int deferUpdateRequests; - int debugStatsExcludeUpdates; - int debugStatsMeasureUpdates; - - // Reference to this applet for inter-applet communication. - public static java.applet.Applet refApplet; - - // - // init() - // - - public void init() { - - readParameters(); - - refApplet = this; - - if (inSeparateFrame) { - vncFrame = new Frame("TigerVNC"); - if (!inAnApplet) { - vncFrame.add("Center", this); - } - vncContainer = vncFrame; - } else { - vncContainer = this; - } - - recordingSync = new Object(); - - options = new OptionsFrame(this); - clipboard = new ClipboardFrame(this); - if (RecordingFrame.checkSecurity()) - rec = new RecordingFrame(this); - - sessionFileName = null; - recordingActive = false; - recordingStatusChanged = false; - cursorUpdatesDef = null; - eightBitColorsDef = null; - - if (inSeparateFrame) { - vncFrame.addWindowListener(this); - vncFrame.addComponentListener(this); - } - - rfbThread = new Thread(this); - rfbThread.start(); - } - - public void update(Graphics g) { - } - - // - // run() - executed by the rfbThread to deal with the RFB socket. - // - - public void run() { - - gridbag = new GridBagLayout(); - vncContainer.setLayout(gridbag); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbc.anchor = GridBagConstraints.NORTHWEST; - - if (showControls) { - buttonPanel = new ButtonPanel(this); - gridbag.setConstraints(buttonPanel, gbc); - vncContainer.add(buttonPanel); - } - try { - connectAndAuthenticate(); - doProtocolInitialisation(); - - // FIXME: Use auto-scaling not only in a separate frame. - if (options.autoScale && inSeparateFrame) { - Dimension screenSize; - try { - screenSize = vncContainer.getToolkit().getScreenSize(); - } catch (Exception e) { - screenSize = new Dimension(0, 0); - } - createCanvas(screenSize.width - 32, screenSize.height - 32); + String os = System.getProperty("os.name"); + if (os.startsWith("Windows")) { + String laf = "com.sun.java.swing.plaf.windows.WindowsLookAndFeel"; + UIManager.setLookAndFeel(laf); } else { - createCanvas(0, 0); - } - - gbc.weightx = 1.0; - gbc.weighty = 1.0; - - if (inSeparateFrame) { - - // Create a panel which itself is resizeable and can hold - // non-resizeable VncCanvas component at the top left corner. - Panel canvasPanel = new Panel(); - canvasPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0)); - canvasPanel.add(vc); - - // Create a ScrollPane which will hold a panel with VncCanvas - // inside. - desktopScrollPane = new ScrollPane(ScrollPane.SCROLLBARS_AS_NEEDED); - gbc.fill = GridBagConstraints.BOTH; - gridbag.setConstraints(desktopScrollPane, gbc); - desktopScrollPane.add(canvasPanel); - // If auto scale is not enabled we don't need to set first frame - // size to fullscreen - if (!options.autoScale) { - vc.isFirstSizeAutoUpdate = false; + 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); } - - // Finally, add our ScrollPane to the Frame window. - vncFrame.add(desktopScrollPane); - vncFrame.setTitle(rfb.desktopName); - vncFrame.pack(); - vc.resizeDesktopFrame(); - - } else { - // Just add the VncCanvas component to the Applet. - gridbag.setConstraints(vc, gbc); - add(vc); - validate(); - } - - if (showControls) { - buttonPanel.enableButtons(); - } - - moveFocusToDesktop(); - processNormalProtocol(); - - } catch (NoRouteToHostException e) { - fatalError("Network error: no route to server: " + host, e); - } catch (UnknownHostException e) { - fatalError("Network error: server name unknown: " + host, e); - } catch (ConnectException e) { - fatalError("Network error: could not connect to server: " + - host + ":" + port, e); - } catch (EOFException e) { - if (showOfflineDesktop) { - e.printStackTrace(); - System.out.println("Network error: remote side closed connection"); - if (vc != null) { - vc.enableInput(false); - } - if (inSeparateFrame) { - vncFrame.setTitle(rfb.desktopName + " [disconnected]"); - } - if (rfb != null && !rfb.closed()) - rfb.close(); - if (showControls && buttonPanel != null) { - buttonPanel.disableButtonsOnDisconnect(); - if (inSeparateFrame) { - vncFrame.pack(); - } else { - validate(); - } - } - } else { - fatalError("Network error: remote side closed connection", e); - } - } catch (IOException e) { - String str = e.getMessage(); - if (str != null && str.length() != 0) { - fatalError("Network Error: " + str, e); - } else { - fatalError(e.toString(), e); - } - } catch (Exception e) { - String str = e.getMessage(); - if (str != null && str.length() != 0) { - fatalError("Error: " + str, e); - } else { - fatalError(e.toString(), e); - } - } - - } - - // - // Create a VncCanvas instance. - // - - void createCanvas(int maxWidth, int maxHeight) throws IOException { - // Determine if Java 2D API is available and use a special - // version of VncCanvas if it is present. - vc = null; - try { - // This throws ClassNotFoundException if there is no Java 2D API. - Class cl = Class.forName("java.awt.Graphics2D"); - // If we could load Graphics2D class, then we can use VncCanvas2D. - cl = Class.forName("com.tigervnc.vncviewer.VncCanvas2"); - Class[] argClasses = { this.getClass(), Integer.TYPE, Integer.TYPE }; - java.lang.reflect.Constructor cstr = cl.getConstructor(argClasses); - Object[] argObjects = - { this, new Integer(maxWidth), new Integer(maxHeight) }; - vc = (VncCanvas)cstr.newInstance(argObjects); - } catch (Exception e) { - System.out.println("Warning: Java 2D API is not available"); - } - - // If we failed to create VncCanvas2D, use old VncCanvas. - if (vc == null) - vc = new VncCanvas(this, maxWidth, maxHeight); - } - - - // - // Process RFB socket messages. - // If the rfbThread is being stopped, ignore any exceptions, - // otherwise rethrow the exception so it can be handled. - // - - void processNormalProtocol() throws Exception { - try { - vc.processNormalProtocol(); - } catch (Exception e) { - if (rfbThread == null) { - System.out.println("Ignoring RFB socket exceptions" + - " because applet is stopping"); - } else { - throw e; - } - } - } - - - // - // Connect to the RFB server and authenticate the user. - // - - void connectAndAuthenticate() throws Exception - { - showConnectionStatus("Initializing..."); - if (inSeparateFrame) { - vncFrame.pack(); - vncFrame.show(); - } else { - validate(); - } - - showConnectionStatus("Connecting to " + host + ", port " + port + "..."); - - rfb = new RfbProto(host, port, this); - showConnectionStatus("Connected to server"); - - rfb.readVersionMsg(); - showConnectionStatus("RFB server supports protocol version " + - rfb.serverMajor + "." + rfb.serverMinor); - - rfb.writeVersionMsg(); - showConnectionStatus("Using RFB protocol version " + - rfb.clientMajor + "." + rfb.clientMinor); - - int secType = rfb.negotiateSecurity(); - doAuthentification(secType); - } - - void doAuthentification(int secType) throws Exception { - switch (secType) { - case RfbProto.SecTypeNone: - showConnectionStatus("No authentication needed"); - rfb.authenticateNone(); - break; - case RfbProto.SecTypeVncAuth: - showConnectionStatus("Performing standard VNC authentication"); - if (passwordParam != null) { - rfb.authenticateVNC(passwordParam); - } else { - String pw = askPassword(); - rfb.authenticateVNC(pw); - } - break; - case RfbProto.SecTypeVeNCrypt: - showConnectionStatus("VeNCrypt chooser"); - secType = rfb.authenticateVeNCrypt(); - doAuthentification(secType); - break; - case RfbProto.SecTypePlain: - showConnectionStatus("Plain authentication"); - { - String user = askUser(); - String pw = askPassword(); - rfb.authenticatePlain(user,pw); - } - break; - case RfbProto.SecTypeTLSNone: - showConnectionStatus("TLSNone"); - rfb.authenticateTLS(); - rfb.authenticateNone(); - break; - case RfbProto.SecTypeTLSVnc: - showConnectionStatus("TLSVnc"); - rfb.authenticateTLS(); - doAuthentification(RfbProto.SecTypeVncAuth); - break; - case RfbProto.SecTypeTLSPlain: - showConnectionStatus("TLSPlain"); - rfb.authenticateTLS(); - doAuthentification(RfbProto.SecTypePlain); - break; - case RfbProto.SecTypeX509None: - showConnectionStatus("X509None"); - rfb.authenticateX509(); - rfb.authenticateNone(); - break; - case RfbProto.SecTypeX509Vnc: - showConnectionStatus("X509Vnc"); - rfb.authenticateX509(); - doAuthentification(RfbProto.SecTypeVncAuth); - break; - case RfbProto.SecTypeX509Plain: - showConnectionStatus("X509Plain"); - rfb.authenticateX509(); - doAuthentification(RfbProto.SecTypePlain); - break; - default: - throw new Exception("Unknown authentication scheme " + secType); - } - } - - - // - // Show a message describing the connection status. - // To hide the connection status label, use (msg == null). - // - - void showConnectionStatus(String msg) - { - if (msg == null) { - if (vncContainer.isAncestorOf(connStatusLabel)) { - vncContainer.remove(connStatusLabel); } - return; - } - - System.out.println(msg); - - if (connStatusLabel == null) { - connStatusLabel = new Label("Status: " + msg); - connStatusLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); - } else { - connStatusLabel.setText("Status: " + msg); - } - - if (!vncContainer.isAncestorOf(connStatusLabel)) { - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.anchor = GridBagConstraints.NORTHWEST; - gbc.weightx = 1.0; - gbc.weighty = 1.0; - gbc.insets = new Insets(20, 30, 20, 30); - gridbag.setConstraints(connStatusLabel, gbc); - vncContainer.add(connStatusLabel); - } - - if (inSeparateFrame) { - vncFrame.pack(); - } else { - validate(); - } + UIManager.put("TitledBorder.titleColor",Color.blue); + } catch (java.lang.Exception exc) { } + VncViewer viewer = new VncViewer(argv); + viewer.start(); } - - // - // Show an authentication panel. - // - - String askUser() throws Exception - { - showConnectionStatus(null); - - AuthPanel authPanel = new AuthPanel(this, false); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbc.anchor = GridBagConstraints.NORTHWEST; - gbc.weightx = 1.0; - gbc.weighty = 1.0; - gbc.ipadx = 100; - gbc.ipady = 50; - gridbag.setConstraints(authPanel, gbc); - vncContainer.add(authPanel); - - if (inSeparateFrame) { - vncFrame.pack(); - } else { - validate(); - } - - authPanel.moveFocusToDefaultField(); - String pw = authPanel.getPassword(); - vncContainer.remove(authPanel); - - return pw; - } - - String askPassword() throws Exception - { - showConnectionStatus(null); - - AuthPanel authPanel = new AuthPanel(this, true); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbc.anchor = GridBagConstraints.NORTHWEST; - gbc.weightx = 1.0; - gbc.weighty = 1.0; - gbc.ipadx = 100; - gbc.ipady = 50; - gridbag.setConstraints(authPanel, gbc); - vncContainer.add(authPanel); - - if (inSeparateFrame) { - vncFrame.pack(); - } else { - validate(); - } - - authPanel.moveFocusToDefaultField(); - String pw = authPanel.getPassword(); - vncContainer.remove(authPanel); - - return pw; - } - - - // - // Do the rest of the protocol initialisation. - // - - void doProtocolInitialisation() throws IOException - { - rfb.writeClientInit(); - rfb.readServerInit(); - - System.out.println("Desktop name is " + rfb.desktopName); - System.out.println("Desktop size is " + rfb.framebufferWidth + " x " + - rfb.framebufferHeight); - - setEncodings(); - - showConnectionStatus(null); - } - - - // - // Send current encoding list to the RFB server. - // - - int[] encodingsSaved; - int nEncodingsSaved; - - void setEncodings() { setEncodings(false); } - void autoSelectEncodings() { setEncodings(true); } - - void setEncodings(boolean autoSelectOnly) { - if (options == null || rfb == null || !rfb.inNormalProtocol) - return; - - int preferredEncoding = options.preferredEncoding; - if (preferredEncoding == -1) { - long kbitsPerSecond = rfb.kbitsPerSecond(); - if (nEncodingsSaved < 1) { - // Choose Tight or ZRLE encoding for the very first update. - System.out.println("Using Tight/ZRLE encodings"); - preferredEncoding = RfbProto.EncodingTight; - } else if (kbitsPerSecond > 2000 && - encodingsSaved[0] != RfbProto.EncodingHextile) { - // Switch to Hextile if the connection speed is above 2Mbps. - System.out.println("Throughput " + kbitsPerSecond + - " kbit/s - changing to Hextile encoding"); - preferredEncoding = RfbProto.EncodingHextile; - } else if (kbitsPerSecond < 1000 && - encodingsSaved[0] != RfbProto.EncodingTight) { - // Switch to Tight/ZRLE if the connection speed is below 1Mbps. - System.out.println("Throughput " + kbitsPerSecond + - " kbit/s - changing to Tight/ZRLE encodings"); - preferredEncoding = RfbProto.EncodingTight; - } else { - // Don't change the encoder. - if (autoSelectOnly) - return; - preferredEncoding = encodingsSaved[0]; + + 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; } - } else { - // Auto encoder selection is not enabled. - if (autoSelectOnly) - return; - } - int[] encodings = new int[20]; - int nEncodings = 0; + if (Configuration.setParam(argv[i])) + continue; - encodings[nEncodings++] = preferredEncoding; - if (options.useCopyRect) { - encodings[nEncodings++] = RfbProto.EncodingCopyRect; - } - - if (preferredEncoding != RfbProto.EncodingTight) { - encodings[nEncodings++] = RfbProto.EncodingTight; - } - if (preferredEncoding != RfbProto.EncodingZRLE) { - encodings[nEncodings++] = RfbProto.EncodingZRLE; - } - if (preferredEncoding != RfbProto.EncodingHextile) { - encodings[nEncodings++] = RfbProto.EncodingHextile; - } - if (preferredEncoding != RfbProto.EncodingZlib) { - encodings[nEncodings++] = RfbProto.EncodingZlib; - } - if (preferredEncoding != RfbProto.EncodingCoRRE) { - encodings[nEncodings++] = RfbProto.EncodingCoRRE; - } - if (preferredEncoding != RfbProto.EncodingRRE) { - encodings[nEncodings++] = RfbProto.EncodingRRE; - } - - if (options.compressLevel >= 0 && options.compressLevel <= 9) { - encodings[nEncodings++] = - RfbProto.EncodingCompressLevel0 + options.compressLevel; - } - if (options.jpegQuality >= 0 && options.jpegQuality <= 9) { - encodings[nEncodings++] = - RfbProto.EncodingQualityLevel0 + options.jpegQuality; - } - - if (options.requestCursorUpdates) { - encodings[nEncodings++] = RfbProto.EncodingXCursor; - encodings[nEncodings++] = RfbProto.EncodingRichCursor; - if (!options.ignoreCursorUpdates) - encodings[nEncodings++] = RfbProto.EncodingPointerPos; - } - - encodings[nEncodings++] = RfbProto.EncodingLastRect; - encodings[nEncodings++] = RfbProto.EncodingNewFBSize; - - boolean encodingsWereChanged = false; - if (nEncodings != nEncodingsSaved) { - encodingsWereChanged = true; - } else { - for (int i = 0; i < nEncodings; i++) { - if (encodings[i] != encodingsSaved[i]) { - encodingsWereChanged = true; - break; + if (argv[i].charAt(0) == '-') { + if (i+1 < argv.length) { + if (Configuration.setParam(argv[i].substring(1), argv[i+1])) { + i++; + continue; + } } + usage(); } - } - if (encodingsWereChanged) { - try { - rfb.writeSetEncodings(encodings, nEncodings); - if (vc != null) { - vc.softCursorFree(); - } - } catch (Exception e) { - e.printStackTrace(); - } - encodingsSaved = encodings; - nEncodingsSaved = nEncodings; + if (vncServerName.getValue() != null) + usage(); + vncServerName.setParam(argv[i]); } } - - // - // setCutText() - send the given cut text to the RFB server. - // - - void setCutText(String text) { - try { - if (rfb != null && rfb.inNormalProtocol) { - rfb.writeClientCutText(text); - } - } catch (Exception e) { - e.printStackTrace(); - } + 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); } - - // - // Order change in session recording status. To stop recording, pass - // null in place of the fname argument. - // - - void setRecordingStatus(String fname) { - synchronized(recordingSync) { - sessionFileName = fname; - recordingStatusChanged = true; - } + public VncViewer() { + applet = true; + firstApplet = true; } - // - // Start or stop session recording. Returns true if this method call - // causes recording of a new session. - // - - boolean checkRecordingStatus() throws IOException { - synchronized(recordingSync) { - if (recordingStatusChanged) { - recordingStatusChanged = false; - if (sessionFileName != null) { - startRecording(); - return true; - } else { - stopRecording(); - } - } - } - return false; + public static void newViewer(VncViewer oldViewer) { + VncViewer viewer = new VncViewer(); + viewer.applet = oldViewer.applet; + viewer.firstApplet = false; + viewer.start(); } - // - // Start session recording. - // - - protected void startRecording() throws IOException { - synchronized(recordingSync) { - if (!recordingActive) { - // Save settings to restore them after recording the session. - cursorUpdatesDef = - options.choices[options.cursorUpdatesIndex].getSelectedItem(); - eightBitColorsDef = - options.choices[options.eightBitColorsIndex].getSelectedItem(); - // Set options to values suitable for recording. - options.choices[options.cursorUpdatesIndex].select("Disable"); - options.choices[options.cursorUpdatesIndex].setEnabled(false); - options.setEncodings(); - options.choices[options.eightBitColorsIndex].select("No"); - options.choices[options.eightBitColorsIndex].setEnabled(false); - options.setColorFormat(); - } else { - rfb.closeSession(); - } - - System.out.println("Recording the session in " + sessionFileName); - rfb.startSession(sessionFileName); - recordingActive = true; - } - } - - // - // Stop session recording. - // - - protected void stopRecording() throws IOException { - synchronized(recordingSync) { - if (recordingActive) { - // Restore options. - options.choices[options.cursorUpdatesIndex].select(cursorUpdatesDef); - options.choices[options.cursorUpdatesIndex].setEnabled(true); - options.setEncodings(); - options.choices[options.eightBitColorsIndex].select(eightBitColorsDef); - options.choices[options.eightBitColorsIndex].setEnabled(true); - options.setColorFormat(); - - rfb.closeSession(); - System.out.println("Session recording stopped."); - } - sessionFileName = null; - recordingActive = false; - } - } - - - // - // readParameters() - read parameters from the html source or from the - // command line. On the command line, the arguments are just a sequence of - // param_name/param_value pairs where the names and values correspond to - // those expected in the html applet tag source. - // - - void readParameters() { - host = readParameter("HOST", !inAnApplet); - if (host == null) { - host = getCodeBase().getHost(); - if (host.equals("")) { - fatalError("HOST parameter not specified"); - } - } - - port = readIntParameter("PORT", 5900); - - // Read "ENCPASSWORD" or "PASSWORD" parameter if specified. - readPasswordParameters(); - - String str; - if (inAnApplet) { - str = readParameter("Open New Window", false); - if (str != null && str.equalsIgnoreCase("Yes")) - inSeparateFrame = true; - } - - // "Show Controls" set to "No" disables button panel. - showControls = true; - str = readParameter("Show Controls", false); - if (str != null && str.equalsIgnoreCase("No")) - showControls = false; - - // "Offer Relogin" set to "No" disables "Login again" and "Close - // window" buttons under error messages in applet mode. - offerRelogin = true; - str = readParameter("Offer Relogin", false); - if (str != null && str.equalsIgnoreCase("No")) - offerRelogin = false; - - // Do we continue showing desktop on remote disconnect? - showOfflineDesktop = false; - str = readParameter("Show Offline Desktop", false); - if (str != null && str.equalsIgnoreCase("Yes")) - showOfflineDesktop = true; - - // Fine tuning options. - deferScreenUpdates = readIntParameter("Defer screen updates", 20); - deferCursorUpdates = readIntParameter("Defer cursor updates", 10); - deferUpdateRequests = readIntParameter("Defer update requests", 0); - - // Debugging options. - debugStatsExcludeUpdates = readIntParameter("DEBUG_XU", 0); - debugStatsMeasureUpdates = readIntParameter("DEBUG_CU", 0); - - // SocketFactory. - socketFactory = readParameter("SocketFactory", false); - } - - // - // Read password parameters. If an "ENCPASSWORD" parameter is set, - // then decrypt the password into the passwordParam string. Otherwise, - // try to read the "PASSWORD" parameter directly to passwordParam. - // - - private void readPasswordParameters() { - String encPasswordParam = readParameter("ENCPASSWORD", false); - if (encPasswordParam == null) { - passwordParam = readParameter("PASSWORD", false); - } else { - // ENCPASSWORD is hexascii-encoded. Decode. - byte[] pw = {0, 0, 0, 0, 0, 0, 0, 0}; - int len = encPasswordParam.length() / 2; - if (len > 8) - len = 8; - for (int i = 0; i < len; i++) { - String hex = encPasswordParam.substring(i*2, i*2+2); - Integer x = new Integer(Integer.parseInt(hex, 16)); - pw[i] = x.byteValue(); - } - // Decrypt the password. - byte[] key = {23, 82, 107, 6, 35, 78, 88, 7}; - DesCipher des = new DesCipher(key); - des.decrypt(pw, 0, pw, 0); - passwordParam = new String(pw); - } - } - - public String readParameter(String name, boolean required) { - if (inAnApplet) { - String s = getParameter(name); - if ((s == null) && required) { - fatalError(name + " parameter not specified"); - } - return s; - } - for (int i = 0; i < mainArgs.length; i += 2) { - if (mainArgs[i].equalsIgnoreCase(name)) { - try { - return mainArgs[i+1]; - } catch (Exception e) { - if (required) { - fatalError(name + " parameter not specified"); - } - return null; - } - } - } - if (required) { - fatalError(name + " parameter not specified"); - } - return null; - } - - int readIntParameter(String name, int defaultValue) { - String str = readParameter(name, false); - int result = defaultValue; - if (str != null) { - try { - result = Integer.parseInt(str); - } catch (NumberFormatException e) { } - } - return result; - } - - // - // moveFocusToDesktop() - move keyboard focus either to VncCanvas. - // - - void moveFocusToDesktop() { - if (vncContainer != null) { - if (vc != null && vncContainer.isAncestorOf(vc)) - vc.requestFocus(); - } + public void init() { + vlog.debug("init called"); + setBackground(Color.white); + logo = getImage(getDocumentBase(), "logo150x150.gif"); } - // - // disconnect() - close connection to server. - // - - synchronized public void disconnect() { - System.out.println("Disconnecting"); - - if (vc != null) { - double sec = (System.currentTimeMillis() - vc.statStartTime) / 1000.0; - double rate = Math.round(vc.statNumUpdates / sec * 100) / 100.0; - long nRealRects = vc.statNumPixelRects; - long nPseudoRects = vc.statNumTotalRects - vc.statNumPixelRects; - System.out.println("Updates received: " + vc.statNumUpdates + " (" + - nRealRects + " rectangles + " + nPseudoRects + - " pseudo), " + rate + " updates/sec"); - long numRectsOther = nRealRects - vc.statNumRectsTight - - vc.statNumRectsZRLE - vc.statNumRectsHextile - - vc.statNumRectsRaw - vc.statNumRectsCopy; - System.out.println("Rectangles:" + - " Tight=" + vc.statNumRectsTight + - "(JPEG=" + vc.statNumRectsTightJPEG + - ") ZRLE=" + vc.statNumRectsZRLE + - " Hextile=" + vc.statNumRectsHextile + - " Raw=" + vc.statNumRectsRaw + - " CopyRect=" + vc.statNumRectsCopy + - " other=" + numRectsOther); - - long raw = vc.statNumBytesDecoded; - long compressed = vc.statNumBytesEncoded; - if (compressed > 0) { - double ratio = Math.round((double)raw / compressed * 1000) / 1000.0; - System.out.println("Pixel data: " + vc.statNumBytesDecoded + - " bytes, " + vc.statNumBytesEncoded + - " compressed, ratio " + ratio); + public void start() { + vlog.debug("start called"); + 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))); } } - - if (rfb != null && !rfb.closed()) - rfb.close(); - options.dispose(); - clipboard.dispose(); - if (rec != null) - rec.dispose(); - - if (inAnApplet) { - showMessage("Disconnected"); - } else { - System.exit(0); - } - } - - // - // fatalError() - print out a fatal error message. - // FIXME: Do we really need two versions of the fatalError() method? - // - - synchronized public void fatalError(String str) { - System.out.println(str); - - if (inAnApplet) { - // vncContainer null, applet not inited, - // can not present the error to the user. - Thread.currentThread().stop(); - } else { - System.exit(1); - } - } - - synchronized public void fatalError(String str, Exception e) { - - if (rfb != null && rfb.closed()) { - // Not necessary to show error message if the error was caused - // by I/O problems after the rfb.close() method call. - System.out.println("RFB thread finished"); - return; - } - - System.out.println(str); - e.printStackTrace(); - - if (rfb != null) - rfb.close(); - - if (inAnApplet) { - showMessage(str); - } else { - System.exit(1); - } - } - - // - // Show message text and optionally "Relogin" and "Close" buttons. - // - - void showMessage(String msg) { - vncContainer.removeAll(); - - Label errLabel = new Label(msg, Label.CENTER); - errLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); - - if (offerRelogin) { - - Panel gridPanel = new Panel(new GridLayout(0, 1)); - Panel outerPanel = new Panel(new FlowLayout(FlowLayout.LEFT)); - outerPanel.add(gridPanel); - vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 16)); - vncContainer.add(outerPanel); - Panel textPanel = new Panel(new FlowLayout(FlowLayout.CENTER)); - textPanel.add(errLabel); - gridPanel.add(textPanel); - gridPanel.add(new ReloginPanel(this)); - - } else { - - vncContainer.setLayout(new FlowLayout(FlowLayout.LEFT, 30, 30)); - vncContainer.add(errLabel); - - } - - if (inSeparateFrame) { - vncFrame.pack(); - } else { - validate(); - } + thread = new Thread(this); + thread.start(); } - // - // Stop the applet. - // Main applet thread will terminate on first exception - // after seeing that rfbThread has been set to null. - // - - public void stop() { - System.out.println("Stopping applet"); - rfbThread = null; - } - - // - // This method is called before the applet is destroyed. - // - - public void destroy() { - System.out.println("Destroying applet"); - - vncContainer.removeAll(); - options.dispose(); - clipboard.dispose(); - if (rec != null) - rec.dispose(); - if (rfb != null && !rfb.closed()) - rfb.close(); - if (inSeparateFrame) - vncFrame.dispose(); - } - - // - // Start/stop receiving mouse events. - // - - public void enableInput(boolean enable) { - vc.enableInput(enable); - } - - // - // Resize framebuffer if autoScale is enabled. - // - - public void componentResized(ComponentEvent e) { - if (e.getComponent() == vncFrame) { - if (options.autoScale) { - if (vc != null) { - if (!vc.isFirstSizeAutoUpdate) { - vc.updateFramebufferSize(); - } - } + 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); } } - } - - // - // Ignore component events we're not interested in. - // - - public void componentShown(ComponentEvent e) { } - public void componentMoved(ComponentEvent e) { } - public void componentHidden(ComponentEvent e) { } - - // - // Close application properly on window close event. - // - - public void windowClosing(WindowEvent evt) { - System.out.println("Closing window"); - if (rfb != null) - disconnect(); - - vncContainer.hide(); - - if (!inAnApplet) { + if (cc != null) cc.deleteWindow(); + nViewers--; + if (!applet && nViewers == 0) { System.exit(0); } } - // - // Ignore window events we're not interested in. - // - - public void windowActivated(WindowEvent evt) {} - public void windowDeactivated (WindowEvent evt) {} - public void windowOpened(WindowEvent evt) {} - public void windowClosed(WindowEvent evt) {} - public void windowIconified(WindowEvent evt) {} - public void windowDeiconified(WindowEvent evt) {} + 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); + 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 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, 9 = High", + 6); + 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/src/com/tigervnc/vncviewer/X509Tunnel.java b/java/src/com/tigervnc/vncviewer/X509Tunnel.java deleted file mode 100644 index ddc3f82a..00000000 --- a/java/src/com/tigervnc/vncviewer/X509Tunnel.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2003 Sun Microsystems, Inc. - * Copyright (C) 2003-2010 Martin Koegler - * Copyright (C) 2006 OCCAM Financial Technology - * - * 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.*; -import java.net.*; -import javax.net.ssl.*; -import java.security.*; -import java.security.cert.*; - -public class X509Tunnel extends TLSTunnelBase -{ - - public X509Tunnel (Socket sock_) - { - super (sock_); - } - - protected void setParam (SSLSocket sock) - { - String[]supported; - ArrayList enabled = new ArrayList (); - - supported = sock.getSupportedCipherSuites (); - - for (int i = 0; i < supported.length; i++) - if (!supported[i].matches (".*DH_anon.*")) - enabled.add (supported[i]); - - sock.setEnabledCipherSuites ((String[])enabled.toArray (new String[0])); - } - - protected void initContext (SSLContext sc) throws java.security. - GeneralSecurityException - { - TrustManager[] myTM = new TrustManager[] - { - new MyX509TrustManager ()}; - sc.init (null, myTM, null); - } - - - class MyX509TrustManager implements X509TrustManager - { - - X509TrustManager tm; - - MyX509TrustManager () throws java.security.GeneralSecurityException - { - TrustManagerFactory tmf = - TrustManagerFactory.getInstance ("SunX509", "SunJSSE"); - KeyStore ks = KeyStore.getInstance ("JKS"); - tmf.init (ks); - 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) - { - MessageBox m = - new MessageBox (e.toString (), MessageBox.MB_OKAYCANCEL); - if (!m.result ()) - throw e; - } - } - - public X509Certificate[] getAcceptedIssuers () - { - return tm.getAcceptedIssuers (); - } - } -} diff --git a/java/src/com/tigervnc/vncviewer/tigervnc.ico b/java/src/com/tigervnc/vncviewer/tigervnc.ico Binary files differnew file mode 100644 index 00000000..50b90f00 --- /dev/null +++ b/java/src/com/tigervnc/vncviewer/tigervnc.ico diff --git a/java/src/com/tigervnc/vncviewer/tigervnc.png b/java/src/com/tigervnc/vncviewer/tigervnc.png Binary files differnew file mode 100644 index 00000000..8ac883ef --- /dev/null +++ b/java/src/com/tigervnc/vncviewer/tigervnc.png |