Mercurial > hg > Applications > TightVNC_orginal
changeset 52:472a9bcacb21 draft default tip
TightVNC 2.7.1.0
line wrap: on
line diff
--- a/README.txt Tue Jul 03 13:20:49 2012 +0900 +++ b/README.txt Wed Aug 07 19:01:17 2013 +0900 @@ -1,6 +1,6 @@ -TightVNC Java Viewer version 2.5.0 -Copyright (C) 2011 GlavSoft LLC. All rights reserved. +TightVNC Java Viewer version 2.7.2 +Copyright (C) 2011, 2012 GlavSoft LLC. All rights reserved. ====================================================================== This software is distributed under the GNU General Public Licence
--- a/build.gradle Tue Jul 03 13:20:49 2012 +0900 +++ b/build.gradle Wed Aug 07 19:01:17 2013 +0900 @@ -3,15 +3,16 @@ sourceCompatibility = 1.6 targetCompatibility = 1.6 -version = '2.5.0' +version = '2.7.2' -baseName = 'tightvnc-jviewer' +project.ext.baseName = 'tightvnc-jviewer' +def buildNo = processBuildNo(version) defaultTasks 'clean', 'dist' configurations { - viewerSwingCompile { extendsFrom compile } - viewerSwingRuntime { extendsFrom viewerSwingCompile, runtime } + viewerSwingCompile { extendsFrom compile } + viewerSwingRuntime { extendsFrom viewerSwingCompile, runtime } } sourceSets { @@ -29,46 +30,102 @@ } } +repositories { + flatDir { + dirs 'src/libs/' + } +} + +dependencies { + viewerSwingCompile group: 'com.jcraft', name: 'jsch', version: '0.1.+', ext: 'jar' + viewerSwingRuntime configurations.viewerSwingCompile + testCompile group: 'junit', name: 'junit', version: '4.+' +} + +def manifestAttributes = ['Main-Class': 'com.glavsoft.viewer.Viewer', + 'Implementation-Version': "${project.version} (${buildNo})", + 'Implementation-Title': 'TightVNC Viewer', + 'Implementation-Vendor': 'GlavSoft LLC.'] + jar { - baseName = project.baseName - version = null + baseName = project.baseName + version = null manifest { - attributes 'Main-Class': 'com.glavsoft.viewer.Viewer' - attributes 'Implementation-Version': "${project.version}" + attributes manifestAttributes + } + def runtimeDeps = configurations.viewerSwingRuntime.collect { + it.isDirectory() ? it : zipTree(it) + } + from(runtimeDeps) { + exclude 'META-INF/**' } } -repositories { - mavenCentral() +task noSshJar (type: Jar, dependsOn: classes) { + baseName = 'nossh/' + project.baseName + version = null + manifest { + attributes manifestAttributes + } + from sourceSets.main.output } -dependencies { - testCompile group: 'junit', name: 'junit', version: '4.+' - archives fileTree(dir: 'src/web', include: '*.html') +artifacts { + archives file('src/web/viewer-applet-example.html') + archives noSshJar } uploadArchives { repositories { - add(new org.apache.ivy.plugins.resolver.FileSystemResolver()) { - name = 'repo' - addArtifactPattern "$projectDir/dist/${project.baseName}-${project.version}/${project.baseName}.[ext]" - descriptor = 'optional' - checkmodified = true - } - } + add(new org.apache.ivy.plugins.resolver.FileSystemResolver()) { + addArtifactPattern("$projectDir/dist/${project.baseName}-${project.version}/[artifact].[ext]") + } + } uploadDescriptor = false } -task dist(dependsOn: uploadArchives) << { - otherFilesUpload("$projectDir/dist/${project.baseName}-${project.version}") -} -def otherFilesUpload(repoDir) { - copy { - from 'src/web' - include '*-applet-*.html' - expand (['archive_name' : project.baseName]) - into repoDir - } +task dist(dependsOn: uploadArchives) + +def processBuildNo(currentVersion) { + final String VERSION = 'version' + final String BUILD = 'build' + + def lastVersion = currentVersion + def lastBuild = 0 + def buildNoFile = new File('.build_no') + if ( ! buildNoFile.exists()) { + buildNoFile.createNewFile() + buildNoFile << "${VERSION}=${lastVersion}\n${BUILD}=${lastBuild}" + } + def versions = [:] + buildNoFile.eachLine { + def splitted = it.split('=') + if (splitted.size() == 2) { + def (key, value) = splitted + switch(key.trim()) { + case VERSION: + lastVersion = value.trim() + break + case BUILD: + try { + lastBuild = value != null ? value.trim() as Integer : 0 + } catch (NumberFormatException) {} + versions[lastVersion] = lastBuild + break + } + } + } + lastVersion = versions[currentVersion] + if (null == lastVersion) { + versions[currentVersion] = 0 + } + ++versions[currentVersion] + def outString = '' + versions.each { v, b -> + outString += "${VERSION}=${v}\n${BUILD}=${b}\n\n" + } + buildNoFile.write(outString) + versions[currentVersion] } @@ -385,3 +442,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
--- a/src/main/java/com/glavsoft/core/SettingsChangedEvent.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/core/SettingsChangedEvent.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/drawing/ColorDecoder.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/drawing/ColorDecoder.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -29,14 +29,15 @@ import com.glavsoft.transport.Reader; public class ColorDecoder { - protected byte redShift; + protected byte redShift; protected byte greenShift; protected byte blueShift; public short redMax; public short greenMax; public short blueMax; - private final int bytesPerPixel; - private final int bytesPerPixelSignificant; + public final int bytesPerPixel; + public final int bytesPerCPixel; + public final int bytesPerPixelTight; private final byte[] buff; private int startShift; @@ -44,7 +45,7 @@ private int addShiftItem; private final boolean isTightSpecific; - public ColorDecoder(PixelFormat pf) { + public ColorDecoder(PixelFormat pf) { redShift = pf.redShift; greenShift = pf.greenShift; blueShift = pf.blueShift; @@ -52,7 +53,14 @@ greenMax = pf.greenMax; blueMax = pf.blueMax; bytesPerPixel = pf.bitsPerPixel / 8; - bytesPerPixelSignificant = 24 == pf.depth && 32 == pf.bitsPerPixel ? 3 : bytesPerPixel; + final long significant = redMax << redShift | greenMax << greenShift | blueMax << blueShift; + bytesPerCPixel = pf.depth <= 24 // as in RFB +// || 32 == pf.depth) // UltraVNC use this... :( + && 32 == pf.bitsPerPixel + && ((significant & 0x00ff000000L) == 0 || (significant & 0x000000ffL) == 0) + ? 3 + : bytesPerPixel; + bytesPerPixelTight = 24 == pf.depth && 32 == pf.bitsPerPixel ? 3 : bytesPerPixel; buff = new byte[bytesPerPixel]; if (0 == pf.bigEndianFlag) { startShift = 0; @@ -63,7 +71,7 @@ startShiftCompact = Math.max(0, pf.depth - 8); addShiftItem = -8; } - isTightSpecific = 4==bytesPerPixel && 3==bytesPerPixelSignificant && + isTightSpecific = 4==bytesPerPixel && 3==bytesPerPixelTight && 255 == redMax && 255 == greenMax && 255 == blueMax; } @@ -72,11 +80,11 @@ } protected int readCompactColor(Reader reader) throws TransportException { - return getCompactColor(reader.readBytes(buff, 0, bytesPerPixelSignificant), 0); + return getCompactColor(reader.readBytes(buff, 0, bytesPerCPixel), 0); } protected int readTightColor(Reader reader) throws TransportException { - return getTightColor(reader.readBytes(buff, 0, bytesPerPixelSignificant), 0); + return getTightColor(reader.readBytes(buff, 0, bytesPerPixelTight), 0); } protected int convertColor(int rawColor) { @@ -92,7 +100,7 @@ comp[2] = (byte) (rawColor >> blueShift & blueMax); } - protected int getTightColor(byte[] bytes, int offset) { + public int getTightColor(byte[] bytes, int offset) { return convertColor(getRawTightColor(bytes, offset)); } @@ -100,7 +108,7 @@ if (isTightSpecific) return (bytes[offset++] & 0xff)<<16 | (bytes[offset++] & 0xff)<<8 | - bytes[offset++] & 0xff; + bytes[offset] & 0xff; else return getRawColor(bytes, offset); } @@ -123,7 +131,7 @@ int shift = startShiftCompact; int item = addShiftItem; int rawColor = (bytes[offset++] & 0xff)<<shift; - for (int i=1; i<bytesPerPixelSignificant; ++i) { + for (int i=1; i< bytesPerCPixel; ++i) { rawColor |= (bytes[offset++] & 0xff)<<(shift+=item); } return convertColor(rawColor);
--- a/src/main/java/com/glavsoft/drawing/Renderer.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/drawing/Renderer.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -38,291 +38,310 @@ */ public abstract class Renderer { - protected Reader reader; + protected Reader reader; + private final Object lock = new Object(); - public abstract void drawJpegImage(byte[] bytes, int offset, - int jpegBufferLength, FramebufferUpdateRectangle rect); + public abstract void drawJpegImage(byte[] bytes, int offset, + int jpegBufferLength, FramebufferUpdateRectangle rect); - protected int width; - protected int height; - protected int bytesPerPixel; - protected int bytesPerPixelSignificant; - protected int[] pixels; - protected SoftCursor cursor; - protected PixelFormat pixelFormat; - private ColorDecoder colorDecoder; + protected int width; + protected int height; + protected int[] pixels; + protected SoftCursor cursor; + public PixelFormat pixelFormat; + protected ColorDecoder colorDecoder; - protected void init(Reader reader, int width, int height, PixelFormat pixelFormat) { - this.reader = reader; - this.width = width; - this.height = height; - initPixelFormat(pixelFormat); - pixels = new int[width * height]; - Arrays.fill(pixels, 0); - } + protected void init(Reader reader, int width, int height, PixelFormat pixelFormat) { + this.reader = reader; + this.width = width; + this.height = height; + initPixelFormat(pixelFormat); + pixels = new int[width * height]; + Arrays.fill(pixels, 0); + } - public synchronized void initPixelFormat(PixelFormat pixelFormat) { - this.pixelFormat = pixelFormat; - bytesPerPixel = pixelFormat.bitsPerPixel / 8; - bytesPerPixelSignificant = - 24 == pixelFormat.depth && 32 == pixelFormat.bitsPerPixel ? 3 : bytesPerPixel; - colorDecoder = new ColorDecoder(pixelFormat); - } + public void initPixelFormat(PixelFormat pixelFormat) { + synchronized (lock) { + this.pixelFormat = pixelFormat; + colorDecoder = new ColorDecoder(pixelFormat); + } + } - /** - * Draw byte array bitmap data - * - * @param bytes bitmap data - * @param x bitmap x position - * @param y bitmap y position - * @param width bitmap width - * @param height bitmap height - */ - public void drawBytes(byte[] bytes, int x, int y, int width, int height) { - int i = 0; - for (int ly = y; ly < y + height; ++ly) { - int end = ly * this.width + x + width; - for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) { - pixels[pixelsOffset] = getPixelColor(bytes, i); - i += bytesPerPixel; - } - } - } + /** + * Draw byte array bitmap data + * + * @param bytes bitmap data + * @param x bitmap x position + * @param y bitmap y position + * @param width bitmap width + * @param height bitmap height + */ + public void drawBytes(byte[] bytes, int x, int y, int width, int height) { + int i = 0; + for (int ly = y; ly < y + height; ++ly) { + int end = ly * this.width + x + width; + for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) { + pixels[pixelsOffset] = getPixelColor(bytes, i); + i += colorDecoder.bytesPerPixel; + } + } + } - /** - * Draw byte array bitmap data (for ZRLE) - */ - public synchronized int drawCompactBytes(byte[] bytes, int offset, int x, int y, int width, int height) { - int i = offset; - for (int ly = y; ly < y + height; ++ly) { - int end = ly * this.width + x + width; - for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) { - pixels[pixelsOffset] = getCompactPixelColor(bytes, i); - i += bytesPerPixelSignificant; - } - } - return i - offset; - } + /** + * Draw byte array bitmap data (for ZRLE) + */ + public int drawCompactBytes(byte[] bytes, int offset, int x, int y, int width, int height) { + synchronized (lock) { + int i = offset; + for (int ly = y; ly < y + height; ++ly) { + int end = ly * this.width + x + width; + for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) { + pixels[pixelsOffset] = getCompactPixelColor(bytes, i); + i += colorDecoder.bytesPerCPixel; + } + } + return i - offset; + } + } - /** - * Draw int (colors) array bitmap data (for ZRLE) - */ - public synchronized void drawColoredBitmap(int[] colors, int x, int y, int width, int height) { - int i = 0; - for (int ly = y; ly < y + height; ++ly) { - int end = ly * this.width + x + width; - for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) { - pixels[pixelsOffset] = colors[i++]; - } - } - } + /** + * Draw int (colors) array bitmap data (for ZRLE) + */ + public void drawColoredBitmap(int[] colors, int x, int y, int width, int height) { + synchronized (lock) { + int i = 0; + for (int ly = y; ly < y + height; ++ly) { + int end = ly * this.width + x + width; + for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) { + pixels[pixelsOffset] = colors[i++]; + } + } + } + } - /** - * Draw byte array bitmap data (for Tight) - */ - public synchronized int drawTightBytes(byte[] bytes, int offset, int x, int y, int width, int height) { - int i = offset; - for (int ly = y; ly < y + height; ++ly) { - int end = ly * this.width + x + width; - for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) { - pixels[pixelsOffset] = colorDecoder.getTightColor(bytes, i); - i += bytesPerPixelSignificant; - } - } - return i - offset; - } + /** + * Draw byte array bitmap data (for Tight) + */ + public int drawTightBytes(byte[] bytes, int offset, int x, int y, int width, int height) { + synchronized (lock) { + int i = offset; + for (int ly = y; ly < y + height; ++ly) { + int end = ly * this.width + x + width; + for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) { + pixels[pixelsOffset] = colorDecoder.getTightColor(bytes, i); + i += colorDecoder.bytesPerPixelTight; + } + } + return i - offset; + } + } - /** - * Draw byte array bitmap data (from array with plain RGB color components. Assumed: rrrrrrrr gggggggg bbbbbbbb) - */ - public synchronized void drawUncaliberedRGBLine(byte[] bytes, int x, int y, int width) { - int end = y * this.width + x + width; - for (int i=3, pixelsOffset = y * this.width + x; pixelsOffset < end; ++pixelsOffset) { - pixels[pixelsOffset] = + /** + * Draw byte array bitmap data (from array with plain RGB color components. Assumed: rrrrrrrr gggggggg bbbbbbbb) + */ + public void drawUncaliberedRGBLine(byte[] bytes, int x, int y, int width) { + synchronized (lock) { + int end = y * this.width + x + width; + for (int i = 3, pixelsOffset = y * this.width + x; pixelsOffset < end; ++pixelsOffset) { + pixels[pixelsOffset] = // (0xff & bytes[i++]) << 16 | // (0xff & bytes[i++]) << 8 | // 0xff & bytes[i++]; - (0xff & 255 * (colorDecoder.redMax & bytes[i++]) / colorDecoder.redMax) << 16 | - (0xff & 255 * (colorDecoder.greenMax & bytes[i++]) / colorDecoder.greenMax) << 8 | - 0xff & 255 * (colorDecoder.blueMax & bytes[i++]) / colorDecoder.blueMax; - } - } + (0xff & 255 * (colorDecoder.redMax & bytes[i++]) / colorDecoder.redMax) << 16 | + (0xff & 255 * (colorDecoder.greenMax & bytes[i++]) / colorDecoder.greenMax) << 8 | + 0xff & 255 * (colorDecoder.blueMax & bytes[i++]) / colorDecoder.blueMax; + } + } + } - /** - * Draw paletted byte array bitmap data - * - * @param buffer bitmap data - * @param rect bitmap location and dimensions - * @param palette colour palette - */ - public synchronized void drawBytesWithPalette(byte[] buffer, FramebufferUpdateRectangle rect, - int[] palette) { - // 2 colors - if (palette.length == 2) { - int dx, dy, n; - int i = rect.y * this.width + rect.x; - int rowBytes = (rect.width + 7) / 8; - byte b; + /** + * Draw paletted byte array bitmap data + * + * @param buffer bitmap data + * @param rect bitmap location and dimensions + * @param palette colour palette + * @param paletteSize number of colors in palette + */ + public void drawBytesWithPalette(byte[] buffer, FramebufferUpdateRectangle rect, + int[] palette, int paletteSize) { + synchronized (lock) { + // 2 colors + if (2 == paletteSize) { + int dx, dy, n; + int i = rect.y * this.width + rect.x; + int rowBytes = (rect.width + 7) / 8; + byte b; - for (dy = 0; dy < rect.height; dy++) { - for (dx = 0; dx < rect.width / 8; dx++) { - b = buffer[dy * rowBytes + dx]; - for (n = 7; n >= 0; n--) { - pixels[i++] = palette[b >> n & 1]; - } - } - for (n = 7; n >= 8 - rect.width % 8; n--) { - pixels[i++] = palette[buffer[dy * rowBytes + dx] >> n & 1]; - } - i += this.width- rect.width; - } - } else { - // 3..255 colors (assuming bytesPixel == 4). - int i = 0; - for (int ly = rect.y; ly < rect.y + rect.height; ++ly) { - for (int lx = rect.x; lx < rect.x + rect.width; ++lx) { - int pixelsOffset = ly * this.width + lx; - pixels[pixelsOffset] = palette[buffer[i++] & 0xFF]; - } - } - } - - } + for (dy = 0; dy < rect.height; dy++) { + for (dx = 0; dx < rect.width / 8; dx++) { + b = buffer[dy * rowBytes + dx]; + for (n = 7; n >= 0; n--) { + pixels[i++] = palette[b >> n & 1]; + } + } + for (n = 7; n >= 8 - rect.width % 8; n--) { + pixels[i++] = palette[buffer[dy * rowBytes + dx] >> n & 1]; + } + i += this.width - rect.width; + } + } else { + // 3..255 colors (assuming bytesPixel == 4). + int i = 0; + for (int ly = rect.y; ly < rect.y + rect.height; ++ly) { + for (int lx = rect.x; lx < rect.x + rect.width; ++lx) { + int pixelsOffset = ly * this.width + lx; + pixels[pixelsOffset] = palette[buffer[i++] & 0xFF]; + } + } + } + } + } - /** - * Copy rectangle region from one position to another. Regions may be overlapped. - * - * @param srcX source rectangle x position - * @param srcY source rectangle y position - * @param dstRect destination rectangle - */ - public synchronized void copyRect(int srcX, int srcY, FramebufferUpdateRectangle dstRect) { - int startSrcY, endSrcY, dstY, deltaY; - if (srcY > dstRect.y) { - startSrcY = srcY; - endSrcY = srcY + dstRect.height; - dstY = dstRect.y; - deltaY = +1; - } else { - startSrcY = srcY + dstRect.height - 1; - endSrcY = srcY -1; - dstY = dstRect.y + dstRect.height - 1; - deltaY = -1; - } - for (int y = startSrcY; y != endSrcY; y += deltaY) { - System.arraycopy(pixels, y * width + srcX, - pixels, dstY * width + dstRect.x, dstRect.width); - dstY += deltaY; - } - } + /** + * Copy rectangle region from one position to another. Regions may be overlapped. + * + * @param srcX source rectangle x position + * @param srcY source rectangle y position + * @param dstRect destination rectangle + */ + public void copyRect(int srcX, int srcY, FramebufferUpdateRectangle dstRect) { + int startSrcY, endSrcY, dstY, deltaY; + if (srcY > dstRect.y) { + startSrcY = srcY; + endSrcY = srcY + dstRect.height; + dstY = dstRect.y; + deltaY = +1; + } else { + startSrcY = srcY + dstRect.height - 1; + endSrcY = srcY - 1; + dstY = dstRect.y + dstRect.height - 1; + deltaY = -1; + } + synchronized (lock) { + for (int y = startSrcY; y != endSrcY; y += deltaY) { + System.arraycopy(pixels, y * width + srcX, + pixels, dstY * width + dstRect.x, dstRect.width); + dstY += deltaY; + } + } + } - /** - * Fill rectangle region with specified colour - * - * @param color colour to fill with - * @param rect rectangle region posions and dimensions - */ - public void fillRect(int color, FramebufferUpdateRectangle rect) { - fillRect(color, rect.x, rect.y, rect.width, rect.height); - } + /** + * Fill rectangle region with specified colour + * + * @param color colour to fill with + * @param rect rectangle region positions and dimensions + */ + public void fillRect(int color, FramebufferUpdateRectangle rect) { + fillRect(color, rect.x, rect.y, rect.width, rect.height); + } - /** - * Fill rectangle region with specified colour - * - * @param color colour to fill with - * @param x rectangle x position - * @param y rectangle y position - * @param width rectangle width - * @param height rectangle height - */ - public synchronized void fillRect(int color, int x, int y, int width, int height) { - int sy = y * this.width + x; - int ey = sy + height * this.width; - for (int i = sy; i < ey; i += this.width) { - Arrays.fill(pixels, i, i + width, color); - } - } + /** + * Fill rectangle region with specified colour + * + * @param color colour to fill with + * @param x rectangle x position + * @param y rectangle y position + * @param width rectangle width + * @param height rectangle height + */ + public void fillRect(int color, int x, int y, int width, int height) { + synchronized (lock) { + int sy = y * this.width + x; + int ey = sy + height * this.width; + for (int i = sy; i < ey; i += this.width) { + Arrays.fill(pixels, i, i + width, color); + } + } + } - /** - * Reads color bytes (PIXEL) from reader, returns int combined RGB - * value consisting of the red component in bits 16-23, the green component - * in bits 8-15, and the blue component in bits 0-7. May be used directly for - * creation awt.Color object - */ - public int readPixelColor(Reader reader) throws TransportException { - return colorDecoder.readColor(reader); - } + /** + * Reads color bytes (PIXEL) from reader, returns int combined RGB + * value consisting of the red component in bits 16-23, the green component + * in bits 8-15, and the blue component in bits 0-7. May be used directly for + * creation awt.Color object + */ + public int readPixelColor(Reader reader) throws TransportException { + return colorDecoder.readColor(reader); + } - public int readTightPixelColor(Reader reader) throws TransportException { - return colorDecoder.readTightColor(reader); - } + public int readTightPixelColor(Reader reader) throws TransportException { + return colorDecoder.readTightColor(reader); + } - public ColorDecoder getColorDecoder() { - return colorDecoder; - } + public ColorDecoder getColorDecoder() { + return colorDecoder; + } - public int getCompactPixelColor(byte[] bytes, int offset) { - return colorDecoder.getCompactColor(bytes, offset); - } + public int getCompactPixelColor(byte[] bytes, int offset) { + return colorDecoder.getCompactColor(bytes, offset); + } - public int getPixelColor(byte[] bytes, int offset) { - return colorDecoder.getColor(bytes, offset); - } + public int getPixelColor(byte[] bytes, int offset) { + return colorDecoder.getColor(bytes, offset); + } - public int getBytesPerPixel() { - return bytesPerPixel; - } + public int getBytesPerPixel() { + return colorDecoder.bytesPerPixel; + } + + public int getBytesPerCPixel() { + return colorDecoder.bytesPerCPixel; + } - public int getBytesPerPixelSignificant() { - return bytesPerPixelSignificant; - } + public int getBytesPerPixelTight() { + return colorDecoder.bytesPerPixelTight; + } + + public void fillColorBitmapWithColor(int[] bitmapData, int decodedOffset, int rlength, int color) { + while (rlength-- > 0) { + bitmapData[decodedOffset++] = color; + } + } - public void fillColorBitmapWithColor(int[] bitmapData, int decodedOffset, int rlength, int color) { - while (rlength-- > 0) { - bitmapData[decodedOffset++] = color; - } - } + /** + * Width of rendered image + * + * @return width + */ + public int getWidth() { + return width; + } - /** - * Width of rendered image - * - * @return width - */ - public int getWidth() { - return width; - } + /** + * Height of rendered image + * + * @return height + */ + public int getHeight() { + return height; + } - /** - * Height of rendered image - * - * @return height - */ - public int getHeight() { - return height; - } + /** + * Read and decode cursor image + * + * @param rect new cursor hot point position and cursor dimensions + * @throws TransportException + */ + public void createCursor(int[] cursorPixels, FramebufferUpdateRectangle rect) + throws TransportException { + synchronized (cursor.getLock()) { + cursor.createCursor(cursorPixels, rect.x, rect.y, rect.width, rect.height); + } + } - /** - * Read and decode cursor image - * - * @param rect new cursor hot point position and cursor dimensions - * @throws TransportException - */ - public void createCursor(int[] cursorPixels, FramebufferUpdateRectangle rect) - throws TransportException { - synchronized (cursor) { - cursor.createCursor(cursorPixels, rect.x, rect.y, rect.width, rect.height); - } - } + /** + * Read and decode new cursor position + * + * @param rect cursor position + */ + public void decodeCursorPosition(FramebufferUpdateRectangle rect) { + synchronized (cursor.getLock()) { + cursor.updatePosition(rect.x, rect.y); + } + } - /** - * Read and decode new cursor position - * - * @param rect cursor position - */ - public void decodeCursorPosition(FramebufferUpdateRectangle rect) { - synchronized (cursor) { - cursor.updatePosition(rect.x, rect.y); - } - } - + public Object getLock() { + return lock; + } } \ No newline at end of file
--- a/src/main/java/com/glavsoft/drawing/SoftCursor.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/drawing/SoftCursor.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -36,8 +36,9 @@ public int rX, rY; public int oldRX, oldRY; public int oldWidth, oldHeight; + private final Object lock = new Object(); - public SoftCursor(int hotX, int hotY, int width, int height) { + public SoftCursor(int hotX, int hotY, int width, int height) { this.hotX = hotX; this.hotY = hotY; oldWidth = this.width = width; @@ -85,4 +86,7 @@ protected abstract void createNewCursorImage(int[] cursorPixels, int hotX, int hotY, int width, int height); + public Object getLock() { + return lock; + } } \ No newline at end of file
--- a/src/main/java/com/glavsoft/exceptions/AuthenticationFailedException.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/exceptions/AuthenticationFailedException.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/exceptions/ClosedConnectionException.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/exceptions/ClosedConnectionException.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/exceptions/CommonException.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/exceptions/CommonException.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/exceptions/CryptoException.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/exceptions/CryptoException.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/exceptions/FatalException.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/exceptions/FatalException.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/exceptions/ProtocolException.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/exceptions/ProtocolException.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/exceptions/TransportException.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/exceptions/TransportException.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/exceptions/UnsupportedProtocolVersionException.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/exceptions/UnsupportedProtocolVersionException.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/exceptions/UnsupportedSecurityTypeException.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/exceptions/UnsupportedSecurityTypeException.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/CapabilityContainer.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/CapabilityContainer.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/ClipboardController.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/ClipboardController.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/IChangeSettingsListener.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/IChangeSettingsListener.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/IPasswordRetriever.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/IPasswordRetriever.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/IRepaintController.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/IRepaintController.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/IRfbSessionListener.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/IRfbSessionListener.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/RfbCapabilityInfo.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/RfbCapabilityInfo.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/client/ClientCutTextMessage.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/client/ClientCutTextMessage.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/client/ClientToServerMessage.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/client/ClientToServerMessage.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/client/FramebufferUpdateRequestMessage.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/client/FramebufferUpdateRequestMessage.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/client/KeyEventMessage.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/client/KeyEventMessage.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/client/PointerEventMessage.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/client/PointerEventMessage.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/client/SetEncodingsMessage.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/client/SetEncodingsMessage.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/client/SetPixelFormatMessage.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/client/SetPixelFormatMessage.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/encoding/EncodingType.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/EncodingType.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/encoding/PixelFormat.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/PixelFormat.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -83,7 +83,7 @@ writer.writeByte(0); // padding bytes } - public static PixelFormat create32bppPixelFormat(int bigEndianFlag) { + public static PixelFormat create24bitColorDepthPixelFormat(int bigEndianFlag) { final PixelFormat pixelFormat = new PixelFormat(); pixelFormat.bigEndianFlag = (byte) bigEndianFlag; pixelFormat.bitsPerPixel = 32; @@ -101,7 +101,7 @@ /** * specifies 65536 colors, 5bit per Red, 6bit per Green, 5bit per Blue */ - public static PixelFormat create16bppPixelFormat(int bigEndianFlag) { + public static PixelFormat create16bitColorDepthPixelFormat(int bigEndianFlag) { final PixelFormat pixelFormat = new PixelFormat(); pixelFormat.bigEndianFlag = (byte) bigEndianFlag; pixelFormat.bitsPerPixel = 16; @@ -119,7 +119,7 @@ /** * specifies 256 colors, 2bit per Blue, 3bit per Green & Red */ - public static PixelFormat create8bppBGRPixelFormat(int bigEndianFlag) { + public static PixelFormat create8bitColorDepthBGRPixelFormat(int bigEndianFlag) { final PixelFormat pixelFormat = new PixelFormat(); pixelFormat.bigEndianFlag = (byte) bigEndianFlag; pixelFormat.bitsPerPixel = 8; @@ -137,7 +137,7 @@ /** * specifies 64 colors, 2bit per Red, Green & Blue */ - public static PixelFormat create6bppPixelFormat(int bigEndianFlag) { + public static PixelFormat create6bitColorDepthPixelFormat(int bigEndianFlag) { final PixelFormat pixelFormat = new PixelFormat(); pixelFormat.bigEndianFlag = (byte) bigEndianFlag; pixelFormat.bitsPerPixel = 8;
--- a/src/main/java/com/glavsoft/rfb/encoding/ServerInitMessage.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/ServerInitMessage.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/encoding/decoder/ByteBuffer.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/ByteBuffer.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/encoding/decoder/CopyRectDecoder.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/CopyRectDecoder.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/encoding/decoder/Decoder.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/Decoder.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/encoding/decoder/DecodersContainer.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/DecodersContainer.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/encoding/decoder/FramebufferUpdateRectangle.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/FramebufferUpdateRectangle.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/encoding/decoder/HextileDecoder.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/HextileDecoder.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/encoding/decoder/RREDecoder.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/RREDecoder.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/encoding/decoder/RawDecoder.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/RawDecoder.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/encoding/decoder/RichCursorDecoder.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/RichCursorDecoder.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/encoding/decoder/TightDecoder.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/TightDecoder.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -24,15 +24,15 @@ package com.glavsoft.rfb.encoding.decoder; -import java.util.logging.Logger; -import java.util.zip.DataFormatException; -import java.util.zip.Inflater; - import com.glavsoft.drawing.ColorDecoder; import com.glavsoft.drawing.Renderer; import com.glavsoft.exceptions.TransportException; import com.glavsoft.transport.Reader; +import java.util.logging.Logger; +import java.util.zip.DataFormatException; +import java.util.zip.Inflater; + /** * Tight protocol extention decoder */ @@ -54,17 +54,16 @@ Inflater[] decoders; private int decoderId; + private int[] palette; - final static int tightZlibBufferSize = 512; - - public TightDecoder() { + public TightDecoder() { reset(); } @Override public void decode(Reader reader, Renderer renderer, FramebufferUpdateRectangle rect) throws TransportException { - int bytesPerPixel = renderer.getBytesPerPixelSignificant(); + int bytesPerPixel = renderer.getBytesPerPixelTight(); /** * bits @@ -88,19 +87,12 @@ renderer.fillRect(color, rect); break; case JPEG_TYPE: - if (bytesPerPixel != 3) { -// throw new EncodingException( -// "Tight doesn't support JPEG subencoding while depth not equal to 24bpp is used"); - } + assert 3 == bytesPerPixel : "Tight doesn't support JPEG subencoding while depth not equal to 24bpp is used"; processJpegType(reader, renderer, rect); break; default: - if (compType > JPEG_TYPE) { -// throw new EncodingException( -// "Compression control byte is incorrect!"); - } else { - processBasicType(compControl, reader, renderer, rect); - } + assert compType <= JPEG_TYPE : "Compression control byte is incorrect!"; + processBasicType(compControl, reader, renderer, rect); } } @@ -112,7 +104,7 @@ if ((compControl & FILTER_ID_MASK) > 0) { // filter byte presence filterId = reader.readUInt8(); } - int bytesPerCPixel = renderer.getBytesPerPixelSignificant(); + int bytesPerCPixel = renderer.getBytesPerPixelTight(); int lengthCurrentbpp = bytesPerCPixel * rect.width * rect.height; byte [] buffer; switch (filterId) { @@ -122,12 +114,12 @@ break; case PALETTE_FILTER: int paletteSize = reader.readUInt8() + 1; - int[] palette = readPalette(paletteSize, reader, renderer); + completePalette(paletteSize, reader, renderer); int dataLength = paletteSize == 2 ? rect.height * ((rect.width + 7) / 8) : rect.width * rect.height; buffer = readTightData(dataLength, reader); - renderer.drawBytesWithPalette(buffer, rect, palette); + renderer.drawBytesWithPalette(buffer, rect, palette, paletteSize); break; case GRADIENT_FILTER: /* @@ -182,20 +174,19 @@ } /** - * Read palette from reader + * Complete palette from reader */ - private int[] readPalette(int paletteSize, Reader reader, Renderer renderer) throws TransportException { + private void completePalette(int paletteSize, Reader reader, Renderer renderer) throws TransportException { /** * When bytesPerPixel == 1 && paletteSize == 2 read 2 bytes of palette * When bytesPerPixel == 1 && paletteSize != 2 - error * When bytesPerPixel == 3 (4) read (paletteSize * 3) bytes of palette * so use renderer.readPixelColor */ - int[] palette = new int[paletteSize]; - for (int i = 0; i < palette.length; ++i) { + if (null == palette) palette = new int[256]; + for (int i = 0; i < paletteSize; ++i) { palette[i] = renderer.readTightPixelColor(reader); } - return palette; } /** @@ -203,7 +194,7 @@ * uncompressed data. When compressed decompresses it. * * @param expectedLength expected data length in bytes - * @param reader + * @param reader data source * @return result data * @throws TransportException */ @@ -224,7 +215,7 @@ * which need to be ignored. Use only first expectedLength bytes. * * @param expectedLength expected data length - * @param reader + * @param reader data source * @return decompressed data (length == expectedLength) / + followed raw data (ignore, please) * @throws TransportException */ @@ -262,7 +253,7 @@ * Lower 7 bit of each byte contains significant data. Max bytes = 3. * Less significant bytes first order. * - * @param reader + * @param reader data source * @return int value * @throws TransportException */ @@ -281,7 +272,7 @@ /** * Flush (reset) zlib decoders when bits 3, 2, 1, 0 of compControl is set - * @param compControl + * @param compControl control flags */ private void resetDecoders(int compControl) { for (int i=0; i < DECODERS_NUM; ++i) {
--- a/src/main/java/com/glavsoft/rfb/encoding/decoder/ZRLEDecoder.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/ZRLEDecoder.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -29,9 +29,11 @@ import com.glavsoft.transport.Reader; public class ZRLEDecoder extends ZlibDecoder { - private static final int DEFAULT_TILE_SIZE = 64; + private static final int MAX_TILE_SIZE = 64; + private int[] decodedBitmap; + private int[] palette; - @Override + @Override public void decode(Reader reader, Renderer renderer, FramebufferUpdateRectangle rect) throws TransportException { int zippedLength = (int) reader.readUInt32(); @@ -41,18 +43,23 @@ int offset = zippedLength; int maxX = rect.x + rect.width; int maxY = rect.y + rect.height; - int [] palette = new int [128]; - for (int tileY = rect.y; tileY < maxY; tileY += DEFAULT_TILE_SIZE) { - int tileHeight = Math.min(maxY - tileY, DEFAULT_TILE_SIZE); + if (null == palette) { + palette = new int [128]; + } + if (null == decodedBitmap) { + decodedBitmap = new int[MAX_TILE_SIZE * MAX_TILE_SIZE]; + } + for (int tileY = rect.y; tileY < maxY; tileY += MAX_TILE_SIZE) { + int tileHeight = Math.min(maxY - tileY, MAX_TILE_SIZE); - for (int tileX = rect.x; tileX < maxX; tileX += DEFAULT_TILE_SIZE) { - int tileWidth = Math.min(maxX - tileX, DEFAULT_TILE_SIZE); + for (int tileX = rect.x; tileX < maxX; tileX += MAX_TILE_SIZE) { + int tileWidth = Math.min(maxX - tileX, MAX_TILE_SIZE); int subencoding = bytes[offset++] & 0x0ff; // 128 -plain RLE, 130-255 - Palette RLE boolean isRle = (subencoding & 128) != 0; // 2 to 16 for raw packed palette data, 130 to 255 for Palette RLE (subencoding - 128) int paletteSize = subencoding & 127; - offset += readPalette(bytes, offset, renderer, palette, paletteSize); + offset += readPalette(bytes, offset, renderer, paletteSize); if (1 == subencoding) { // A solid tile consisting of a single colour renderer.fillRect(palette[0], tileX, tileY, tileWidth, tileHeight); continue; @@ -61,24 +68,22 @@ if (0 == paletteSize) { // subencoding == 128 (or paletteSize == 0) - Plain RLE offset += decodePlainRle(bytes, offset, renderer, tileX, tileY, tileWidth, tileHeight); } else { - offset += decodePaletteRle(bytes, offset, renderer, palette, tileX, tileY, tileWidth, tileHeight, paletteSize); + offset += decodePaletteRle(bytes, offset, renderer, tileX, tileY, tileWidth, tileHeight); } } else { if (0 == paletteSize) { // subencoding == 0 (or paletteSize == 0) - raw CPIXEL data offset += decodeRaw(bytes, offset, renderer, tileX, tileY, tileWidth, tileHeight); } else { - offset += decodePacked(bytes, offset, renderer, palette, paletteSize, tileX, tileY, tileWidth, tileHeight); + offset += decodePacked(bytes, offset, renderer, paletteSize, tileX, tileY, tileWidth, tileHeight); } } } } - } private int decodePlainRle(byte[] bytes, int offset, Renderer renderer, int tileX, int tileY, int tileWidth, int tileHeight) { - int bytesPerCPixel = renderer.getBytesPerPixelSignificant(); - int [] decodedBitmap = new int[tileWidth * tileHeight]; + int bytesPerCPixel = renderer.getBytesPerCPixel(); int decodedOffset = 0; int decodedEnd = tileWidth * tileHeight; int index = offset; @@ -98,8 +103,7 @@ } private int decodePaletteRle(byte[] bytes, int offset, Renderer renderer, - int[] palette, int tileX, int tileY, int tileWidth, int tileHeight, int paletteSize) { - int [] decodedBitmap = new int[tileWidth * tileHeight]; + int tileX, int tileY, int tileWidth, int tileHeight) { int decodedOffset = 0; int decodedEnd = tileWidth * tileHeight; int index = offset; @@ -121,8 +125,7 @@ } private int decodePacked(byte[] bytes, int offset, Renderer renderer, - int[] palette, int paletteSize, int tileX, int tileY, int tileWidth, int tileHeight) { - int [] decodedBytes = new int[tileWidth * tileHeight]; + int paletteSize, int tileX, int tileY, int tileWidth, int tileHeight) { int bitsPerPalletedPixel = paletteSize > 16 ? 8 : paletteSize > 4 ? 4 : paletteSize > 2 ? 2 : 1; int packedOffset = offset; @@ -140,11 +143,11 @@ bitsRemain -= bitsPerPalletedPixel; int index = byteProcessed >> bitsRemain & (1 << bitsPerPalletedPixel) - 1 & 127; int color = palette[index]; - renderer.fillColorBitmapWithColor(decodedBytes, decodedOffset, 1, color); + renderer.fillColorBitmapWithColor(decodedBitmap, decodedOffset, 1, color); ++decodedOffset; } } - renderer.drawColoredBitmap(decodedBytes, tileX, tileY, tileWidth, tileHeight); + renderer.drawColoredBitmap(decodedBitmap, tileX, tileY, tileWidth, tileHeight); return packedOffset - offset; } @@ -153,13 +156,12 @@ return renderer.drawCompactBytes(bytes, offset, tileX, tileY, tileWidth, tileHeight); } - private int readPalette(byte[] bytes, int offset, Renderer renderer, - int[] palette, int paletteSize) { + private int readPalette(byte[] bytes, int offset, Renderer renderer, int paletteSize) { + final int bytesPerCPixel = renderer.getBytesPerCPixel(); for (int i=0; i<paletteSize; ++i) { - palette[i] = renderer.getCompactPixelColor(bytes, - offset + i*renderer.getBytesPerPixelSignificant()); + palette[i] = renderer.getCompactPixelColor(bytes, offset + i* bytesPerCPixel); } - return paletteSize * renderer.getBytesPerPixelSignificant(); + return paletteSize * bytesPerCPixel; } }
--- a/src/main/java/com/glavsoft/rfb/encoding/decoder/ZlibDecoder.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/encoding/decoder/ZlibDecoder.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/protocol/LocalPointer.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/LocalPointer.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/protocol/MessageQueue.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/MessageQueue.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -24,10 +24,11 @@ package com.glavsoft.rfb.protocol; +import com.glavsoft.rfb.client.ClientToServerMessage; + import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; - -import com.glavsoft.rfb.client.ClientToServerMessage; +import java.util.concurrent.TimeUnit; /** * @author dime at tightvnc.com @@ -43,8 +44,15 @@ queue.offer(message); } + /** + * Retrieves and removes the head of this queue, waiting if necessary until an element becomes available. + * Retrieves and removes the head of this queue, waiting up to the certain wait time if necessary for + * an element to become available. + * @return the head of this queue, or null if the specified waiting time elapses before an element is available + * @throws InterruptedException - if interrupted while waiting + */ public ClientToServerMessage get() throws InterruptedException { - return queue.take(); + return queue.poll(1, TimeUnit.SECONDS); } }
--- a/src/main/java/com/glavsoft/rfb/protocol/Protocol.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/Protocol.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -42,7 +42,7 @@ public class Protocol implements ProtocolContext, IChangeSettingsListener { private ProtocolState state; - private final Logger logger = Logger.getLogger("com.glavsoft.rfb.protocol"); + private final Logger logger; private final IPasswordRetriever passwordRetriever; private final ProtocolSettings settings; private int fbWidth; @@ -50,7 +50,7 @@ private PixelFormat pixelFormat; private final Reader reader; private final Writer writer; - private String remoteDesctopName; + private String remoteDesktopName; private MessageQueue messageQueue; private final DecodersContainer decoders; private SenderTask senderTask; @@ -60,8 +60,10 @@ private PixelFormat serverPixelFormat; private Thread senderThread; private Thread receiverThread; + private boolean isTight; + private String protocolVersion; - public Protocol(Reader reader, Writer writer, + public Protocol(Reader reader, Writer writer, IPasswordRetriever passwordRetriever, ProtocolSettings settings) { this.reader = reader; this.writer = writer; @@ -70,7 +72,8 @@ decoders = new DecodersContainer(); decoders.instantiateDecodersWhenNeeded(settings.encodings); state = new HandshakeState(this); - } + logger = Logger.getLogger(getClass().getName()); + } @Override public void changeStateTo(ProtocolState state) { @@ -80,7 +83,7 @@ public void handshake() throws UnsupportedProtocolVersionException, UnsupportedSecurityTypeException, AuthenticationFailedException, TransportException, FatalException { while (state.next()) { - continue; + // continue; } this.messageQueue = new MessageQueue(); } @@ -100,12 +103,12 @@ @Override public String getRemoteDesktopName() { - return remoteDesctopName; + return remoteDesktopName; } @Override public void setRemoteDesktopName(String name) { - remoteDesctopName = name; + remoteDesktopName = name; } @Override @@ -138,12 +141,7 @@ return settings; } - @Override - public Logger getLogger() { - return logger; - } - - @Override + @Override public Writer getWriter() { return writer; } @@ -169,14 +167,14 @@ IRepaintController repaintController, ClipboardController clipboardController) { this.rfbSessionListener = rfbSessionListener; this.repaintController = repaintController; -// if (settings.getBitsPerPixel() == 0) { -// settings.setBitsPerPixel(pixelFormat.bitsPerPixel); // the same the server sent when not initialized yet +// if (settings.getColorDepth() == 0) { +// settings.setColorDepth(pixelFormat.depth); // the same the server sent when not initialized yet // } serverPixelFormat = pixelFormat; - serverPixelFormat.trueColourFlag = 1; // correct flag - we don't support color maps + correctServerPixelFormat(); setPixelFormat(createPixelFormat(settings)); sendMessage(new SetPixelFormatMessage(pixelFormat)); - logger.fine("sent: "+pixelFormat); + logger.fine("sent: " + pixelFormat); sendSupportedEncodingsMessage(settings); settings.addListener(this); // to support pixel format (color depth), and encodings changes @@ -184,18 +182,33 @@ sendRefreshMessage(); senderTask = new SenderTask(messageQueue, writer, this); - senderThread = new Thread(senderTask); + senderThread = new Thread(senderTask, "RfbSenderTask"); senderThread.start(); decoders.resetDecoders(); receiverTask = new ReceiverTask( reader, repaintController, clipboardController, decoders, this); - receiverThread = new Thread(receiverTask); + receiverThread = new Thread(receiverTask, "RfbReceiverTask"); receiverThread.start(); } - @Override + private void correctServerPixelFormat() { + // correct true color flag - we don't support color maps, so always set it up + serverPixelFormat.trueColourFlag = 1; + // correct .depth to use actual depth 24 instead of incorrect 32, used by ex. UltraVNC server, that cause + // protocol incompatibility in ZRLE encoding + final long significant = serverPixelFormat.redMax << serverPixelFormat.redShift | + serverPixelFormat.greenMax << serverPixelFormat.greenShift | + serverPixelFormat.blueMax << serverPixelFormat.blueShift; + if (32 == serverPixelFormat.bitsPerPixel && + ((significant & 0x00ff000000L) == 0 || (significant & 0x000000ffL) == 0) && + 32 == serverPixelFormat.depth) { + serverPixelFormat.depth = 24; + } + } + + @Override public void sendMessage(ClientToServerMessage message) { messageQueue.put(message); } @@ -212,22 +225,22 @@ */ private PixelFormat createPixelFormat(ProtocolSettings settings) { int serverBigEndianFlag = serverPixelFormat.bigEndianFlag; - switch (settings.getBitsPerPixel()) { - case ProtocolSettings.BPP_32: - return PixelFormat.create32bppPixelFormat(serverBigEndianFlag); - case ProtocolSettings.BPP_16: - return PixelFormat.create16bppPixelFormat(serverBigEndianFlag); - case ProtocolSettings.BPP_8: - return PixelFormat.create8bppBGRPixelFormat(serverBigEndianFlag); - case ProtocolSettings.BPP_6: - return PixelFormat.create6bppPixelFormat(serverBigEndianFlag); - case ProtocolSettings.BPP_3: + switch (settings.getColorDepth()) { + case ProtocolSettings.COLOR_DEPTH_24: + return PixelFormat.create24bitColorDepthPixelFormat(serverBigEndianFlag); + case ProtocolSettings.COLOR_DEPTH_16: + return PixelFormat.create16bitColorDepthPixelFormat(serverBigEndianFlag); + case ProtocolSettings.COLOR_DEPTH_8: + return PixelFormat.create8bitColorDepthBGRPixelFormat(serverBigEndianFlag); + case ProtocolSettings.COLOR_DEPTH_6: + return PixelFormat.create6bitColorDepthPixelFormat(serverBigEndianFlag); + case ProtocolSettings.COLOR_DEPTH_3: return PixelFormat.create3bppPixelFormat(serverBigEndianFlag); - case ProtocolSettings.BPP_SERVER_SETTINGS: + case ProtocolSettings.COLOR_DEPTH_SERVER_SETTINGS: return serverPixelFormat; default: // unsupported bpp, use default - return PixelFormat.create32bppPixelFormat(serverBigEndianFlag); + return PixelFormat.create24bitColorDepthPixelFormat(serverBigEndianFlag); } } @@ -237,7 +250,7 @@ if (settings.isChangedEncodings()) { sendSupportedEncodingsMessage(settings); } - if (settings.changedBitsPerPixel() && receiverTask != null) { + if (settings.isChangedColorDepth() && receiverTask != null) { receiverTask.queueUpdatePixelFormat(createPixelFormat(settings)); } } @@ -275,4 +288,24 @@ } } + @Override + public void setTight(boolean isTight) { + this.isTight = isTight; + } + + @Override + public boolean isTight() { + return isTight; + } + + @Override + public void setProtocolVersion(String protocolVersion) { + this.protocolVersion = protocolVersion; + } + + @Override + public String getProtocolVersion() { + return protocolVersion; + } + }
--- a/src/main/java/com/glavsoft/rfb/protocol/ProtocolContext.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/ProtocolContext.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -41,9 +41,7 @@ ProtocolSettings getSettings(); - Logger getLogger(); - - Writer getWriter(); + Writer getWriter(); Reader getReader(); int getFbWidth(); @@ -65,4 +63,10 @@ void cleanUpSession(String message); + void setTight(boolean isTight); + boolean isTight(); + + void setProtocolVersion(String protocolVersion); + String getProtocolVersion(); + } \ No newline at end of file
--- a/src/main/java/com/glavsoft/rfb/protocol/ProtocolSettings.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/ProtocolSettings.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -31,6 +31,7 @@ import com.glavsoft.rfb.encoding.EncodingType; import com.glavsoft.rfb.protocol.auth.SecurityType; +import java.io.Serializable; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -39,23 +40,26 @@ /** * Protocol Settings class */ -public class ProtocolSettings implements Cloneable { +public class ProtocolSettings implements Serializable { + private static final long serialVersionUID = 1L; + private static final EncodingType DEFAULT_PREFERRED_ENCODING = EncodingType.TIGHT; public static final int DEFAULT_JPEG_QUALITY = 6; private static final int DEFAULT_COMPRESSION_LEVEL = -6; - // bits per pixel constants - public static final int BPP_32 = 32; - public static final int BPP_16 = 16; - public static final int BPP_8 = 8; - public static final int BPP_6 = 6; - public static final int BPP_3 = 3; + // color depth constants + public static final int COLOR_DEPTH_32 = 32; + public static final int COLOR_DEPTH_24 = 24; + public static final int COLOR_DEPTH_16 = 16; + public static final int COLOR_DEPTH_8 = 8; + public static final int COLOR_DEPTH_6 = 6; + public static final int COLOR_DEPTH_3 = 3; - public static final int BPP_SERVER_SETTINGS = 0; + public static final int COLOR_DEPTH_SERVER_SETTINGS = 0; - private static final int DEFAULT_BITS_PER_PIXEL = BPP_32; + private static final int DEFAULT_COLOR_DEPTH = COLOR_DEPTH_24; - public static final int CHANGED_VIEW_ONLY = 1 << 0; + public static final int CHANGED_VIEW_ONLY = 1; // 1 << 0; public static final int CHANGED_ENCODINGS = 1 << 1; public static final int CHANGED_ALLOW_COPY_RECT = 1 << 2; public static final int CHANGED_SHOW_REMOTE_CURSOR = 1 << 3; @@ -64,9 +68,10 @@ public static final int CHANGED_JPEG_QUALITY = 1 << 6; public static final int CHANGED_ALLOW_CLIPBOARD_TRANSFER = 1 << 7; public static final int CHANGED_CONVERT_TO_ASCII = 1 << 8; - public static final int CHANGED_BITS_PER_PIXEL = 1 << 9; + public static final int CHANGED_COLOR_DEPTH = 1 << 9; + public static final int CHANGED_SHARED = 1 << 10; - private int changedSettingsMask; + private transient int changedSettingsMask; private boolean sharedFlag; private boolean viewOnly; @@ -78,78 +83,77 @@ private int jpegQuality; private boolean allowClipboardTransfer; private boolean convertToAscii; - private int bitsPerPixel; - - private String protocolVersion; - private boolean isTight; + private int colorDepth; - public LinkedHashSet<EncodingType> encodings; + public transient LinkedHashSet<EncodingType> encodings; + private transient final List<IChangeSettingsListener> listeners; - private final List<IChangeSettingsListener> listeners; - - public CapabilityContainer + public transient CapabilityContainer tunnelingCapabilities, authCapabilities, serverMessagesCapabilities, clientMessagesCapabilities, encodingTypesCapabilities; - private String remoteCharsetName; + private transient String remoteCharsetName; public static ProtocolSettings getDefaultSettings() { ProtocolSettings settings = new ProtocolSettings(); settings.initKnownAuthCapabilities(settings.authCapabilities); settings.initKnownEncodingTypesCapabilities(settings.encodingTypesCapabilities); - - settings.sharedFlag = true; - settings.viewOnly = false; - settings.showRemoteCursor = true; - settings.mouseCursorTrack = LocalPointer.ON; - settings.preferredEncoding = DEFAULT_PREFERRED_ENCODING; - settings.allowCopyRect = true; - settings.compressionLevel = DEFAULT_COMPRESSION_LEVEL; - settings.jpegQuality = DEFAULT_JPEG_QUALITY; - settings.convertToAscii = false; - settings.allowClipboardTransfer = true; - settings.bitsPerPixel = 0;//DEFAULT_BITS_PER_PIXEL; - settings.refine(); - settings.changedSettingsMask = 0; return settings; } private ProtocolSettings() { - listeners = new LinkedList<IChangeSettingsListener>(); + sharedFlag = true; + viewOnly = false; + showRemoteCursor = true; + mouseCursorTrack = LocalPointer.ON; + preferredEncoding = DEFAULT_PREFERRED_ENCODING; + allowCopyRect = true; + compressionLevel = DEFAULT_COMPRESSION_LEVEL; + jpegQuality = DEFAULT_JPEG_QUALITY; + convertToAscii = false; + allowClipboardTransfer = true; + colorDepth = COLOR_DEPTH_SERVER_SETTINGS; + refine(); + + listeners = new LinkedList<IChangeSettingsListener>(); tunnelingCapabilities = new CapabilityContainer(); authCapabilities = new CapabilityContainer(); serverMessagesCapabilities = new CapabilityContainer(); clientMessagesCapabilities = new CapabilityContainer(); encodingTypesCapabilities = new CapabilityContainer(); changedSettingsMask = 0; - } + } - private ProtocolSettings(ProtocolSettings s) { + public ProtocolSettings(ProtocolSettings s) { this(); + copyDataFrom(s); + changedSettingsMask = s.changedSettingsMask; + encodings = s.encodings; + } - changedSettingsMask = s.changedSettingsMask; + public void copyDataFrom(ProtocolSettings s) { + copyDataFrom(s, 0); + } - sharedFlag = s.sharedFlag; - viewOnly = s.viewOnly; - preferredEncoding = s.preferredEncoding; - allowCopyRect = s.allowCopyRect; - showRemoteCursor = s.showRemoteCursor; - mouseCursorTrack = s.mouseCursorTrack; - compressionLevel = s.compressionLevel; - jpegQuality = s.jpegQuality; - allowClipboardTransfer = s.allowClipboardTransfer; - convertToAscii = s.convertToAscii; - bitsPerPixel = s.bitsPerPixel; + public void copyDataFrom(ProtocolSettings s, int mask) { + if (null == s) return; + if ((mask & CHANGED_SHARED) == 0) setSharedFlag(s.sharedFlag); + if ((mask & CHANGED_VIEW_ONLY) == 0) setViewOnly(s.viewOnly); + if ((mask & CHANGED_ALLOW_COPY_RECT) == 0) setAllowCopyRect(s.allowCopyRect); + if ((mask & CHANGED_SHOW_REMOTE_CURSOR) == 0) setShowRemoteCursor(s.showRemoteCursor); + if ((mask & CHANGED_ALLOW_CLIPBOARD_TRANSFER) == 0) setAllowClipboardTransfer(s.allowClipboardTransfer); - encodings = s.encodings; + if ((mask & CHANGED_MOUSE_CURSOR_TRACK) == 0) setMouseCursorTrack(s.mouseCursorTrack); + if ((mask & CHANGED_COMPRESSION_LEVEL) == 0) setCompressionLevel(s.compressionLevel); + if ((mask & CHANGED_JPEG_QUALITY) == 0) setJpegQuality(s.jpegQuality); + if ((mask & CHANGED_CONVERT_TO_ASCII) == 0) setConvertToAscii(s.convertToAscii); + if ((mask & CHANGED_COLOR_DEPTH) == 0) setColorDepth(s.colorDepth); + if ((mask & CHANGED_ENCODINGS) == 0) setPreferredEncoding(s.preferredEncoding); + } - protocolVersion = s.protocolVersion; - isTight = s.isTight; - } - - private void initKnownAuthCapabilities(CapabilityContainer cc) { + private void initKnownAuthCapabilities(CapabilityContainer cc) { cc.addEnabled(SecurityType.NONE_AUTHENTICATION.getId(), RfbCapabilityInfo.VENDOR_STANDARD, RfbCapabilityInfo.AUTHENTICATION_NO_AUTH); cc.addEnabled(SecurityType.VNC_AUTHENTICATION.getId(), @@ -195,7 +199,10 @@ } public void setSharedFlag(boolean sharedFlag) { - this.sharedFlag = sharedFlag; + if (this.sharedFlag != sharedFlag) { + this.sharedFlag = sharedFlag; + changedSettingsMask |= CHANGED_SHARED; + } } public boolean isViewOnly() { @@ -214,33 +221,36 @@ } - public int getBitsPerPixel() { - return bitsPerPixel; + public int getColorDepth() { + return colorDepth; } /** - * Set bpp only in 3, 6, 8, 16, 32. When bpp is wrong, it resets to {@link #DEFAULT_BITS_PER_PIXEL} + * Set depth only in 3, 6, 8, 16, 32. When depth is wrong, it resets to {@link #DEFAULT_COLOR_DEPTH} */ - public void setBitsPerPixel(int bpp) { - if (bitsPerPixel != bpp) { - changedSettingsMask |= CHANGED_BITS_PER_PIXEL; - switch (bpp) { - case BPP_32: - case BPP_16: - case BPP_8: - case BPP_6: - case BPP_3: - case BPP_SERVER_SETTINGS: - bitsPerPixel = bpp; + public void setColorDepth(int depth) { + if (colorDepth != depth) { + changedSettingsMask |= CHANGED_COLOR_DEPTH; + switch (depth) { + case COLOR_DEPTH_32: + colorDepth = COLOR_DEPTH_24; + break; + case COLOR_DEPTH_24: + case COLOR_DEPTH_16: + case COLOR_DEPTH_8: + case COLOR_DEPTH_6: + case COLOR_DEPTH_3: + case COLOR_DEPTH_SERVER_SETTINGS: + colorDepth = depth; break; default: - bitsPerPixel = DEFAULT_BITS_PER_PIXEL; + colorDepth = DEFAULT_COLOR_DEPTH; } refine(); } } - private void refine() { + public void refine() { LinkedHashSet<EncodingType> encodings = new LinkedHashSet<EncodingType>(); if (EncodingType.RAW_ENCODING == preferredEncoding) { // when RAW selected send no ordinary encodings so only default RAW encoding will be enabled @@ -252,7 +262,7 @@ EncodingType.COMPRESS_LEVEL_0.getId() + compressionLevel)); } if (jpegQuality > 0 && jpegQuality < 10 && - (bitsPerPixel == BPP_32 || bitsPerPixel == BPP_SERVER_SETTINGS)) { + (colorDepth == COLOR_DEPTH_24 || colorDepth == COLOR_DEPTH_SERVER_SETTINGS)) { encodings.add(EncodingType.byId( EncodingType.JPEG_QUALITY_LEVEL_0.getId() + jpegQuality)); } @@ -295,7 +305,8 @@ } public void fireListeners() { - final SettingsChangedEvent event = new SettingsChangedEvent(new ProtocolSettings(this)); + if (null == listeners) return; + final SettingsChangedEvent event = new SettingsChangedEvent(new ProtocolSettings(this)); changedSettingsMask = 0; for (IChangeSettingsListener listener : listeners) { listener.settingsChanged(event); @@ -388,22 +399,6 @@ return allowClipboardTransfer; } - public void setTight(boolean isTight) { - this.isTight = isTight; - } - - public boolean isTight() { - return isTight; - } - - public void setProtocolVersion(String protocolVersion) { - this.protocolVersion = protocolVersion; - } - - public String getProtocolVersion() { - return protocolVersion; - } - public boolean isConvertToAscii() { return convertToAscii; } @@ -419,8 +414,8 @@ return (changedSettingsMask & CHANGED_ENCODINGS) == CHANGED_ENCODINGS; } - public boolean changedBitsPerPixel() { - return (changedSettingsMask & CHANGED_BITS_PER_PIXEL) == CHANGED_BITS_PER_PIXEL; + public boolean isChangedColorDepth() { + return (changedSettingsMask & CHANGED_COLOR_DEPTH) == CHANGED_COLOR_DEPTH; } public void setRemoteCharsetName(String remoteCharsetName) { @@ -431,4 +426,20 @@ return remoteCharsetName; } + @Override + public String toString() { + return "ProtocolSettings{" + + "sharedFlag=" + sharedFlag + + ", viewOnly=" + viewOnly + + ", preferredEncoding=" + preferredEncoding + + ", allowCopyRect=" + allowCopyRect + + ", showRemoteCursor=" + showRemoteCursor + + ", mouseCursorTrack=" + mouseCursorTrack + + ", compressionLevel=" + compressionLevel + + ", jpegQuality=" + jpegQuality + + ", allowClipboardTransfer=" + allowClipboardTransfer + + ", convertToAscii=" + convertToAscii + + ", colorDepth=" + colorDepth + + '}'; + } }
--- a/src/main/java/com/glavsoft/rfb/protocol/ReceiverTask.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/ReceiverTask.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -105,8 +105,8 @@ logger.severe("Unsupported server message. Id = " + messageId); } } catch (TransportException e) { - logger.severe("Close session: " + e.getMessage()); if (isRunning) { + logger.severe("Close session: " + e.getMessage()); context.cleanUpSession("Connection closed."); } stopTask(); @@ -119,7 +119,7 @@ } catch (CommonException e) { logger.severe(e.getMessage()); if (isRunning) { - context.cleanUpSession("Connection closed."); + context.cleanUpSession("Connection closed.."); } stopTask(); } catch (Throwable te) { @@ -173,7 +173,7 @@ } else if (rect.getEncodingType() == EncodingType.DESKTOP_SIZE) { fullscreenFbUpdateIncrementalRequest = new FramebufferUpdateRequestMessage(0, 0, rect.width, rect.height, true); - synchronized (renderer) { + synchronized (renderer.getLock()) { renderer = repaintController.createRenderer(reader, rect.width, rect.height, context.getPixelFormat()); }
--- a/src/main/java/com/glavsoft/rfb/protocol/SenderTask.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/SenderTask.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -46,7 +46,7 @@ * request * @param messageQueue queue to poll messages * @param writer writer to send messages out - * @param protocolContext + * @param protocolContext protocol */ public SenderTask(MessageQueue messageQueue, Writer writer, ProtocolContext protocolContext) { this.queue = messageQueue; @@ -67,7 +67,7 @@ } catch (InterruptedException e) { // nop } catch (TransportException e) { - Logger.getLogger("com.glavsoft.rfb.protocol").severe("Close session: " + e.getMessage()); + Logger.getLogger(getClass().getName()).severe("Close session: " + e.getMessage()); if (isRunning) { protocolContext.cleanUpSession("Connection closed"); }
--- a/src/main/java/com/glavsoft/rfb/protocol/auth/AuthHandler.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/auth/AuthHandler.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/protocol/auth/NoneAuthentication.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/auth/NoneAuthentication.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/protocol/auth/SecurityType.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/auth/SecurityType.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/protocol/auth/TightAuthentication.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/auth/TightAuthentication.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/protocol/auth/VncAuthentication.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/auth/VncAuthentication.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -50,6 +50,7 @@ throws TransportException, FatalException { byte [] challenge = reader.readBytes(16); String password = passwordRetriever.getPassword(); + if (null == password) return false; byte [] key = new byte[8]; System.arraycopy(password.getBytes(), 0, key, 0, Math.min(key.length, password.getBytes().length)); writer.write(encrypt(challenge, key)); @@ -58,8 +59,6 @@ /** * Encript challenge by key using DES - * @param challenge - * @param key * @return encripted bytes * @throws CryptoException on problem with DES algorithm support or smth about */ @@ -70,8 +69,7 @@ SecretKey secretKey = keyFactory.generateSecret(desKeySpec); Cipher desCipher = Cipher.getInstance("DES/ECB/NoPadding"); desCipher.init(Cipher.ENCRYPT_MODE, secretKey); - byte[] textEncrypted = desCipher.doFinal(challenge); - return textEncrypted; + return desCipher.doFinal(challenge); } catch (NoSuchAlgorithmException e) { throw new CryptoException("Cannot encrypt challenge", e); } catch (NoSuchPaddingException e) {
--- a/src/main/java/com/glavsoft/rfb/protocol/state/AuthenticationState.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/state/AuthenticationState.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -24,12 +24,7 @@ package com.glavsoft.rfb.protocol.state; -import com.glavsoft.exceptions.AuthenticationFailedException; -import com.glavsoft.exceptions.ClosedConnectionException; -import com.glavsoft.exceptions.FatalException; -import com.glavsoft.exceptions.TransportException; -import com.glavsoft.exceptions.UnsupportedProtocolVersionException; -import com.glavsoft.exceptions.UnsupportedSecurityTypeException; +import com.glavsoft.exceptions.*; import com.glavsoft.rfb.protocol.ProtocolContext; import com.glavsoft.rfb.protocol.auth.AuthHandler; @@ -62,7 +57,7 @@ checkSecurityResult(); } changeStateTo(isTight ? new InitTightState(context) : new InitState(context)); - context.getSettings().setTight(isTight); + context.setTight(isTight); } /**
--- a/src/main/java/com/glavsoft/rfb/protocol/state/HandshakeState.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/state/HandshakeState.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -28,6 +28,7 @@ import com.glavsoft.exceptions.UnsupportedProtocolVersionException; import com.glavsoft.rfb.protocol.ProtocolContext; +import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -57,7 +58,7 @@ private void handshake() throws TransportException, UnsupportedProtocolVersionException { String protocolString = reader.readString(PROTOCOL_STRING_LENGTH); - logger.info("Server sent protocol string: " + protocolString.substring(0, protocolString.length() - 1)); + Logger.getLogger(getClass().getName()).info("Server sent protocol string: " + protocolString.substring(0, protocolString.length() - 1)); Pattern pattern = Pattern.compile(PROTOCOL_STRING_REGEXP); final Matcher matcher = pattern.matcher(protocolString); if ( ! matcher.matches()) @@ -76,21 +77,21 @@ if (minor >= MIN_SUPPORTED_VERSION_MINOR && minor < 7) { changeStateTo(new SecurityType33State(context)); - context.getSettings().setProtocolVersion(PROTOCOL_VERSION_3_3); + context.setProtocolVersion(PROTOCOL_VERSION_3_3); minor = 3; } else if (7 == minor) { changeStateTo(new SecurityType37State(context)); - context.getSettings().setProtocolVersion(PROTOCOL_VERSION_3_7); + context.setProtocolVersion(PROTOCOL_VERSION_3_7); minor = 7; } else if (minor >= MAX_SUPPORTED_VERSION_MINOR) { changeStateTo(new SecurityTypeState(context)); - context.getSettings().setProtocolVersion(PROTOCOL_VERSION_3_8); + context.setProtocolVersion(PROTOCOL_VERSION_3_8); minor = 8; } else throw new UnsupportedProtocolVersionException( "Unsupported protocol version: " + protocolString); writer.write(("RFB 00" + major + ".00" + minor + "\n").getBytes()); - logger.info("Set protocol version to: " + context.getSettings().getProtocolVersion()); + Logger.getLogger(getClass().getName()).info("Set protocol version to: " + context.getProtocolVersion()); } }
--- a/src/main/java/com/glavsoft/rfb/protocol/state/InitState.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/state/InitState.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -33,6 +33,8 @@ import com.glavsoft.rfb.protocol.ProtocolContext; import com.glavsoft.rfb.protocol.ProtocolSettings; +import java.util.logging.Logger; + /** * ClientInit * @@ -79,13 +81,12 @@ context.setFbWidth(serverInitMessage.getFrameBufferWidth()); context.setFbHeight(serverInitMessage.getFrameBufferHeight()); context.setRemoteDesktopName(serverInitMessage.getName()); - logger.fine(serverInitMessage.toString()); + Logger.getLogger(getClass().getName()).fine(serverInitMessage.toString()); } protected ServerInitMessage getServerInitMessage() throws TransportException { writer.write(context.getSettings().getSharedFlag()); - ServerInitMessage serverInitMessage = new ServerInitMessage(reader); - return serverInitMessage; + return new ServerInitMessage(reader); }
--- a/src/main/java/com/glavsoft/rfb/protocol/state/InitTightState.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/state/InitTightState.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/protocol/state/ProtocolState.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/state/ProtocolState.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -24,26 +24,18 @@ package com.glavsoft.rfb.protocol.state; -import java.util.logging.Logger; - -import com.glavsoft.exceptions.AuthenticationFailedException; -import com.glavsoft.exceptions.FatalException; -import com.glavsoft.exceptions.TransportException; -import com.glavsoft.exceptions.UnsupportedProtocolVersionException; -import com.glavsoft.exceptions.UnsupportedSecurityTypeException; +import com.glavsoft.exceptions.*; import com.glavsoft.rfb.protocol.ProtocolContext; import com.glavsoft.transport.Reader; import com.glavsoft.transport.Writer; abstract public class ProtocolState { protected ProtocolContext context; - protected Logger logger; protected Reader reader; protected Writer writer; public ProtocolState(ProtocolContext context) { this.context = context; - this.logger = context.getLogger(); this.reader = context.getReader(); this.writer = context.getWriter(); }
--- a/src/main/java/com/glavsoft/rfb/protocol/state/SecurityType33State.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/state/SecurityType33State.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -29,6 +29,8 @@ import com.glavsoft.rfb.protocol.ProtocolContext; import com.glavsoft.rfb.protocol.auth.AuthHandler; +import java.util.logging.Logger; + public class SecurityType33State extends SecurityType37State { public SecurityType33State(ProtocolContext context) { @@ -38,9 +40,9 @@ @Override protected void negotiateAboutSecurityType() throws TransportException, UnsupportedSecurityTypeException { - logger.info("Get Security Type"); + Logger.getLogger(getClass().getName()).info("Get Security Type"); int type = reader.readInt32(); - logger.info("Type received: " + type); + Logger.getLogger(getClass().getName()).info("Type received: " + type); if (0 == type) // throw exception with reason throw new UnsupportedSecurityTypeException(reader.readString()); @@ -48,7 +50,7 @@ context.getSettings().authCapabilities); if (typeSelected != null) { setUseSecurityResult(typeSelected); - logger.info("Type accepted: " + typeSelected.getName()); + Logger.getLogger(getClass().getName()).info("Type accepted: " + typeSelected.getName()); } else throw new UnsupportedSecurityTypeException( "No security types supported. Server sent '" +
--- a/src/main/java/com/glavsoft/rfb/protocol/state/SecurityType37State.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/state/SecurityType37State.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/rfb/protocol/state/SecurityTypeState.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/rfb/protocol/state/SecurityTypeState.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -33,6 +33,8 @@ import com.glavsoft.rfb.protocol.auth.SecurityType; import com.glavsoft.utils.Strings; +import java.util.logging.Logger; + public class SecurityTypeState extends ProtocolState { public SecurityTypeState(ProtocolContext context) { @@ -52,13 +54,13 @@ // throw exception with reason throw new UnsupportedSecurityTypeException(reader.readString()); byte[] secTypes = reader.readBytes(secTypesNum); - logger.info("Security Types received (" + secTypesNum + "): " - + Strings.toString(secTypes)); + Logger.getLogger(getClass().getName()).info("Security Types received (" + secTypesNum + "): " + + Strings.toString(secTypes)); AuthHandler typeSelected = selectAuthHandler( secTypes, context.getSettings().authCapabilities); setUseSecurityResult(typeSelected); writer.writeByte(typeSelected.getId()); - logger.info("Security Type accepted: " + typeSelected.getName()); + Logger.getLogger(getClass().getName()).info("Security Type accepted: " + typeSelected.getName()); changeStateTo(new AuthenticationState(context, typeSelected)); }
--- a/src/main/java/com/glavsoft/transport/Reader.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/transport/Reader.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -32,6 +32,7 @@ public class Reader { final static Charset ISO_8859_1 = Charset.forName("ISO-8859-1"); + final static Charset UTF8 = Charset.forName("UTF-8"); private final DataInputStream is; public Reader(InputStream is) { @@ -80,7 +81,17 @@ } catch (EOFException e) { throw new ClosedConnectionException(e); } catch (IOException e) { - throw new TransportException("Cannot read int16", e); + throw new TransportException("Cannot read int32", e); + } + } + + public long readInt64() throws TransportException { + try { + return is.readLong(); + } catch (EOFException e) { + throw new ClosedConnectionException(e); + } catch (IOException e) { + throw new TransportException("Cannot read int32", e); } } @@ -98,21 +109,35 @@ /** * Read 32-bit string length and then string themself by it length * Use this method only when sure no character accept ASCII will be read. - * Use readBytes and character encoding conversion instead. + * Use readBytes and character encoding conversion instead or {@link #readUtf8String} method + * when utf-8 encoding needed. * * @return String read * @throws TransportException */ public String readString() throws TransportException { - // unset most sighificant (sign) bit 'cause InputStream#readFully - // reads - // [int] length bytes from stream. Change when realy need read sthing more - // than - // 2147483647 bytes lenght + // unset most significant (sign) bit 'cause InputStream#readFully reads + // [int] length bytes from stream. Change when really need read string more + // than 2147483647 bytes length int length = readInt32() & Integer.MAX_VALUE; return readString(length); } + /** + * Read 32-bit string length and then string themself by it length + * Assume UTF-8 character encoding used + * + * @return String read + * @throws TransportException + */ + public String readUtf8String() throws TransportException { + // unset most significant (sign) bit 'cause InputStream#readFully reads + // [int] length bytes from stream. Change when really need read string more + // than 2147483647 bytes length + int length = readInt32() & Integer.MAX_VALUE; + return new String(readBytes(length)); + } + public byte[] readBytes(int length) throws TransportException { byte b[] = new byte[length]; return readBytes(b, 0, length); @@ -128,4 +153,14 @@ throw new TransportException("Cannot read " + length + " bytes array", e); } } + + public void skip(int length) throws TransportException { + try { + is.skipBytes(length); + } catch (EOFException e) { + throw new ClosedConnectionException(e); + } catch (IOException e) { + throw new TransportException("Cannot skip " + length + " bytes", e); + } + } } \ No newline at end of file
--- a/src/main/java/com/glavsoft/transport/Writer.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/transport/Writer.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -73,6 +73,14 @@ write(i); } + public void writeInt64(long i) throws TransportException { + try { + os.writeLong(i); + } catch (IOException e) { + throw new TransportException("Cannot write long", e); + } + } + public void write(int i) throws TransportException { try { os.writeInt(i);
--- a/src/main/java/com/glavsoft/utils/Keymap.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/utils/Keymap.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/main/java/com/glavsoft/utils/Strings.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/main/java/com/glavsoft/utils/Strings.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/AbstractConnectionWorkerFactory.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,36 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer; + +/** + * @author dime at tightvnc.com + */ +public abstract class AbstractConnectionWorkerFactory { + public abstract NetworkConnectionWorker createNetworkConnectionWorker(); + + public abstract RfbConnectionWorker createRfbConnectionWorker(); + + public abstract void setPredefinedPassword(String predefinedPassword); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/CancelConnectionException.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,37 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer; + +import com.glavsoft.exceptions.CommonException; + +/** + * @author dime at tightvnc.com + */ +public class CancelConnectionException extends CommonException { + + public CancelConnectionException(String message) { + super(message); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/ConnectionErrorException.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,37 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer; + +import com.glavsoft.exceptions.CommonException; + +/** + * @author dime at tightvnc.com + */ +public class ConnectionErrorException extends CommonException { + + public ConnectionErrorException(String message) { + super(message); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/ConnectionPresenter.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,290 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer; + +import com.glavsoft.rfb.protocol.ProtocolSettings; +import com.glavsoft.utils.Strings; +import com.glavsoft.viewer.mvp.Presenter; +import com.glavsoft.viewer.swing.ConnectionParams; +import com.glavsoft.viewer.swing.WrongParameterException; +import com.glavsoft.viewer.swing.gui.ConnectionView; +import com.glavsoft.viewer.swing.gui.ConnectionsHistory; + +import java.net.Socket; +import java.util.logging.Logger; + +/** + * @author dime at tightvnc.com + */ +public class ConnectionPresenter extends Presenter { + public static final String PROPERTY_HOST_NAME = "HostName"; + public static final String PROPERTY_RFB_PORT_NUMBER = "PortNumber"; + public static final String PROPERTY_USE_SSH = "UseSsh"; + private static final String PROPERTY_SSH_USER_NAME = "SshUserName"; + private static final String PROPERTY_SSH_HOST_NAME = "SshHostName"; + private static final String PROPERTY_SSH_PORT_NUMBER = "SshPortNumber"; + private static final String PROPERTY_STATUS_BAR_MESSAGE = "Message"; + private static final String PROPERTY_CONNECTION_IN_PROGRESS = "ConnectionInProgress"; + public static final String CONNECTION_PARAMS_MODEL = "ConnectionParamsModel"; + public static final String CONNECTIONS_HISTORY_MODEL = "ConnectionsHistoryModel"; + public static final String CONNECTION_VIEW = "ConnectionView"; + + private final boolean hasSshSupport; + private final boolean allowInteractive; + private ConnectionsHistory connectionsHistory; + private ProtocolSettings rfbSettings; + private UiSettings uiSettings; + private final Logger logger; + private RfbConnectionWorker rfbConnectionWorker; + private AbstractConnectionWorkerFactory connectionWorkerFactory; + private NetworkConnectionWorker networkConnectionWorker; + private boolean needReconnection = true; + + public ConnectionPresenter(boolean hasSshSupport, boolean allowInteractive) { + this.hasSshSupport = hasSshSupport; + this.allowInteractive = allowInteractive; + logger = Logger.getLogger(getClass().getName()); + } + + public void startConnection(ProtocolSettings rfbSettings, UiSettings uiSettings, int paramSettingsMask) + throws IllegalStateException { + this.rfbSettings = rfbSettings; + this.uiSettings = uiSettings; + if ( ! isModelRegisteredByName(CONNECTION_PARAMS_MODEL)) { + throw new IllegalStateException("No Connection Params model added."); + } + connectionsHistory = new ConnectionsHistory(); + addModel(CONNECTIONS_HISTORY_MODEL, connectionsHistory); + syncModels(paramSettingsMask); + if (allowInteractive) { + show(); + populate(); + } else { + connect(); + } + } + + public void setUseSsh(boolean useSsh) { + setModelProperty(PROPERTY_USE_SSH, useSsh, boolean.class); + } + + public void submitConnection(String hostName) throws WrongParameterException { + if (Strings.isTrimmedEmpty(hostName)) { + throw new WrongParameterException("Host name is empty", PROPERTY_HOST_NAME); + } + setModelProperty(PROPERTY_HOST_NAME, hostName); + + final String rfbPort = (String) getViewPropertyOrNull(PROPERTY_RFB_PORT_NUMBER); + setModelProperty(PROPERTY_RFB_PORT_NUMBER, rfbPort); + try { + throwPossiblyHappenedException(); + } catch (Throwable e) { + throw new WrongParameterException("Wrong Port", PROPERTY_HOST_NAME); + } + setSshOptions(); + + saveHistory(); + populateFrom(CONNECTIONS_HISTORY_MODEL); + + connect(); + } + + public void saveHistory() { + final ConnectionParams cp = (ConnectionParams) getModel(CONNECTION_PARAMS_MODEL); + connectionsHistory.reorder(cp, rfbSettings, uiSettings); + connectionsHistory.save(); + } + + private void connect() { + final ConnectionParams connectionParams = (ConnectionParams) getModel(CONNECTION_PARAMS_MODEL); + // TODO check connectionWorkerFactory is init + networkConnectionWorker = connectionWorkerFactory.createNetworkConnectionWorker(); + networkConnectionWorker.setConnectionParams(connectionParams); + networkConnectionWorker.setPresenter(this); + networkConnectionWorker.setHasSshSupport(hasSshSupport); + networkConnectionWorker.execute(); + } + + public void connectionFailed() { + cancelConnection(); + if (allowInteractive) { + enableConnectionDialog(); + } else { + connect(); + } + } + + public void connectionCancelled() { + cancelConnection(); + if (allowInteractive) { + enableConnectionDialog(); + } else { + final ConnectionView connectionView = (ConnectionView) getView(CONNECTION_VIEW); + if (connectionView != null) { + connectionView.closeApp(); + } + } + } + + private void enableConnectionDialog() { + setViewProperty(PROPERTY_CONNECTION_IN_PROGRESS, false, boolean.class); + } + + public void successfulNetworkConnection(Socket workingSocket) { // EDT + logger.info("Connected"); + showMessage("Connected"); + rfbConnectionWorker = connectionWorkerFactory.createRfbConnectionWorker(); + rfbConnectionWorker.setWorkingSocket(workingSocket); + rfbConnectionWorker.setRfbSettings(rfbSettings); + rfbConnectionWorker.setUiSettings(uiSettings); + rfbConnectionWorker.setConnectionString( + getModelProperty(PROPERTY_HOST_NAME) + ":" + getModelProperty(PROPERTY_RFB_PORT_NUMBER)); + rfbConnectionWorker.execute(); + } + + public void successfulRfbConnection() { + enableConnectionDialog(); + getView(CONNECTION_VIEW).closeView(); + } + + public void cancelConnection() { + if (networkConnectionWorker != null) { + networkConnectionWorker.cancel(); + } + if (rfbConnectionWorker != null) { + rfbConnectionWorker.cancel(); + } + } + + public void showConnectionErrorDialog(String message) { + final ConnectionView connectionView = (ConnectionView) getView(CONNECTION_VIEW); + if (connectionView != null) { + connectionView.showConnectionErrorDialog(message); + } + } + + public void showReconnectDialog(String errorTitle, String errorMessage) { + final ConnectionView connectionView = (ConnectionView) getView(CONNECTION_VIEW); + if (connectionView != null) { + connectionView.showReconnectDialog(errorTitle, errorMessage); + } + } + + private void setSshOptions() { + if (hasSshSupport) { + try { + final boolean useSsh = (Boolean)getViewProperty(PROPERTY_USE_SSH); + setModelProperty(PROPERTY_USE_SSH, useSsh, boolean.class); + } catch (PropertyNotFoundException e) { + //nop + } + setModelProperty(PROPERTY_SSH_USER_NAME, getViewPropertyOrNull(PROPERTY_SSH_USER_NAME)); + setModelProperty(PROPERTY_SSH_HOST_NAME, getViewPropertyOrNull(PROPERTY_SSH_HOST_NAME)); + setModelProperty(PROPERTY_SSH_PORT_NUMBER, getViewPropertyOrNull(PROPERTY_SSH_PORT_NUMBER)); + setViewProperty(PROPERTY_SSH_PORT_NUMBER, getModelProperty(PROPERTY_SSH_PORT_NUMBER)); + } + } + + private void syncModels(int paramSettingsMask) { + final ConnectionParams cp = (ConnectionParams) getModel(CONNECTION_PARAMS_MODEL); + final ConnectionParams mostSuitableConnection = connectionsHistory.getMostSuitableConnection(cp); + cp.completeEmptyFieldsFrom(mostSuitableConnection); + rfbSettings.copyDataFrom(connectionsHistory.getProtocolSettings(mostSuitableConnection), paramSettingsMask & 0xffff); + uiSettings.copyDataFrom(connectionsHistory.getUiSettingsData(mostSuitableConnection), (paramSettingsMask >> 16) & 0xffff); + if ( ! cp.isHostNameEmpty()) { + connectionsHistory.reorder(cp, rfbSettings, uiSettings); + } + +// protocolSettings.addListener(connectionsHistory); +// uiSettings.addListener(connectionsHistory); + } + + public void populateFromHistoryItem(ConnectionParams connectionParams) { + setModelProperty(PROPERTY_HOST_NAME, connectionParams.hostName); + setModelProperty(PROPERTY_RFB_PORT_NUMBER, connectionParams.getPortNumber(), int.class); + setModelProperty(PROPERTY_USE_SSH, connectionParams.useSsh(), boolean.class); + setModelProperty(PROPERTY_SSH_HOST_NAME, connectionParams.sshHostName); + setModelProperty(PROPERTY_SSH_PORT_NUMBER, connectionParams.getSshPortNumber(), int.class); + setModelProperty(PROPERTY_SSH_USER_NAME, connectionParams.sshUserName); + populateFrom(CONNECTION_PARAMS_MODEL); + rfbSettings.copyDataFrom(connectionsHistory.getProtocolSettings(connectionParams)); + uiSettings.copyDataFrom(connectionsHistory.getUiSettingsData(connectionParams)); + } + + public void clearHistory() { + connectionsHistory.clear(); + connectionsHistory.reorder((ConnectionParams) getModel(CONNECTION_PARAMS_MODEL), rfbSettings, uiSettings); + populateFrom(CONNECTIONS_HISTORY_MODEL); + clearMessage(); + } + + public void showMessage(String message) { + setViewProperty(PROPERTY_STATUS_BAR_MESSAGE, message); + } + + public void clearMessage() { + showMessage(""); + } + + public void setConnectionWorkerFactory(AbstractConnectionWorkerFactory connectionWorkerFactory) { + this.connectionWorkerFactory = connectionWorkerFactory; + } + + public void reconnect(String predefinedPassword) { + connectionWorkerFactory.setPredefinedPassword(predefinedPassword); + if (allowInteractive) { + clearMessage(); + enableConnectionDialog(); + show(); + populate(); + } else if (needReconnection) { + connect(); + } + } + + public void clearPredefinedPassword() { + connectionWorkerFactory.setPredefinedPassword(null); + } + + public UiSettings getUiSettings() { + return uiSettings; + } + + public ProtocolSettings getRfbSettings() { + return rfbSettings; + } + + public boolean needReconnection() { + return needReconnection; + } + + public void setNeedReconnection(boolean need) { + needReconnection = need; + } + + public boolean allowInteractive() { + return allowInteractive; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/ConnectionWorker.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,47 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer; + +/** + * @author dime at tightvnc.com + */ +public interface ConnectionWorker<T> { + /** + * The same as in {@link javax.swing.SwingWorker} + */ + T doInBackground() throws Exception; + + /** + * The same as in {@link javax.swing.SwingWorker} + */ + void execute(); + + /** + * Should cancel worker. + * + * @return true if cancelled successful, false if not (ex. worker is already cancelled) + */ + boolean cancel(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/NetworkConnectionWorker.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,41 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer; + +import com.glavsoft.viewer.swing.ConnectionParams; + +import java.net.Socket; + +/** + * @author dime at tightvnc.com + */ +public interface NetworkConnectionWorker extends ConnectionWorker<Socket> { + + void setConnectionParams(ConnectionParams connectionParams); + + void setPresenter(ConnectionPresenter presenter); + + void setHasSshSupport(boolean hasSshSupport); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/RfbConnectionWorker.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,46 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer; + +import com.glavsoft.rfb.protocol.Protocol; +import com.glavsoft.rfb.protocol.ProtocolSettings; + +import javax.swing.*; +import java.net.Socket; + +/** + * @author dime at tightvnc.com + */ +public interface RfbConnectionWorker extends ConnectionWorker<Void> { + + void setWorkingSocket(Socket workingSocket); + + void setRfbSettings(ProtocolSettings rfbSettings); + + void setUiSettings(UiSettings uiSettings); + + void setConnectionString(String connectionString); + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/UiSettings.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,190 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer; + +import com.glavsoft.core.SettingsChangedEvent; +import com.glavsoft.rfb.IChangeSettingsListener; +import com.glavsoft.viewer.swing.LocalMouseCursorShape; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.LinkedList; +import java.util.List; + +/** + * @author dime at tightvnc.com + */ +public class UiSettings { + + public static final int MIN_SCALE_PERCENT = 10; + public static final int MAX_SCALE_PERCENT = 500; + private static final int SCALE_PERCENT_ZOOMING_STEP = 10; + + @SuppressWarnings("PointlessBitwiseExpression") + public static final int CHANGED_SCALE_FACTOR = 1 << 0; + public static final int CHANGED_MOUSE_CURSOR_SHAPE = 1 << 1; + public static final int CHANGED_FULL_SCREEN = 1 << 2; + + private final List<IChangeSettingsListener> listeners = new LinkedList<IChangeSettingsListener>(); + private int changedSettingsMask = 0; + + private final UiSettingsData uiSettingsData; + public boolean showControls = true; + + public UiSettings() { + uiSettingsData = new UiSettingsData(); + changedSettingsMask = 0; + } + + public UiSettings(UiSettings uiSettings) { + uiSettingsData = new UiSettingsData( + uiSettings.getScalePercent(), uiSettings.getMouseCursorShape(), uiSettings.isFullScreen()); + this.changedSettingsMask = uiSettings.changedSettingsMask; + } + + public double getScaleFactor() { + return uiSettingsData.getScalePercent() / 100.; + } + + public void setScalePercent(double scalePercent) { + if (this.uiSettingsData.setScalePercent(scalePercent)) { + changedSettingsMask |= CHANGED_SCALE_FACTOR; + } + } + + public void addListener(IChangeSettingsListener listener) { + listeners.add(listener); + } + + void fireListeners() { + if (null == listeners) return; + final SettingsChangedEvent event = new SettingsChangedEvent(new UiSettings(this)); + changedSettingsMask = 0; + for (IChangeSettingsListener listener : listeners) { + listener.settingsChanged(event); + } + } + + public void zoomOut() { + double oldScaleFactor = uiSettingsData.getScalePercent(); + double scaleFactor = (int)(this.uiSettingsData.getScalePercent() / SCALE_PERCENT_ZOOMING_STEP) * SCALE_PERCENT_ZOOMING_STEP; + if (scaleFactor == oldScaleFactor) { + scaleFactor -= SCALE_PERCENT_ZOOMING_STEP; + } + if (scaleFactor < MIN_SCALE_PERCENT) { + scaleFactor = MIN_SCALE_PERCENT; + } + setScalePercent(scaleFactor); + fireListeners(); + } + + public void zoomIn() { + double scaleFactor = (int)(this.uiSettingsData.getScalePercent() / SCALE_PERCENT_ZOOMING_STEP) * SCALE_PERCENT_ZOOMING_STEP + SCALE_PERCENT_ZOOMING_STEP; + if (scaleFactor > MAX_SCALE_PERCENT) { + scaleFactor = MAX_SCALE_PERCENT; + } + setScalePercent(scaleFactor); + fireListeners(); + } + + public void zoomAsIs() { + setScalePercent(100); + fireListeners(); + } + + public void zoomToFit(int containerWidth, int containerHeight, int fbWidth, int fbHeight) { + int scalePromille = Math.min(1000 * containerWidth / fbWidth, + 1000 * containerHeight / fbHeight); + while (fbWidth * scalePromille / 1000. > containerWidth || + fbHeight * scalePromille / 1000. > containerHeight) { + scalePromille -= 1; + } + setScalePercent(scalePromille / 10.); + fireListeners(); + } + + public boolean isChangedMouseCursorShape() { + return (changedSettingsMask & CHANGED_MOUSE_CURSOR_SHAPE) == CHANGED_MOUSE_CURSOR_SHAPE; + } + + public static boolean isUiSettingsChangedFired(SettingsChangedEvent event) { + return event.getSource() instanceof UiSettings; + } + + public double getScalePercent() { + return uiSettingsData.getScalePercent(); + } + + public String getScalePercentFormatted() { + NumberFormat numberFormat = new DecimalFormat("###.#"); + return numberFormat.format(uiSettingsData.getScalePercent()); + } + + public LocalMouseCursorShape getMouseCursorShape() { + return uiSettingsData.getMouseCursorShape(); + } + + public void setMouseCursorShape(LocalMouseCursorShape mouseCursorShape) { + if (this.uiSettingsData.setMouseCursorShape(mouseCursorShape)) { + changedSettingsMask |= CHANGED_MOUSE_CURSOR_SHAPE; + fireListeners(); + } + } + + public void copyDataFrom(UiSettingsData other) { + copyDataFrom(other, 0); + } + public void copyDataFrom(UiSettingsData other, int mask) { + if (null == other) return; + if ((mask & CHANGED_SCALE_FACTOR) == 0) uiSettingsData.setScalePercent(other.getScalePercent()); + if ((mask & CHANGED_MOUSE_CURSOR_SHAPE) == 0) uiSettingsData.setMouseCursorShape(other.getMouseCursorShape()); + if ((mask & CHANGED_FULL_SCREEN) == 0) uiSettingsData.setFullScreen(other.isFullScreen()); + } + + public void setFullScreen(boolean isFullScreen) { + if (uiSettingsData.setFullScreen(isFullScreen)) { + changedSettingsMask |= CHANGED_FULL_SCREEN; + fireListeners(); + } + } + + public boolean isFullScreen() { + return uiSettingsData.isFullScreen(); + } + + public UiSettingsData getData() { + return uiSettingsData; + } + + @Override + public String toString() { + return "UiSettings{" + + "scalePercent=" + uiSettingsData.getScalePercent() + + ", fullScreen=" + uiSettingsData.isFullScreen() + + ", mouseCursorShape=" + uiSettingsData.getMouseCursorShape() + + '}'; + } + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/UiSettingsData.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,102 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer; + +import com.glavsoft.viewer.swing.LocalMouseCursorShape; + +import java.io.Serializable; + +/** + * @author dime at tightvnc.com + */ +public class UiSettingsData implements Serializable { + private static final long serialVersionUID = 1L; + private double scalePercent; + private LocalMouseCursorShape mouseCursorShape; + private boolean fullScreen; + + + public UiSettingsData() { + scalePercent = 100; + mouseCursorShape = LocalMouseCursorShape.DOT; + fullScreen = false; + } + + public UiSettingsData(double scalePercent, LocalMouseCursorShape mouseCursorShape, boolean fullScreen) { + this.scalePercent = scalePercent; + this.mouseCursorShape = mouseCursorShape; + this.fullScreen = fullScreen; + } + + public UiSettingsData(UiSettingsData other) { + this(other.getScalePercent(), other.getMouseCursorShape(), other.isFullScreen()); + } + + public double getScalePercent() { + return scalePercent; + } + + public boolean setScalePercent(double scalePercent) { + if (this.scalePercent != scalePercent) { + this.scalePercent = scalePercent; + return true; + } + return false; + } + + + public LocalMouseCursorShape getMouseCursorShape() { + return mouseCursorShape; + } + + public boolean setMouseCursorShape(LocalMouseCursorShape mouseCursorShape) { + if (this.mouseCursorShape != mouseCursorShape && mouseCursorShape != null) { + this.mouseCursorShape = mouseCursorShape; + return true; + } + return false; + } + + public boolean isFullScreen() { + return fullScreen; + } + + public boolean setFullScreen(boolean fullScreen) { + if (this.fullScreen != fullScreen) { + this.fullScreen = fullScreen; + return true; + } + return false; + } + + @Override + public String toString() { + return "UiSettingsData{" + + "scalePercent=" + scalePercent + + ", mouseCursorShape=" + mouseCursorShape + + ", fullScreen=" + fullScreen + + '}'; + } +} \ No newline at end of file
--- a/src/viewer_swing/java/com/glavsoft/viewer/Viewer.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/Viewer.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -24,105 +24,42 @@ package com.glavsoft.viewer; -import com.glavsoft.core.SettingsChangedEvent; -import com.glavsoft.exceptions.*; -import com.glavsoft.rfb.IChangeSettingsListener; -import com.glavsoft.rfb.IPasswordRetriever; -import com.glavsoft.rfb.IRfbSessionListener; -import com.glavsoft.rfb.client.KeyEventMessage; -import com.glavsoft.rfb.protocol.Protocol; -import com.glavsoft.rfb.protocol.ProtocolContext; import com.glavsoft.rfb.protocol.ProtocolSettings; -import com.glavsoft.transport.Reader; -import com.glavsoft.transport.Writer; -import com.glavsoft.utils.Keymap; -import com.glavsoft.utils.Strings; import com.glavsoft.viewer.cli.Parser; -import com.glavsoft.viewer.swing.*; -import com.glavsoft.viewer.swing.gui.OptionsDialog; -import com.glavsoft.viewer.swing.gui.PasswordDialog; +import com.glavsoft.viewer.mvp.View; +import com.glavsoft.viewer.swing.ConnectionParams; +import com.glavsoft.viewer.swing.ParametersHandler; +import com.glavsoft.viewer.swing.SwingConnectionWorkerFactory; +import com.glavsoft.viewer.swing.SwingViewerWindowFactory; +import com.glavsoft.viewer.swing.gui.ConnectionView; import javax.swing.*; import java.awt.*; -import java.awt.Dialog.ModalityType; -import java.awt.event.*; +import java.awt.event.WindowEvent; +import java.awt.event.WindowListener; import java.io.IOException; import java.io.InputStream; -import java.net.Socket; -import java.util.LinkedList; -import java.util.List; import java.util.jar.Attributes; import java.util.jar.Manifest; -import java.util.logging.Logger; +import java.util.logging.*; @SuppressWarnings("serial") -public class Viewer extends JApplet implements Runnable, IRfbSessionListener, WindowListener, - IChangeSettingsListener { - public static final int DEFAULT_PORT = 5900; - - public static Logger logger = Logger.getLogger("com.glavsoft"); - private static final Insets BUTTONS_MARGIN = new Insets(2, 2, 2, 2); - private boolean isZoomToFitSelected; - private boolean forceReconnection; - private String reconnectionReason; - private ContainerManager containerManager; - - public Protocol getWorkingProtocol() { - return workingProtocol; - } +public class Viewer extends JApplet implements Runnable, WindowListener { - public boolean isZoomToFitSelected() { - return isZoomToFitSelected; - } - - public Surface getSurface() { - return surface; - } - - public UiSettings getUiSettings() { - return uiSettings; - } - - public void setZoomToFitSelected(boolean zoomToFitSelected) { - isZoomToFitSelected = zoomToFitSelected; - } + private Logger logger; + private int paramsMask; + private boolean allowAppletInteractiveConnections; - /** - * Ask user for password if needed - */ - public class PasswordChooser implements IPasswordRetriever { - private final String passwordPredefined; - private final ParametersHandler.ConnectionParams connectionParams; - PasswordDialog passwordDialog; - private final JFrame owner; - private final WindowListener onClose; - - public PasswordChooser(String passwordPredefined, ParametersHandler.ConnectionParams connectionParams, - JFrame owner, WindowListener onClose) { - this.passwordPredefined = passwordPredefined; - this.connectionParams = connectionParams; - this.owner = owner; - this.onClose = onClose; - } + private final ConnectionParams connectionParams; + private String passwordFromParams; + boolean isSeparateFrame = true; + boolean isApplet = true; + private final ProtocolSettings settings; + private final UiSettings uiSettings; + private volatile boolean isAppletStopped = false; + private ConnectionPresenter connectionPresenter; - @Override - public String getPassword() { - return Strings.isTrimmedEmpty(passwordPredefined) ? - getPasswordFromGUI() : - passwordPredefined; - } - - private String getPasswordFromGUI() { - if (null == passwordDialog) { - passwordDialog = new PasswordDialog(owner, onClose, isApplet); - } - passwordDialog.setServerHostName(connectionParams.hostName); - passwordDialog.setVisible(true); - return passwordDialog.getPassword(); - } - } - - public static void main(String[] args) { + public static void main(String[] args) { Parser parser = new Parser(); ParametersHandler.completeParserOptions(parser); @@ -135,7 +72,7 @@ SwingUtilities.invokeLater(viewer); } - public static void printUsage(String additional) { + public static void printUsage(String additional) { System.out.println("Usage: java -jar (progfilename) [hostname [port_number]] [Options]\n" + " or\n"+ " java -jar (progfilename) [Options]\n" + @@ -145,73 +82,52 @@ "Both option name and option value are case insensitive."); } - - private final ParametersHandler.ConnectionParams connectionParams; - protected String passwordFromParams; - protected Socket workingSocket; - protected Protocol workingProtocol; - protected JFrame containerFrame; - boolean isSeparateFrame = true; - protected boolean isApplet = true; - protected boolean showControls = true; - protected Surface surface; - protected final ProtocolSettings settings; - protected final UiSettings uiSettings; - protected boolean tryAgain; - private boolean isAppletStopped = false; - private volatile boolean isStoppingProcess; - private List<JComponent> kbdButtons; - public Viewer() { - connectionParams = new ParametersHandler.ConnectionParams(); + logger = Logger.getLogger(getClass().getName()); + connectionParams = new ConnectionParams(); settings = ProtocolSettings.getDefaultSettings(); uiSettings = new UiSettings(); } private Viewer(Parser parser) { this(); - ParametersHandler.completeSettingsFromCLI(parser, connectionParams, settings, uiSettings); - showControls = ParametersHandler.showControls; + setLoggingLevel(parser.isSet(ParametersHandler.ARG_VERBOSE) ? Level.FINE : + parser.isSet(ParametersHandler.ARG_VERBOSE_MORE) ? Level.FINER : + Level.INFO); + + paramsMask = ParametersHandler.completeSettingsFromCLI(parser, connectionParams, settings, uiSettings); passwordFromParams = parser.getValueFor(ParametersHandler.ARG_PASSWORD); logger.info("TightVNC Viewer version " + ver()); isApplet = false; } + private void setLoggingLevel(Level levelToSet) { + final Logger appLogger = Logger.getLogger("com.glavsoft"); + appLogger.setLevel(levelToSet); + ConsoleHandler ch = null; + for (Handler h : appLogger.getHandlers()) { + if (h instanceof ConsoleHandler) { + ch = (ConsoleHandler) h; + break; + } + } + if (null == ch) { + ch = new ConsoleHandler(); + appLogger.addHandler(ch); + } +// ch.setFormatter(new SimpleFormatter()); + ch.setLevel(levelToSet); + } - @Override - public void rfbSessionStopped(final String reason) { - if (isStoppingProcess) - return; - cleanUpUISessionAndConnection(); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - forceReconnection = true; - reconnectionReason = reason; - } - }); - // start new session - SwingUtilities.invokeLater(this); - } - private synchronized void cleanUpUISessionAndConnection() { - isStoppingProcess = true; - if (workingSocket != null && workingSocket.isConnected()) { - try { - workingSocket.close(); - } catch (IOException e) { /*nop*/ } - } - if (containerFrame != null) { - containerFrame.dispose(); - containerFrame = null; - } - isStoppingProcess = false; - } - - @Override + @Override public void windowClosing(WindowEvent e) { if (e != null && e.getComponent() != null) { - e.getWindow().setVisible(false); + final Window w = e.getWindow(); + if (w != null) { + w.setVisible(false); + w.dispose(); + } } closeApp(); } @@ -219,16 +135,18 @@ /** * Closes App(lication) or stops App(let). */ - protected void closeApp() { - if (workingProtocol != null) { - workingProtocol.cleanUpSession(); - } - cleanUpUISessionAndConnection(); - tryAgain = false; - if (isApplet) { - logger.severe("Applet is stopped."); - isAppletStopped = true; - repaint(); + public void closeApp() { + if (connectionPresenter != null) { + connectionPresenter.cancelConnection(); + logger.info("Connections cancelled."); + } + if (isApplet) { + if ( ! isAppletStopped) { + logger.severe("Applet is stopped."); + isAppletStopped = true; + repaint(); + stop(); + } } else { System.exit(0); } @@ -253,321 +171,64 @@ @Override public void init() { - ParametersHandler.completeSettingsFromApplet(this, connectionParams, settings, uiSettings); - showControls = ParametersHandler.showControls; + paramsMask = ParametersHandler.completeSettingsFromApplet(this, connectionParams, settings, uiSettings); isSeparateFrame = ParametersHandler.isSeparateFrame; passwordFromParams = getParameter(ParametersHandler.ARG_PASSWORD); isApplet = true; - + allowAppletInteractiveConnections = ParametersHandler.allowAppletInteractiveConnections; repaint(); - SwingUtilities.invokeLater(this); - } + + try { + SwingUtilities.invokeAndWait(this); + } catch (Exception e) { + logger.severe(e.getMessage()); + } + } @Override public void start() { - setSurfaceToHandleKbdFocus(); super.start(); } - @Override - public void run() { - ConnectionManager connectionManager = new ConnectionManager(this, isApplet); - - if (forceReconnection) { - connectionManager.showReconnectDialog("Connection lost", reconnectionReason); - forceReconnection = false; - } - tryAgain = true; - while (tryAgain) { - workingSocket = connectionManager.connectToHost(connectionParams, settings); - if (null == workingSocket) { - closeApp(); - break; - } - logger.info("Connected"); - - try { - workingSocket.setTcpNoDelay(true); // disable Nagle algorithm - Reader reader = new Reader(workingSocket.getInputStream()); - Writer writer = new Writer(workingSocket.getOutputStream()); - - workingProtocol = new Protocol(reader, writer, - new PasswordChooser(passwordFromParams, connectionParams, containerFrame, this), - settings); - workingProtocol.handshake(); - - ClipboardControllerImpl clipboardController = - new ClipboardControllerImpl(workingProtocol, settings.getRemoteCharsetName()); - clipboardController.setEnabled(settings.isAllowClipboardTransfer()); - settings.addListener(clipboardController); - - surface = new Surface(workingProtocol, this, uiSettings.getScaleFactor()); - settings.addListener(this); - uiSettings.addListener(surface); - containerFrame = createContainer(); - connectionManager.setContainerFrame(containerFrame); - updateFrameTitle(); - - workingProtocol.startNormalHandling(this, surface, clipboardController); - tryAgain = false; - } catch (UnsupportedProtocolVersionException e) { - connectionManager.showReconnectDialog("Unsupported Protocol Version", e.getMessage()); - logger.severe(e.getMessage()); - } catch (UnsupportedSecurityTypeException e) { - connectionManager.showReconnectDialog("Unsupported Security Type", e.getMessage()); - logger.severe(e.getMessage()); - } catch (AuthenticationFailedException e) { - passwordFromParams = null; - connectionManager.showReconnectDialog("Authentication Failed", e.getMessage()); - logger.severe(e.getMessage()); - } catch (TransportException e) { - connectionManager.showReconnectDialog("Connection Error", "Connection Error" + ": " + e.getMessage()); - logger.severe(e.getMessage()); - } catch (IOException e) { - connectionManager.showReconnectDialog("Connection Error", "Connection Error" + ": " + e.getMessage()); - logger.severe(e.getMessage()); - } catch (FatalException e) { - connectionManager.showReconnectDialog("Connection Error", "Connection Error" + ": " + e.getMessage()); - logger.severe(e.getMessage()); - } - } - } - - protected JFrame createContainer() { - containerManager = new ContainerManager(this); - Container container = containerManager.createContainer(surface, isSeparateFrame, isApplet); + private boolean checkJsch() { + try { + Class.forName("com.jcraft.jsch.JSch"); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } - if (showControls) { - createButtonsPanel(container, workingProtocol, containerManager); - containerManager.registerResizeListener(container); - containerManager.updateZoomButtonsState(); - } - setSurfaceToHandleKbdFocus(); - return isSeparateFrame ? (JFrame)container : null; - } - - public void packContainer() { - containerManager.pack(); - } - - protected void createButtonsPanel(final Container container, final ProtocolContext context, ContainerManager containerManager) { - JPanel buttonBar = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 1)); - - JButton optionsButton = new JButton(Utils.getButtonIcon("options")); - optionsButton.setToolTipText("Set Options"); - optionsButton.setMargin(BUTTONS_MARGIN); - buttonBar.add(optionsButton); - optionsButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - showOptionsDialog(); - setSurfaceToHandleKbdFocus(); - } - }); - - JButton infoButton = new JButton(Utils.getButtonIcon("info")); - infoButton.setToolTipText("Show connection info"); - infoButton.setMargin(BUTTONS_MARGIN); - buttonBar.add(infoButton); - infoButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - showConnectionInfoMessage(context.getRemoteDesktopName()); - setSurfaceToHandleKbdFocus(); - } - }); - - buttonBar.add(Box.createHorizontalStrut(10)); - JButton refreshButton = new JButton(Utils.getButtonIcon("refresh")); - refreshButton.setToolTipText("Refresh screen"); - refreshButton.setMargin(BUTTONS_MARGIN); - buttonBar.add(refreshButton); - refreshButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - context.sendRefreshMessage(); - setSurfaceToHandleKbdFocus(); - } - }); - - containerManager.addZoomButtons(buttonBar, BUTTONS_MARGIN); - - kbdButtons = new LinkedList<JComponent>(); - buttonBar.add(Box.createHorizontalStrut(10)); - JButton ctrlAltDelButton = new JButton(Utils.getButtonIcon("ctrl-alt-del")); - ctrlAltDelButton.setToolTipText("Send 'Ctrl-Alt-Del'"); - ctrlAltDelButton.setMargin(BUTTONS_MARGIN); - buttonBar.add(ctrlAltDelButton); - kbdButtons.add(ctrlAltDelButton); - ctrlAltDelButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - sendCtrlAltDel(context); - setSurfaceToHandleKbdFocus(); - } - }); + @Override + public void run() { - JButton winButton = new JButton(Utils.getButtonIcon("win")); - winButton.setToolTipText("Send 'Win' key as 'Ctrl-Esc'"); - winButton.setMargin(BUTTONS_MARGIN); - buttonBar.add(winButton); - kbdButtons.add(winButton); - winButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - sendWinKey(context); - setSurfaceToHandleKbdFocus(); - } - }); - - JToggleButton ctrlButton = new JToggleButton(Utils.getButtonIcon("ctrl")); - ctrlButton.setToolTipText("Ctrl Lock"); - ctrlButton.setMargin(BUTTONS_MARGIN); - buttonBar.add(ctrlButton); - kbdButtons.add(ctrlButton); - ctrlButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - setSurfaceToHandleKbdFocus(); - } - }); - ctrlButton.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED) { - context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true)); - } else { - context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, false)); - } - } - }); - - JToggleButton altButton = new JToggleButton(Utils.getButtonIcon("alt")); - kbdButtons.add(altButton); - altButton.setToolTipText("Alt Lock"); - altButton.setMargin(BUTTONS_MARGIN); - buttonBar.add(altButton); - kbdButtons.add(altButton); - altButton.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange() == ItemEvent.SELECTED) { - context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, true)); - } else { - context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, false)); - } - } - }); - altButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - setSurfaceToHandleKbdFocus(); - } - }); - ModifierButtonEventListener modifierButtonListener = new ModifierButtonEventListener(); - modifierButtonListener.addButton(KeyEvent.VK_CONTROL, ctrlButton); - modifierButtonListener.addButton(KeyEvent.VK_ALT, altButton); - surface.addModifierListener(modifierButtonListener); - -// JButton fileTransferButton = new JButton(Utils.getButtonIcon("file-transfer")); -// fileTransferButton.setMargin(buttonsMargin); -// buttonBar.add(fileTransferButton); - - buttonBar.add(Box.createHorizontalStrut(10)); - - JButton closeButton = new JButton(Utils.getButtonIcon("close")); - closeButton.setToolTipText(isApplet ? "Disconnect" : "Close"); - closeButton.setMargin(BUTTONS_MARGIN); - closeButton.setAlignmentX(RIGHT_ALIGNMENT); - buttonBar.add(closeButton); - closeButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - closeApp(); - } - }); + final boolean hasJsch = checkJsch(); + final boolean allowInteractive = allowAppletInteractiveConnections || ! isApplet; + connectionPresenter = new ConnectionPresenter(hasJsch, allowInteractive); + connectionPresenter.addModel("ConnectionParamsModel", connectionParams); + final ConnectionView connectionView = new ConnectionView( + Viewer.this, // appWindowListener + connectionPresenter, hasJsch); + connectionPresenter.addView(ConnectionPresenter.CONNECTION_VIEW, connectionView); + if (isApplet) { + connectionPresenter.addView("AppletStatusStringView", new View() { + @Override + public void showView() { /*nop*/ } + @Override + public void closeView() { /*nop*/ } + @SuppressWarnings("UnusedDeclaration") + public void setMessage(String message) { + Viewer.this.getAppletContext().showStatus(message); + } + }); + } - container.add(buttonBar, BorderLayout.NORTH); - } - - protected void updateFrameTitle() { - if (containerFrame != null) { - containerFrame.setTitle( - workingProtocol.getRemoteDesktopName() + " [zoom: " + uiSettings.getScalePercentFormatted() + "%]"); - } - } - - protected void setSurfaceToHandleKbdFocus() { - if (surface != null && ! surface.requestFocusInWindow()) { - surface.requestFocus(); - } - } - - @Override - public void settingsChanged(SettingsChangedEvent e) { - ProtocolSettings settings = (ProtocolSettings) e.getSource(); - setEnabledKbdButtons( ! settings.isViewOnly()); - } - - private void setEnabledKbdButtons(boolean enabled) { - if (kbdButtons != null) { - for (JComponent b : kbdButtons) { - b.setEnabled(enabled); - } - } - } - - private void showOptionsDialog() { - OptionsDialog optionsDialog = new OptionsDialog(containerFrame); - optionsDialog.initControlsFromSettings(settings, false); - optionsDialog.setVisible(true); - } + SwingViewerWindowFactory viewerWindowFactory = new SwingViewerWindowFactory(isSeparateFrame, isApplet, this); - private void showConnectionInfoMessage(final String title) { - StringBuilder message = new StringBuilder(); - message.append("Connected to: ").append(title).append("\n"); - message.append("Host: ").append(connectionParams.hostName) - .append(" Port: ").append(connectionParams.portNumber).append("\n\n"); - - message.append("Desktop geometry: ") - .append(String.valueOf(surface.getWidth())) - .append(" \u00D7 ") // multiplication sign - .append(String.valueOf(surface.getHeight())).append("\n"); - message.append("Color format: ") - .append(String.valueOf(Math.round(Math.pow(2, workingProtocol.getPixelFormat().depth)))) - .append(" colors (") - .append(String.valueOf(workingProtocol.getPixelFormat().depth)) - .append(" bits)\n"); - message.append("Current protocol version: ") - .append(settings.getProtocolVersion()); - if (settings.isTight()) { - message.append("tight"); - } - message.append("\n"); + connectionPresenter.setConnectionWorkerFactory( + new SwingConnectionWorkerFactory(connectionView.getFrame(), passwordFromParams, connectionPresenter, viewerWindowFactory)); - JOptionPane infoPane = new JOptionPane(message.toString(), JOptionPane.INFORMATION_MESSAGE); - final JDialog infoDialog = infoPane.createDialog(containerFrame, "VNC connection info"); - infoDialog.setModalityType(ModalityType.MODELESS); - try { - infoDialog.setAlwaysOnTop(true); - } catch (SecurityException e) { /*nop*/ } - infoDialog.setVisible(true); - } - - private void sendCtrlAltDel(ProtocolContext context) { - context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true)); - context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, true)); - context.sendMessage(new KeyEventMessage(Keymap.K_DELETE, true)); - context.sendMessage(new KeyEventMessage(Keymap.K_DELETE, false)); - context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, false)); - context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, false)); - } - - private void sendWinKey(ProtocolContext context) { - context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true)); - context.sendMessage(new KeyEventMessage(Keymap.K_ESCAPE, true)); - context.sendMessage(new KeyEventMessage(Keymap.K_ESCAPE, false)); - context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, false)); + connectionPresenter.startConnection(settings, uiSettings, paramsMask); } @Override @@ -583,7 +244,7 @@ @Override public void windowDeactivated(WindowEvent e) { /* nop */ } - protected static String ver() { + public static String ver() { final InputStream mfStream = Viewer.class.getClassLoader().getResourceAsStream( "META-INF/MANIFEST.MF"); if (null == mfStream) {
--- a/src/viewer_swing/java/com/glavsoft/viewer/cli/Parser.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/cli/Parser.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -24,13 +24,13 @@ package com.glavsoft.viewer.cli; +import com.glavsoft.utils.Strings; + import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import com.glavsoft.utils.Strings; - /** * Command line interface parameters parser */ @@ -70,7 +70,7 @@ public boolean isSet(String param) { Option op = options.get(param.toLowerCase()); - return op != null ? op.isSet : false; + return op != null && op.isSet; } public boolean isSetPlainOptions() { @@ -89,7 +89,6 @@ * Command line interface option */ private static class Option { - @SuppressWarnings("unused") protected String opName, defaultValue, desc, value; protected boolean isSet = false; public Option(String opName, String defaultValue, String desc) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/mvp/Model.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,33 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer.mvp; + +/** + * @author dime at tightvnc.com + */ +public interface Model { + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/mvp/Presenter.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,259 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer.mvp; + +import com.glavsoft.exceptions.CommonException; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Logger; + +/** + * @author dime at tightvnc.com + */ +public class Presenter { + private final Map<String, View> registeredViews; + private final Map<String, Model> registeredModels; + static private Logger logger = Logger.getLogger(Presenter.class.getName()); + private Throwable savedInvocationTargetException; + + public Presenter() { + registeredViews = new HashMap<String, View>(); + registeredModels = new HashMap<String, Model>(); + } + + public void addView(String name, View view) { + registeredViews.put(name, view); + } + + public void addModel(String name, Model model) { + registeredModels.put(name, model); + } + + protected void populate() { + savedInvocationTargetException = null; + for (Map.Entry<String, Model> entry : registeredModels.entrySet()) { + String modelName = entry.getKey(); + Model model = entry.getValue(); + populateFrom(modelName, model); + } + } + + public void populateFrom(String modelName) { + Model model = registeredModels.get(modelName); + if (modelName != null) { + populateFrom(modelName, model); + } else { + logger.finer("Cannot find model: " + modelName); + } + } + + private void populateFrom(String modelName, Model model) { + Method methods[] = model.getClass().getDeclaredMethods(); + for (Method m : methods) { + if (m.getName().startsWith("get") && m.getParameterTypes().length == 0) { + String propertyName = m.getName().substring(3); + try { + final Object property = m.invoke(model); + logger.finest("Load: " + modelName + ".get" + propertyName + "() # => " + property + + " type: " + m.getReturnType()); + setViewProperty(propertyName, property, m.getReturnType()); // TODO this can set savedInvocationTargetEx, so what to do whith it? + } catch (IllegalAccessException e) { + // nop + } catch (InvocationTargetException e) { + savedInvocationTargetException = e.getCause(); // TODO may be skip it? + break; + } + } + } + } + + protected boolean isModelRegisteredByName(String modelName) { + return registeredModels.containsKey(modelName); + } + + protected Model getModel(String modelName) { + return registeredModels.get(modelName); + } + + protected void show() { + for (View v : registeredViews.values()) { + v.showView(); + } + } + + + protected void save() { + savedInvocationTargetException = null; + for (Map.Entry<String, Model> entry : registeredModels.entrySet()) { + String modelName = entry.getKey(); + Model model = entry.getValue(); + Method methods[] = model.getClass().getDeclaredMethods(); + for (Method m : methods) { + if (m.getName().startsWith("set")) { + String propertyName = m.getName().substring(3); + try { + final Object viewProperty = getViewProperty(propertyName); + m.invoke(model, viewProperty); + logger.finest("Save: " + modelName + ".set" + propertyName + "( " + viewProperty + " )"); + } catch (IllegalAccessException e) { + // nop + } catch (InvocationTargetException e) { + savedInvocationTargetException = e.getCause(); + break; + } catch (PropertyNotFoundException e) { + // nop + } + } + } + } + } + + public Object getViewPropertyOrNull(String propertyName) { + try { + return getViewProperty(propertyName); + } catch (PropertyNotFoundException e) { + return null; + } + } + + public Object getViewProperty(String propertyName) throws PropertyNotFoundException { + savedInvocationTargetException = null; + logger.finest("get" + propertyName + "()"); + for (Map.Entry<String, View> entry : registeredViews.entrySet()) { + String viewName = entry.getKey(); + View view = entry.getValue(); + try { + Method getter = view.getClass().getMethod("get" + propertyName, new Class[0]); + final Object res = getter.invoke(view); + logger.finest("----from view: " + viewName + ".get" + propertyName + "() # +> " + res); + return res; + // oops, only first getter will be found TODO? + } catch (NoSuchMethodException e) { + // nop + } catch (InvocationTargetException e) { + savedInvocationTargetException = e.getCause(); + break; + } catch (IllegalAccessException e) { + // nop + } + } + throw new PropertyNotFoundException(propertyName); + } + + public Object getModelProperty(String propertyName) { + savedInvocationTargetException = null; + logger.finest("get" + propertyName + "()"); + for (String modelName : registeredModels.keySet()) { + Model model = registeredModels.get(modelName); + try { + Method getter = model.getClass().getMethod("get" + propertyName, new Class[0]); + final Object res = getter.invoke(model); + logger.finest("----from model: " + modelName + ".get" + propertyName + "() # +> " + res); + return res; + // oops, only first getter will be found TODO? + } catch (NoSuchMethodException e) { + // nop + } catch (InvocationTargetException e) { + savedInvocationTargetException = e.getCause(); + break; + } catch (IllegalAccessException e) { + // nop + } + } +// savedInvocationTargetException = new PropertyNotFoundException(propertyName); + return null; + } + + public void setViewProperty(String propertyName, Object newValue) { + setViewProperty(propertyName, newValue, newValue.getClass()); + } + + public void setViewProperty(String propertyName, Object newValue, Class<?> valueType) { + savedInvocationTargetException = null; + logger.finest("set" + propertyName + "( " + newValue + " ) type: " + valueType); + for (Map.Entry<String, View> entry : registeredViews.entrySet()) { + String viewName = entry.getKey(); + View view = entry.getValue(); + try { + Method setter = view.getClass().getMethod("set" + propertyName, valueType); + setter.invoke(view, newValue); + logger.finest("----to view: " + viewName + ".set" + propertyName + "( " + newValue + " )"); + } catch (NoSuchMethodException e) { + // nop + } catch (InvocationTargetException e) { + e.getCause().printStackTrace(); + savedInvocationTargetException = e.getCause(); + break; + } catch (IllegalAccessException e) { + // nop + } + } + } + + protected void throwPossiblyHappenedException() throws Throwable { + if (savedInvocationTargetException != null) { + savedInvocationTargetException = null; + throw savedInvocationTargetException; + } + } + + protected View getView(String name) { + return registeredViews.get(name); + } + + protected class PropertyNotFoundException extends CommonException { + public PropertyNotFoundException(String message) { + super(message); + } + } + + public void setModelProperty(String propertyName, Object newValue) { + setModelProperty(propertyName, newValue, newValue.getClass()); + } + + public void setModelProperty(String propertyName, Object newValue, Class<?> valueType) { + savedInvocationTargetException = null; + logger.finest("set" + propertyName + "( " + newValue + " )"); + for (Map.Entry<String, Model> entry : registeredModels.entrySet()) { + String modelName = entry.getKey(); + Model model = entry.getValue(); + try { + Method method = model.getClass().getMethod("set" + propertyName, valueType); + method.invoke(model, newValue); + logger.finest("----for model: " + modelName); + } catch (NoSuchMethodException e) { + // nop + } catch (InvocationTargetException e) { + savedInvocationTargetException = e.getCause(); + break; + } catch (IllegalAccessException e) { + // nop + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/mvp/View.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,33 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer.mvp; + +/** + * @author dime at tightvnc.com + */ +public interface View { + void showView(); + void closeView(); +}
--- a/src/viewer_swing/java/com/glavsoft/viewer/swing/ClipboardControllerImpl.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/ClipboardControllerImpl.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/ConnectionParams.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,220 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer.swing; + +import com.glavsoft.utils.Strings; +import com.glavsoft.viewer.mvp.Model; + +/** +* @author dime at tightvnc.com +*/ +public class ConnectionParams implements Model { + public static final int DEFAULT_SSH_PORT = 22; + private static final int DEFAULT_RFB_PORT = 5900; + + public String hostName; + private int portNumber; + public String sshUserName; + public String sshHostName; + private int sshPortNumber; + + private boolean useSsh; + + public ConnectionParams(String hostName, int portNumber, boolean useSsh, String sshHostName, int sshPortNumber, String sshUserName) { + this.hostName = hostName; + this.portNumber = portNumber; + this.sshUserName = sshUserName; + this.sshHostName = sshHostName; + this.sshPortNumber = sshPortNumber; + this.useSsh = useSsh; + } + + public ConnectionParams(ConnectionParams cp) { + this.hostName = cp.hostName != null? cp.hostName: ""; + this.portNumber = cp.portNumber; + this.sshUserName = cp.sshUserName; + this.sshHostName = cp.sshHostName; + this.sshPortNumber = cp.sshPortNumber; + this.useSsh = cp.useSsh; + } + + public ConnectionParams() { + hostName = ""; + sshUserName = ""; + sshHostName = ""; + } + + public boolean isHostNameEmpty() { + return Strings.isTrimmedEmpty(hostName); + } + + public void parseRfbPortNumber(String port) throws WrongParameterException { + try { + portNumber = Integer.parseInt(port); + } catch (NumberFormatException e) { + portNumber = 0; + if ( ! Strings.isTrimmedEmpty(port)) { + throw new WrongParameterException("Wrong port number: " + port + "\nMust be in 0..65535"); + } + } + if (portNumber > 65535 || portNumber < 0) throw new WrongParameterException("Port number is out of range: " + port + "\nMust be in 0..65535"); + } + public void parseSshPortNumber(String port) { + try { + sshPortNumber = Integer.parseInt(port); + } catch (NumberFormatException e) { /*nop*/ } + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + public String getHostName() { + return this.hostName; + } + + public void setPortNumber(String port) throws WrongParameterException { + this.parseRfbPortNumber(port); + } + + public void setPortNumber(int port) { + this.portNumber = port; + } + + public int getPortNumber() { + return 0 == portNumber ? DEFAULT_RFB_PORT : portNumber; + } + + public void setSshPortNumber(String port) { + this.parseSshPortNumber(port); + } + + public void setSshPortNumber(int port) { + this.sshPortNumber = port; + } + + public int getSshPortNumber() { + return 0 == sshPortNumber ? DEFAULT_SSH_PORT: sshPortNumber; + } + + public void setUseSsh(boolean useSsh) { + this.useSsh = useSsh; + } + + public boolean useSsh() { + return useSsh && ! Strings.isTrimmedEmpty(sshHostName); + } + + public boolean getUseSsh() { + return this.useSsh(); + } + + public String getSshUserName() { + return this.sshUserName; + } + + public void setSshUserName(String sshUserName) { + this.sshUserName = sshUserName; + } + + public String getSshHostName() { + return this.sshHostName; + } + + public void setSshHostName(String sshHostName) { + this.sshHostName = sshHostName; + } + + public void completeEmptyFieldsFrom(ConnectionParams from) { + if (null == from) return; + if (Strings.isTrimmedEmpty(hostName) && ! Strings.isTrimmedEmpty(from.hostName)) { + hostName = from.hostName; + } + if ( 0 == portNumber && from.portNumber != 0) { + portNumber = from.portNumber; + } + if (Strings.isTrimmedEmpty(sshUserName) && ! Strings.isTrimmedEmpty(from.sshUserName)) { + sshUserName = from.sshUserName; + } + if (Strings.isTrimmedEmpty(sshHostName) && ! Strings.isTrimmedEmpty(from.sshHostName)) { + sshHostName = from.sshHostName; + } + if ( 0 == sshPortNumber && from.sshPortNumber != 0) { + sshPortNumber = from.sshPortNumber; + } + useSsh |= from.useSsh; + } + + @Override + public String toString() { + return hostName != null ? hostName : ""; +// return (hostName != null ? hostName : "") + ":" + portNumber + " " + useSsh + " " + sshUserName + "@" + sshHostName + ":" + sshPortNumber; + } + + public String toPrint() { + return "ConnectionParams{" + + "hostName='" + hostName + '\'' + + ", portNumber=" + portNumber + + ", sshUserName='" + sshUserName + '\'' + + ", sshHostName='" + sshHostName + '\'' + + ", sshPortNumber=" + sshPortNumber + + ", useSsh=" + useSsh + + '}'; + } + + @Override + public boolean equals(Object obj) { + if (null == obj || ! (obj instanceof ConnectionParams)) return false; + if (this == obj) return true; + ConnectionParams o = (ConnectionParams) obj; + return isEqualsNullable(hostName, o.hostName) && getPortNumber() == o.getPortNumber() && + useSsh == o.useSsh && isEqualsNullable(sshHostName, o.sshHostName) && + getSshPortNumber() == o.getSshPortNumber() && isEqualsNullable(sshUserName, o.sshUserName); + } + + private boolean isEqualsNullable(String one, String another) { + //noinspection StringEquality + return one == another || (null == one? "" : one).equals(null == another? "" : another); + } + + @Override + public int hashCode() { + long hash = (hostName != null? hostName.hashCode() : 0) + + portNumber * 17 + + (useSsh ? 781 : 693) + + (sshHostName != null? sshHostName.hashCode() : 0) * 23 + + (sshUserName != null? sshUserName.hashCode() : 0) * 37 + + sshPortNumber * 41; + return (int)hash; + } + + public void clearFields() { + hostName = ""; + portNumber = 0; + useSsh = false; + sshHostName = null; + sshUserName = null; + sshPortNumber = 0; + } +}
--- a/src/viewer_swing/java/com/glavsoft/viewer/swing/KeyEventListener.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/KeyEventListener.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -54,8 +54,8 @@ final int location = e.getKeyLocation(); if (0xffff == keyChar) { keyChar = convertToAscii? convertor.convert(keyChar, e) : 0; } if (keyChar < 0x20) { - if (e.isControlDown()) { - keyChar += 0x60; // TODO: From legacy code. What's this? + if (e.isControlDown() && keyChar != e.getKeyCode()) { + keyChar += 0x60; // to differ Ctrl-H from Ctrl-Backspace } else { switch (keyChar) { case KeyEvent.VK_BACK_SPACE: keyChar = K_BACK_SPACE; break;
--- a/src/viewer_swing/java/com/glavsoft/viewer/swing/KeyboardConvertor.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/KeyboardConvertor.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/LocalMouseCursorShape.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,21 @@ +package com.glavsoft.viewer.swing; + +/** + * @author dime at tightvnc.com + */ +public enum LocalMouseCursorShape { + DOT("dot"), + SMALL_DOT("smalldot"), + SYSTEM_DEFAULT("default"), + NO_CURSOR("nocursor"); + + private String cursorName; + + LocalMouseCursorShape(String name) { + this.cursorName = name; + } + + public String getCursorName() { + return cursorName; + } +}
--- a/src/viewer_swing/java/com/glavsoft/viewer/swing/ModifierButtonEventListener.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/ModifierButtonEventListener.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/viewer_swing/java/com/glavsoft/viewer/swing/MouseEventListener.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/MouseEventListener.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -24,17 +24,16 @@ package com.glavsoft.viewer.swing; +import com.glavsoft.rfb.IRepaintController; +import com.glavsoft.rfb.client.PointerEventMessage; +import com.glavsoft.rfb.protocol.ProtocolContext; + +import javax.swing.event.MouseInputAdapter; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.event.MouseWheelListener; -import javax.swing.event.MouseInputAdapter; - -import com.glavsoft.rfb.IRepaintController; -import com.glavsoft.rfb.client.PointerEventMessage; -import com.glavsoft.rfb.protocol.ProtocolContext; - public class MouseEventListener extends MouseInputAdapter implements MouseWheelListener { private static final byte BUTTON_LEFT = 1; @@ -56,8 +55,6 @@ public void processMouseEvent(MouseEvent mouseEvent, MouseWheelEvent mouseWheelEvent, boolean moved) { - // TODO handle scaling - byte buttonMask = 0; if (null == mouseEvent && mouseWheelEvent != null) { mouseEvent = mouseWheelEvent; @@ -106,11 +103,6 @@ } @Override - public void mouseEntered(MouseEvent mouseEvent) { - // TODO Auto-generated method stub - } - - @Override public void mouseDragged(MouseEvent mouseEvent) { processMouseEvent(mouseEvent, null, true); }
--- a/src/viewer_swing/java/com/glavsoft/viewer/swing/ParametersHandler.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/ParametersHandler.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -28,7 +28,7 @@ import com.glavsoft.rfb.protocol.LocalPointer; import com.glavsoft.rfb.protocol.ProtocolSettings; import com.glavsoft.utils.Strings; -import com.glavsoft.viewer.Viewer; +import com.glavsoft.viewer.UiSettings; import com.glavsoft.viewer.cli.Parser; import javax.swing.*; @@ -36,6 +36,7 @@ public class ParametersHandler { public static final String ARG_LOCAL_POINTER = "LocalPointer"; public static final String ARG_SCALING_FACTOR = "ScalingFactor"; + public static final String ARG_FULL_SCREEN = "FullScreen"; public static final String ARG_COLOR_DEPTH = "ColorDepth"; public static final String ARG_JPEG_IMAGE_QUALITY = "JpegImageQuality"; public static final String ARG_COMPRESSION_LEVEL = "CompressionLevel"; @@ -49,30 +50,23 @@ public static final String ARG_PORT = "port"; public static final String ARG_HOST = "host"; public static final String ARG_HELP = "help"; + public static final String ARG_VERBOSE = "v"; + public static final String ARG_VERBOSE_MORE = "vv"; public static final String ARG_CONVERT_TO_ASCII = "ConvertToASCII"; public static final String ARG_ALLOW_CLIPBOARD_TRANSFER = "AllowClipboardTransfer"; public static final String ARG_REMOTE_CHARSET = "RemoteCharset"; + public static final String ARG_SSH_HOST = "sshHost"; + public static final String ARG_SSH_USER = "sshUser"; + public static final String ARG_SSH_PORT = "sshPort"; + public static final String ARG_ALLOW_APPLET_INTERACTIVE_CONNECTIONS = "AllowAppletInteractiveConnections"; - public static class ConnectionParams { - public String hostName; - public int portNumber = Viewer.DEFAULT_PORT; - public boolean isHostNameEmpty() { - return Strings.isTrimmedEmpty(hostName); - } - void parsePortNumber(String port) { - try { - portNumber = Integer.parseInt(port); - } catch (NumberFormatException e) { /*nop*/ } - } - } + public static boolean isSeparateFrame; + public static boolean allowAppletInteractiveConnections; - public static boolean showControls; - public static boolean isSeparateFrame; - - public static void completeParserOptions(Parser parser) { + public static void completeParserOptions(Parser parser) { parser.addOption(ARG_HELP, null, "Print this help."); - parser.addOption(ARG_HOST, null, "Server host name."); - parser.addOption(ARG_PORT, "5900", "Port number."); + parser.addOption(ARG_HOST, "", "Server host name."); + parser.addOption(ARG_PORT, "0", "Port number."); parser.addOption(ARG_PASSWORD, null, "Password to the server."); parser.addOption(ARG_SHOW_CONTROLS, null, "Set to \"No\" if you want to get rid of that " + "button panel at the top. Default: \"Yes\"."); @@ -94,9 +88,12 @@ "\"Tight\" and \"Zlib\" encodings. Values: 1-9. Level 1 uses minimum of CPU " + "time on the server but achieves weak compression ratios. Level 9 offers best " + "compression but may be slow."); - parser.addOption(ARG_JPEG_IMAGE_QUALITY, null, "Use the specified image quality level " + + //noinspection ConstantConditions + parser.addOption(ARG_JPEG_IMAGE_QUALITY, null, "Use the specified image quality level " + "in \"Tight\" encoding. Values: 1-9, Lossless. Default value: " + - (String.valueOf(ProtocolSettings.DEFAULT_JPEG_QUALITY)) + (ProtocolSettings.DEFAULT_JPEG_QUALITY > 0 ? + String.valueOf(ProtocolSettings.DEFAULT_JPEG_QUALITY) : + "\"Lossless\"") + ". To prevent server of using " + "lossy JPEG compression in \"Tight\" encoding, use \"Lossless\" value here."); parser.addOption(ARG_LOCAL_POINTER, null, "Possible values: on/yes/true (draw pointer locally), off/no/false (let server draw pointer), hide). " + @@ -107,17 +104,26 @@ parser.addOption(ARG_SCALING_FACTOR, null, "Scale local representation of the remote desktop on startup. " + "The value is interpreted as scaling factor in percents. The default value of 100% " + "corresponds to the original framebuffer size."); - } + parser.addOption(ARG_FULL_SCREEN, null, "Full screen mode. Possible values: yes/true and no/false. Default: no."); + parser.addOption(ARG_SSH_HOST, "", "SSH host name."); + parser.addOption(ARG_SSH_PORT, "0", + "SSH port number. When empty, standard SSH port number (" + ConnectionParams.DEFAULT_SSH_PORT + ") is used."); + parser.addOption(ARG_SSH_USER, "", "SSH user name."); + parser.addOption(ARG_ALLOW_APPLET_INTERACTIVE_CONNECTIONS, null, "Allow applet interactively connect to other hosts then in HostName param or hostbase. Possible values: yes/true, no/false. Default: false."); + parser.addOption(ARG_VERBOSE, null, "Verbose console output."); + parser.addOption(ARG_VERBOSE_MORE, null, "More verbose console output."); - public static void completeSettingsFromCLI(Parser parser, ConnectionParams connectionParams, ProtocolSettings rfbSettings, UiSettings uiSettings) { - completeSettings(parser.getValueFor(ARG_HOST), parser.getValueFor(ARG_PORT), - parser.getValueFor(ARG_SHOW_CONTROLS), parser.getValueFor(ARG_VIEW_ONLY), - parser.getValueFor(ARG_ALLOW_CLIPBOARD_TRANSFER), parser.getValueFor(ARG_REMOTE_CHARSET), - parser.getValueFor(ARG_ALLOW_COPY_RECT), parser.getValueFor(ARG_SHARE_DESKTOP), - parser.getValueFor(ARG_ENCODING), parser.getValueFor(ARG_COMPRESSION_LEVEL), - parser.getValueFor(ARG_JPEG_IMAGE_QUALITY), parser.getValueFor(ARG_COLOR_DEPTH), - parser.getValueFor(ARG_SCALING_FACTOR), parser.getValueFor(ARG_LOCAL_POINTER), - parser.getValueFor(ARG_CONVERT_TO_ASCII), connectionParams, rfbSettings, uiSettings); + } + + public static int completeSettingsFromCLI(final Parser parser, ConnectionParams connectionParams, ProtocolSettings rfbSettings, UiSettings uiSettings) { + int mask = completeSettings( + new ParamsRetriever() { + @Override + public String getParamByName(String name) { + return parser.getValueFor(name); + } + }, + connectionParams, rfbSettings, uiSettings); // when hostName == a.b.c.d:3 where :3 is display num (X Window) we need add display num to port number if ( ! Strings.isTrimmedEmpty(connectionParams.hostName)) { splitConnectionParams(connectionParams, connectionParams.hostName); @@ -125,9 +131,14 @@ if (parser.isSetPlainOptions()) { splitConnectionParams(connectionParams, parser.getPlainOptionAt(0)); if (parser.getPlainOptionsNumber() > 1) { - connectionParams.parsePortNumber(parser.getPlainOptionAt(1)); - } + try { + connectionParams.parseRfbPortNumber(parser.getPlainOptionAt(1)); + } catch (WrongParameterException e) { + //nop + } + } } + return mask; } @@ -142,55 +153,96 @@ String[] splitted = host.split(":"); connectionParams.hostName = splitted[0]; if (splitted.length > 1) { - connectionParams.parsePortNumber(splitted[splitted.length - 1]); - } + try { + connectionParams.parseRfbPortNumber(splitted[splitted.length - 1]); + } catch (WrongParameterException e) { + //nop + } + } } else { connectionParams.hostName = host; } } - private static void completeSettings(String hostName, String portNumber, - String showControlsParam, String viewOnlyParam, String allowClipboardTransfer, - String remoteCharsetName, - String allowCopyRectParam, - String shareDesktopParam, String encodingParam, String compressionLevelParam, - String jpegQualityParam, String colorDepthParam, String scaleFactorParam, - String localPointerParam, - String convertToAsciiParam, ConnectionParams connectionParams, ProtocolSettings rfbSettings, UiSettings uiSettings) { + interface ParamsRetriever { + String getParamByName(String name); + } + private static int completeSettings(ParamsRetriever pr, ConnectionParams connectionParams, ProtocolSettings rfbSettings, UiSettings uiSettings) { + String hostName = pr.getParamByName(ARG_HOST); + String portNumber = pr.getParamByName(ARG_PORT); + String showControlsParam = pr.getParamByName(ARG_SHOW_CONTROLS); + String viewOnlyParam = pr.getParamByName(ARG_VIEW_ONLY); + String allowClipboardTransfer = pr.getParamByName(ARG_ALLOW_CLIPBOARD_TRANSFER); + String remoteCharsetName = pr.getParamByName(ARG_REMOTE_CHARSET); + String allowCopyRectParam = pr.getParamByName(ARG_ALLOW_COPY_RECT); + String shareDesktopParam = pr.getParamByName(ARG_SHARE_DESKTOP); + String encodingParam = pr.getParamByName(ARG_ENCODING); + String compressionLevelParam = pr.getParamByName(ARG_COMPRESSION_LEVEL); + String jpegQualityParam = pr.getParamByName(ARG_JPEG_IMAGE_QUALITY); + String colorDepthParam = pr.getParamByName(ARG_COLOR_DEPTH); + String scaleFactorParam = pr.getParamByName(ARG_SCALING_FACTOR); + String fullScreenParam = pr.getParamByName(ARG_FULL_SCREEN); + String localPointerParam = pr.getParamByName(ARG_LOCAL_POINTER); + String convertToAsciiParam = pr.getParamByName(ARG_CONVERT_TO_ASCII); + String sshHostNameParam = pr.getParamByName(ARG_SSH_HOST); + String sshPortNumberParam = pr.getParamByName(ARG_SSH_PORT); + String sshUserNameParam = pr.getParamByName(ARG_SSH_USER); + connectionParams.hostName = hostName; - try { - connectionParams.portNumber = Integer.parseInt(portNumber); - } catch (NumberFormatException e) { /* nop */ } + try { + connectionParams.parseRfbPortNumber(portNumber); + } catch (WrongParameterException e) { + //nop + } - showControls = parseBooleanOrDefault(showControlsParam, true); + connectionParams.sshHostName = sshHostNameParam; + connectionParams.setUseSsh( ! Strings.isTrimmedEmpty(sshHostNameParam)); + connectionParams.parseSshPortNumber(sshPortNumberParam); + connectionParams.sshUserName = sshUserNameParam; + + int rfbMask = 0; + uiSettings.showControls = parseBooleanOrDefault(showControlsParam, true); + allowAppletInteractiveConnections = + parseBooleanOrDefault(pr.getParamByName(ARG_ALLOW_APPLET_INTERACTIVE_CONNECTIONS), false); rfbSettings.setViewOnly(parseBooleanOrDefault(viewOnlyParam, false)); + if (isGiven(viewOnlyParam)) rfbMask |= ProtocolSettings.CHANGED_VIEW_ONLY; rfbSettings.setAllowClipboardTransfer(parseBooleanOrDefault(allowClipboardTransfer, true)); - rfbSettings.setRemoteCharsetName(remoteCharsetName); - rfbSettings.setAllowCopyRect(parseBooleanOrDefault(allowCopyRectParam, true)); - rfbSettings.setSharedFlag(parseBooleanOrDefault(shareDesktopParam, true)); - rfbSettings.setConvertToAscii(parseBooleanOrDefault(convertToAsciiParam, false)); - if (EncodingType.TIGHT.getName().equalsIgnoreCase(encodingParam)) { + if (isGiven(allowClipboardTransfer)) rfbMask |= ProtocolSettings.CHANGED_ALLOW_CLIPBOARD_TRANSFER; + rfbSettings.setRemoteCharsetName(remoteCharsetName); + rfbSettings.setAllowCopyRect(parseBooleanOrDefault(allowCopyRectParam, true)); + if (isGiven(allowCopyRectParam)) rfbMask |= ProtocolSettings.CHANGED_ALLOW_COPY_RECT; + rfbSettings.setSharedFlag(parseBooleanOrDefault(shareDesktopParam, true)); + if (isGiven(shareDesktopParam)) rfbMask |= ProtocolSettings.CHANGED_SHARED; + rfbSettings.setConvertToAscii(parseBooleanOrDefault(convertToAsciiParam, false)); + if (isGiven(convertToAsciiParam)) rfbMask |= ProtocolSettings.CHANGED_CONVERT_TO_ASCII; + if (EncodingType.TIGHT.getName().equalsIgnoreCase(encodingParam)) { rfbSettings.setPreferredEncoding(EncodingType.TIGHT); + rfbMask |= ProtocolSettings.CHANGED_ENCODINGS; } if (EncodingType.HEXTILE.getName().equalsIgnoreCase(encodingParam)) { rfbSettings.setPreferredEncoding(EncodingType.HEXTILE); + rfbMask |= ProtocolSettings.CHANGED_ENCODINGS; } if (EncodingType.ZRLE.getName().equalsIgnoreCase(encodingParam)) { rfbSettings.setPreferredEncoding(EncodingType.ZRLE); + rfbMask |= ProtocolSettings.CHANGED_ENCODINGS; } if (EncodingType.RAW_ENCODING.getName().equalsIgnoreCase(encodingParam)) { rfbSettings.setPreferredEncoding(EncodingType.RAW_ENCODING); + rfbMask |= ProtocolSettings.CHANGED_ENCODINGS; } - try { + try { int compLevel = Integer.parseInt(compressionLevelParam); if (compLevel > 0 && compLevel <= 9) { rfbSettings.setCompressionLevel(compLevel); + rfbMask |= ProtocolSettings.CHANGED_COMPRESSION_LEVEL; } } catch (NumberFormatException e) { /* nop */ } try { int jpegQuality = Integer.parseInt(jpegQualityParam); if (jpegQuality > 0 && jpegQuality <= 9) { rfbSettings.setJpegQuality(jpegQuality); + rfbMask |= ProtocolSettings.CHANGED_JPEG_QUALITY; } } catch (NumberFormatException e) { if ("lossless".equalsIgnoreCase(jpegQualityParam)) { @@ -199,63 +251,67 @@ } try { int colorDepth = Integer.parseInt(colorDepthParam); - rfbSettings.setBitsPerPixel(colorDepth); + rfbSettings.setColorDepth(colorDepth); + rfbMask |= ProtocolSettings.CHANGED_COLOR_DEPTH; } catch (NumberFormatException e) { /* nop */ } + int uiMask = 0; if (scaleFactorParam != null) { try { int scaleFactor = Integer.parseInt(scaleFactorParam.replaceAll("\\D", "")); if (scaleFactor >= 10 && scaleFactor <= 200) { uiSettings.setScalePercent(scaleFactor); + uiMask |= UiSettings.CHANGED_SCALE_FACTOR; } } catch (NumberFormatException e) { /* nop */ } } + uiSettings.setFullScreen(parseBooleanOrDefault(fullScreenParam, false)); + if (isGiven(fullScreenParam)) uiMask |= UiSettings.CHANGED_FULL_SCREEN; - if ("on".equalsIgnoreCase(localPointerParam) || + if ("on".equalsIgnoreCase(localPointerParam) || "true".equalsIgnoreCase(localPointerParam) || "yes".equalsIgnoreCase(localPointerParam)) { rfbSettings.setMouseCursorTrack(LocalPointer.ON); + rfbMask |= ProtocolSettings.CHANGED_MOUSE_CURSOR_TRACK; } if ("off".equalsIgnoreCase(localPointerParam) || "no".equalsIgnoreCase(localPointerParam) || "false".equalsIgnoreCase(localPointerParam)) { rfbSettings.setMouseCursorTrack(LocalPointer.OFF); + rfbMask |= ProtocolSettings.CHANGED_MOUSE_CURSOR_TRACK; } if ("hide".equalsIgnoreCase(localPointerParam) || "hidden".equalsIgnoreCase(localPointerParam)) { rfbSettings.setMouseCursorTrack(LocalPointer.HIDE); + rfbMask |= ProtocolSettings.CHANGED_MOUSE_CURSOR_TRACK; } + return (uiMask << 16) | rfbMask; } + private static boolean isGiven(String param) { + return ! Strings.isTrimmedEmpty(param); + } + static boolean parseBooleanOrDefault(String param, boolean defaultValue) { return defaultValue ? ! ("no".equalsIgnoreCase(param) || "false".equalsIgnoreCase(param)) : "yes".equalsIgnoreCase(param) || "true".equalsIgnoreCase(param); } - public static void completeSettingsFromApplet(JApplet applet, + public static int completeSettingsFromApplet(final JApplet applet, ConnectionParams connectionParams, ProtocolSettings rfbSettings, UiSettings uiSettings) { - - String host = applet.getParameter(ARG_HOST); - if (Strings.isTrimmedEmpty(host)) { - host = applet.getCodeBase().getHost(); - } - completeSettings(host, - applet.getParameter(ARG_PORT), - applet.getParameter(ARG_SHOW_CONTROLS), - applet.getParameter(ARG_VIEW_ONLY), - applet.getParameter(ARG_ALLOW_CLIPBOARD_TRANSFER), - applet.getParameter(ARG_REMOTE_CHARSET), - applet.getParameter(ARG_ALLOW_COPY_RECT), - applet.getParameter(ARG_SHARE_DESKTOP), - applet.getParameter(ARG_ENCODING), - applet.getParameter(ARG_COMPRESSION_LEVEL), - applet.getParameter(ARG_JPEG_IMAGE_QUALITY), - applet.getParameter(ARG_COLOR_DEPTH), - applet.getParameter(ARG_SCALING_FACTOR), - applet.getParameter(ARG_LOCAL_POINTER), - applet.getParameter(ARG_CONVERT_TO_ASCII), - connectionParams, rfbSettings, uiSettings); - isSeparateFrame = parseBooleanOrDefault(applet.getParameter(ARG_OPEN_NEW_WINDOW), true); + isSeparateFrame = parseBooleanOrDefault(applet.getParameter(ARG_OPEN_NEW_WINDOW), true); + final int paramsMask = completeSettings( + new ParamsRetriever() { + @Override + public String getParamByName(String name) { + return applet.getParameter(name); + } + }, + connectionParams, rfbSettings, uiSettings); + if ( ! allowAppletInteractiveConnections && connectionParams.isHostNameEmpty()) { + connectionParams.hostName = applet.getCodeBase().getHost(); + } + return paramsMask; }
--- a/src/viewer_swing/java/com/glavsoft/viewer/swing/RendererImpl.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/RendererImpl.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -37,7 +37,8 @@ import java.util.concurrent.TimeoutException; public class RendererImpl extends Renderer implements ImageObserver { - private final Image offscreanImage; + CyclicBarrier barrier = new CyclicBarrier(2); + private final Image offscreanImage; public RendererImpl(Reader reader, int width, int height, PixelFormat pixelFormat) { if (0 == width) width = 1; if (0 == height) height = 1; @@ -61,7 +62,6 @@ * @param jpegBufferLength jpeg image data array length * @param rect image location and dimensions */ - CyclicBarrier barier = new CyclicBarrier(2); @Override public void drawJpegImage(byte[] bytes, int offset, int jpegBufferLength, FramebufferUpdateRectangle rect) { @@ -69,7 +69,7 @@ offset, jpegBufferLength); Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1, this); try { - barier.await(3, TimeUnit.SECONDS); + barrier.await(3, TimeUnit.SECONDS); } catch (InterruptedException e) { // nop } catch (BrokenBarrierException e) { @@ -87,7 +87,7 @@ boolean isReady = (infoflags & (ALLBITS | ABORT)) != 0; if (isReady) { try { - barier.await(); + barrier.await(); } catch (InterruptedException e) { // nop } catch (BrokenBarrierException e) {
--- a/src/viewer_swing/java/com/glavsoft/viewer/swing/SoftCursorImpl.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/SoftCursorImpl.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //-------------------------------------------------------------------------
--- a/src/viewer_swing/java/com/glavsoft/viewer/swing/Surface.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/Surface.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -33,7 +33,7 @@ import com.glavsoft.rfb.protocol.ProtocolContext; import com.glavsoft.rfb.protocol.ProtocolSettings; import com.glavsoft.transport.Reader; -import com.glavsoft.viewer.Viewer; +import com.glavsoft.viewer.UiSettings; import javax.swing.*; import java.awt.*; @@ -51,8 +51,9 @@ private ModifierButtonEventListener modifierButtonListener; private boolean isUserInputEnabled = false; private final ProtocolContext context; - private double scaleFactor; - private final Viewer viewer; + private SwingViewerWindow viewerWindow; + private double scaleFactor; + public Dimension oldSize; @Override public boolean isDoubleBuffered() { @@ -61,19 +62,25 @@ return false; } - public Surface(ProtocolContext context, Viewer viewer, double scaleFactor) { + public Surface(ProtocolContext context, double scaleFactor, LocalMouseCursorShape mouseCursorShape) { this.context = context; - this.viewer = viewer; this.scaleFactor = scaleFactor; init(context.getFbWidth(), context.getFbHeight()); + oldSize = getPreferredSize(); if ( ! context.getSettings().isViewOnly()) { setUserInputEnabled(true, context.getSettings().isConvertToAscii()); } showCursor = context.getSettings().isShowRemoteCursor(); + setLocalCursorShape(mouseCursorShape); } - private void setUserInputEnabled(boolean enable, boolean convertToAscii) { + // TODO Extract abstract/interface ViewerWindow from SwingViewerWindow + public void setViewerWindow(SwingViewerWindow viewerWindow) { + this.viewerWindow = viewerWindow; + } + + private void setUserInputEnabled(boolean enable, boolean convertToAscii) { if (enable == isUserInputEnabled) return; isUserInputEnabled = enable; if (enable) { @@ -105,7 +112,7 @@ @Override public Renderer createRenderer(Reader reader, int width, int height, PixelFormat pixelFormat) { renderer = new RendererImpl(reader, width, height, pixelFormat); - synchronized (renderer) { + synchronized (renderer.getLock()) { cursor = renderer.getCursor(); } init(renderer.getWidth(), renderer.getHeight()); @@ -121,21 +128,22 @@ private void updateFrameSize() { setSize(getPreferredSize()); - viewer.packContainer(); + viewerWindow.pack(); requestFocus(); } @Override public void paintComponent(Graphics g) { + if (null == renderer) return; ((Graphics2D)g).scale(scaleFactor, scaleFactor); ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - synchronized (renderer) { + synchronized (renderer.getLock()) { Image offscreenImage = renderer.getOffscreenImage(); if (offscreenImage != null) { g.drawImage(offscreenImage, 0, 0, null); } } - synchronized (cursor) { + synchronized (cursor.getLock()) { Image cursorImage = cursor.getImage(); if (showCursor && cursorImage != null && (scaleFactor != 1 || @@ -179,7 +187,7 @@ @Override public void repaintCursor() { - synchronized (cursor) { + synchronized (cursor.getLock()) { repaint((int)(cursor.oldRX * scaleFactor), (int)(cursor.oldRY * scaleFactor), (int)Math.ceil(cursor.oldWidth * scaleFactor) + 1, (int)Math.ceil(cursor.oldHeight * scaleFactor) + 1); repaint((int)(cursor.rX * scaleFactor), (int)(cursor.rY * scaleFactor), @@ -189,14 +197,14 @@ @Override public void updateCursorPosition(short x, short y) { - synchronized (cursor) { + synchronized (cursor.getLock()) { cursor.updatePosition(x, y); repaintCursor(); } } private void showCursor(boolean show) { - synchronized (cursor) { + synchronized (cursor.getLock()) { showCursor = show; } } @@ -215,14 +223,26 @@ setUserInputEnabled( ! settings.isViewOnly(), settings.isConvertToAscii()); showCursor(settings.isShowRemoteCursor()); } else if (UiSettings.isUiSettingsChangedFired(e)) { - UiSettings settings = (UiSettings) e.getSource(); - scaleFactor = settings.getScaleFactor(); + UiSettings uiSettings = (UiSettings) e.getSource(); + oldSize = getPreferredSize(); + scaleFactor = uiSettings.getScaleFactor(); + if (uiSettings.isChangedMouseCursorShape()) { + setLocalCursorShape(uiSettings.getMouseCursorShape()); + } } mouseEventListener.setScaleFactor(scaleFactor); updateFrameSize(); } - @Override + public void setLocalCursorShape(LocalMouseCursorShape cursorShape) { + if (LocalMouseCursorShape.SYSTEM_DEFAULT == cursorShape) { + setCursor(Cursor.getDefaultCursor()); + } else { + setCursor(Utils.getCursor(cursorShape)); + } + } + + @Override public void setPixelFormat(PixelFormat pixelFormat) { if (renderer != null) { renderer.initPixelFormat(pixelFormat);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/SwingConnectionWorkerFactory.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,66 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer.swing; + +import com.glavsoft.viewer.AbstractConnectionWorkerFactory; +import com.glavsoft.viewer.ConnectionPresenter; +import com.glavsoft.viewer.NetworkConnectionWorker; +import com.glavsoft.viewer.RfbConnectionWorker; + +import javax.swing.*; + +/** + * @author dime at tightvnc.com + */ +public class SwingConnectionWorkerFactory extends AbstractConnectionWorkerFactory { + + private JFrame parentWindow; + private String predefinedPassword; + private final ConnectionPresenter presenter; + private final SwingViewerWindowFactory viewerWindowFactory; + + public SwingConnectionWorkerFactory(JFrame parentWindow, String predefinedPassword, ConnectionPresenter presenter, + SwingViewerWindowFactory viewerWindowFactory) { + this.parentWindow = parentWindow; + this.predefinedPassword = predefinedPassword; + this.presenter = presenter; + this.viewerWindowFactory = viewerWindowFactory; + } + + @Override + public NetworkConnectionWorker createNetworkConnectionWorker() { + return new SwingNetworkConnectionWorker(parentWindow); + } + + @Override + public RfbConnectionWorker createRfbConnectionWorker() { + return new SwingRfbConnectionWorker(predefinedPassword, presenter, parentWindow, viewerWindowFactory); + } + + @Override + public void setPredefinedPassword(String predefinedPassword) { + this.predefinedPassword = predefinedPassword; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/SwingNetworkConnectionWorker.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,181 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer.swing; + +import com.glavsoft.viewer.*; +import com.glavsoft.viewer.swing.ssh.SshConnectionManager; + +import javax.swing.*; +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.AccessControlException; +import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import java.util.logging.Logger; + + +public class SwingNetworkConnectionWorker extends SwingWorker<Socket, String> implements NetworkConnectionWorker { + public static final int MAX_HOSTNAME_LENGTH_FOR_MESSAGES = 40; + private final JFrame parentWindow; + private Logger logger; + private boolean hasSshSupport; + private ConnectionParams connectionParams; + private ConnectionPresenter presenter; + + + public SwingNetworkConnectionWorker(JFrame parentWindow) { + this.parentWindow = parentWindow; + logger = Logger.getLogger(getClass().getName()); + } + + @Override + public Socket doInBackground() throws Exception { + String s = "<b>" +connectionParams.hostName + "</b>:" + connectionParams.getPortNumber(); + if (connectionParams.useSsh()) { + s += " <i>(via ssh://" + connectionParams.sshUserName + "@" + connectionParams.sshHostName + ":" + connectionParams.getSshPortNumber() + ")</i>"; + } + + String message = "<html>Trying to connect to " + s + "</html>"; + logger.info(message.replaceAll("<[^<>]+?>", "")); + publish(message); + int port; + String host; + if (hasSshSupport && connectionParams.useSsh()) { + SshConnectionManager sshConnectionManager = new SshConnectionManager(parentWindow); + message = "Creating SSH tunnel to " + connectionParams.sshHostName + ":" + connectionParams.getSshPortNumber(); + logger.info(message); + publish(message); + port = sshConnectionManager.connect(connectionParams); + if (sshConnectionManager.isConnected() ) { + host = "127.0.0.1"; + message = "SSH tunnel established: " + host + ":" + port; + logger.info(message); + publish(message); + } else { + throw new ConnectionErrorException("Could not create SSH tunnel: " + sshConnectionManager.getErrorMessage()); + } + } else { + host = connectionParams.hostName; + port = connectionParams.getPortNumber(); + } + + message = "Connecting to host " + host + ":" + port + (connectionParams.useSsh() ? " (tunneled)" : ""); + logger.info(message); + publish(message); + + return new Socket(host, port); + } + + private String formatHostString(String hostName) { + if (hostName.length() <= MAX_HOSTNAME_LENGTH_FOR_MESSAGES) { + return hostName; + } else { + return hostName.substring(0, MAX_HOSTNAME_LENGTH_FOR_MESSAGES) + "..."; + } + } + + @Override + protected void process(List<String> strings) { // EDT + String message = strings.get(strings.size() - 1); // get last + presenter.showMessage(message); + } + + @Override + protected void done() { // EDT + try { + final Socket socket = get(); + presenter.successfulNetworkConnection(socket); + } catch (CancellationException e) { + logger.info("Cancelled"); + presenter.showMessage("Cancelled"); + presenter.connectionFailed(); + } catch (InterruptedException e) { + logger.info("Interrupted"); + presenter.showMessage("Interrupted"); + presenter.connectionFailed(); + } catch (ExecutionException e) { + String errorMessage = null; + try { + throw e.getCause(); + } catch (UnknownHostException uhe) { + logger.severe("Unknown host: " + connectionParams.hostName); + errorMessage = "Unknown host: '" + formatHostString(connectionParams.hostName) + "'"; + } catch (IOException ioe) { + logger.severe("Couldn't connect to '" + connectionParams.hostName + + ":" + connectionParams.getPortNumber() + "':\n" + ioe.getMessage()); + logger.log(Level.FINEST, "Couldn't connect to '" + connectionParams.hostName + + ":" + connectionParams.getPortNumber() + "':\n" + ioe.getMessage(), ioe); + errorMessage = "Couldn't connect to '" + formatHostString(connectionParams.hostName) + + ":" + connectionParams.getPortNumber() + "':\n" + ioe.getMessage(); + } catch (CancelConnectionException cce) { + logger.severe("Cancelled: " + cce.getMessage()); + } catch (AccessControlException ace) { + logger.severe("Couldn't connect to: " + + connectionParams.hostName + ":" + connectionParams.getPortNumber() + + ": " + ace.getMessage()); + logger.log(Level.FINEST, "Couldn't connect to: " + + connectionParams.hostName + ":" + connectionParams.getPortNumber() + + ": " + ace.getMessage(), ace); + errorMessage = "Access control error"; + } catch (ConnectionErrorException cee) { + logger.severe(cee.getMessage() + " host: " + + connectionParams.hostName + ":" + connectionParams.getPortNumber()); + errorMessage = cee.getMessage() + "\nHost: " + + formatHostString(connectionParams.hostName) + ":" + connectionParams.getPortNumber(); + } catch (Throwable throwable) { + logger.log(Level.FINEST, "Couldn't connect to '" + formatHostString(connectionParams.hostName) + + ":" + connectionParams.getPortNumber() + "':\n" + throwable.getMessage(), throwable); + errorMessage = "Couldn't connect to '" + formatHostString(connectionParams.hostName) + + ":" + connectionParams.getPortNumber() + "':\n" + throwable.getMessage(); + } + presenter.showConnectionErrorDialog(errorMessage); + presenter.clearMessage(); + presenter.connectionFailed(); + } + } + + @Override + public void setConnectionParams(ConnectionParams connectionParams) { + this.connectionParams = connectionParams; + } + + @Override + public void setPresenter(ConnectionPresenter presenter) { + this.presenter = presenter; + } + + @Override + public void setHasSshSupport(boolean hasSshSupport) { + this.hasSshSupport = hasSshSupport; + } + + @Override + public boolean cancel() { + return super.cancel(true); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/SwingRfbConnectionWorker.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,272 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer.swing; + +import com.glavsoft.exceptions.*; +import com.glavsoft.rfb.IPasswordRetriever; +import com.glavsoft.rfb.IRfbSessionListener; +import com.glavsoft.rfb.protocol.Protocol; +import com.glavsoft.rfb.protocol.ProtocolSettings; +import com.glavsoft.transport.Reader; +import com.glavsoft.transport.Writer; +import com.glavsoft.utils.Strings; +import com.glavsoft.viewer.*; +import com.glavsoft.viewer.swing.gui.PasswordDialog; + +import javax.swing.*; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.Socket; +import java.util.List; +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.logging.Logger; + +/** +* @author dime at tightvnc.com +*/ +public class SwingRfbConnectionWorker extends SwingWorker<Void, String> implements RfbConnectionWorker, IRfbSessionListener { + + private String predefinedPassword; + private ConnectionPresenter presenter; + private JFrame parentWindow; + private SwingViewerWindowFactory viewerWindowFactory; + private Logger logger; + private volatile boolean isStoppingProcess; + private SwingViewerWindow viewerWindow; + protected String connectionString; + protected Protocol workingProtocol; + protected Socket workingSocket; + protected ProtocolSettings rfbSettings; + protected UiSettings uiSettings; + + @Override + public Void doInBackground() throws Exception { + if (null == workingSocket) throw new ConnectionErrorException("Null socket"); + workingSocket.setTcpNoDelay(true); // disable Nagle algorithm + Reader reader = new Reader(workingSocket.getInputStream()); + Writer writer = new Writer(workingSocket.getOutputStream()); + + workingProtocol = new Protocol(reader, writer, + new PasswordChooser(connectionString, parentWindow, this), + rfbSettings); + String message = "Handshaking with remote host"; + logger.info(message); + publish(message); + + workingProtocol.handshake(); +// tryAgain = false; + return null; + } + + public SwingRfbConnectionWorker(String predefinedPassword, ConnectionPresenter presenter, JFrame parentWindow, + SwingViewerWindowFactory viewerWindowFactory) { + this.predefinedPassword = predefinedPassword; + this.presenter = presenter; + this.parentWindow = parentWindow; + this.viewerWindowFactory = viewerWindowFactory; + logger = Logger.getLogger(getClass().getName()); + } + + + @Override + protected void process(List<String> strings) { // EDT + String message = strings.get(strings.size() - 1); // get last + presenter.showMessage(message); + } + + @Override + protected void done() { // EDT + try { + get(); + presenter.showMessage("Handshake established"); + ClipboardControllerImpl clipboardController = + new ClipboardControllerImpl(workingProtocol, rfbSettings.getRemoteCharsetName()); + clipboardController.setEnabled(rfbSettings.isAllowClipboardTransfer()); + rfbSettings.addListener(clipboardController); + viewerWindow = viewerWindowFactory.createViewerWindow( + workingProtocol, rfbSettings, uiSettings, connectionString, presenter); + + workingProtocol.startNormalHandling(this, viewerWindow.getSurface(), clipboardController); + presenter.showMessage("Started"); + + presenter.successfulRfbConnection(); + } catch (CancellationException e) { + logger.info("Cancelled"); + presenter.showMessage("Cancelled"); + presenter.connectionCancelled(); + } catch (InterruptedException e) { + logger.info("Interrupted"); + presenter.showMessage("Interrupted"); + presenter.connectionFailed(); + } catch (ExecutionException ee) { + String errorTitle; + String errorMessage; + try { + throw ee.getCause(); + } catch (UnsupportedProtocolVersionException e) { + errorTitle = "Unsupported Protocol Version"; + errorMessage = e.getMessage(); + logger.severe(errorMessage); + } catch (UnsupportedSecurityTypeException e) { + errorTitle = "Unsupported Security Type"; + errorMessage = e.getMessage(); + logger.severe(errorMessage); + } catch (AuthenticationFailedException e) { + errorTitle = "Authentication Failed"; + errorMessage = e.getMessage(); + logger.severe(errorMessage); + presenter.clearPredefinedPassword(); + } catch (TransportException e) { +// if ( ! isAppletStopped) { + errorTitle = "Connection Error"; + errorMessage = "Connection Error: " + e.getMessage(); + logger.severe(errorMessage); +// } + } catch (IOException e) { + errorTitle = "Connection Error"; + errorMessage = "Connection Error: " + e.getMessage(); + logger.severe(errorMessage); + } catch (FatalException e) { + errorTitle = "Connection Error"; + errorMessage = "Connection Error: " + e.getMessage(); + logger.severe(errorMessage); + } catch (Throwable e) { + errorTitle = "Error"; + errorMessage = "Error: " + e.getMessage(); + logger.severe(errorMessage); + } + presenter.showReconnectDialog(errorTitle, errorMessage); + presenter.clearMessage(); + presenter.connectionFailed(); + } + } + + @Override + public void rfbSessionStopped(final String reason) { + if (workingProtocol != null) { + workingProtocol.cleanUpSession(); + } + if (isStoppingProcess) return; + cleanUpUISessionAndConnection(); + logger.info("Rfb session stopped: " + reason); + if (presenter.needReconnection()) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + presenter.showReconnectDialog("Connection error", reason); + presenter.reconnect(predefinedPassword); + } + }); + } + } + + @Override + public boolean cancel() { + boolean res = super.cancel(true); + if (res && workingProtocol != null) { + workingProtocol.cleanUpSession(); + } + cleanUpUISessionAndConnection(); + return res; + } + + private synchronized void cleanUpUISessionAndConnection() { + isStoppingProcess = true; + if (workingSocket != null && workingSocket.isConnected()) { + try { + workingSocket.close(); + } catch (IOException e) { /*nop*/ } + } + if (viewerWindow != null) { + viewerWindow.close(); + } + isStoppingProcess = false; + } + + @Override + public void setWorkingSocket(Socket workingSocket) { + this.workingSocket = workingSocket; + } + + @Override + public void setRfbSettings(ProtocolSettings rfbSettings) { + this.rfbSettings = rfbSettings; + } + + @Override + public void setUiSettings(UiSettings uiSettings) { + this.uiSettings = uiSettings; + } + + @Override + public void setConnectionString(String connectionString) { + this.connectionString = connectionString; + } + + /** + * Ask user for password if needed + */ + private class PasswordChooser implements IPasswordRetriever { + PasswordDialog passwordDialog; + private String connectionString; + private final JFrame owner; + private final ConnectionWorker onCancel; + + private PasswordChooser(String connectionString, JFrame parentWindow, ConnectionWorker onCancel) { + this.connectionString = connectionString; + this.owner = parentWindow; + this.onCancel = onCancel; + } + + @Override + public String getPassword() { + return Strings.isTrimmedEmpty(predefinedPassword) ? + getPasswordFromGUI() : + predefinedPassword; + } + + private String getPasswordFromGUI() { + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + if (null == passwordDialog) { + passwordDialog = new PasswordDialog(owner, onCancel); + } + passwordDialog.setServerHostName(connectionString); + passwordDialog.toFront(); + passwordDialog.setVisible(true); + } + }); + } catch (InterruptedException e) { + //nop + } catch (InvocationTargetException e) { + //nop + } + return passwordDialog.getPassword(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/SwingViewerWindow.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,896 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer.swing; + +import com.glavsoft.core.SettingsChangedEvent; +import com.glavsoft.rfb.IChangeSettingsListener; +import com.glavsoft.rfb.client.KeyEventMessage; +import com.glavsoft.rfb.protocol.Protocol; +import com.glavsoft.rfb.protocol.ProtocolContext; +import com.glavsoft.rfb.protocol.ProtocolSettings; +import com.glavsoft.utils.Keymap; +import com.glavsoft.viewer.ConnectionPresenter; +import com.glavsoft.viewer.UiSettings; +import com.glavsoft.viewer.Viewer; +import com.glavsoft.viewer.swing.gui.OptionsDialog; + +import javax.swing.*; +import javax.swing.border.BevelBorder; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.*; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; + +public class SwingViewerWindow implements IChangeSettingsListener { + public static final int FS_SCROLLING_ACTIVE_BORDER = 20; + private JToggleButton zoomFitButton; + private JToggleButton zoomFullScreenButton; + private JButton zoomInButton; + private JButton zoomOutButton; + private JButton zoomAsIsButton; + private JPanel outerPanel; + private JScrollPane scroller; + private JFrame frame; + private boolean forceResizable = true; + private ButtonsBar buttonsBar; + private Surface surface; + private boolean isSeparateFrame; + private final boolean isApplet; + private Viewer viewer; + private String connectionString; + private ConnectionPresenter presenter; + private Rectangle oldContainerBounds; + private volatile boolean isFullScreen; + private Border oldScrollerBorder; + private JLayeredPane lpane; + private EmptyButtonsBarMouseAdapter buttonsBarMouseAdapter; + private String remoteDesktopName; + private ProtocolSettings rfbSettings; + private UiSettings uiSettings; + private Protocol workingProtocol; + + private boolean isZoomToFitSelected; + private List<JComponent> kbdButtons; + + public SwingViewerWindow(Protocol workingProtocol, ProtocolSettings rfbSettings, UiSettings uiSettings, Surface surface, + boolean isSeparateFrame, boolean isApplet, Viewer viewer, String connectionString, + ConnectionPresenter presenter) { + this.workingProtocol = workingProtocol; + this.rfbSettings = rfbSettings; + this.uiSettings = uiSettings; + this.surface = surface; + this.isSeparateFrame = isSeparateFrame; + this.isApplet = isApplet; + this.viewer = viewer; + this.connectionString = connectionString; + this.presenter = presenter; + createContainer(surface, isApplet, viewer); + + if (uiSettings.showControls) { + createButtonsPanel(workingProtocol, isSeparateFrame? frame: viewer); + if (isSeparateFrame) registerResizeListener(frame); + updateZoomButtonsState(); + } + if (uiSettings.isFullScreen()) { + switchOnFullscreenMode(); + } + setSurfaceToHandleKbdFocus(); + if (isSeparateFrame) { + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // nop + } + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + frame.toFront(); + } + }); + } + }).start(); + } + } + + private void createContainer(final Surface surface, boolean isApplet, JApplet appletWindow) { + outerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)) { + @Override + public Dimension getSize() { + return surface.getPreferredSize(); + } + @Override + public Dimension getPreferredSize() { + return surface.getPreferredSize(); + } + }; + outerPanel.setBackground(Color.DARK_GRAY); + lpane = new JLayeredPane() { + @Override + public Dimension getSize() { + return surface.getPreferredSize(); + } + @Override + public Dimension getPreferredSize() { + return surface.getPreferredSize(); + } + }; + lpane.setPreferredSize(surface.getPreferredSize()); + lpane.add(surface, JLayeredPane.DEFAULT_LAYER, 0); + outerPanel.add(lpane); + + scroller = new JScrollPane(outerPanel); + if (isSeparateFrame) { + frame = new JFrame(); + if ( ! isApplet) { + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + } + frame.setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE); + Utils.setApplicationIconsForWindow(frame); + frame.setLayout(new BorderLayout(0, 0)); + frame.add(scroller, BorderLayout.CENTER); + +// frame.pack(); + outerPanel.setSize(surface.getPreferredSize()); + internalPack(null); + frame.setVisible(true); + frame.validate(); + } else { + appletWindow.setLayout(new BorderLayout(0, 0)); + appletWindow.add(scroller, BorderLayout.CENTER); + appletWindow.validate(); + } + } + + public void pack() { + final Dimension outerPanelOldSize = outerPanel.getSize(); + outerPanel.setSize(surface.getPreferredSize()); + if (isSeparateFrame && ! isZoomToFitSelected()) { + internalPack(outerPanelOldSize); + } + if (buttonsBar != null) { + updateZoomButtonsState(); + } + updateWindowTitle(); + } + + public boolean isZoomToFitSelected() { + return isZoomToFitSelected; + } + + public void setZoomToFitSelected(boolean zoomToFitSelected) { + isZoomToFitSelected = zoomToFitSelected; + } + + public void setRemoteDesktopName(String name) { + remoteDesktopName = name; + updateWindowTitle(); + } + + private void updateWindowTitle() { + if (isSeparateFrame) { + frame.setTitle(remoteDesktopName + " [zoom: " + uiSettings.getScalePercentFormatted() + "%]"); + } + } + + private void internalPack(Dimension outerPanelOldSize) { + final Rectangle workareaRectangle = getWorkareaRectangle(); + if (workareaRectangle.equals(frame.getBounds())) { + forceResizable = true; + } + final boolean isHScrollBar = scroller.getHorizontalScrollBar().isShowing() && ! forceResizable; + final boolean isVScrollBar = scroller.getVerticalScrollBar().isShowing() && ! forceResizable; + + boolean isWidthChangeable = true; + boolean isHeightChangeable = true; + if (outerPanelOldSize != null && surface.oldSize != null) { + isWidthChangeable = forceResizable || + (outerPanelOldSize.width == surface.oldSize.width && ! isHScrollBar); + isHeightChangeable = forceResizable || + (outerPanelOldSize.height == surface.oldSize.height && ! isVScrollBar); + } + forceResizable = false; + frame.validate(); + + final Insets containerInsets = frame.getInsets(); + Dimension preferredSize = frame.getPreferredSize(); + Rectangle preferredRectangle = new Rectangle(frame.getLocation(), preferredSize); + + if (null == outerPanelOldSize && workareaRectangle.contains(preferredRectangle)) { + frame.pack(); + } else { + Dimension minDimension = new Dimension( + containerInsets.left + containerInsets.right, containerInsets.top + containerInsets.bottom); + if (buttonsBar != null && buttonsBar.isVisible) { + minDimension.width += buttonsBar.getWidth(); + minDimension.height += buttonsBar.getHeight(); + } + Dimension dim = new Dimension(preferredSize); + Point location = frame.getLocation(); + if ( ! isWidthChangeable) { + dim.width = frame.getWidth(); + } else { + if (isVScrollBar) dim.width += scroller.getVerticalScrollBar().getWidth(); + if (dim.width < minDimension.width) dim.width = minDimension.width; + + int dx = location.x - workareaRectangle.x; + if (dx < 0) { + dx = 0; + location.x = workareaRectangle.x; + } + int w = workareaRectangle.width - dx; + if (w < dim.width) { + int dw = dim.width - w; + if (dw < dx) { + location.x -= dw; + } else { + dim.width = workareaRectangle.width; + location.x = workareaRectangle.x; + } + } + } + if ( ! isHeightChangeable) { + dim.height = frame.getHeight(); + } else { + + if (isHScrollBar) dim.height += scroller.getHorizontalScrollBar().getHeight(); + if (dim.height < minDimension.height) dim.height = minDimension.height; + + int dy = location.y - workareaRectangle.y; + if (dy < 0) { + dy = 0; + location.y = workareaRectangle.y; + } + int h = workareaRectangle.height - dy; + if (h < dim.height) { + int dh = dim.height - h; + if (dh < dy) { + location.y -= dh; + } else { + dim.height = workareaRectangle.height; + location.y = workareaRectangle.y; + } + } + } + if ( ! location.equals(frame.getLocation())) { + frame.setLocation(location); + } + if ( ! isFullScreen ) { + frame.setSize(dim); + } + } + scroller.revalidate(); + } + + private Rectangle getWorkareaRectangle() { + final GraphicsConfiguration graphicsConfiguration = frame.getGraphicsConfiguration(); + final Rectangle screenBounds = graphicsConfiguration.getBounds(); + final Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(graphicsConfiguration); + + screenBounds.x += screenInsets.left; + screenBounds.y += screenInsets.top; + screenBounds.width -= screenInsets.left + screenInsets.right; + screenBounds.height -= screenInsets.top + screenInsets.bottom; + return screenBounds; + } + + void addZoomButtons() { + buttonsBar.createStrut(); + zoomOutButton = buttonsBar.createButton("zoom-out", "Zoom Out", new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + zoomFitButton.setSelected(false); + uiSettings.zoomOut(); + } + }); + zoomInButton = buttonsBar.createButton("zoom-in", "Zoom In", new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + zoomFitButton.setSelected(false); + uiSettings.zoomIn(); + } + }); + zoomAsIsButton = buttonsBar.createButton("zoom-100", "Zoom 100%", new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + zoomFitButton.setSelected(false); + forceResizable = false; + uiSettings.zoomAsIs(); + } + }); + + zoomFitButton = buttonsBar.createToggleButton("zoom-fit", "Zoom to Fit Window", + new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + setZoomToFitSelected(true); + forceResizable = true; + zoomToFit(); + updateZoomButtonsState(); + } else { + setZoomToFitSelected(false); + } + setSurfaceToHandleKbdFocus(); + } + }); + + zoomFullScreenButton = buttonsBar.createToggleButton("zoom-fullscreen", "Full Screen", + new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + updateZoomButtonsState(); + if (e.getStateChange() == ItemEvent.SELECTED) { + uiSettings.setFullScreen(switchOnFullscreenMode()); + } else { + switchOffFullscreenMode(); + uiSettings.setFullScreen(false); + } + setSurfaceToHandleKbdFocus(); + } + }); + if ( ! isSeparateFrame) { + zoomFullScreenButton.setEnabled(false); + zoomFitButton.setEnabled(false); + } + } + + protected void setSurfaceToHandleKbdFocus() { + if (surface != null && ! surface.requestFocusInWindow()) { + surface.requestFocus(); + } + } + + boolean switchOnFullscreenMode() { + zoomFullScreenButton.setSelected(true); + oldContainerBounds = frame.getBounds(); + setButtonsBarVisible(false); + forceResizable = true; + frame.dispose(); + frame.setUndecorated(true); + frame.setResizable(false); + frame.setVisible(true); + try { + frame.getGraphicsConfiguration().getDevice().setFullScreenWindow(frame); + isFullScreen = true; + scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER); + scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + oldScrollerBorder = scroller.getBorder(); + scroller.setBorder(new EmptyBorder(0, 0, 0, 0)); + new FullscreenBorderDetectionThread(frame).start(); + } catch (Exception ex) { + Logger.getLogger(this.getClass().getName()).info("Cannot switch into FullScreen mode: " + ex.getMessage()); + return false; + } + return true; + } + + private void switchOffFullscreenMode() { + if (isFullScreen) { + zoomFullScreenButton.setSelected(false); + isFullScreen = false; + setButtonsBarVisible(true); + try { + frame.dispose(); + frame.setUndecorated(false); + frame.setResizable(true); + frame.getGraphicsConfiguration().getDevice().setFullScreenWindow(null); + } catch (Exception e) { + // nop + } + scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scroller.setBorder(oldScrollerBorder); + this.frame.setBounds(oldContainerBounds); + frame.setVisible(true); + pack(); + } + } + + private void zoomToFit() { + Dimension scrollerSize = scroller.getSize(); + Insets scrollerInsets = scroller.getInsets(); + uiSettings.zoomToFit(scrollerSize.width - scrollerInsets.left - scrollerInsets.right, + scrollerSize.height - scrollerInsets.top - scrollerInsets.bottom + + (isFullScreen ? buttonsBar.getHeight() : 0), + workingProtocol.getFbWidth(), workingProtocol.getFbHeight()); + } + + void registerResizeListener(Container container) { + container.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent e) { + if (isZoomToFitSelected()) { + zoomToFit(); + updateZoomButtonsState(); + updateWindowTitle(); + setSurfaceToHandleKbdFocus(); + } + } + }); + } + + void updateZoomButtonsState() { + zoomOutButton.setEnabled(uiSettings.getScalePercent() > UiSettings.MIN_SCALE_PERCENT); + zoomInButton.setEnabled(uiSettings.getScalePercent() < UiSettings.MAX_SCALE_PERCENT); + zoomAsIsButton.setEnabled(uiSettings.getScalePercent() != 100); + } + + public ButtonsBar createButtonsBar() { + buttonsBar = new ButtonsBar(); + return buttonsBar; + } + + public void setButtonsBarVisible(boolean isVisible) { + setButtonsBarVisible(isVisible, frame); + } + + private void setButtonsBarVisible(boolean isVisible, Container container) { + buttonsBar.setVisible(isVisible); + if (isVisible) { + buttonsBar.borderOff(); + container.add(buttonsBar.bar, BorderLayout.NORTH); + container.validate(); + } else { + container.remove(buttonsBar.bar); + buttonsBar.borderOn(); + } + } + + public void setButtonsBarVisibleFS(boolean isVisible) { + if (isVisible) { + if ( ! buttonsBar.isVisible) { + lpane.add(buttonsBar.bar, JLayeredPane.POPUP_LAYER, 0); + final int bbWidth = buttonsBar.bar.getPreferredSize().width; + buttonsBar.bar.setBounds( + scroller.getViewport().getViewPosition().x + (scroller.getWidth() - bbWidth)/2, 0, + bbWidth, buttonsBar.bar.getPreferredSize().height); + + // prevent mouse events to through down to Surface + if (null == buttonsBarMouseAdapter) buttonsBarMouseAdapter = new EmptyButtonsBarMouseAdapter(); + buttonsBar.bar.addMouseListener(buttonsBarMouseAdapter); + } + } else { + buttonsBar.bar.removeMouseListener(buttonsBarMouseAdapter); + lpane.remove(buttonsBar.bar); + lpane.repaint(buttonsBar.bar.getBounds()); + } + buttonsBar.setVisible(isVisible); + } + + public Surface getSurface() { + return surface; + } + + void close() { + if (isSeparateFrame && frame != null) { + frame.setVisible(false); + frame.dispose(); + } + } + + public static class ButtonsBar { + private static final Insets BUTTONS_MARGIN = new Insets(2, 2, 2, 2); + private JPanel bar; + private boolean isVisible; + + public ButtonsBar() { + bar = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 1)); + } + + public JButton createButton(String iconId, String tooltipText, ActionListener actionListener) { + JButton button = new JButton(Utils.getButtonIcon(iconId)); + button.setToolTipText(tooltipText); + button.setMargin(BUTTONS_MARGIN); + bar.add(button); + button.addActionListener(actionListener); + return button; + } + + public void createStrut() { + bar.add(Box.createHorizontalStrut(10)); + } + + public JToggleButton createToggleButton(String iconId, String tooltipText, ItemListener itemListener) { + JToggleButton button = new JToggleButton(Utils.getButtonIcon(iconId)); + button.setToolTipText(tooltipText); + button.setMargin(BUTTONS_MARGIN); + bar.add(button); + button.addItemListener(itemListener); + return button; + } + + public void setVisible(boolean isVisible) { + this.isVisible = isVisible; + if (isVisible) bar.revalidate(); + } + + public int getWidth() { + return bar.getMinimumSize().width; + } + public int getHeight() { + return bar.getMinimumSize().height; + } + + public void borderOn() { + bar.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); + } + + public void borderOff() { + bar.setBorder(BorderFactory.createEmptyBorder()); + } + } + + private static class EmptyButtonsBarMouseAdapter extends MouseAdapter { + // empty + } + + private class FullscreenBorderDetectionThread extends Thread { + public static final int SHOW_HIDE_BUTTONS_BAR_DELAY_IN_MILLS = 700; + private final JFrame frame; + private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + private ScheduledFuture<?> futureForShow; + private ScheduledFuture<?> futureForHide; + private Point mousePoint, oldMousePoint; + private Point viewPosition; + + public FullscreenBorderDetectionThread(JFrame frame) { + super("FS border detector"); + this.frame = frame; + } + + public void run() { + setPriority(Thread.MIN_PRIORITY); + while(isFullScreen) { + mousePoint = MouseInfo.getPointerInfo().getLocation(); + if (null == oldMousePoint) oldMousePoint = mousePoint; + SwingUtilities.convertPointFromScreen(mousePoint, frame); + viewPosition = scroller.getViewport().getViewPosition(); + processButtonsBarVisibility(); + + boolean needScrolling = processVScroll() || processHScroll(); + oldMousePoint = mousePoint; + if (needScrolling) { + cancelShowExecutor(); + setButtonsBarVisibleFS(false); + makeScrolling(viewPosition); + } + try { + Thread.sleep(100); + } catch (Exception e) { + // nop + } + } + } + + private boolean processHScroll() { + if (mousePoint.x < FS_SCROLLING_ACTIVE_BORDER) { + if (viewPosition.x > 0) { + int delta = FS_SCROLLING_ACTIVE_BORDER - mousePoint.x; + if (mousePoint.y != oldMousePoint.y) delta *= 2; // speedify scrolling on mouse moving + viewPosition.x -= delta; + if (viewPosition.x < 0) viewPosition.x = 0; + return true; + } + } else if (mousePoint.x > (frame.getWidth() - FS_SCROLLING_ACTIVE_BORDER)) { + final Rectangle viewRect = scroller.getViewport().getViewRect(); + final int right = viewRect.width + viewRect.x; + if (right < outerPanel.getSize().width) { + int delta = FS_SCROLLING_ACTIVE_BORDER - (frame.getWidth() - mousePoint.x); + if (mousePoint.y != oldMousePoint.y) delta *= 2; // speedify scrolling on mouse moving + viewPosition.x += delta; + if (viewPosition.x + viewRect.width > outerPanel.getSize().width) viewPosition.x = + outerPanel.getSize().width - viewRect.width; + return true; + } + } + return false; + } + + private boolean processVScroll() { + if (mousePoint.y < FS_SCROLLING_ACTIVE_BORDER) { + if (viewPosition.y > 0) { + int delta = FS_SCROLLING_ACTIVE_BORDER - mousePoint.y; + if (mousePoint.x != oldMousePoint.x) delta *= 2; // speedify scrolling on mouse moving + viewPosition.y -= delta; + if (viewPosition.y < 0) viewPosition.y = 0; + return true; + } + } else if (mousePoint.y > (frame.getHeight() - FS_SCROLLING_ACTIVE_BORDER)) { + final Rectangle viewRect = scroller.getViewport().getViewRect(); + final int bottom = viewRect.height + viewRect.y; + if (bottom < outerPanel.getSize().height) { + int delta = FS_SCROLLING_ACTIVE_BORDER - (frame.getHeight() - mousePoint.y); + if (mousePoint.x != oldMousePoint.x) delta *= 2; // speedify scrolling on mouse moving + viewPosition.y += delta; + if (viewPosition.y + viewRect.height > outerPanel.getSize().height) viewPosition.y = + outerPanel.getSize().height - viewRect.height; + return true; + } + } + return false; + } + + private void processButtonsBarVisibility() { + if (mousePoint.y < 1) { + cancelHideExecutor(); + // show buttons bar after delay + if (! buttonsBar.isVisible && (null == futureForShow || futureForShow.isDone())) { + futureForShow = scheduler.schedule(new Runnable() { + @Override + public void run() { + showButtonsBar(); + } + }, SHOW_HIDE_BUTTONS_BAR_DELAY_IN_MILLS, TimeUnit.MILLISECONDS); + } + } else { + cancelShowExecutor(); + } + if (buttonsBar.isVisible && mousePoint.y <= buttonsBar.getHeight()) { + cancelHideExecutor(); + } + if (buttonsBar.isVisible && mousePoint.y > buttonsBar.getHeight()) { + // hide buttons bar after delay + if (null == futureForHide || futureForHide.isDone()) { + futureForHide = scheduler.schedule(new Runnable() { + @Override + public void run() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + setButtonsBarVisibleFS(false); + SwingViewerWindow.this.frame.validate(); + } + }); + } + }, SHOW_HIDE_BUTTONS_BAR_DELAY_IN_MILLS, TimeUnit.MILLISECONDS); + } + } + } + + private void cancelHideExecutor() { + cancelExecutor(futureForHide); + } + private void cancelShowExecutor() { + cancelExecutor(futureForShow); + } + + private void cancelExecutor(ScheduledFuture<?> future) { + if (future != null && ! future.isDone()) { + future.cancel(true); + } + } + + private void makeScrolling(final Point viewPosition) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + scroller.getViewport().setViewPosition(viewPosition); + final Point mousePosition = surface.getMousePosition(); + if (mousePosition != null) { + final MouseEvent mouseEvent = new MouseEvent(frame, 0, 0, 0, + mousePosition.x, mousePosition.y, 0, false); + for (MouseMotionListener mml : surface.getMouseMotionListeners()) { + mml.mouseMoved(mouseEvent); + } + } + } + }); + } + + private void showButtonsBar() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + setButtonsBarVisibleFS(true); + } + }); + } + } + + protected void createButtonsPanel(final ProtocolContext context, Container container) { + final SwingViewerWindow.ButtonsBar buttonsBar = createButtonsBar(); + + buttonsBar.createButton("options", "Set Options", new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showOptionsDialog(); + setSurfaceToHandleKbdFocus(); + } + }); + + buttonsBar.createButton("info", "Show connection info", new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showConnectionInfoMessage(); + setSurfaceToHandleKbdFocus(); + } + }); + + buttonsBar.createStrut(); + + buttonsBar.createButton("refresh", "Refresh screen", new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + context.sendRefreshMessage(); + setSurfaceToHandleKbdFocus(); + } + }); + + addZoomButtons(); + + kbdButtons = new LinkedList<JComponent>(); + + buttonsBar.createStrut(); + + JButton ctrlAltDelButton = buttonsBar.createButton("ctrl-alt-del", "Send 'Ctrl-Alt-Del'", new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + sendCtrlAltDel(context); + setSurfaceToHandleKbdFocus(); + } + }); + kbdButtons.add(ctrlAltDelButton); + + JButton winButton = buttonsBar.createButton("win", "Send 'Win' key as 'Ctrl-Esc'", new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + sendWinKey(context); + setSurfaceToHandleKbdFocus(); + } + }); + kbdButtons.add(winButton); + + JToggleButton ctrlButton = buttonsBar.createToggleButton("ctrl", "Ctrl Lock", + new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true)); + } else { + context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, false)); + } + setSurfaceToHandleKbdFocus(); + } + }); + kbdButtons.add(ctrlButton); + + JToggleButton altButton = buttonsBar.createToggleButton("alt", "Alt Lock", + new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + if (e.getStateChange() == ItemEvent.SELECTED) { + context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, true)); + } else { + context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, false)); + } + setSurfaceToHandleKbdFocus(); + } + }); + kbdButtons.add(altButton); + + ModifierButtonEventListener modifierButtonListener = new ModifierButtonEventListener(); + modifierButtonListener.addButton(KeyEvent.VK_CONTROL, ctrlButton); + modifierButtonListener.addButton(KeyEvent.VK_ALT, altButton); + surface.addModifierListener(modifierButtonListener); + +// JButton fileTransferButton = new JButton(Utils.getButtonIcon("file-transfer")); +// fileTransferButton.setMargin(buttonsMargin); +// buttonBar.add(fileTransferButton); + + buttonsBar.createStrut(); + + buttonsBar.createButton("close", isApplet ? "Disconnect" : "Close", new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (frame != null) { + frame.setVisible(false); + frame.dispose(); + } + presenter.setNeedReconnection(false); + presenter.cancelConnection(); + viewer.closeApp(); + } + }).setAlignmentX(JComponent.RIGHT_ALIGNMENT); + + setButtonsBarVisible(true, container); + } + + private void sendCtrlAltDel(ProtocolContext context) { + context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true)); + context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, true)); + context.sendMessage(new KeyEventMessage(Keymap.K_DELETE, true)); + context.sendMessage(new KeyEventMessage(Keymap.K_DELETE, false)); + context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, false)); + context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, false)); + } + + private void sendWinKey(ProtocolContext context) { + context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true)); + context.sendMessage(new KeyEventMessage(Keymap.K_ESCAPE, true)); + context.sendMessage(new KeyEventMessage(Keymap.K_ESCAPE, false)); + context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, false)); + } + + @Override + public void settingsChanged(SettingsChangedEvent e) { + if (ProtocolSettings.isRfbSettingsChangedFired(e)) { + ProtocolSettings settings = (ProtocolSettings) e.getSource(); + setEnabledKbdButtons( ! settings.isViewOnly()); + } + } + + private void setEnabledKbdButtons(boolean enabled) { + if (kbdButtons != null) { + for (JComponent b : kbdButtons) { + b.setEnabled(enabled); + } + } + } + + private void showOptionsDialog() { + OptionsDialog optionsDialog = new OptionsDialog(frame); + optionsDialog.initControlsFromSettings(rfbSettings, uiSettings, false); + optionsDialog.setVisible(true); + presenter.saveHistory(); + } + + private void showConnectionInfoMessage() { + StringBuilder message = new StringBuilder(); + message.append("TightVNC Viewer v.").append(Viewer.ver()).append("\n\n"); + message.append("Connected to: ").append(remoteDesktopName).append("\n"); + message.append("Host: ").append(connectionString).append("\n\n"); + + message.append("Desktop geometry: ") + .append(String.valueOf(surface.getWidth())) + .append(" \u00D7 ") // multiplication sign + .append(String.valueOf(surface.getHeight())).append("\n"); + message.append("Color format: ") + .append(String.valueOf(Math.round(Math.pow(2, workingProtocol.getPixelFormat().depth)))) + .append(" colors (") + .append(String.valueOf(workingProtocol.getPixelFormat().depth)) + .append(" bits)\n"); + message.append("Current protocol version: ") + .append(workingProtocol.getProtocolVersion()); + if (workingProtocol.isTight()) { + message.append("tight"); + } + message.append("\n"); + + JOptionPane infoPane = new JOptionPane(message.toString(), JOptionPane.INFORMATION_MESSAGE); + final JDialog infoDialog = infoPane.createDialog(frame, "VNC connection info"); + infoDialog.setModalityType(Dialog.ModalityType.MODELESS); + infoDialog.setVisible(true); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/SwingViewerWindowFactory.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,37 @@ +package com.glavsoft.viewer.swing; + +import com.glavsoft.rfb.protocol.Protocol; +import com.glavsoft.rfb.protocol.ProtocolSettings; +import com.glavsoft.viewer.ConnectionPresenter; +import com.glavsoft.viewer.UiSettings; +import com.glavsoft.viewer.Viewer; + +/** + * @author dime at tightvnc.com + */ +public class SwingViewerWindowFactory { + + private final boolean isSeparateFrame; + private final boolean isApplet; + private final Viewer viewer; + + public SwingViewerWindowFactory(boolean isSeparateFrame, boolean isApplet, Viewer viewer) { + this.isSeparateFrame = isSeparateFrame; + this.isApplet = isApplet; + this.viewer = viewer; + } + + public SwingViewerWindow createViewerWindow(Protocol workingProtocol, + ProtocolSettings rfbSettings, UiSettings uiSettings, + String connectionString, ConnectionPresenter presenter) { + Surface surface = new Surface(workingProtocol, uiSettings.getScaleFactor(), uiSettings.getMouseCursorShape()); + final SwingViewerWindow viewerWindow = new SwingViewerWindow(workingProtocol, rfbSettings, uiSettings, + surface, isSeparateFrame, isApplet, viewer, connectionString, presenter); + surface.setViewerWindow(viewerWindow); + viewerWindow.setRemoteDesktopName(workingProtocol.getRemoteDesktopName()); + rfbSettings.addListener(viewerWindow); + uiSettings.addListener(surface); + return viewerWindow; + } + +}
--- a/src/viewer_swing/java/com/glavsoft/viewer/swing/Utils.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/Utils.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -24,13 +24,16 @@ package com.glavsoft.viewer.swing; -import java.awt.Image; -import java.awt.Toolkit; +import javax.swing.*; +import java.awt.*; +import java.awt.image.ImageObserver; import java.net.URL; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; - -import javax.swing.ImageIcon; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** * Utils for Swing GUI @@ -38,12 +41,7 @@ public class Utils { private static List<Image> icons; - /** - * Get application icons - * - * @return icons list - */ - public static List<Image> getIcons() { + private static List<Image> getApplicationIcons() { if (icons != null) { return icons; } @@ -69,4 +67,70 @@ URL resource = Utils.class.getResource("/com/glavsoft/viewer/images/button-"+name+".png"); return resource != null ? new ImageIcon(resource) : null; } + + private static Map<LocalMouseCursorShape, Cursor> cursorCash = new HashMap<LocalMouseCursorShape, Cursor>(); + public static Cursor getCursor(LocalMouseCursorShape cursorShape) { + Cursor cursor = cursorCash.get(cursorShape); + if (cursor != null) return cursor; + String name = cursorShape.getCursorName(); + URL resource = Utils.class.getResource("/com/glavsoft/viewer/images/cursor-"+name+".png"); + if (resource != null) { + Image image = Toolkit.getDefaultToolkit().getImage(resource); + if (image != null) { + final CountDownLatch done = new CountDownLatch(1); + image.getWidth(new ImageObserver() { + @Override + public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) { + boolean isReady = (infoflags & (ALLBITS | ABORT)) != 0; + if (isReady) { + done.countDown(); + } + return ! isReady; + } + }); + try { + done.await(3, TimeUnit.SECONDS); + } catch (InterruptedException e) { + return Cursor.getDefaultCursor(); + } + int w = image.getWidth(null); + int h = image.getHeight(null); + if (w < 0 || h < 0) return Cursor.getDefaultCursor(); + w = (int)((w-0.5) / 2); + h = (int)((h-0.5) / 2); + cursor = Toolkit.getDefaultToolkit().createCustomCursor( + image, new Point(w > 0 ? w: 0, h > 0 ? h : 0), name); + if (cursor != null) cursorCash.put(cursorShape, cursor); + } + } + return cursor != null ? cursor : Cursor.getDefaultCursor(); + } + + public static void decorateDialog(Window dialog) { + try { + dialog.setAlwaysOnTop(true); + } catch (SecurityException e) { + // nop + } + dialog.pack(); + if (dialog instanceof JDialog) { + ((JDialog)dialog).setModalityType(Dialog.ModalityType.APPLICATION_MODAL); + } + dialog.toFront(); + Utils.setApplicationIconsForWindow(dialog); + } + + public static void setApplicationIconsForWindow(Window window) { + List<Image> icons = getApplicationIcons(); + if (icons.size() != 0) { + window.setIconImages(icons); + } + } + + public static void centerWindow(Window window) { + Point locationPoint = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint(); + Rectangle bounds = window.getBounds(); + locationPoint.setLocation(locationPoint.x - bounds.width/2, locationPoint.y - bounds.height/2); + window.setLocation(locationPoint); + } }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/WrongParameterException.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,48 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer.swing; + +import com.glavsoft.exceptions.CommonException; + +/** + * @author dime at tightvnc.com + */ +public class WrongParameterException extends CommonException { + + private String propertyName; + + public WrongParameterException(String message) { + super(message); + } + + public String getPropertyName() { + return propertyName; + } + + public WrongParameterException(String message, String propertyName) { + super(message); + this.propertyName = propertyName; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/gui/AutoCompletionComboEditorDocument.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,112 @@ +package com.glavsoft.viewer.swing.gui; + +import javax.swing.*; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.JTextComponent; +import javax.swing.text.PlainDocument; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; + +/** + * @author dime at tightvnc.com + * + * Using idea by Thomas Bierhance from http://www.orbital-computer.de/JComboBox/ + */ +public class AutoCompletionComboEditorDocument extends PlainDocument { + + private ComboBoxModel model; + private boolean selecting; + private JComboBox comboBox; + private final boolean hidePopupOnFocusLoss; + private JTextComponent editor; + + public AutoCompletionComboEditorDocument(final JComboBox comboBox) { + this.comboBox = comboBox; + this.model = comboBox.getModel(); + this.editor = (JTextComponent)comboBox.getEditor().getEditorComponent(); + editor.setDocument(this); + comboBox.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (!selecting) highlightCompletedText(0); + } + }); + + Object selectedItem = comboBox.getSelectedItem(); + if (selectedItem!=null) { + setText(selectedItem.toString()); + highlightCompletedText(0); + } + hidePopupOnFocusLoss = System.getProperty("java.version").startsWith("1.5"); + editor.addFocusListener(new FocusAdapter() { + @Override + public void focusLost(FocusEvent e) { + if (hidePopupOnFocusLoss) comboBox.setPopupVisible(false); + } + }); + } + + @Override + public void remove(int offs, int len) throws BadLocationException { + if (selecting) return; + super.remove(offs, len); + } + + @Override + public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { + if (selecting) return; + super.insertString(offs, str, a); + Object item = lookupItem(getText(0, getLength())); + if (item != null) { + setSelectedItem(item); + setText(item.toString()); + highlightCompletedText(offs + str.length()); + if (comboBox.isDisplayable()) comboBox.setPopupVisible(true); + } + } + + private void setText(String text) { + try { + super.remove(0, getLength()); + super.insertString(0, text, null); + } catch (BadLocationException e) { + throw new RuntimeException(e); + } + } + + private void setSelectedItem(Object item) { + selecting = true; + model.setSelectedItem(item); + selecting = false; + } + + private void highlightCompletedText(int offs) { + JTextComponent editor = (JTextComponent) comboBox.getEditor().getEditorComponent(); + editor.setSelectionStart(offs); + editor.setSelectionEnd(getLength()); + } + + private Object lookupItem(String pattern) { + Object selectedItem = model.getSelectedItem(); + if (selectedItem != null && startsWithIgnoreCase(selectedItem, pattern)) { + return selectedItem; + } else { + for (int i = 0, n = model.getSize(); i < n; i++) { + Object currentItem = model.getElementAt(i); + if (startsWithIgnoreCase(currentItem, pattern)) { + return currentItem; + } + } + } + return null; + } + + private boolean startsWithIgnoreCase(Object currentItem, String pattern) { + return currentItem.toString().toLowerCase().startsWith(pattern.toLowerCase()); + } + + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/gui/ConnectionView.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,471 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer.swing.gui; + +import com.glavsoft.viewer.mvp.View; +import com.glavsoft.viewer.swing.ConnectionParams; +import com.glavsoft.viewer.ConnectionPresenter; +import com.glavsoft.viewer.swing.Utils; +import com.glavsoft.viewer.swing.WrongParameterException; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.*; +import java.util.LinkedList; + +/** + * Dialog window for connection parameters get from. + */ +@SuppressWarnings("serial") +public class ConnectionView extends JPanel implements View { + private static final int PADDING = 4; + public static final int COLUMNS_HOST_FIELD = 30; + public static final int COLUMNS_PORT_USER_FIELD = 13; + public static final String CLOSE = "Close"; + public static final String CANCEL = "Cancel"; + private WindowListener appWindowListener; + private final boolean hasSshSupport; + private final JTextField serverPortField; + private JCheckBox useSshTunnelingCheckbox; + private final JComboBox serverNameCombo; + private JTextField sshUserField; + private JTextField sshHostField; + private JTextField sshPortField; + private JLabel sshUserLabel; + private JLabel sshHostLabel; + private JLabel sshPortLabel; + private JLabel ssUserWarningLabel; + private JButton clearHistoryButton; + private JButton connectButton; + private final JFrame view; + private final ConnectionPresenter presenter; + private final StatusBar statusBar; + private boolean connectionInProgress; + private JButton closeCancelButton; + + public ConnectionView(final WindowListener appWindowListener, + final ConnectionPresenter presenter, boolean useSsh) { + this.appWindowListener = appWindowListener; + this.hasSshSupport = useSsh; + this.presenter = presenter; + + setLayout(new BorderLayout(0, 0)); + JPanel optionsPane = new JPanel(new GridBagLayout()); + add(optionsPane, BorderLayout.CENTER); + optionsPane.setBorder(new EmptyBorder(PADDING, PADDING, PADDING, PADDING)); + + setLayout(new GridBagLayout()); + + int gridRow = 0; + + serverNameCombo = new JComboBox(); + initConnectionsHistoryCombo(); + serverNameCombo.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + Object item = serverNameCombo.getSelectedItem(); + if (item instanceof ConnectionParams) { + presenter.populateFromHistoryItem((ConnectionParams) item); + } + } + }); + + addFormFieldRow(optionsPane, gridRow, new JLabel("Remote Host:"), serverNameCombo, true); + ++gridRow; + + serverPortField = new JTextField(COLUMNS_PORT_USER_FIELD); + + addFormFieldRow(optionsPane, gridRow, new JLabel("Port:"), serverPortField, false); + ++gridRow; + + if (this.hasSshSupport) { + gridRow = createSshOptions(optionsPane, gridRow); + } + + JPanel buttonPanel = createButtons(); + + GridBagConstraints cButtons = new GridBagConstraints(); + cButtons.gridx = 0; cButtons.gridy = gridRow; + cButtons.weightx = 100; cButtons.weighty = 100; + cButtons.gridwidth = 2; cButtons.gridheight = 1; + optionsPane.add(buttonPanel, cButtons); + + view = new JFrame("New TightVNC Connection"); + view.add(this, BorderLayout.CENTER); + statusBar = new StatusBar(); + view.add(statusBar, BorderLayout.SOUTH); + + view.getRootPane().setDefaultButton(connectButton); + view.addWindowListener(appWindowListener); +// view.setResizable(false); + Utils.decorateDialog(view); + Utils.centerWindow(view); + } + + private void initConnectionsHistoryCombo() { + serverNameCombo.setEditable(true); + + new AutoCompletionComboEditorDocument(serverNameCombo); // use autocompletion feature for ComboBox + serverNameCombo.setRenderer(new HostnameComboboxRenderer()); + + ConnectionParams prototypeDisplayValue = new ConnectionParams(); + prototypeDisplayValue.hostName = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXЧЧ"; + serverNameCombo.setPrototypeDisplayValue(prototypeDisplayValue); + } + + public void showReconnectDialog(final String title, final String message) { + JOptionPane reconnectPane = new JOptionPane(message + "\nTry another connection?", + JOptionPane.QUESTION_MESSAGE, JOptionPane.YES_NO_OPTION); + final JDialog reconnectDialog = reconnectPane.createDialog(ConnectionView.this, title); + Utils.decorateDialog(reconnectDialog); + reconnectDialog.setVisible(true); + if (reconnectPane.getValue() == null || + (Integer)reconnectPane.getValue() == JOptionPane.NO_OPTION) { + presenter.setNeedReconnection(false); + closeView(); + view.dispose(); + closeApp(); + } else { + // TODO return when allowInteractive, close window otherwise +// forceConnectionDialog = allowInteractive; + } + } + + public void setConnectionInProgress(boolean enable) { + if (enable) { + connectionInProgress = true; + closeCancelButton.setText(CANCEL); + connectButton.setEnabled(false); + } else { + connectionInProgress = false; + closeCancelButton.setText(CLOSE); + connectButton.setEnabled(true); + } + } + + private JPanel createButtons() { + JPanel buttonPanel = new JPanel(); + + closeCancelButton = new JButton(CLOSE); + closeCancelButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (connectionInProgress) { + presenter.cancelConnection(); + setConnectionInProgress(false); + } else { + closeView(); + closeApp(); + } + } + }); + + connectButton = new JButton("Connect"); + buttonPanel.add(connectButton); + connectButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + setMessage(""); + Object item = serverNameCombo.getSelectedItem(); + String hostName = item instanceof ConnectionParams ? + ((ConnectionParams) item).hostName : + (String) item; + try { + setConnectionInProgress(true); + presenter.submitConnection(hostName); + } catch (WrongParameterException wpe) { + if (ConnectionPresenter.PROPERTY_HOST_NAME.equals(wpe.getPropertyName())) { + serverNameCombo.requestFocusInWindow(); + } + if (ConnectionPresenter.PROPERTY_RFB_PORT_NUMBER.equals(wpe.getPropertyName())) { + serverPortField.requestFocusInWindow(); + } + showConnectionErrorDialog(wpe.getMessage()); + setConnectionInProgress(false); + } + } + }); + + JButton optionsButton = new JButton("Options..."); + buttonPanel.add(optionsButton); + optionsButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + OptionsDialog od = new OptionsDialog(view); + od.initControlsFromSettings(presenter.getRfbSettings(), presenter.getUiSettings(), true); + od.setVisible(true); + view.toFront(); + } + }); + + clearHistoryButton = new JButton("Clear history"); + clearHistoryButton.setToolTipText("Clear connections history"); + buttonPanel.add(clearHistoryButton); + clearHistoryButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + presenter.clearHistory(); + clearHistoryButton.setEnabled(false); + view.toFront(); + } + }); + buttonPanel.add(closeCancelButton); + return buttonPanel; + } + + private int createSshOptions(JPanel pane, int gridRow) { + GridBagConstraints cUseSshTunnelLabel = new GridBagConstraints(); + cUseSshTunnelLabel.gridx = 0; cUseSshTunnelLabel.gridy = gridRow; + cUseSshTunnelLabel.weightx = 100; cUseSshTunnelLabel.weighty = 100; + cUseSshTunnelLabel.gridwidth = 2; cUseSshTunnelLabel.gridheight = 1; + cUseSshTunnelLabel.anchor = GridBagConstraints.LINE_START; + cUseSshTunnelLabel.ipadx = PADDING; + cUseSshTunnelLabel.ipady = 10; + useSshTunnelingCheckbox = new JCheckBox("Use SSH tunneling"); + pane.add(useSshTunnelingCheckbox, cUseSshTunnelLabel); + ++gridRow; + + sshHostLabel = new JLabel("SSH Server:"); + sshHostField = new JTextField(COLUMNS_HOST_FIELD); + addFormFieldRow(pane, gridRow, sshHostLabel, sshHostField, true); + ++gridRow; + + sshPortLabel = new JLabel("SSH Port:"); + sshPortField = new JTextField(COLUMNS_PORT_USER_FIELD); + addFormFieldRow(pane, gridRow, sshPortLabel, sshPortField, false); + ++gridRow; + + sshUserLabel = new JLabel("SSH User:"); + sshUserField = new JTextField(COLUMNS_PORT_USER_FIELD); + JPanel sshUserFieldPane = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); + sshUserFieldPane.add(sshUserField); + ssUserWarningLabel = new JLabel(" (will be asked if not specified)"); + sshUserFieldPane.add(ssUserWarningLabel); + addFormFieldRow(pane, gridRow, sshUserLabel, sshUserFieldPane, false); + ++gridRow; + + useSshTunnelingCheckbox.addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + final boolean useSsh = e.getStateChange() == ItemEvent.SELECTED; + setUseSsh(useSsh); + presenter.setUseSsh(useSsh); + } + }); + + return gridRow; + } + + private void addFormFieldRow(JPanel pane, int gridRow, JLabel label, JComponent field, boolean fill) { + GridBagConstraints cLabel = new GridBagConstraints(); + cLabel.gridx = 0; cLabel.gridy = gridRow; + cLabel.weightx = 0; + cLabel.weighty = 100; + cLabel.gridwidth = cLabel.gridheight = 1; + cLabel.anchor = GridBagConstraints.LINE_END; + cLabel.ipadx = PADDING; + cLabel.ipady = 10; + pane.add(label, cLabel); + + GridBagConstraints cField = new GridBagConstraints(); + cField.gridx = 1; cField.gridy = gridRow; + cField.weightx = 0; cField.weighty = 100; + cField.gridwidth = cField.gridheight = 1; + cField.anchor = GridBagConstraints.LINE_START; + if (fill) cField.fill = GridBagConstraints.HORIZONTAL; + pane.add(field, cField); + } + + /* + * Implicit View interface + */ + public void setMessage(String message) { + statusBar.setMessage(message); + } + + @SuppressWarnings("UnusedDeclaration") + public void setPortNumber(int portNumber) { + serverPortField.setText(String.valueOf(portNumber)); + } + + @SuppressWarnings("UnusedDeclaration") + public String getPortNumber() { + return serverPortField.getText(); + } + + @SuppressWarnings("UnusedDeclaration") + public void setSshHostName(String sshHostName) { + if (hasSshSupport) { + sshHostField.setText(sshHostName); + } + } + + @SuppressWarnings("UnusedDeclaration") + public String getSshHostName() { + if (hasSshSupport) { + return sshHostField.getText(); + } else { return ""; } + } + + @SuppressWarnings("UnusedDeclaration") + public void setSshPortNumber(int sshPortNumber) { + if (hasSshSupport) { + sshPortField.setText(String.valueOf(sshPortNumber)); + } + } + + @SuppressWarnings("UnusedDeclaration") + public String getSshPortNumber() { + if (hasSshSupport) { + return sshPortField.getText(); + } else { return ""; } + } + + @SuppressWarnings("UnusedDeclaration") + public void setSshUserName(String sshUserName) { + if (hasSshSupport) { + sshUserField.setText(sshUserName); + } + } + + @SuppressWarnings("UnusedDeclaration") + public String getSshUserName() { + if (hasSshSupport) { + return sshUserField.getText(); + } else { return ""; } + } + + @SuppressWarnings("UnusedDeclaration") + public void setUseSsh(boolean useSsh) { + if (hasSshSupport) { + useSshTunnelingCheckbox.setSelected(useSsh); + sshUserLabel.setEnabled(useSsh); + sshUserField.setEnabled(useSsh); + ssUserWarningLabel.setEnabled(useSsh); + sshHostLabel.setEnabled(useSsh); + sshHostField.setEnabled(useSsh); + sshPortLabel.setEnabled(useSsh); + sshPortField.setEnabled(useSsh); + } + } + + @SuppressWarnings("UnusedDeclaration") + public boolean getUseSsh() { + return useSshTunnelingCheckbox.isSelected(); + } + + @SuppressWarnings("UnusedDeclaration") + public void setConnectionsList(LinkedList<ConnectionParams> connections) { + serverNameCombo.removeAllItems(); + for (ConnectionParams cp : connections) { + serverNameCombo.addItem(new ConnectionParams(cp)); + } + serverNameCombo.setPopupVisible(false); + clearHistoryButton.setEnabled(serverNameCombo.getItemCount() > 0); + } + /* + * /Implicit View interface + */ + + @Override + public void showView() { + view.setVisible(true); + view.toFront(); + view.repaint(); + } + + @Override + public void closeView() { + view.setVisible(false); + } + + public void showConnectionErrorDialog(final String message) { + JOptionPane errorPane = new JOptionPane(message, JOptionPane.ERROR_MESSAGE); + final JDialog errorDialog = errorPane.createDialog(view, "Connection error"); + Utils.decorateDialog(errorDialog); + errorDialog.setVisible(true); + if ( ! presenter.allowInteractive()) { + presenter.cancelConnection(); + closeApp(); + } + } + + public void closeApp() { + appWindowListener.windowClosing(null); + } + + public JFrame getFrame() { + return view; + } + +} + +class StatusBar extends JPanel { + + private JLabel messageLabel; + + public StatusBar() { + setLayout(new BorderLayout()); + setPreferredSize(new Dimension(10, 23)); + + messageLabel = new JLabel(""); + final Font f = messageLabel.getFont(); + messageLabel.setFont(f.deriveFont(f.getStyle() & ~Font.BOLD)); + add(messageLabel, BorderLayout.CENTER); + + JPanel rightPanel = new JPanel(new BorderLayout()); + rightPanel.setOpaque(false); + + + add(rightPanel, BorderLayout.EAST); + setBorder(new Border() { + @Override + public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + Color oldColor = g.getColor(); + g.translate(x, y); + g.setColor(c.getBackground().darker()); + g.drawLine(0, 0, width -1, 0); + g.setColor(c.getBackground().brighter()); + g.drawLine(0, 1, width -1, 1); + g.translate(-x, -y); + g.setColor(oldColor); + } + @Override + public Insets getBorderInsets(Component c) { + return new Insets(2, 2, 2, 2); + } + @Override + public boolean isBorderOpaque() { + return false; + } + }); + } + + public void setMessage(String message) { + messageLabel.setText(message); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/gui/ConnectionsHistory.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,338 @@ +package com.glavsoft.viewer.swing.gui; + +import com.glavsoft.rfb.protocol.ProtocolSettings; +import com.glavsoft.utils.Strings; +import com.glavsoft.viewer.mvp.Model; +import com.glavsoft.viewer.swing.ConnectionParams; +import com.glavsoft.viewer.UiSettings; +import com.glavsoft.viewer.UiSettingsData; + +import java.io.*; +import java.security.AccessControlException; +import java.util.*; +import java.util.logging.Logger; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; + +/** + * @author dime at tightvnc.com + */ +public class ConnectionsHistory implements Model { + private static int MAX_ITEMS = 32; + public static final String CONNECTIONS_HISTORY_ROOT_NODE = "com/glavsoft/viewer/connectionsHistory"; + public static final String NODE_HOST_NAME = "hostName"; + public static final String NODE_PORT_NUMBER = "portNumber"; + public static final String NODE_SSH_USER_NAME = "sshUserName"; + public static final String NODE_SSH_HOST_NAME = "sshHostName"; + public static final String NODE_SSH_PORT_NUMBER = "sshPortNumber"; + public static final String NODE_USE_SSH = "useSsh"; + public static final String NODE_PROTOCOL_SETTINGS = "protocolSettings"; + public static final String NODE_UI_SETTINGS = "uiSettings"; + private final Logger logger; + + private Map<ConnectionParams, ProtocolSettings> protocolSettingsMap; + private Map<ConnectionParams, UiSettingsData> uiSettingsDataMap; + LinkedList<ConnectionParams> connections; + + public ConnectionsHistory() { + logger = Logger.getLogger(getClass().getName()); + init(); + retrieve(); + } + + private void init() { + protocolSettingsMap = new HashMap<ConnectionParams, ProtocolSettings>(); + uiSettingsDataMap = new HashMap<ConnectionParams, UiSettingsData>(); + connections = new LinkedList<ConnectionParams>(); + } + + private void retrieve() { + Preferences connectionsHistoryNode; + try { + connectionsHistoryNode = getConnectionHistoryNode(); + } catch (AccessControlException ace) { + return; + } + try { + final String[] orderNums; + orderNums = connectionsHistoryNode.childrenNames(); + SortedMap<Integer, ConnectionParams> conns = new TreeMap<Integer, ConnectionParams>(); + HashSet<ConnectionParams> uniques = new HashSet<ConnectionParams>(); + for (String orderNum : orderNums) { + int num = 0; + try { + num = Integer.parseInt(orderNum); + } catch (NumberFormatException skip) { + //nop + } + Preferences node = connectionsHistoryNode.node(orderNum); + String hostName = node.get(NODE_HOST_NAME, null); + if (null == hostName) continue; // skip entries without hostName field + ConnectionParams cp = new ConnectionParams(hostName, node.getInt(NODE_PORT_NUMBER, 0), + node.getBoolean(NODE_USE_SSH, false), node.get(NODE_SSH_HOST_NAME, ""), node.getInt(NODE_SSH_PORT_NUMBER, 0), node.get(NODE_SSH_USER_NAME, "") + ); + if (uniques.contains(cp)) continue; // skip duplicates + uniques.add(cp); + conns.put(num, cp); + logger.finest("deserialialize: " + cp.toPrint()); + retrieveProtocolSettings(node, cp); + retrieveUiSettings(node, cp); + } + int itemsCount = 0; + for (ConnectionParams cp : conns.values()) { + if (itemsCount < MAX_ITEMS) { + connections.add(cp); + } else { + connectionsHistoryNode.node(cp.hostName).removeNode(); + } + ++itemsCount; + } + } catch (BackingStoreException e) { + logger.severe("Cannot retrieve connections history info: " + e.getMessage()); + } + } + + private void retrieveUiSettings(Preferences node, ConnectionParams cp) { + byte[] bytes = node.getByteArray(NODE_UI_SETTINGS, new byte[0]); + if (bytes.length != 0) { + try { + UiSettingsData settings = (UiSettingsData) (new ObjectInputStream( + new ByteArrayInputStream(bytes))).readObject(); + uiSettingsDataMap.put(cp, settings); + logger.finest("deserialialize: " + settings); + } catch (IOException e) { + logger.info("Cannot deserialize UiSettings: " + e.getMessage()); + } catch (ClassNotFoundException e) { + logger.severe("Cannot deserialize UiSettings : " + e.getMessage()); + } + } + } + + private void retrieveProtocolSettings(Preferences node, ConnectionParams cp) { + byte[] bytes = node.getByteArray(NODE_PROTOCOL_SETTINGS, new byte[0]); + if (bytes.length != 0) { + try { + ProtocolSettings settings = (ProtocolSettings) (new ObjectInputStream( + new ByteArrayInputStream(bytes))).readObject(); + settings.refine(); + protocolSettingsMap.put(cp, settings); + logger.finest("deserialialize: " + settings); + } catch (IOException e) { + logger.info("Cannot deserialize ProtocolSettings: " + e.getMessage()); + } catch (ClassNotFoundException e) { + logger.severe("Cannot deserialize ProtocolSettings : " + e.getMessage()); + } + } + } + + /** + * Implicit Model interface + */ + @SuppressWarnings("UnusedDeclaration") + public LinkedList<ConnectionParams> getConnectionsList() { + return connections; + } + + public ProtocolSettings getProtocolSettings(ConnectionParams cp) { + return protocolSettingsMap.get(cp); + } + + public UiSettingsData getUiSettingsData(ConnectionParams cp) { + return uiSettingsDataMap.get(cp); + } + + public void save() { + try { + cleanStorage(); + Preferences connectionsHistoryNode = getConnectionHistoryNode(); + int num = 0; + for (ConnectionParams cp : connections) { + if (num >= MAX_ITEMS) break; + if ( ! Strings.isTrimmedEmpty(cp.hostName)) { + addNode(cp, connectionsHistoryNode, num++); + } + } + } catch (AccessControlException ace) { /*nop*/ } + } + + + public void clear() { + cleanStorage(); + init(); + } + + private void cleanStorage() { + Preferences connectionsHistoryNode = getConnectionHistoryNode(); + try { + for (String host : connectionsHistoryNode.childrenNames()) { + connectionsHistoryNode.node(host).removeNode(); + } + } catch (BackingStoreException e) { + logger.severe("Cannot remove node: " + e.getMessage()); + } + } + + private Preferences getConnectionHistoryNode() { + Preferences root = Preferences.userRoot(); + return root.node(CONNECTIONS_HISTORY_ROOT_NODE); + } + + private void addNode(ConnectionParams connectionParams, Preferences connectionsHistoryNode, int orderNum) { + ProtocolSettings protocolSettings = protocolSettingsMap.get(connectionParams); + UiSettingsData uiSettingsData = uiSettingsDataMap.get(connectionParams); + final Preferences node = connectionsHistoryNode.node(String.valueOf(orderNum)); + serializeConnectionParams(node, connectionParams); + serializeProtocolSettings(node, protocolSettings); + serializeUiSettingsData(node, uiSettingsData); + try { + node.flush(); + } catch (BackingStoreException e) { + logger.severe("Cannot retrieve connections history info: " + e.getMessage()); + } + } + + private void serializeUiSettingsData(Preferences node, UiSettingsData uiSettingsData) { + if (uiSettingsData != null) { + try { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); + objectOutputStream.writeObject(uiSettingsData); + node.putByteArray(NODE_UI_SETTINGS, byteArrayOutputStream.toByteArray()); + logger.finest("serialized (" + node.name() + ") " + uiSettingsData); + } catch (IOException e) { + logger.severe("Cannot serialize UiSettings: " + e.getMessage()); + } + } + } + + private void serializeProtocolSettings(Preferences node, ProtocolSettings protocolSettings) { + if (protocolSettings != null) { + try { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); + objectOutputStream.writeObject(protocolSettings); + node.putByteArray(NODE_PROTOCOL_SETTINGS, byteArrayOutputStream.toByteArray()); + logger.finest("serialized (" + node.name() + ") " + protocolSettings); + } catch (IOException e) { + logger.severe("Cannot serialize ProtocolSettings: " + e.getMessage()); + } + } + } + + private void serializeConnectionParams(Preferences node, ConnectionParams connectionParams) { + node.put(NODE_HOST_NAME, connectionParams.hostName); + node.putInt(NODE_PORT_NUMBER, connectionParams.getPortNumber()); + if (connectionParams.useSsh()) { + node.putBoolean(NODE_USE_SSH, connectionParams.useSsh()); + node.put(NODE_SSH_USER_NAME, connectionParams.sshUserName != null ? connectionParams.sshUserName: ""); + node.put(NODE_SSH_HOST_NAME, connectionParams.sshHostName != null ? connectionParams.sshHostName: ""); + node.putInt(NODE_SSH_PORT_NUMBER, connectionParams.getSshPortNumber()); + } + logger.finest("serialized (" + node.name() + ") " + connectionParams.toPrint()); + } + + public void reorder(ConnectionParams connectionParams) { + reorder(connectionParams, getProtocolSettings(connectionParams), getUiSettingsData(connectionParams)); + } + + public void reorder(ConnectionParams connectionParams, ProtocolSettings protocolSettings, UiSettings uiSettings) { + reorder(connectionParams, protocolSettings, uiSettings != null? uiSettings.getData() : null); + } + + private void reorder(ConnectionParams connectionParams, ProtocolSettings protocolSettings, UiSettingsData uiSettingsData) { + while (connections.remove(connectionParams)) {/*empty - remove all occurrences (if any)*/} + LinkedList<ConnectionParams> cpList = new LinkedList<ConnectionParams>(); + cpList.addAll(connections); + + connections.clear(); + connections.add(new ConnectionParams(connectionParams)); + connections.addAll(cpList); + storeSettings(connectionParams, protocolSettings, uiSettingsData); + } + + private void storeSettings(ConnectionParams connectionParams, ProtocolSettings protocolSettings, UiSettingsData uiSettingsData) { + if (protocolSettings != null) { + ProtocolSettings savedSettings = protocolSettingsMap.get(connectionParams); + if (savedSettings != null) { + savedSettings.copyDataFrom(protocolSettings); + } else { + protocolSettingsMap.put(new ConnectionParams(connectionParams), new ProtocolSettings(protocolSettings)); + } + } + if (uiSettingsData != null) { + uiSettingsDataMap.put(new ConnectionParams(connectionParams), new UiSettingsData(uiSettingsData)); + } + } + + /** + * Search most suitable connectionParams (cp) from history. + * When history is empty, returns original parameter. + * When original parameter is empty (null or hostName is empty) returns the very first cp from history. + * Then subsequently compare cp from history with original for most fields will match in sequent of + * hostName, portNumber, useSsh, sshHostName, sshPortName, sshPortNumber. + * When any match found it returns. + * When no matches found returns the very first cp from history. + * + * @param orig connectionParams to search + * @return most suitable cp + */ + public ConnectionParams getMostSuitableConnection(ConnectionParams orig) { + ConnectionParams res = connections.isEmpty()? orig: connections.get(0); + if (null == orig || Strings.isTrimmedEmpty(orig.hostName)) return res; + for (ConnectionParams cp : connections) { + if (orig.equals(cp)) return cp; + if (compareTextFields(orig.hostName, res.hostName, cp.hostName)) { + res = cp; + continue; + } + if (orig.hostName.equals(cp.hostName) && + comparePorts(orig.getPortNumber(), res.getPortNumber(), cp.getPortNumber())) { + res = cp; + continue; + } + if (orig.hostName.equals(cp.hostName) && + orig.getPortNumber() == cp.getPortNumber() && + orig.useSsh() == cp.useSsh() && orig.useSsh() != res.useSsh()) { + res = cp; + continue; + } + if (orig.hostName.equals(cp.hostName) && + orig.getPortNumber() == cp.getPortNumber() && + orig.useSsh() && cp.useSsh() && + compareTextFields(orig.sshHostName, res.sshHostName, cp.sshHostName)) { + res = cp; + continue; + } + if (orig.hostName.equals(cp.hostName) && + orig.getPortNumber() == cp.getPortNumber() && + orig.useSsh() && cp.useSsh() && + orig.sshHostName != null && orig.sshHostName.equals(cp.hostName) && + comparePorts(orig.getSshPortNumber(), res.getSshPortNumber(), cp.getSshPortNumber())) { + res = cp; + continue; + } + if (orig.hostName.equals(cp.hostName) && + orig.getPortNumber() == cp.getPortNumber() && + orig.useSsh() && cp.useSsh() && + orig.sshHostName != null && orig.sshHostName.equals(cp.hostName) && + orig.getSshPortNumber() == cp.getSshPortNumber() && + compareTextFields(orig.sshUserName, res.sshUserName, cp.sshUserName)) { + res = cp; + } + } + return res; + } + + private boolean comparePorts(int orig, int res, int test) { + return orig == test && orig != res; + } + + private boolean compareTextFields(String orig, String res, String test) { + return (orig != null && test != null && res != null) && + orig.equals(test) && ! orig.equals(res); + } + + public boolean isEmpty() { + return connections.isEmpty(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/gui/HostnameComboboxRenderer.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,35 @@ +package com.glavsoft.viewer.swing.gui; + +import com.glavsoft.viewer.swing.ConnectionParams; + +import javax.swing.*; +import java.awt.*; + +/** + * @author dime at tightvnc.com + */ +public class HostnameComboboxRenderer extends DefaultListCellRenderer { + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + String stringValue = renderListItem((ConnectionParams)value); + setText(stringValue); + setFont(getFont().deriveFont(Font.PLAIN)); + if (isSelected) { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } else { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + return this; + } + + public String renderListItem(ConnectionParams cp) { + String s = "<html><b>" +cp.hostName + "</b>:" + cp.getPortNumber(); + if (cp.useSsh()) { + s += " <i>(via ssh://" + cp.sshUserName + "@" + cp.sshHostName + ":" + cp.getSshPortNumber() + ")</i>"; + } + return s + "</html>"; + } +}
--- a/src/viewer_swing/java/com/glavsoft/viewer/swing/gui/OptionsDialog.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/gui/OptionsDialog.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -27,6 +27,8 @@ import com.glavsoft.rfb.encoding.EncodingType; import com.glavsoft.rfb.protocol.LocalPointer; import com.glavsoft.rfb.protocol.ProtocolSettings; +import com.glavsoft.viewer.swing.LocalMouseCursorShape; +import com.glavsoft.viewer.UiSettings; import javax.swing.*; import java.awt.*; @@ -43,14 +45,11 @@ private JSlider compressionLevel; private JCheckBox viewOnlyCheckBox; private ProtocolSettings settings; - private JCheckBox sharedSession; + private UiSettings uiSettings; + private JCheckBox sharedSession; private RadioButtonSelectedState<LocalPointer> mouseCursorTrackSelected; private Map<LocalPointer, JRadioButton> mouseCursorTrackMap; - -// private RadioButtonSelectedState<Integer> colorDepthSelected; -// private Map<Integer, JRadioButton> colorDepthMap; - private JCheckBox useCompressionLevel; private JCheckBox useJpegQuality; private JLabel jpegQualityPoorLabel; @@ -61,9 +60,11 @@ private JComboBox encodings; private JCheckBox disableClipboardTransfer; private JComboBox colorDepth; + private RadioButtonSelectedState<LocalMouseCursorShape> mouseCursorShapeSelected; + private HashMap<LocalMouseCursorShape, JRadioButton> mouseCursorShapeMap; - public OptionsDialog(Window owner) { - super(owner, "Connection Options", ModalityType.MODELESS); + public OptionsDialog(Window owner) { + super(owner, "Connection Options", ModalityType.DOCUMENT_MODAL); final WindowAdapter onClose = new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { @@ -83,25 +84,11 @@ pack(); } - public void initControlsFromSettings(ProtocolSettings settings, boolean isOnConnect) { + public void initControlsFromSettings(ProtocolSettings settings, UiSettings uiSettings, boolean isOnConnect) { this.settings = settings; - - viewOnlyCheckBox.setSelected(settings.isViewOnly()); + this.uiSettings = uiSettings; -// String scaling = String.valueOf((int)(settings.scaling * 100)); -// int i = 0; boolean isNotSetScale = true; -// while ( scaleCombo.getItemAt(i) != null) { -// String item = (String)scaleCombo.getItemAt(i); -// if (item.equals(scaling)) { -// scaleCombo.setSelectedIndex(i); -// isNotSetScale = false; -// break; -// } -// ++i; -// } -// if (isNotSetScale) { -// scaleCombo.setSelectedItem(SCALE_100); -// } + viewOnlyCheckBox.setSelected(settings.isViewOnly()); int i = 0; boolean isNotSetEncoding = true; while ( encodings.getItemAt(i) != null) { @@ -122,19 +109,14 @@ mouseCursorTrackMap.get(settings.getMouseCursorTrack()).setSelected(true); mouseCursorTrackSelected.setSelected(settings.getMouseCursorTrack()); + mouseCursorShapeMap.get(uiSettings.getMouseCursorShape()).setSelected(true); + mouseCursorShapeSelected.setSelected(uiSettings.getMouseCursorShape()); -// settings.setBitsPerPixel(settings.getBitsPerPixel()); // when 0 set default bpp value -// final JRadioButton colorDepthRadioButton = colorDepthMap.get(settings.getBitsPerPixel()); -// if (colorDepthRadioButton != null) { -// colorDepthRadioButton.setSelected(true); -// } -// colorDepthSelected.setSelected(settings.getBitsPerPixel()); - - int bpp = settings.getBitsPerPixel(); + int depth = settings.getColorDepth(); i = 0; boolean isNotSet = true; while ( colorDepth.getItemAt(i) != null) { - int itemBpp = ((ColorDepthSelectItem)colorDepth.getItemAt(i)).bpp; - if (itemBpp == bpp) { + int itemDepth = ((ColorDepthSelectItem)colorDepth.getItemAt(i)).depth; + if (itemDepth == depth) { colorDepth.setSelectedIndex(i); isNotSet = false; break; @@ -163,8 +145,9 @@ settings.setSharedFlag(sharedSession.isSelected()); settings.setMouseCursorTrack(mouseCursorTrackSelected.getSelected()); + uiSettings.setMouseCursorShape(mouseCursorShapeSelected.getSelected()); - settings.setBitsPerPixel(((ColorDepthSelectItem)colorDepth.getSelectedItem()).bpp); + settings.setColorDepth(((ColorDepthSelectItem) colorDepth.getSelectedItem()).depth); settings.setCompressionLevel(useCompressionLevel.isSelected() ? compressionLevel.getValue() : @@ -192,9 +175,8 @@ box.setAlignmentX(LEFT_ALIGNMENT); box.add(createRestrictionsPanel()); -// box.add(createDisplayPanel()); box.add(createMouseCursorPanel()); -// box.add(createLocalShapePanel()); + box.add(createLocalShapePanel()); sharedSession = new JCheckBox("Request shared session"); box.add(new JPanel(new FlowLayout(FlowLayout.LEFT)).add(sharedSession)); @@ -205,7 +187,6 @@ private JPanel createRestrictionsPanel() { JPanel restrictionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); -// restrictionsPanel.setAlignmentX(LEFT_ALIGNMENT); restrictionsPanel.setBorder( BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), "Restrictions")); @@ -272,10 +253,10 @@ } private static class ColorDepthSelectItem { - final int bpp; + final int depth; final String title; - public ColorDepthSelectItem(int bpp, String title) { - this.bpp = bpp; + public ColorDepthSelectItem(int depth, String title) { + this.depth = depth; this.title = title; } @Override @@ -290,32 +271,24 @@ colorDepthPanel.add(new JLabel("Color format: ")); colorDepth = new JComboBox(); - colorDepth.addItem(new ColorDepthSelectItem(ProtocolSettings.BPP_SERVER_SETTINGS, + colorDepth.addItem(new ColorDepthSelectItem(ProtocolSettings.COLOR_DEPTH_SERVER_SETTINGS, "Server's default")); - colorDepth.addItem(new ColorDepthSelectItem(ProtocolSettings.BPP_32, + colorDepth.addItem(new ColorDepthSelectItem(ProtocolSettings.COLOR_DEPTH_24, "16 777 216 colors")); - colorDepth.addItem(new ColorDepthSelectItem(ProtocolSettings.BPP_16, + colorDepth.addItem(new ColorDepthSelectItem(ProtocolSettings.COLOR_DEPTH_16, "65 536 colors")); - colorDepth.addItem(new ColorDepthSelectItem(ProtocolSettings.BPP_8, + colorDepth.addItem(new ColorDepthSelectItem(ProtocolSettings.COLOR_DEPTH_8, "256 colors")); - colorDepth.addItem(new ColorDepthSelectItem(ProtocolSettings.BPP_6, + colorDepth.addItem(new ColorDepthSelectItem(ProtocolSettings.COLOR_DEPTH_6, "64 colors")); - colorDepth.addItem(new ColorDepthSelectItem(ProtocolSettings.BPP_3, + colorDepth.addItem(new ColorDepthSelectItem(ProtocolSettings.COLOR_DEPTH_3, "8 colors")); colorDepthPanel.add(colorDepth); colorDepth.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { - final ColorDepthSelectItem selectedItem = - (ColorDepthSelectItem) colorDepth.getSelectedItem(); - setEnabled(selectedItem.bpp > ProtocolSettings.BPP_8 || - selectedItem.bpp == ProtocolSettings.BPP_SERVER_SETTINGS, - useJpegQuality); - setEnabled(useJpegQuality.isSelected() && - (selectedItem.bpp > ProtocolSettings.BPP_8 || - selectedItem.bpp == ProtocolSettings.BPP_SERVER_SETTINGS), - jpegQuality, jpegQualityPoorLabel, jpegQualityBestLabel); + setJpegQualityPaneEnable(); } }); return colorDepthPanel; @@ -356,17 +329,20 @@ protected void setJpegQualityPaneEnable() { if (useJpegQuality != null && colorDepth != null) { - int bpp = ((ColorDepthSelectItem)colorDepth.getSelectedItem()).bpp; - setEnabled( - bpp > ProtocolSettings.BPP_8 || bpp == ProtocolSettings.BPP_SERVER_SETTINGS, - useJpegQuality); - setEnabled(useJpegQuality.isSelected() && - (bpp > ProtocolSettings.BPP_8 || bpp == ProtocolSettings.BPP_SERVER_SETTINGS), + int depth = ((ColorDepthSelectItem)colorDepth.getSelectedItem()).depth; + setEnabled(whetherJpegQualityPaneBeEnabled(depth), useJpegQuality); + setEnabled(useJpegQuality.isSelected() && whetherJpegQualityPaneBeEnabled(depth), jpegQuality, jpegQualityPoorLabel, jpegQualityBestLabel); } } - private void addCompressionLevelPane(JPanel encodingsPanel) { + private boolean whetherJpegQualityPaneBeEnabled(int depth) { + return ProtocolSettings.COLOR_DEPTH_16 == depth || + ProtocolSettings.COLOR_DEPTH_24 == depth || + ProtocolSettings.COLOR_DEPTH_SERVER_SETTINGS == depth; + } + + private void addCompressionLevelPane(JPanel encodingsPanel) { useCompressionLevel = new JCheckBox("Custom compression level:"); useCompressionLevel.setAlignmentX(LEFT_ALIGNMENT); encodingsPanel.add(useCompressionLevel); @@ -410,7 +386,6 @@ } } - /* private JPanel createLocalShapePanel() { JPanel localCursorShapePanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); // localCursorShapePanel.setLayout(new BoxLayout(localCursorShapePanel, BoxLayout.Y_AXIS)); @@ -420,23 +395,29 @@ Box localCursorShapeBox = Box.createVerticalBox(); localCursorShapePanel.add(localCursorShapeBox); - JRadioButton dotCursorRadio = new JRadioButton("Dot cursor"); - JRadioButton smallDotCursorRadio = new JRadioButton("Small dot cursor"); - JRadioButton arrowCursorRadio = new JRadioButton("Default cursor"); - JRadioButton noCursorRadio = new JRadioButton("No local cursor"); - localCursorShapeBox.add(dotCursorRadio); - localCursorShapeBox.add(smallDotCursorRadio); - localCursorShapeBox.add(arrowCursorRadio); - localCursorShapeBox.add(noCursorRadio); - ButtonGroup localCursorButtonGroup = new ButtonGroup(); - localCursorButtonGroup.add(dotCursorRadio); - localCursorButtonGroup.add(smallDotCursorRadio); - localCursorButtonGroup.add(arrowCursorRadio); - localCursorButtonGroup.add(noCursorRadio); + ButtonGroup mouseCursorShapeTrackGroup = new ButtonGroup(); + mouseCursorShapeSelected = new RadioButtonSelectedState<LocalMouseCursorShape>(); + mouseCursorShapeMap = new HashMap<LocalMouseCursorShape, JRadioButton>(); + + addRadioButton("Dot cursor", LocalMouseCursorShape.DOT, + mouseCursorShapeSelected, mouseCursorShapeMap, localCursorShapeBox, + mouseCursorShapeTrackGroup); + + addRadioButton("Small dot cursor", LocalMouseCursorShape.SMALL_DOT, + mouseCursorShapeSelected, mouseCursorShapeMap, localCursorShapeBox, + mouseCursorShapeTrackGroup); + + addRadioButton("System default cursor", LocalMouseCursorShape.SYSTEM_DEFAULT, + mouseCursorShapeSelected, mouseCursorShapeMap, localCursorShapeBox, + mouseCursorShapeTrackGroup); + + addRadioButton("No local cursor", LocalMouseCursorShape.NO_CURSOR, + mouseCursorShapeSelected, mouseCursorShapeMap, localCursorShapeBox, + mouseCursorShapeTrackGroup); + return localCursorShapePanel; } -*/ - + private JPanel createMouseCursorPanel() { JPanel mouseCursorPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); mouseCursorPanel.setBorder(
--- a/src/viewer_swing/java/com/glavsoft/viewer/swing/gui/PasswordDialog.java Tue Jul 03 13:20:49 2012 +0900 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/gui/PasswordDialog.java Wed Aug 07 19:01:17 2013 +0900 @@ -1,4 +1,4 @@ -// Copyright (C) 2010, 2011 GlavSoft LLC. +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. // All rights reserved. // //------------------------------------------------------------------------- @@ -24,6 +24,7 @@ package com.glavsoft.viewer.swing.gui; +import com.glavsoft.viewer.ConnectionWorker; import com.glavsoft.viewer.swing.Utils; import javax.swing.*; @@ -31,8 +32,8 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.WindowListener; -import java.util.List; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; /** * Dialog to ask password @@ -44,17 +45,19 @@ private static final int PADDING = 4; private final JLabel messageLabel; - public PasswordDialog(Frame owner, final WindowListener onClose, boolean isApplet) { - super(owner, "Login", isApplet ? ModalityType.APPLICATION_MODAL : ModalityType.TOOLKIT_MODAL); - try { - setAlwaysOnTop(true); - } catch (SecurityException e) { /*nop*/ } - addWindowListener(onClose); + public PasswordDialog(Frame owner, final ConnectionWorker onCancel) { + super(owner, "VNC Authentication", true); + addWindowListener(new WindowAdapter() { + @Override + public void windowClosed(WindowEvent windowEvent) { + onCancel.cancel(); + } + }); JPanel pane = new JPanel(new GridLayout(0, 1, PADDING, PADDING)); add(pane); pane.setBorder(new EmptyBorder(PADDING, PADDING, PADDING, PADDING)); - messageLabel = new JLabel("Server requires password authentication"); + messageLabel = new JLabel("Server requires VNC authentication"); pane.add(messageLabel); JPanel passwordPanel = new JPanel(); @@ -74,36 +77,32 @@ } }); - JButton closeButton = new JButton("Close"); + JButton closeButton = new JButton("Cancel"); buttonPanel.add(closeButton); closeButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { + password = null; setVisible(false); - onClose.windowClosing(null); + onCancel.cancel(); } }); pane.add(buttonPanel); getRootPane().setDefaultButton(loginButton); - - List<Image> icons = Utils.getIcons(); - if (icons.size() != 0) { - setIconImages(icons); - } - - // center dialog - Point locationPoint = GraphicsEnvironment.getLocalGraphicsEnvironment().getCenterPoint(); - pack(); - Rectangle bounds = getBounds(); - locationPoint.setLocation( - locationPoint.x - bounds.width/2, locationPoint.y - bounds.height/2); - setLocation(locationPoint); + Utils.decorateDialog(this); + Utils.centerWindow(this); + addWindowFocusListener(new WindowAdapter() { + @Override + public void windowGainedFocus(WindowEvent e) { + passwordField.requestFocusInWindow(); + } + }); } public void setServerHostName(String serverHostName) { - messageLabel.setText("Server '" + serverHostName + "' requires password authentication"); + messageLabel.setText("Server '" + serverHostName + "' requires VNC authentication"); pack(); } @@ -111,4 +110,6 @@ return password; } + + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/ssh/SshConnectionManager.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,167 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer.swing.ssh; + +import com.glavsoft.utils.Strings; +import com.glavsoft.viewer.CancelConnectionException; +import com.glavsoft.viewer.swing.ConnectionParams; +import com.glavsoft.viewer.swing.Utils; +import com.jcraft.jsch.*; + +import javax.swing.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.util.logging.Logger; +import java.util.prefs.Preferences; + +public class SshConnectionManager implements SshKnownHostsManager { + + public static final String SSH_NODE = "com/glavsoft/viewer/ssh"; + public static final String KNOWN_HOSTS = "known_hosts"; + private Session session; + private String errorMessage = ""; + private final JFrame parentWindow; + private JSch jsch; + + public SshConnectionManager(JFrame parentWindow) { + this.parentWindow = parentWindow; + } + + public int connect(ConnectionParams connectionParams) throws CancelConnectionException { + if (Strings.isTrimmedEmpty(connectionParams.sshUserName)) { + connectionParams.sshUserName = getInteractivelySshUserName(); + } + + if (session != null && session.isConnected()) { + session.disconnect(); + } + jsch = new JSch(); + try { + jsch.setKnownHosts(getKnownHostsStream()); + } catch (JSchException e) { + Logger.getLogger(this.getClass().getName()).severe("Cannot set JSCH known hosts: " + e.getMessage()); + } + int port = 0; + try { + session = jsch.getSession( + connectionParams.sshUserName, connectionParams.sshHostName, connectionParams.getSshPortNumber()); + UserInfo ui = new SwingSshUserInfo(parentWindow); + session.setUserInfo(ui); + session.connect(); + sync(); + port = session.setPortForwardingL(0, connectionParams.hostName, connectionParams.getPortNumber()); + } catch (JSchException e) { + session.disconnect(); + errorMessage = e.getMessage(); + } + return port; + } + + private String getInteractivelySshUserName() throws CancelConnectionException { + class Pair { + int intRes; + String stringRes; + } + final Pair result = new Pair(); + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + final JOptionPane pane = new JOptionPane("Please enter the user name for SSH connection:", + JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION); + pane.setWantsInput(true); + final JDialog dialog = pane.createDialog(parentWindow, "SSH User Name"); + Utils.decorateDialog(dialog); + dialog.setVisible(true); + result.stringRes = pane.getInputValue() != null ? (String) pane.getInputValue() : ""; + result.intRes = pane.getValue() != null ? (Integer) pane.getValue() : JOptionPane.OK_CANCEL_OPTION; + dialog.dispose(); + } + }); + } catch (InterruptedException e) { + Logger.getLogger(getClass().getName()).severe(e.getMessage()); + } catch (InvocationTargetException e) { + Logger.getLogger(getClass().getName()).severe(e.getMessage()); + } + if (result.intRes != JOptionPane.OK_OPTION) { + throw new CancelConnectionException("No Ssh User Name entered"); + } + return result.stringRes; + } + + public boolean isConnected() { + return session.isConnected(); + } + + public String getErrorMessage() { + return errorMessage; + } + + private InputStream getKnownHostsStream() { + Preferences sshNode = Preferences.userRoot().node(SSH_NODE); + return new ByteArrayInputStream(sshNode.getByteArray(KNOWN_HOSTS, new byte[0])); + } + + @Override + public void sync() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + HostKeyRepository repository = jsch.getHostKeyRepository(); + try { + HostKey[] hostKey = repository.getHostKey(); + if (null == hostKey) return; + for (HostKey hk : hostKey) { + String host = hk.getHost(); + String type = hk.getType(); + if (type.equals("UNKNOWN")) { + write(out, host); + write(out, "\n"); + continue; + } + write(out, host); + write(out, " "); + write(out, type); + write(out, " "); + write(out, hk.getKey()); + write(out, "\n"); + } + } catch (IOException e) { + Logger.getLogger(this.getClass().getName()).severe("Cannot sync JSCH known hosts: " + e.getMessage()); + } + Preferences sshNode = Preferences.userRoot().node(SSH_NODE); + sshNode.putByteArray(KNOWN_HOSTS, out.toByteArray()); + } + + private void write(ByteArrayOutputStream out, String str) throws IOException { + try { + out.write(str.getBytes("UTF-8")); + } catch (java.io.UnsupportedEncodingException e) { + out.write(str.getBytes()); + } + } + +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/ssh/SshKnownHostsManager.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,32 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer.swing.ssh; + +/** + * @author dime at tightvnc.com + */ +public interface SshKnownHostsManager { + void sync(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/viewer_swing/java/com/glavsoft/viewer/swing/ssh/SwingSshUserInfo.java Wed Aug 07 19:01:17 2013 +0900 @@ -0,0 +1,242 @@ +// Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. +// All rights reserved. +// +//------------------------------------------------------------------------- +// This file is part of the TightVNC software. Please visit our Web site: +// +// http://www.tightvnc.com/ +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +//------------------------------------------------------------------------- +// + +package com.glavsoft.viewer.swing.ssh; + +import com.glavsoft.utils.Strings; +import com.glavsoft.viewer.swing.Utils; +import com.jcraft.jsch.UIKeyboardInteractive; +import com.jcraft.jsch.UserInfo; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.lang.reflect.InvocationTargetException; +import java.util.logging.Logger; + +/** +* @author dime at tightvnc.com +*/ +class SwingSshUserInfo implements UserInfo, UIKeyboardInteractive { + private String password; + private String passphrase; + private final JFrame parentFrame; + + SwingSshUserInfo(JFrame parentFrame) { + this.parentFrame = parentFrame; + } + + @Override + public String getPassphrase() { + return passphrase; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public boolean promptPassword(final String message) { + final int[] result = new int[1]; + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + final JTextField passwordField = new JPasswordField(20); + Object[] ob = {message, passwordField}; + JOptionPane pane = new JOptionPane(ob, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION); + final JDialog dialog = pane.createDialog(parentFrame, "SSH Authentication"); + Utils.decorateDialog(dialog); + dialog.addWindowFocusListener(new WindowAdapter() { + @Override + public void windowGainedFocus(WindowEvent e) { + passwordField.requestFocusInWindow(); + } + }); + dialog.setVisible(true); + result[0] = pane.getValue() != null ? (Integer)pane.getValue() : JOptionPane.CLOSED_OPTION; + if (JOptionPane.OK_OPTION == result[0]) { + password = passwordField.getText(); + } + dialog.dispose(); + } + }); + } catch (InterruptedException e) { + getLogger().severe(e.getMessage()); + } catch (InvocationTargetException e) { + getLogger().severe(e.getMessage()); + } + + return JOptionPane.OK_OPTION == result[0]; + } + + @Override + public boolean promptPassphrase(final String message) { + final int[] result = new int[1]; + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + JTextField passphraseField = new JPasswordField(20); + Object[] ob = {message, passphraseField}; + JOptionPane pane = + new JOptionPane(ob, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION); + final JDialog dialog = pane.createDialog(parentFrame, "SSH Authentication"); + Utils.decorateDialog(dialog); + dialog.setVisible(true); + result[0] = pane.getValue() != null ? (Integer)pane.getValue() : JOptionPane.CLOSED_OPTION; + if (JOptionPane.OK_OPTION == result[0]) { + passphrase = passphraseField.getText(); + } + dialog.dispose(); + } + }); + } catch (InterruptedException e) { + getLogger().severe(e.getMessage()); + } catch (InvocationTargetException e) { + getLogger().severe(e.getMessage()); + } + return JOptionPane.OK_OPTION == result[0]; + } + + @Override + public boolean promptYesNo(final String message) { + final int[] result = new int[1]; + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + JOptionPane pane = new JOptionPane(message, JOptionPane.WARNING_MESSAGE, JOptionPane.YES_NO_OPTION); + final JDialog dialog = pane.createDialog(parentFrame, "SSH: Warning"); + Utils.decorateDialog(dialog); + dialog.setVisible(true); + result[0] = pane.getValue() != null ? (Integer)pane.getValue() : JOptionPane.CLOSED_OPTION; + dialog.dispose(); + } + }); + } catch (InterruptedException e) { + getLogger().severe(e.getMessage()); + } catch (InvocationTargetException e) { + getLogger().severe(e.getMessage()); + } + return JOptionPane.YES_OPTION == result[0]; + } + + @Override + public void showMessage(final String message) { + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + JOptionPane pane = new JOptionPane(message, JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION); + final JDialog dialog = pane.createDialog(parentFrame, "SSH"); + Utils.decorateDialog(dialog); + dialog.setVisible(true); + dialog.dispose(); + } + }); + } catch (InterruptedException e) { + getLogger().severe(e.getMessage()); + } catch (InvocationTargetException e) { + getLogger().severe(e.getMessage()); + } + } + + @Override + public String[] promptKeyboardInteractive(final String destination, + final String name, + final String instruction, + final String[] prompt, + final boolean[] echo) { + class WrapRes { + String[] stringsRes; + } + final WrapRes wrapRes = new WrapRes(); + try { + SwingUtilities.invokeAndWait(new Runnable() { + @Override + public void run() { + Container panel = new JPanel(); + panel.setLayout(new GridBagLayout()); + + final GridBagConstraints gbc = + new GridBagConstraints(0, 0, 1, 1, 1, 1, + GridBagConstraints.NORTHWEST, + GridBagConstraints.NONE, + new Insets(0, 0, 0, 0), 0, 0); + gbc.weightx = 1.0; + gbc.gridwidth = GridBagConstraints.REMAINDER; + gbc.gridx = 0; + panel.add(new JLabel(instruction), gbc); + gbc.gridy++; + gbc.gridwidth = GridBagConstraints.RELATIVE; + JTextField[] texts = new JTextField[prompt.length]; + for (int i = 0; i < prompt.length; i++) { + gbc.fill = GridBagConstraints.NONE; + gbc.gridx = 0; + gbc.weightx = 1; + panel.add(new JLabel(prompt[i]), gbc); + gbc.gridx = 1; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weighty = 1; + if (echo[i]) { + texts[i] = new JTextField(20); + } else { + texts[i] = new JPasswordField(20); + } + panel.add(texts[i], gbc); + gbc.gridy++; + } + + final String title = "SSH authentication for " + destination + (Strings.isTrimmedEmpty(name) ? "" : (": " + name)); + + final JOptionPane pane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION); + final JDialog dialog = pane.createDialog(parentFrame, title); + Utils.decorateDialog(dialog); + dialog.setVisible(true); + int result = pane.getValue() != null ? (Integer)pane.getValue() : JOptionPane.CLOSED_OPTION; + wrapRes.stringsRes = null; + if (JOptionPane.OK_OPTION == result) { + wrapRes.stringsRes = new String[prompt.length]; + for (int i = 0; i < prompt.length; i++) { + wrapRes.stringsRes[i] = texts[i].getText(); + } + } + dialog.dispose(); + } + }); + } catch (InterruptedException e) { + getLogger().severe(e.getMessage()); + } catch (InvocationTargetException e) { + getLogger().severe(e.getMessage()); + } + return wrapRes.stringsRes; + } + + private Logger getLogger() { + return Logger.getLogger(getClass().getName()); + } +}
Binary file src/viewer_swing/resources/com/glavsoft/viewer/images/button-zoom-fullscreen.png has changed
--- a/src/web/viewer-applet-example.html Tue Jul 03 13:20:49 2012 +0900 +++ b/src/web/viewer-applet-example.html Wed Aug 07 19:01:17 2013 +0900 @@ -1,13 +1,31 @@ -<!-- Copyright (C) 2010, 2011, 2012 GlavSoft LLC. --> +<!-- Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC. --> <!-- All rights reserved. --> -<!-- --> -<!-- DO NOT USE OR REDISTRIBUTE THE CODE, UNLESS YOU HAVE OBTAINED A LICENSE. --> +<!-- --> +<!-- ------------------------------------------------------------------------- --> +<!-- This file is part of the TightVNC software. Please visit our Web site: --> +<!-- --> +<!-- http://www.tightvnc.com/ --> +<!-- --> +<!-- This program is free software; you can redistribute it and/or modify --> +<!-- it under the terms of the GNU General Public License as published by --> +<!-- the Free Software Foundation; either version 2 of the License, or --> +<!-- (at your option) any later version. --> +<!-- --> +<!-- This program is distributed in the hope that it will be useful, --> +<!-- but WITHOUT ANY WARRANTY; without even the implied warranty of --> +<!-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --> +<!-- GNU General Public License for more details. --> +<!-- --> +<!-- You should have received a copy of the GNU General Public License along --> +<!-- with this program; if not, write to the Free Software Foundation, Inc., --> +<!-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. --> +<!-- ------------------------------------------------------------------------- --> <!-- --> <html> <title>TightVNC desktop</title> <body> - <applet archive="${archive_name}.jar" + <applet archive="tightvnc-jviewer.jar" code="com.glavsoft.viewer.Viewer" width="1" height="1"> <param name="Host" value="localhost" /> <!-- Host to connect. Default: the host from which the applet was loaded. --> @@ -29,7 +47,13 @@ <param name="colorDepth" value="" /> <!-- Reserved for future. Possible values: 6, 8, 16, 24, 32 (equals to 24). Only 24/32 is supported now --> <param name="ScalingFactor" value="100" /> <!-- Scale local representation of the remote desktop on startup. Default is 100 means 100% --> - </applet> + <!--param name="AllowAppletInteractiveConnections" value="no" /--> <!-- Allow applet interactively connect to other hosts then in HostName param or hostbase. Possible values: yes/true, no/false. Default: false. --> + <!-- SSH tunneling options --> + <param name="sshHost" value="" /><!-- SSH host name. --> + <param name="sshUser" value="" /><!-- SSH port number. When empty, standard SSH port number (22) is used --> + <param name="sshPort" value="" /><!-- SSH user name. --> + + </applet> <br /> <a href="http://www.tightvnc.com/">TightVNC Web Site</a> </body>