mirror of
https://github.com/TermoraDev/termora.git
synced 2026-01-16 02:12:58 +08:00
feat: support VNC protocol
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2010 - 2014 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.core;
|
||||
|
||||
/**
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public class SettingsChangedEvent {
|
||||
private final Object source;
|
||||
|
||||
public SettingsChangedEvent(Object source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public Object getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
144
plugins/vnc/src/main/java/com/glavsoft/drawing/ColorDecoder.java
Normal file
144
plugins/vnc/src/main/java/com/glavsoft/drawing/ColorDecoder.java
Normal file
@@ -0,0 +1,144 @@
|
||||
// Copyright (C) 2010 - 2014 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.drawing;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public class ColorDecoder {
|
||||
protected byte redShift;
|
||||
protected byte greenShift;
|
||||
protected byte blueShift;
|
||||
public short redMax;
|
||||
public short greenMax;
|
||||
public short blueMax;
|
||||
public final int bytesPerPixel;
|
||||
public final int bytesPerCPixel;
|
||||
public final int bytesPerPixelTight;
|
||||
private final byte[] buff;
|
||||
|
||||
private int startShift;
|
||||
private int startShiftCompact;
|
||||
private int addShiftItem;
|
||||
private final boolean isTightSpecific;
|
||||
|
||||
public ColorDecoder(PixelFormat pf) {
|
||||
redShift = pf.redShift;
|
||||
greenShift = pf.greenShift;
|
||||
blueShift = pf.blueShift;
|
||||
redMax = pf.redMax;
|
||||
greenMax = pf.greenMax;
|
||||
blueMax = pf.blueMax;
|
||||
bytesPerPixel = pf.bitsPerPixel / 8;
|
||||
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;
|
||||
startShiftCompact = 0;
|
||||
addShiftItem = 8;
|
||||
} else {
|
||||
startShift = pf.bitsPerPixel - 8;
|
||||
startShiftCompact = Math.max(0, pf.depth - 8);
|
||||
addShiftItem = -8;
|
||||
}
|
||||
isTightSpecific = 4==bytesPerPixel && 3==bytesPerPixelTight &&
|
||||
255 == redMax && 255 == greenMax && 255 == blueMax;
|
||||
}
|
||||
|
||||
protected int readColor(Transport transport) throws TransportException {
|
||||
return getColor(transport.readBytes(buff, 0, bytesPerPixel), 0);
|
||||
}
|
||||
|
||||
protected int readCompactColor(Transport transport) throws TransportException {
|
||||
return getCompactColor(transport.readBytes(buff, 0, bytesPerCPixel), 0);
|
||||
}
|
||||
|
||||
protected int readTightColor(Transport transport) throws TransportException {
|
||||
return getTightColor(transport.readBytes(buff, 0, bytesPerPixelTight), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert rfb encoded pixel color into 0x00rrggbb int value.
|
||||
* @param rawColor - bytes are ordered in right sequence (little/big endian transformations already done
|
||||
* @return 0x00rrggbb
|
||||
*/
|
||||
protected int convertColor(int rawColor) {
|
||||
return 255 * (rawColor >> redShift & redMax) / redMax << 16 |
|
||||
255 * (rawColor >> greenShift & greenMax) / greenMax << 8 |
|
||||
255 * (rawColor >> blueShift & blueMax) / blueMax;
|
||||
}
|
||||
|
||||
public void fillRawComponents(byte[] comp, byte[] bytes, int offset) {
|
||||
int rawColor = getRawTightColor(bytes, offset);
|
||||
comp[0] = (byte) (rawColor >> redShift & redMax);
|
||||
comp[1] = (byte) (rawColor >> greenShift & greenMax);
|
||||
comp[2] = (byte) (rawColor >> blueShift & blueMax);
|
||||
}
|
||||
|
||||
public int getTightColor(byte[] bytes, int offset) {
|
||||
return convertColor(getRawTightColor(bytes, offset));
|
||||
}
|
||||
|
||||
private int getRawTightColor(byte[] bytes, int offset) {
|
||||
if (isTightSpecific)
|
||||
return (bytes[offset++] & 0xff)<<16 |
|
||||
(bytes[offset++] & 0xff)<<8 |
|
||||
bytes[offset] & 0xff;
|
||||
else
|
||||
return getRawColor(bytes, offset);
|
||||
}
|
||||
|
||||
protected int getColor(byte[] bytes, int offset) {
|
||||
return convertColor(getRawColor(bytes, offset));
|
||||
}
|
||||
|
||||
private int getRawColor(byte[] bytes, int offset) {
|
||||
int shift = startShift;
|
||||
int item = addShiftItem;
|
||||
int rawColor = (bytes[offset++] & 0xff)<<shift;
|
||||
for (int i=1; i<bytesPerPixel; ++i) {
|
||||
rawColor |= (bytes[offset++] & 0xff)<<(shift+=item);
|
||||
}
|
||||
return rawColor;
|
||||
}
|
||||
|
||||
protected int getCompactColor(byte[] bytes, int offset) {
|
||||
int shift = startShiftCompact;
|
||||
int item = addShiftItem;
|
||||
int rawColor = (bytes[offset++] & 0xff)<<shift;
|
||||
for (int i=1; i< bytesPerCPixel; ++i) {
|
||||
rawColor |= (bytes[offset++] & 0xff)<<(shift+=item);
|
||||
}
|
||||
return convertColor(rawColor);
|
||||
}
|
||||
|
||||
}
|
||||
340
plugins/vnc/src/main/java/com/glavsoft/drawing/Renderer.java
Normal file
340
plugins/vnc/src/main/java/com/glavsoft/drawing/Renderer.java
Normal file
@@ -0,0 +1,340 @@
|
||||
// Copyright (C) 2010 - 2014 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.drawing;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||
import com.glavsoft.rfb.encoding.decoder.FramebufferUpdateRectangle;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/**
|
||||
* Render bitmap data
|
||||
*
|
||||
* @author dime @ tightvnc.com
|
||||
*/
|
||||
public abstract class Renderer {
|
||||
protected final ReentrantLock lock = new ReentrantLock();
|
||||
|
||||
public abstract void drawJpegImage(byte[] bytes, int offset,
|
||||
int jpegBufferLength, FramebufferUpdateRectangle rect);
|
||||
|
||||
protected int width;
|
||||
protected int height;
|
||||
protected int[] pixels;
|
||||
protected SoftCursor cursor;
|
||||
protected ColorDecoder colorDecoder;
|
||||
|
||||
protected void init(int width, int height, PixelFormat pixelFormat) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
initColorDecoder(pixelFormat);
|
||||
pixels = new int[width * height];
|
||||
Arrays.fill(pixels, 0);
|
||||
}
|
||||
|
||||
public void initColorDecoder(PixelFormat pixelFormat) {
|
||||
lock.lock();
|
||||
colorDecoder = new ColorDecoder(pixelFormat);
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
lock.lock();
|
||||
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;
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw byte array bitmap data (for ZRLE)
|
||||
*/
|
||||
public int drawCompactBytes(byte[] bytes, int offset, int x, int y, int width, int height) {
|
||||
int i = offset;
|
||||
lock.lock();
|
||||
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;
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
return i - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw int (colors) array bitmap data (for ZRLE)
|
||||
*/
|
||||
public void drawColoredBitmap(int[] colors, int x, int y, int width, int height) {
|
||||
int i = 0;
|
||||
lock.lock();
|
||||
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++];
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw byte array bitmap data (for Tight)
|
||||
*/
|
||||
public int drawTightBytes(byte[] bytes, int offset, int x, int y, int width, int height) {
|
||||
int i = offset;
|
||||
lock.lock();
|
||||
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;
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
return i - offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
int end = y * this.width + x + width;
|
||||
lock.lock();
|
||||
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;
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
lock.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];
|
||||
}
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
lock.lock();
|
||||
for (int y = startSrcY; y != endSrcY; y += deltaY) {
|
||||
System.arraycopy(pixels, y * width + srcX,
|
||||
pixels, dstY * width + dstRect.x, dstRect.width);
|
||||
dstY += deltaY;
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 void fillRect(int color, int x, int y, int width, int height) {
|
||||
lock.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);
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads color bytes (PIXEL) from transport, 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(Transport transport) throws TransportException {
|
||||
return colorDecoder.readColor(transport);
|
||||
}
|
||||
|
||||
public int readTightPixelColor(Transport transport) throws TransportException {
|
||||
return colorDecoder.readTightColor(transport);
|
||||
}
|
||||
|
||||
public ColorDecoder getColorDecoder() {
|
||||
return colorDecoder;
|
||||
}
|
||||
|
||||
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 getBytesPerPixel() {
|
||||
return colorDecoder.bytesPerPixel;
|
||||
}
|
||||
|
||||
public int getBytesPerCPixel() {
|
||||
return colorDecoder.bytesPerCPixel;
|
||||
}
|
||||
|
||||
public int getBytesPerPixelTight() {
|
||||
return colorDecoder.bytesPerPixelTight;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 new cursor position
|
||||
*
|
||||
* @param rect cursor position
|
||||
*/
|
||||
public void decodeCursorPosition(FramebufferUpdateRectangle rect) {
|
||||
synchronized (cursor.getLock()) {
|
||||
cursor.updatePosition(rect.x, rect.y);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// Copyright (C) 2010 - 2014 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.drawing;
|
||||
|
||||
/**
|
||||
* Abstract class for operations with soft cursor positions, dimensions and
|
||||
* hot point position.
|
||||
*/
|
||||
public abstract class SoftCursor {
|
||||
|
||||
protected int hotX, hotY;
|
||||
protected int x, y;
|
||||
public int width, height;
|
||||
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) {
|
||||
this.hotX = hotX;
|
||||
this.hotY = hotY;
|
||||
oldWidth = this.width = width;
|
||||
oldHeight = this.height = height;
|
||||
oldRX = rX = 0;
|
||||
oldRY = rY = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update cursor position
|
||||
*
|
||||
* @param newX
|
||||
* @param newY
|
||||
*/
|
||||
public void updatePosition(int newX, int newY) {
|
||||
oldRX = rX; oldRY = rY;
|
||||
oldWidth = width; oldHeight = height;
|
||||
x = newX; y = newY;
|
||||
rX = x - hotX; rY = y - hotY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set new cursor dimensions and hot point position
|
||||
*
|
||||
* @param hotX
|
||||
* @param hotY
|
||||
* @param width
|
||||
* @param height
|
||||
*/
|
||||
public void setNewDimensions(int hotX, int hotY, int width, int height) {
|
||||
this.hotX = hotX;
|
||||
this.hotY = hotY;
|
||||
oldWidth = this.width;
|
||||
oldHeight = this.height;
|
||||
oldRX = rX; oldRY = rY;
|
||||
rX = x - hotX; rY = y - hotY;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public void createCursor(int[] cursorPixels, int hotX, int hotY, int width, int height) {
|
||||
createNewCursorImage(cursorPixels, hotX, hotY, width, height);
|
||||
setNewDimensions(hotX, hotY, width, height);
|
||||
}
|
||||
|
||||
protected abstract void createNewCursorImage(int[] cursorPixels, int hotX, int hotY, int width, int height);
|
||||
|
||||
public Object getLock() {
|
||||
return lock;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2010 - 2014 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.exceptions;
|
||||
|
||||
/**
|
||||
* Throws when authentication was wrong
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class AuthenticationFailedException extends ProtocolException {
|
||||
|
||||
private String reason;
|
||||
|
||||
public AuthenticationFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
public AuthenticationFailedException(String message, String reason) {
|
||||
super(message);
|
||||
this.reason = reason;
|
||||
}
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2010 - 2014 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.exceptions;
|
||||
|
||||
/**
|
||||
* Throwed when connection closed (EOF)
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ClosedConnectionException extends TransportException {
|
||||
|
||||
public ClosedConnectionException(Throwable exception) {
|
||||
super(exception);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2010 - 2014 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.exceptions;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class CommonException extends Exception {
|
||||
|
||||
public CommonException(Throwable exception) {
|
||||
super(exception);
|
||||
}
|
||||
public CommonException(String message, Throwable exception) {
|
||||
super(message, exception);
|
||||
}
|
||||
public CommonException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2010 - 2014 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.exceptions;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class CouldNotConnectException extends TransportException {
|
||||
|
||||
public CouldNotConnectException(Throwable exception) {
|
||||
super(exception);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2010 - 2014 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.exceptions;
|
||||
|
||||
/**
|
||||
* Throws when problem with DES (or smth else) cryptosystem occured
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class CryptoException extends FatalException {
|
||||
public CryptoException(String message, Throwable exception) {
|
||||
super(message, exception);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2010 - 2014 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.exceptions;
|
||||
|
||||
/**
|
||||
* Trhows when further normal program execution unavailable
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class FatalException extends CommonException {
|
||||
public FatalException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2010 - 2014 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.exceptions;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ProtocolException extends CommonException {
|
||||
public ProtocolException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2010 - 2014 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.exceptions;
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class TransportException extends CommonException {
|
||||
public TransportException(String message, Throwable exception) {
|
||||
super(message, exception);
|
||||
}
|
||||
public TransportException(Throwable exception) {
|
||||
super(exception);
|
||||
}
|
||||
public TransportException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2010 - 2014 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.exceptions;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class UnsupportedProtocolVersionException extends ProtocolException {
|
||||
public UnsupportedProtocolVersionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2010 - 2014 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.exceptions;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class UnsupportedSecurityTypeException extends ProtocolException {
|
||||
public UnsupportedSecurityTypeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb;
|
||||
|
||||
/**
|
||||
* Interface for handling clipboard texts
|
||||
*/
|
||||
public interface ClipboardController extends IChangeSettingsListener {
|
||||
void updateSystemClipboard(byte[] bytes);
|
||||
|
||||
/**
|
||||
* Get text clipboard contens when needed send to remote, or null vise versa
|
||||
* Implement this method such a way in swing context, because swing's clipboard
|
||||
* update listener invoked only on DataFlavor changes not content changes.
|
||||
* Implement as returned null on systems where clipboard listeners work correctly.
|
||||
*
|
||||
* @return clipboad string contents if it is changed from last method call
|
||||
* or null when clipboard contains non text object or clipboard contents didn't changed
|
||||
*/
|
||||
String getRenewedClipboardText();
|
||||
|
||||
/**
|
||||
* Returns clipboard text content previously retrieved frim system clipboard by
|
||||
* updateSavedClippoardContent()
|
||||
*
|
||||
* @return clipboard text content
|
||||
*/
|
||||
String getClipboardText();
|
||||
|
||||
/**
|
||||
* Enable/disable clipboard transfer
|
||||
*
|
||||
* @param enable
|
||||
*/
|
||||
void setEnabled(boolean enable);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb;
|
||||
|
||||
import com.glavsoft.core.SettingsChangedEvent;
|
||||
|
||||
/**
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public interface IChangeSettingsListener {
|
||||
void settingsChanged(SettingsChangedEvent event);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||
import com.glavsoft.rfb.encoding.decoder.FramebufferUpdateRectangle;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* Interface for sending repaint event from worker thread to GUI thread
|
||||
*/
|
||||
public interface IRepaintController extends IChangeSettingsListener {
|
||||
void repaintBitmap(FramebufferUpdateRectangle rect);
|
||||
void repaintBitmap(int x, int y, int width, int height);
|
||||
void repaintCursor();
|
||||
void updateCursorPosition(short x, short y);
|
||||
Renderer createRenderer(Transport transport, int width, int height, PixelFormat pixelFormat);
|
||||
void setPixelFormat(PixelFormat pixelFormat);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb;
|
||||
|
||||
public interface IRequestString {
|
||||
String getResult();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb;
|
||||
|
||||
/**
|
||||
* Fires Rfb session [start]-stop events
|
||||
*/
|
||||
public interface IRfbSessionListener {
|
||||
|
||||
/**
|
||||
* Fired after rfb session closed (threads stopped, other resources fried).
|
||||
* Note: this event may be fired from unpredictable thread.
|
||||
* @param reason reason to show why session closed
|
||||
*/
|
||||
void rfbSessionStopped(String reason);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* Structure used to describe protocol options such as tunneling methods,
|
||||
* authentication schemes and message types (protocol versions 3.7t, 3.8t).
|
||||
* typedef struct _rfbCapabilityInfo {
|
||||
* CARD32 code; // numeric identifier
|
||||
* CARD8 vendorSignature[4]; // vendor identification
|
||||
* CARD8 nameSignature[8]; // abbreviated option name
|
||||
* } rfbCapabilityInfo;
|
||||
*/
|
||||
public class RfbCapabilityInfo {
|
||||
/*
|
||||
* Vendors known by TightVNC: standard VNC/RealVNC, TridiaVNC, and TightVNC.
|
||||
* #define rfbStandardVendor "STDV"
|
||||
* #define rfbTridiaVncVendor "TRDV"
|
||||
* #define rfbTightVncVendor "TGHT"
|
||||
*/
|
||||
public static final String VENDOR_STANDARD = "STDV";
|
||||
public static final String VENDOR_TRIADA = "TRDV";
|
||||
public static final String VENDOR_TIGHT = "TGHT";
|
||||
|
||||
public static final String TUNNELING_NO_TUNNEL = "NOTUNNEL";
|
||||
|
||||
public static final String AUTHENTICATION_NO_AUTH = "NOAUTH__";
|
||||
public static final String AUTHENTICATION_VNC_AUTH ="VNCAUTH_";
|
||||
|
||||
public static final String ENCODING_COPYRECT = "COPYRECT";
|
||||
public static final String ENCODING_HEXTILE = "HEXTILE_";
|
||||
public static final String ENCODING_ZLIB = "ZLIB____";
|
||||
public static final String ENCODING_ZRLE = "ZRLE____";
|
||||
public static final String ENCODING_RRE = "RRE_____";
|
||||
public static final String ENCODING_TIGHT = "TIGHT___";
|
||||
// "Pseudo" encoding types
|
||||
public static final String ENCODING_RICH_CURSOR = "RCHCURSR";
|
||||
public static final String ENCODING_CURSOR_POS = "POINTPOS";
|
||||
public static final String ENCODING_DESKTOP_SIZE = "NEWFBSIZ";
|
||||
|
||||
private int code;
|
||||
private String vendorSignature;
|
||||
private String nameSignature;
|
||||
private boolean enable;
|
||||
|
||||
public RfbCapabilityInfo(int code, String vendorSignature, String nameSignature) {
|
||||
this.code = code;
|
||||
this.vendorSignature = vendorSignature;
|
||||
this.nameSignature = nameSignature;
|
||||
enable = true;
|
||||
}
|
||||
|
||||
public RfbCapabilityInfo() {
|
||||
this(0, "", "");
|
||||
}
|
||||
|
||||
public RfbCapabilityInfo readFrom(Transport transport) throws TransportException {
|
||||
code = transport.readInt32();
|
||||
vendorSignature = transport.readString(4);
|
||||
nameSignature = transport.readString(8);
|
||||
enable = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
RfbCapabilityInfo that = (RfbCapabilityInfo) o;
|
||||
return code == that.code &&
|
||||
nameSignature.equals(that.nameSignature) &&
|
||||
vendorSignature.equals(that.vendorSignature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = code;
|
||||
result = 31 * result + vendorSignature.hashCode();
|
||||
result = 31 * result + nameSignature.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setEnable(boolean enable) {
|
||||
this.enable = enable;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getVendorSignature() {
|
||||
return vendorSignature;
|
||||
}
|
||||
|
||||
public String getNameSignature() {
|
||||
return nameSignature;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RfbCapabilityInfo{" +
|
||||
"code=" + code +
|
||||
", vendorSignature='" + vendorSignature + '\'' +
|
||||
", nameSignature='" + nameSignature + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static com.glavsoft.utils.Strings.getBytesWithCharset;
|
||||
|
||||
/**
|
||||
* ClientCutText
|
||||
* The client has new ISO 8859-1 (Latin-1) text in its cut buffer. Ends of lines are repre-
|
||||
* sented by the linefeed / newline character (value 10) alone. No carriage-return (value
|
||||
* 13) is needed. There is currently no way to transfer text outside the Latin-1 character
|
||||
* set.
|
||||
* 1 - U8 - 6
|
||||
* 3 - - padding
|
||||
* 4 - U32 - length
|
||||
* length - U8 array - text
|
||||
*/
|
||||
public class ClientCutTextMessage implements ClientToServerMessage {
|
||||
private final byte [] bytes;
|
||||
|
||||
public ClientCutTextMessage(String str, Charset charset) {
|
||||
final byte[] b = charset != null? getBytesWithCharset(str, charset): str.getBytes();
|
||||
this.bytes = Arrays.copyOf(b, b.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.CLIENT_CUT_TEXT.id)
|
||||
.zero(3) // padding
|
||||
.writeInt32(bytes.length)
|
||||
.write(bytes)
|
||||
.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClientCutTextMessage: [length: " + bytes.length +", text: ...]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.client;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public enum ClientMessageType {
|
||||
SET_PIXEL_FORMAT(0),
|
||||
SET_ENCODINGS(2),
|
||||
FRAMEBUFFER_UPDATE_REQUEST(3),
|
||||
KEY_EVENT(4),
|
||||
POINTER_EVENT(5),
|
||||
CLIENT_CUT_TEXT(6),
|
||||
|
||||
VIDEO_RECTANGLE_SELECTION(151),
|
||||
VIDEO_FREEZE(152);
|
||||
|
||||
final public int id;
|
||||
|
||||
ClientMessageType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
private static final ClientMessageType [] standardTypes =
|
||||
{SET_PIXEL_FORMAT, SET_ENCODINGS, FRAMEBUFFER_UPDATE_REQUEST, KEY_EVENT, POINTER_EVENT, CLIENT_CUT_TEXT};
|
||||
|
||||
public static boolean isStandardType(ClientMessageType type) {
|
||||
for (final ClientMessageType it : standardTypes) {
|
||||
if (it == type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ClientMessageType byId(int id) {
|
||||
for (ClientMessageType type : values()) {
|
||||
if (type.id == id)
|
||||
return type;
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported client message type: " + id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public interface ClientToServerMessage {
|
||||
void send(Transport transport) throws TransportException;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
|
||||
public class FramebufferUpdateRequestMessage implements ClientToServerMessage {
|
||||
private final boolean incremental;
|
||||
private final int height;
|
||||
private final int width;
|
||||
private final int y;
|
||||
private final int x;
|
||||
|
||||
public FramebufferUpdateRequestMessage(int x, int y, int width,
|
||||
int height, boolean incremental) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.incremental = incremental;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.FRAMEBUFFER_UPDATE_REQUEST.id)
|
||||
.writeByte(incremental ? 1 : 0)
|
||||
.writeInt16(x)
|
||||
.writeInt16(y)
|
||||
.writeInt16(width)
|
||||
.writeInt16(height)
|
||||
.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FramebufferUpdateRequestMessage: [x: " + x + " y: " + y
|
||||
+ " width: " + width + " height: " + height +
|
||||
" incremental: " + incremental + "]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* A key press or release. Down-flag is non-zero (true) if the key is now pressed, zero
|
||||
* (false) if it is now released. The key itself is specified using the "keysym" values
|
||||
* defined by the X Window System.
|
||||
* 1 - U8 - message-type
|
||||
* 1 - U8 - down-flag
|
||||
* 2 - - - padding
|
||||
* 4 - U32 - key
|
||||
* For most ordinary keys, the "keysym" is the same as the corresponding ASCII value.
|
||||
* For full details, see The Xlib Reference Manual, published by O'Reilly & Associates,
|
||||
* or see the header file <X11/keysymdef.h> from any X Window System installation.
|
||||
*/
|
||||
public class KeyEventMessage implements ClientToServerMessage {
|
||||
|
||||
private final int key;
|
||||
private final boolean downFlag;
|
||||
|
||||
public KeyEventMessage(int key, boolean downFlag) {
|
||||
this.downFlag = downFlag;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.KEY_EVENT.id)
|
||||
.writeByte(downFlag ? 1 : 0)
|
||||
.zero(2) // padding
|
||||
.write(key)
|
||||
.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[KeyEventMessage: [down-flag: "+downFlag + ", key: " + key +"("+Integer.toHexString(key)+")]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public class PointerEventMessage implements ClientToServerMessage {
|
||||
private final byte buttonMask;
|
||||
private final short x;
|
||||
private final short y;
|
||||
|
||||
public PointerEventMessage(byte buttonMask, short x, short y) {
|
||||
this.buttonMask = buttonMask;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.POINTER_EVENT.id)
|
||||
.writeByte(buttonMask)
|
||||
.writeInt16(x)
|
||||
.writeInt16(y)
|
||||
.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PointerEventMessage: [x: "+ x +", y: "+ y + ", button-mask: " +
|
||||
buttonMask +"]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.encoding.EncodingType;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class SetEncodingsMessage implements ClientToServerMessage {
|
||||
private final Set<EncodingType> encodings;
|
||||
|
||||
public SetEncodingsMessage(Set<EncodingType> set) {
|
||||
this.encodings = set;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.SET_ENCODINGS.id)
|
||||
.zero(1) // padding byte
|
||||
.writeInt16(encodings.size());
|
||||
for (EncodingType enc : encodings) {
|
||||
transport.writeInt32(enc.getId());
|
||||
}
|
||||
transport.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("SetEncodingsMessage: [encodings: ");
|
||||
for (EncodingType enc : encodings) {
|
||||
sb.append(enc.name()).append(',');
|
||||
}
|
||||
sb.setLength(sb.length()-1);
|
||||
return sb.append(']').toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public class SetPixelFormatMessage implements ClientToServerMessage {
|
||||
private final PixelFormat pixelFormat;
|
||||
|
||||
public SetPixelFormatMessage(PixelFormat pixelFormat) {
|
||||
this.pixelFormat = pixelFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.SET_PIXEL_FORMAT.id)
|
||||
.zero(3);
|
||||
pixelFormat.send(transport);
|
||||
transport.flush();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public class VideoFreezeMessage implements ClientToServerMessage {
|
||||
|
||||
private boolean freeze;
|
||||
|
||||
public VideoFreezeMessage(boolean freeze) {
|
||||
this.freeze = freeze;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.VIDEO_FREEZE.id)
|
||||
.writeByte(freeze ? 1 : 0)
|
||||
.flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.client;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public class VideoRectangleSelectionMessage implements ClientToServerMessage {
|
||||
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
public VideoRectangleSelectionMessage(int x, int y, int width, int height) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.writeByte(ClientMessageType.VIDEO_RECTANGLE_SELECTION.id)
|
||||
.zero(1) // padding
|
||||
.writeInt16(x)
|
||||
.writeInt16(y)
|
||||
.writeInt16(width)
|
||||
.writeInt16(height)
|
||||
.flush();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding;
|
||||
|
||||
import com.glavsoft.rfb.encoding.decoder.CopyRectDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.CursorPosDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.Decoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.DesctopSizeDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.FakeDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.HextileDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.RREDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.RawDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.RichCursorDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.TightDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.ZRLEDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.ZlibDecoder;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
/**
|
||||
* Encoding types
|
||||
*/
|
||||
public enum EncodingType {
|
||||
/**
|
||||
* Desktop data representes as raw bytes stream
|
||||
*/
|
||||
RAW_ENCODING(0, "Raw", RawDecoder.class),
|
||||
/**
|
||||
* Specfies encodings which allow to copy part of image in client's
|
||||
* framebuffer from one place to another.
|
||||
*/
|
||||
COPY_RECT(1, "CopyRect", CopyRectDecoder.class),
|
||||
RRE(2, "RRE", RREDecoder.class),
|
||||
/**
|
||||
* Hextile encoding, uses palettes, filling and raw subencoding
|
||||
*/
|
||||
HEXTILE(5, "Hextile", HextileDecoder.class),
|
||||
/**
|
||||
* This encoding is like raw but previously all data compressed with zlib.
|
||||
*/
|
||||
ZLIB(6, "ZLib", ZlibDecoder.class),
|
||||
/**
|
||||
* Tight Encoding for slow connection. It is uses raw data, palettes, filling
|
||||
* and jpeg subencodings
|
||||
*/
|
||||
TIGHT(7, "Tight", TightDecoder.class),
|
||||
//ZlibHex(8),
|
||||
/**
|
||||
* ZRLE Encoding is like Hextile but previously all data compressed with zlib.
|
||||
*/
|
||||
ZRLE(16, "ZRLE", ZRLEDecoder.class),
|
||||
|
||||
/**
|
||||
* Rich Cursor pseudo encoding which allows to transfer cursor shape
|
||||
* with transparency
|
||||
*/
|
||||
RICH_CURSOR(0xFFFFFF11, "RichCursor", RichCursorDecoder.class),
|
||||
/**
|
||||
* Desktop Size Pseudo encoding allows to notificate client about
|
||||
* remote screen resolution changed.
|
||||
*/
|
||||
DESKTOP_SIZE(0xFFFFFF21, "DesctopSize", DesctopSizeDecoder.class),
|
||||
/**
|
||||
* Cusros position encoding allows to transfer remote cursor position to
|
||||
* client side.
|
||||
*/
|
||||
CURSOR_POS(0xFFFFFF18, "CursorPos", CursorPosDecoder.class),
|
||||
|
||||
COMPRESS_LEVEL_0(0xFFFFFF00 + 0, "CompressionLevel0", FakeDecoder.class),
|
||||
COMPRESS_LEVEL_1(0xFFFFFF00 + 1, "CompressionLevel1", null),
|
||||
COMPRESS_LEVEL_2(0xFFFFFF00 + 2, "CompressionLevel2", null),
|
||||
COMPRESS_LEVEL_3(0xFFFFFF00 + 3, "CompressionLevel3", null),
|
||||
COMPRESS_LEVEL_4(0xFFFFFF00 + 4, "CompressionLevel4", null),
|
||||
COMPRESS_LEVEL_5(0xFFFFFF00 + 5, "CompressionLevel5", null),
|
||||
COMPRESS_LEVEL_6(0xFFFFFF00 + 6, "CompressionLevel6", null),
|
||||
COMPRESS_LEVEL_7(0xFFFFFF00 + 7, "CompressionLevel7", null),
|
||||
COMPRESS_LEVEL_8(0xFFFFFF00 + 8, "CompressionLevel8", null),
|
||||
COMPRESS_LEVEL_9(0xFFFFFF00 + 9, "CompressionLevel9", null),
|
||||
|
||||
JPEG_QUALITY_LEVEL_0(0xFFFFFFE0 + 0, "JpegQualityLevel0", FakeDecoder.class),
|
||||
JPEG_QUALITY_LEVEL_1(0xFFFFFFE0 + 1, "JpegQualityLevel1", null),
|
||||
JPEG_QUALITY_LEVEL_2(0xFFFFFFE0 + 2, "JpegQualityLevel2", null),
|
||||
JPEG_QUALITY_LEVEL_3(0xFFFFFFE0 + 3, "JpegQualityLevel3", null),
|
||||
JPEG_QUALITY_LEVEL_4(0xFFFFFFE0 + 4, "JpegQualityLevel4", null),
|
||||
JPEG_QUALITY_LEVEL_5(0xFFFFFFE0 + 5, "JpegQualityLevel5", null),
|
||||
JPEG_QUALITY_LEVEL_6(0xFFFFFFE0 + 6, "JpegQualityLevel6", null),
|
||||
JPEG_QUALITY_LEVEL_7(0xFFFFFFE0 + 7, "JpegQualityLevel7", null),
|
||||
JPEG_QUALITY_LEVEL_8(0xFFFFFFE0 + 8, "JpegQualityLevel8", null),
|
||||
JPEG_QUALITY_LEVEL_9(0xFFFFFFE0 + 9, "JpegQualityLevel9", null);
|
||||
|
||||
private final int id;
|
||||
private final String name;
|
||||
public final Class<? extends Decoder> klass;
|
||||
|
||||
private EncodingType(int id, String name, Class<? extends Decoder> klass) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.klass = klass;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static final LinkedHashSet<EncodingType> ordinaryEncodings = new LinkedHashSet<EncodingType>();
|
||||
static {
|
||||
ordinaryEncodings.add(ZRLE);
|
||||
ordinaryEncodings.add(TIGHT);
|
||||
ordinaryEncodings.add(ZLIB);
|
||||
ordinaryEncodings.add(HEXTILE);
|
||||
ordinaryEncodings.add(RRE);
|
||||
ordinaryEncodings.add(COPY_RECT);
|
||||
ordinaryEncodings.add(RAW_ENCODING);
|
||||
}
|
||||
|
||||
public static final LinkedHashSet<EncodingType> pseudoEncodings = new LinkedHashSet<EncodingType>();
|
||||
static {
|
||||
pseudoEncodings.add(RICH_CURSOR);
|
||||
pseudoEncodings.add(CURSOR_POS);
|
||||
pseudoEncodings.add(DESKTOP_SIZE);
|
||||
}
|
||||
|
||||
public static EncodingType byId(int id) {
|
||||
// TODO needs to speedup with hash usage?
|
||||
for (EncodingType type : values()) {
|
||||
if (type.getId() == id)
|
||||
return type;
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported encoding code: " + id);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* Pixel Format:
|
||||
* 1 - U8 - bits-per-pixel
|
||||
* 1 - U8 - depth
|
||||
* 1 - U8 - big-endian-flag
|
||||
* 1 - U8 - true-color-flag
|
||||
* 2 - U16 - red-max
|
||||
* 2 - U16 - green-max
|
||||
* 2 - U16 - blue-max
|
||||
* 1 - U8 - red-shift
|
||||
* 1 - U8 - green-shift
|
||||
* 1 - U8 - blue-shift
|
||||
* 3 - - padding
|
||||
*/
|
||||
public class PixelFormat {
|
||||
public byte bitsPerPixel;
|
||||
public byte depth;
|
||||
public byte bigEndianFlag;
|
||||
public byte trueColourFlag;
|
||||
public short redMax;
|
||||
public short greenMax;
|
||||
public short blueMax;
|
||||
public byte redShift;
|
||||
public byte greenShift;
|
||||
public byte blueShift;
|
||||
|
||||
public void fill(Transport transport) throws TransportException {
|
||||
bitsPerPixel = transport.readByte();
|
||||
depth = transport.readByte();
|
||||
bigEndianFlag = transport.readByte();
|
||||
trueColourFlag = transport.readByte();
|
||||
redMax = transport.readInt16();
|
||||
greenMax = transport.readInt16();
|
||||
blueMax = transport.readInt16();
|
||||
redShift = transport.readByte();
|
||||
greenShift = transport.readByte();
|
||||
blueShift = transport.readByte();
|
||||
transport.readBytes(3); // skip padding bytes
|
||||
}
|
||||
|
||||
public void send(Transport transport) throws TransportException {
|
||||
transport.write(bitsPerPixel)
|
||||
.write(depth)
|
||||
.write(bigEndianFlag)
|
||||
.write(trueColourFlag)
|
||||
.write(redMax)
|
||||
.write(greenMax)
|
||||
.write(blueMax)
|
||||
.write(redShift)
|
||||
.write(greenShift)
|
||||
.write(blueShift)
|
||||
.writeInt16(0) // padding bytes
|
||||
.writeByte(0); // padding bytes
|
||||
}
|
||||
|
||||
public static PixelFormat create24bitColorDepthPixelFormat(int bigEndianFlag) {
|
||||
final PixelFormat pixelFormat = new PixelFormat();
|
||||
pixelFormat.bigEndianFlag = (byte) bigEndianFlag;
|
||||
pixelFormat.bitsPerPixel = 32;
|
||||
pixelFormat.blueMax = 255;
|
||||
pixelFormat.blueShift = 0;
|
||||
pixelFormat.greenMax = 255;
|
||||
pixelFormat.greenShift = 8;
|
||||
pixelFormat.redMax = 255;
|
||||
pixelFormat.redShift = 16;
|
||||
pixelFormat.depth = 24;
|
||||
pixelFormat.trueColourFlag = 1;
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* specifies 65536 colors, 5bit per Red, 6bit per Green, 5bit per Blue
|
||||
*/
|
||||
public static PixelFormat create16bitColorDepthPixelFormat(int bigEndianFlag) {
|
||||
final PixelFormat pixelFormat = new PixelFormat();
|
||||
pixelFormat.bigEndianFlag = (byte) bigEndianFlag;
|
||||
pixelFormat.bitsPerPixel = 16;
|
||||
pixelFormat.blueMax = 31;
|
||||
pixelFormat.blueShift = 0;
|
||||
pixelFormat.greenMax = 63;
|
||||
pixelFormat.greenShift = 5;
|
||||
pixelFormat.redMax = 31;
|
||||
pixelFormat.redShift = 11;
|
||||
pixelFormat.depth = 16;
|
||||
pixelFormat.trueColourFlag = 1;
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* specifies 256 colors, 2bit per Blue, 3bit per Green & Red
|
||||
*/
|
||||
public static PixelFormat create8bitColorDepthBGRPixelFormat(int bigEndianFlag) {
|
||||
final PixelFormat pixelFormat = new PixelFormat();
|
||||
pixelFormat.bigEndianFlag = (byte) bigEndianFlag;
|
||||
pixelFormat.bitsPerPixel = 8;
|
||||
pixelFormat.redMax = 7;
|
||||
pixelFormat.redShift = 0;
|
||||
pixelFormat.greenMax = 7;
|
||||
pixelFormat.greenShift = 3;
|
||||
pixelFormat.blueMax = 3;
|
||||
pixelFormat.blueShift = 6;
|
||||
pixelFormat.depth = 8;
|
||||
pixelFormat.trueColourFlag = 1;
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* specifies 64 colors, 2bit per Red, Green & Blue
|
||||
*/
|
||||
public static PixelFormat create6bitColorDepthPixelFormat(int bigEndianFlag) {
|
||||
final PixelFormat pixelFormat = new PixelFormat();
|
||||
pixelFormat.bigEndianFlag = (byte) bigEndianFlag;
|
||||
pixelFormat.bitsPerPixel = 8;
|
||||
pixelFormat.blueMax = 3;
|
||||
pixelFormat.blueShift = 0;
|
||||
pixelFormat.greenMax = 3;
|
||||
pixelFormat.greenShift = 2;
|
||||
pixelFormat.redMax = 3;
|
||||
pixelFormat.redShift = 4;
|
||||
pixelFormat.depth = 6;
|
||||
pixelFormat.trueColourFlag = 1;
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* specifies 8 colors, 1bit per Red, Green & Blue
|
||||
*/
|
||||
public static PixelFormat create3bitColorDepthPixelFormat(int bigEndianFlag) {
|
||||
final PixelFormat pixelFormat = new PixelFormat();
|
||||
pixelFormat.bigEndianFlag = (byte) bigEndianFlag;
|
||||
pixelFormat.bitsPerPixel = 8;
|
||||
pixelFormat.blueMax = 1;
|
||||
pixelFormat.blueShift = 0;
|
||||
pixelFormat.greenMax = 1;
|
||||
pixelFormat.greenShift = 1;
|
||||
pixelFormat.redMax = 1;
|
||||
pixelFormat.redShift = 2;
|
||||
pixelFormat.depth = 3;
|
||||
pixelFormat.trueColourFlag = 1;
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PixelFormat: [bits-per-pixel: " + String.valueOf(0xff & bitsPerPixel) +
|
||||
", depth: " + String.valueOf(0xff & depth) +
|
||||
", big-endian-flag: " + String.valueOf(0xff & bigEndianFlag) +
|
||||
", true-color-flag: " + String.valueOf(0xff & trueColourFlag) +
|
||||
", red-max: " + String.valueOf(0xffff & redMax) +
|
||||
", green-max: " + String.valueOf(0xffff & greenMax) +
|
||||
", blue-max: " + String.valueOf(0xffff & blueMax) +
|
||||
", red-shift: " + String.valueOf(0xff & redShift) +
|
||||
", green-shift: " + String.valueOf(0xff & greenShift) +
|
||||
", blue-shift: " + String.valueOf(0xff & blueShift) +
|
||||
"]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* Struct filled from the ServerInit message
|
||||
* 2 - U16 - framebuffer-width
|
||||
* 2 - U16 - framebuffer-height
|
||||
* 16 - PixelFormat - server-pixel-format
|
||||
* 4 - U32 - name-length
|
||||
* name-length - U8 array - name-string
|
||||
*/
|
||||
public class ServerInitMessage {
|
||||
protected String name;
|
||||
protected int framebufferWidth;
|
||||
protected int framebufferHeight;
|
||||
protected PixelFormat pixelFormat;
|
||||
|
||||
public ServerInitMessage readFrom(Transport transport) throws TransportException {
|
||||
framebufferWidth = transport.readUInt16();
|
||||
framebufferHeight = transport.readUInt16();
|
||||
pixelFormat = new PixelFormat();
|
||||
pixelFormat.fill(transport);
|
||||
name = transport.readString();
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getFramebufferWidth() {
|
||||
return framebufferWidth;
|
||||
}
|
||||
|
||||
public int getFramebufferHeight() {
|
||||
return framebufferHeight;
|
||||
}
|
||||
|
||||
public PixelFormat getPixelFormat() {
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServerInitMessage{" +
|
||||
"name='" + name + '\'' +
|
||||
", framebufferWidth=" + framebufferWidth +
|
||||
", framebufferHeight=" + framebufferHeight +
|
||||
", pixelFormat=" + pixelFormat +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
/**
|
||||
* Resizeable to needed length byte buffer
|
||||
* Singleton for share among decoders.
|
||||
*/
|
||||
public class ByteBuffer {
|
||||
private static ThreadLocal<ByteBuffer> threadLocal = new ThreadLocal<ByteBuffer>() {
|
||||
@Override
|
||||
protected ByteBuffer initialValue() {
|
||||
return new ByteBuffer();
|
||||
}
|
||||
};
|
||||
private byte [] buffer = new byte[0];
|
||||
|
||||
private ByteBuffer() { /*empty*/ }
|
||||
|
||||
public static ByteBuffer getInstance() {
|
||||
return threadLocal.get();
|
||||
}
|
||||
|
||||
public static void removeInstance() {
|
||||
threadLocal.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for buffer capacity is enougth ( < length) and enlarge it if not
|
||||
*/
|
||||
public void correctBufferCapacity(int length) {
|
||||
// procondition: buffer != null
|
||||
assert (buffer != null);
|
||||
if (buffer.length < length) {
|
||||
buffer = new byte[length];
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getBuffer(int length) {
|
||||
correctBufferCapacity(length);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public class CopyRectDecoder extends Decoder {
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer,
|
||||
FramebufferUpdateRectangle rect) throws TransportException {
|
||||
int srcX = transport.readUInt16();
|
||||
int srcY = transport.readUInt16();
|
||||
if (rect.width == 0 || rect.height == 0) return;
|
||||
renderer.copyRect(srcX, srcY, rect);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public class CursorPosDecoder extends Decoder {
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer, FramebufferUpdateRectangle rect) throws TransportException {
|
||||
renderer.decodeCursorPosition(rect);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
|
||||
public abstract class Decoder {
|
||||
/**
|
||||
* Decode rectangle data.
|
||||
*/
|
||||
abstract public void decode(Transport transport, Renderer renderer,
|
||||
FramebufferUpdateRectangle rect) throws TransportException;
|
||||
|
||||
/**
|
||||
* Reset decoder when needed. Ex. reset ZLib stream inflaters for Z* and Tight decoders.
|
||||
*/
|
||||
public void reset() { /*empty*/ }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public class DesctopSizeDecoder extends Decoder {
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer, FramebufferUpdateRectangle rect) throws TransportException {
|
||||
// do nothing, all real job will be done in ReceiverTask.framebufferUpdateMessage()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public class FakeDecoder extends Decoder {
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer, FramebufferUpdateRectangle rect) throws TransportException {
|
||||
throw new IllegalStateException("Improper use of fake decoder");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.encoding.EncodingType;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* Header for framebuffer-update-rectangle header server message
|
||||
* 2 - U16 - x-position
|
||||
* 2 - U16 - y-position
|
||||
* 2 - U16 - width
|
||||
* 2 - U16 - height
|
||||
* 4 - S32 - encoding-type
|
||||
* and then follows the pixel data in the specified encoding
|
||||
*/
|
||||
public class FramebufferUpdateRectangle {
|
||||
public int x;
|
||||
public int y;
|
||||
public int width;
|
||||
public int height;
|
||||
private EncodingType encodingType;
|
||||
|
||||
public FramebufferUpdateRectangle() {
|
||||
// nop
|
||||
}
|
||||
|
||||
public FramebufferUpdateRectangle(int x, int y, int w, int h) {
|
||||
this.x = x; this.y = y;
|
||||
width = w; height = h;
|
||||
}
|
||||
|
||||
public void fill(Transport transport) throws TransportException {
|
||||
x = transport.readUInt16();
|
||||
y = transport.readUInt16();
|
||||
width = transport.readUInt16();
|
||||
height = transport.readUInt16();
|
||||
int encoding = transport.readInt32();
|
||||
encodingType = EncodingType.byId(encoding);
|
||||
}
|
||||
|
||||
public EncodingType getEncodingType() {
|
||||
return encodingType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FramebufferUpdateRect: [x: " + x + ", y: " + y +
|
||||
", width: " + width + ", height: " + height +
|
||||
", encodingType: " + encodingType +
|
||||
"]";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
|
||||
public class HextileDecoder extends Decoder {
|
||||
private static final int DEFAULT_TILE_SIZE = 16;
|
||||
private static final int RAW_MASK = 1;
|
||||
private static final int BACKGROUND_SPECIFIED_MASK = 2;
|
||||
private static final int FOREGROUND_SPECIFIED_MASK = 4;
|
||||
private static final int ANY_SUBRECTS_MASK = 8;
|
||||
private static final int SUBRECTS_COLOURED_MASK = 16;
|
||||
private static final int FG_COLOR_INDEX = 0;
|
||||
private static final int BG_COLOR_INDEX = 1;
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer,
|
||||
FramebufferUpdateRectangle rect) throws TransportException {
|
||||
if (rect.width == 0 || rect.height == 0) return;
|
||||
int[] colors = new int[] {-1, -1};
|
||||
int maxX = rect.x + rect.width;
|
||||
int maxY = rect.y + rect.height;
|
||||
for (int tileY = rect.y; tileY < maxY;
|
||||
tileY += DEFAULT_TILE_SIZE) {
|
||||
int tileHeight = Math.min(maxY - tileY, DEFAULT_TILE_SIZE);
|
||||
for (int tileX = rect.x; tileX < maxX;
|
||||
tileX += DEFAULT_TILE_SIZE) {
|
||||
int tileWidth = Math.min(maxX - tileX, DEFAULT_TILE_SIZE);
|
||||
decodeTile(transport, renderer, colors, tileX,
|
||||
tileY, tileWidth, tileHeight);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void decodeTile(Transport transport,
|
||||
Renderer renderer, int[] colors,
|
||||
int tileX, int tileY, int tileWidth, int tileHeight)
|
||||
throws TransportException {
|
||||
int subencoding = transport.readUInt8();
|
||||
if ((subencoding & RAW_MASK) != 0) {
|
||||
RawDecoder.getInstance().decode(transport, renderer,
|
||||
tileX, tileY, tileWidth, tileHeight);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((subencoding & BACKGROUND_SPECIFIED_MASK) != 0) {
|
||||
colors[BG_COLOR_INDEX] = renderer.readPixelColor(transport);
|
||||
}
|
||||
renderer.fillRect(colors[BG_COLOR_INDEX],
|
||||
tileX, tileY, tileWidth, tileHeight);
|
||||
|
||||
if ((subencoding & FOREGROUND_SPECIFIED_MASK) != 0) {
|
||||
colors[FG_COLOR_INDEX] = renderer.readPixelColor(transport);
|
||||
}
|
||||
|
||||
if ((subencoding & ANY_SUBRECTS_MASK) == 0)
|
||||
return;
|
||||
|
||||
int numberOfSubrectangles = transport.readUInt8();
|
||||
boolean colorSpecified = (subencoding & SUBRECTS_COLOURED_MASK) != 0;
|
||||
for (int i = 0; i < numberOfSubrectangles; ++i) {
|
||||
int color = colorSpecified ? renderer.readPixelColor(transport) : colors[FG_COLOR_INDEX];
|
||||
colors[FG_COLOR_INDEX] = color;
|
||||
byte dimensions = transport.readByte(); // bits 7-4 for x, bits 3-0 for y
|
||||
int subtileX = dimensions >> 4 & 0x0f;
|
||||
int subtileY = dimensions & 0x0f;
|
||||
dimensions = transport.readByte(); // bits 7-4 for w, bits 3-0 for h
|
||||
int subtileWidth = 1 + (dimensions >> 4 & 0x0f);
|
||||
int subtileHeight = 1 + (dimensions & 0x0f);
|
||||
renderer.fillRect(color,
|
||||
tileX + subtileX, tileY + subtileY,
|
||||
subtileWidth, subtileHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public class RREDecoder extends Decoder {
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer,
|
||||
FramebufferUpdateRectangle rect) throws TransportException {
|
||||
int numOfSubrectangles = transport.readInt32();
|
||||
int color = renderer.readPixelColor(transport);
|
||||
renderer.fillRect(color, rect);
|
||||
for (int i = 0; i < numOfSubrectangles; ++i) {
|
||||
color = renderer.readPixelColor(transport);
|
||||
int x = transport.readUInt16();
|
||||
int y = transport.readUInt16();
|
||||
int width = transport.readUInt16();
|
||||
int height = transport.readUInt16();
|
||||
renderer.fillRect(color, rect.x + x, rect.y + y, width, height);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public class RawDecoder extends Decoder {
|
||||
private static RawDecoder instance = new RawDecoder();
|
||||
public static RawDecoder getInstance() {
|
||||
return instance;
|
||||
}
|
||||
private RawDecoder() { /*empty*/ }
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport,
|
||||
Renderer renderer, FramebufferUpdateRectangle rect) throws TransportException {
|
||||
decode(transport, renderer, rect.x, rect.y, rect.width, rect.height);
|
||||
}
|
||||
|
||||
public void decode(Transport transport, Renderer renderer, int x, int y,
|
||||
int width, int height) throws TransportException {
|
||||
int length = width * height * renderer.getBytesPerPixel();
|
||||
byte [] bytes = ByteBuffer.getInstance().getBuffer(length);
|
||||
transport.readBytes(bytes, 0, length);
|
||||
renderer.drawBytes(bytes, x, y, width, height);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* Decoder for RichCursor pseudo encoding
|
||||
*/
|
||||
public class RichCursorDecoder extends Decoder {
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer,
|
||||
FramebufferUpdateRectangle rect) throws TransportException {
|
||||
int bytesPerPixel = renderer.getBytesPerPixel();
|
||||
int length = rect.width * rect.height * bytesPerPixel;
|
||||
if (0 == length)
|
||||
return;
|
||||
byte[] buffer = ByteBuffer.getInstance().getBuffer(length);
|
||||
transport.readBytes(buffer, 0, length);
|
||||
|
||||
StringBuilder sb = new StringBuilder(" ");
|
||||
for (int i=0; i<length; ++i) {
|
||||
sb.append(Integer.toHexString(buffer[i]&0xff)).append(" ");
|
||||
}
|
||||
int scanLine = (rect.width + 7) / 8;
|
||||
byte[] bitmask = new byte[scanLine * rect.height];
|
||||
transport.readBytes(bitmask, 0, bitmask.length);
|
||||
|
||||
sb = new StringBuilder(" ");
|
||||
for (byte aBitmask : bitmask) {
|
||||
sb.append(Integer.toHexString(aBitmask & 0xff)).append(" ");
|
||||
}
|
||||
int[] cursorPixels = new int[rect.width * rect.height];
|
||||
for (int y = 0; y < rect.height; ++y) {
|
||||
for (int x = 0; x < rect.width; ++x) {
|
||||
int offset = y * rect.width + x;
|
||||
cursorPixels[offset] = isBitSet(bitmask[y * scanLine + x / 8], x % 8) ?
|
||||
0xFF000000 | renderer.getPixelColor(buffer, offset * bytesPerPixel) :
|
||||
0; // transparent
|
||||
}
|
||||
}
|
||||
renderer.createCursor(cursorPixels, rect);
|
||||
}
|
||||
|
||||
private boolean isBitSet(byte aByte, int index) {
|
||||
return (aByte & 1 << 7 - index) > 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.ColorDecoder;
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
/**
|
||||
* Tight protocol extention decoder
|
||||
*/
|
||||
public class TightDecoder extends Decoder {
|
||||
private static Logger logger = Logger.getLogger("com.glavsoft.rfb.encoding.decoder");
|
||||
|
||||
private static final int FILL_TYPE = 0x08;
|
||||
private static final int JPEG_TYPE = 0x09;
|
||||
|
||||
private static final int FILTER_ID_MASK = 0x40;
|
||||
private static final int STREAM_ID_MASK = 0x30;
|
||||
|
||||
private static final int BASIC_FILTER = 0x00;
|
||||
private static final int PALETTE_FILTER = 0x01;
|
||||
private static final int GRADIENT_FILTER = 0x02;
|
||||
private static final int MIN_SIZE_TO_COMPRESS = 12;
|
||||
|
||||
static final int DECODERS_NUM = 4;
|
||||
Inflater[] decoders;
|
||||
|
||||
private int decoderId;
|
||||
private int[] palette;
|
||||
|
||||
public TightDecoder() {
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer,
|
||||
FramebufferUpdateRectangle rect) throws TransportException {
|
||||
int bytesPerPixel = renderer.getBytesPerPixelTight();
|
||||
|
||||
/**
|
||||
* bits
|
||||
* 7 - FILL or JPEG type
|
||||
* 6 - filter presence flag
|
||||
* 5, 4 - decoder to use when Basic type (bit 7 not set)
|
||||
* or
|
||||
* 4 - JPEG type when set bit 7
|
||||
* 3 - reset decoder #3
|
||||
* 2 - reset decoder #2
|
||||
* 1 - reset decoder #1
|
||||
* 0 - reset decoder #0
|
||||
*/
|
||||
int compControl = transport.readUInt8();
|
||||
resetDecoders(compControl);
|
||||
|
||||
int compType = compControl >> 4 & 0x0F;
|
||||
switch (compType) {
|
||||
case FILL_TYPE:
|
||||
int color = renderer.readTightPixelColor(transport);
|
||||
renderer.fillRect(color, rect);
|
||||
break;
|
||||
case JPEG_TYPE:
|
||||
assert 3 == bytesPerPixel : "Tight doesn't support JPEG subencoding while depth not equal to 24bpp is used";
|
||||
processJpegType(transport, renderer, rect);
|
||||
break;
|
||||
default:
|
||||
assert compType <= JPEG_TYPE : "Compression control byte is incorrect!";
|
||||
processBasicType(compControl, transport, renderer, rect);
|
||||
}
|
||||
}
|
||||
|
||||
private void processBasicType(int compControl, Transport transport,
|
||||
Renderer renderer, FramebufferUpdateRectangle rect) throws TransportException {
|
||||
decoderId = (compControl & STREAM_ID_MASK) >> 4;
|
||||
|
||||
int filterId = 0;
|
||||
if ((compControl & FILTER_ID_MASK) > 0) { // filter byte presence
|
||||
filterId = transport.readUInt8();
|
||||
}
|
||||
int bytesPerCPixel = renderer.getBytesPerPixelTight();
|
||||
int lengthCurrentbpp = bytesPerCPixel * rect.width * rect.height;
|
||||
byte [] buffer;
|
||||
switch (filterId) {
|
||||
case BASIC_FILTER:
|
||||
buffer = readTightData(lengthCurrentbpp, transport);
|
||||
renderer.drawTightBytes(buffer, 0, rect.x, rect.y, rect.width, rect.height);
|
||||
break;
|
||||
case PALETTE_FILTER:
|
||||
int paletteSize = transport.readUInt8() + 1;
|
||||
completePalette(paletteSize, transport, renderer);
|
||||
int dataLength = paletteSize == 2 ?
|
||||
rect.height * ((rect.width + 7) / 8) :
|
||||
rect.width * rect.height;
|
||||
buffer = readTightData(dataLength, transport);
|
||||
renderer.drawBytesWithPalette(buffer, rect, palette, paletteSize);
|
||||
break;
|
||||
case GRADIENT_FILTER:
|
||||
/*
|
||||
* The "gradient" filter pre-processes pixel data with a simple algorithm
|
||||
* which converts each color component to a difference between a "predicted"
|
||||
* intensity and the actual intensity. Such a technique does not affect
|
||||
* uncompressed data size, but helps to compress photo-like images better.
|
||||
* Pseudo-code for converting intensities to differences is the following:
|
||||
*
|
||||
* P[i,j] := V[i-1,j] + V[i,j-1] - V[i-1,j-1];
|
||||
* if (P[i,j] < 0) then P[i,j] := 0;
|
||||
* if (P[i,j] > MAX) then P[i,j] := MAX;
|
||||
* D[i,j] := V[i,j] - P[i,j];
|
||||
*
|
||||
* Here V[i,j] is the intensity of a color component for a pixel at
|
||||
* coordinates (i,j). MAX is the maximum value of intensity for a color
|
||||
* component.*/
|
||||
buffer = readTightData(bytesPerCPixel * rect.width * rect.height, transport);
|
||||
byte [][] opRows = new byte[2][rect.width * 3 + 3];
|
||||
int opRowIndex = 0;
|
||||
byte [] components = new byte[3];
|
||||
int pixelOffset = 0;
|
||||
ColorDecoder colorDecoder = renderer.getColorDecoder();
|
||||
for (int i = 0; i < rect.height; ++i) {
|
||||
// exchange thisRow and prevRow:
|
||||
byte [] thisRow = opRows[opRowIndex];
|
||||
byte [] prevRow = opRows[opRowIndex = (opRowIndex + 1) % 2];
|
||||
for (int j = 3; j < rect.width * 3 + 3; j += 3) {
|
||||
colorDecoder.fillRawComponents(components, buffer, pixelOffset);
|
||||
pixelOffset += bytesPerCPixel;
|
||||
int
|
||||
d = (0xff & prevRow[j + 0]) + // "upper" pixel (from prev row)
|
||||
(0xff & thisRow[j + 0 - 3]) - // prev pixel
|
||||
(0xff & prevRow[j + 0 - 3]); // "diagonal" prev pixel
|
||||
thisRow[j + 0] = (byte) (components[0] + (d < 0 ? 0 : d > colorDecoder.redMax ? colorDecoder.redMax: d) & colorDecoder.redMax);
|
||||
d = (0xff & prevRow[j + 1]) +
|
||||
(0xff & thisRow[j + 1 - 3]) -
|
||||
(0xff & prevRow[j + 1 - 3]);
|
||||
thisRow[j + 1] = (byte) (components[1] + (d < 0 ? 0 : d > colorDecoder.greenMax ? colorDecoder.greenMax: d) & colorDecoder.greenMax);
|
||||
d = (0xff & prevRow[j + 2]) +
|
||||
(0xff & thisRow[j + 2 - 3]) -
|
||||
(0xff & prevRow[j + 2 - 3]);
|
||||
thisRow[j + 2] = (byte) (components[2] + (d < 0 ? 0 : d > colorDecoder.blueMax ? colorDecoder.blueMax: d) & colorDecoder.blueMax);
|
||||
}
|
||||
renderer.drawUncaliberedRGBLine(thisRow, rect.x, rect.y + i, rect.width);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete palette from transport
|
||||
*/
|
||||
private void completePalette(int paletteSize, Transport transport, 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
|
||||
*/
|
||||
if (null == palette) palette = new int[256];
|
||||
for (int i = 0; i < paletteSize; ++i) {
|
||||
palette[i] = renderer.readTightPixelColor(transport);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads compressed (expected length >= MIN_SIZE_TO_COMPRESS) or
|
||||
* uncompressed data. When compressed decompresses it.
|
||||
*
|
||||
* @param expectedLength expected data length in bytes
|
||||
* @param transport data source
|
||||
* @return result data
|
||||
* @throws TransportException
|
||||
*/
|
||||
private byte[] readTightData(int expectedLength, Transport transport) throws TransportException {
|
||||
if (expectedLength < MIN_SIZE_TO_COMPRESS) {
|
||||
byte [] buffer = ByteBuffer.getInstance().getBuffer(expectedLength);
|
||||
transport.readBytes(buffer, 0, expectedLength);
|
||||
return buffer;
|
||||
} else
|
||||
return readCompressedData(expectedLength, transport);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads compressed data length, then read compressed data into rawBuffer
|
||||
* and decompress data with expected length == length
|
||||
*
|
||||
* Note: returned data contains not only decompressed data but raw data at array tail
|
||||
* which need to be ignored. Use only first expectedLength bytes.
|
||||
*
|
||||
* @param expectedLength expected data length
|
||||
* @param transport data source
|
||||
* @return decompressed data (length == expectedLength) / + followed raw data (ignore, please)
|
||||
* @throws TransportException
|
||||
*/
|
||||
private byte[] readCompressedData(int expectedLength, Transport transport) throws TransportException {
|
||||
int rawDataLength = readCompactSize(transport);
|
||||
|
||||
byte [] buffer = ByteBuffer.getInstance().getBuffer(expectedLength + rawDataLength);
|
||||
// read compressed (raw) data behind space allocated for decompressed data
|
||||
transport.readBytes(buffer, expectedLength, rawDataLength);
|
||||
if (null == decoders[decoderId]) {
|
||||
decoders[decoderId] = new Inflater();
|
||||
}
|
||||
Inflater decoder = decoders[decoderId];
|
||||
decoder.setInput(buffer, expectedLength, rawDataLength);
|
||||
try {
|
||||
decoder.inflate(buffer, 0, expectedLength);
|
||||
} catch (DataFormatException e) {
|
||||
logger.throwing("TightDecoder", "readCompressedData", e);
|
||||
throw new TransportException("cannot inflate tight compressed data", e);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private void processJpegType(Transport transport, Renderer renderer,
|
||||
FramebufferUpdateRectangle rect) throws TransportException {
|
||||
int jpegBufferLength = readCompactSize(transport);
|
||||
byte [] bytes = ByteBuffer.getInstance().getBuffer(jpegBufferLength);
|
||||
transport.readBytes(bytes, 0, jpegBufferLength);
|
||||
renderer.drawJpegImage(bytes, 0, jpegBufferLength, rect);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read an integer from transport in compact representation (from 1 to 3 bytes).
|
||||
* Highest bit of read byte set to 1 means next byte contains data.
|
||||
* Lower 7 bit of each byte contains significant data. Max bytes = 3.
|
||||
* Less significant bytes first order.
|
||||
*
|
||||
* @param transport data source
|
||||
* @return int value
|
||||
* @throws TransportException
|
||||
*/
|
||||
private int readCompactSize(Transport transport) throws TransportException {
|
||||
int b = transport.readUInt8();
|
||||
int size = b & 0x7F;
|
||||
if ((b & 0x80) != 0) {
|
||||
b = transport.readUInt8();
|
||||
size += (b & 0x7F) << 7;
|
||||
if ((b & 0x80) != 0) {
|
||||
size += transport.readUInt8() << 14;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush (reset) zlib decoders when bits 3, 2, 1, 0 of compControl is set
|
||||
* @param compControl control flags
|
||||
*/
|
||||
private void resetDecoders(int compControl) {
|
||||
for (int i=0; i < DECODERS_NUM; ++i) {
|
||||
if ((compControl & 1) != 0 && decoders[i] != null) {
|
||||
decoders[i].reset();
|
||||
}
|
||||
compControl >>= 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
decoders = new Inflater[DECODERS_NUM];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public class ZRLEDecoder extends ZlibDecoder {
|
||||
private static final int MAX_TILE_SIZE = 64;
|
||||
private int[] decodedBitmap;
|
||||
private int[] palette;
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer,
|
||||
FramebufferUpdateRectangle rect) throws TransportException {
|
||||
int zippedLength = (int) transport.readUInt32();
|
||||
if (0 == zippedLength) return;
|
||||
int length = rect.width * rect.height * renderer.getBytesPerCPixel()
|
||||
+ (rect.width / MAX_TILE_SIZE + 1) * (rect.height / MAX_TILE_SIZE + 1);
|
||||
byte[] bytes = unzip(transport, zippedLength, length);
|
||||
int offset = zippedLength;
|
||||
int maxX = rect.x + rect.width;
|
||||
int maxY = rect.y + rect.height;
|
||||
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 += 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, paletteSize);
|
||||
if (1 == subencoding) { // A solid tile consisting of a single colour
|
||||
renderer.fillRect(palette[0], tileX, tileY, tileWidth, tileHeight);
|
||||
continue;
|
||||
}
|
||||
if (isRle) {
|
||||
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, 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, 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.getBytesPerCPixel();
|
||||
int decodedOffset = 0;
|
||||
int decodedEnd = tileWidth * tileHeight;
|
||||
int index = offset;
|
||||
while (decodedOffset < decodedEnd) {
|
||||
int color = renderer.getCompactPixelColor(bytes, index);
|
||||
index += bytesPerCPixel;
|
||||
int rlength = 1;
|
||||
do {
|
||||
rlength += bytes[index] & 0x0ff;
|
||||
} while ((bytes[index++] & 0x0ff) == 255);
|
||||
assert rlength <= decodedEnd - decodedOffset;
|
||||
renderer.fillColorBitmapWithColor(decodedBitmap, decodedOffset, rlength, color);
|
||||
decodedOffset += rlength;
|
||||
}
|
||||
renderer.drawColoredBitmap(decodedBitmap, tileX, tileY, tileWidth, tileHeight);
|
||||
return index - offset;
|
||||
}
|
||||
|
||||
private int decodePaletteRle(byte[] bytes, int offset, Renderer renderer,
|
||||
int tileX, int tileY, int tileWidth, int tileHeight) {
|
||||
int decodedOffset = 0;
|
||||
int decodedEnd = tileWidth * tileHeight;
|
||||
int index = offset;
|
||||
while (decodedOffset < decodedEnd) {
|
||||
int colorIndex = bytes[index++];
|
||||
int color = palette[colorIndex & 127];
|
||||
int rlength = 1;
|
||||
if ((colorIndex & 128) != 0) {
|
||||
do {
|
||||
rlength += bytes[index] & 0x0ff;
|
||||
} while (bytes[index++] == (byte) 255);
|
||||
}
|
||||
assert rlength <= decodedEnd - decodedOffset;
|
||||
renderer.fillColorBitmapWithColor(decodedBitmap, decodedOffset, rlength, color);
|
||||
decodedOffset += rlength;
|
||||
}
|
||||
renderer.drawColoredBitmap(decodedBitmap, tileX, tileY, tileWidth, tileHeight);
|
||||
return index - offset;
|
||||
}
|
||||
|
||||
private int decodePacked(byte[] bytes, int offset, Renderer renderer,
|
||||
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;
|
||||
int decodedOffset = 0;
|
||||
for (int i = 0; i < tileHeight; ++i) {
|
||||
int decodedRowEnd = decodedOffset + tileWidth;
|
||||
int byteProcessed = 0;
|
||||
int bitsRemain = 0;
|
||||
|
||||
while (decodedOffset < decodedRowEnd) {
|
||||
if (bitsRemain == 0) {
|
||||
byteProcessed = bytes[packedOffset++];
|
||||
bitsRemain = 8;
|
||||
}
|
||||
bitsRemain -= bitsPerPalletedPixel;
|
||||
int index = byteProcessed >> bitsRemain & (1 << bitsPerPalletedPixel) - 1 & 127;
|
||||
int color = palette[index];
|
||||
renderer.fillColorBitmapWithColor(decodedBitmap, decodedOffset, 1, color);
|
||||
++decodedOffset;
|
||||
}
|
||||
}
|
||||
renderer.drawColoredBitmap(decodedBitmap, tileX, tileY, tileWidth, tileHeight);
|
||||
return packedOffset - offset;
|
||||
}
|
||||
|
||||
private int decodeRaw(byte[] bytes, int offset, Renderer renderer,
|
||||
int tileX, int tileY, int tileWidth, int tileHeight) throws TransportException {
|
||||
return renderer.drawCompactBytes(bytes, offset, tileX, tileY, tileWidth, tileHeight);
|
||||
}
|
||||
|
||||
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* bytesPerCPixel);
|
||||
}
|
||||
return paletteSize * bytesPerCPixel;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.encoding.decoder;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
public class ZlibDecoder extends Decoder {
|
||||
private Inflater decoder;
|
||||
|
||||
@Override
|
||||
public void decode(Transport transport, Renderer renderer,
|
||||
FramebufferUpdateRectangle rect) throws TransportException {
|
||||
int zippedLength = (int) transport.readUInt32();
|
||||
if (0 == zippedLength) return;
|
||||
int length = rect.width * rect.height * renderer.getBytesPerPixel();
|
||||
byte[] bytes = unzip(transport, zippedLength, length);
|
||||
Transport unzippedReader =
|
||||
new Transport(
|
||||
new ByteArrayInputStream(bytes, zippedLength, length));
|
||||
RawDecoder.getInstance().decode(unzippedReader, renderer, rect);
|
||||
}
|
||||
|
||||
protected byte[] unzip(Transport transport, int zippedLength, int length)
|
||||
throws TransportException {
|
||||
byte [] bytes = ByteBuffer.getInstance().getBuffer(zippedLength + length);
|
||||
transport.readBytes(bytes, 0, zippedLength);
|
||||
if (null == decoder) {
|
||||
decoder = new Inflater();
|
||||
}
|
||||
decoder.setInput(bytes, 0, zippedLength);
|
||||
try {
|
||||
decoder.inflate(bytes, zippedLength, length);
|
||||
} catch (DataFormatException e) {
|
||||
throw new TransportException("cannot inflate Zlib data", e);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
decoder = null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol;
|
||||
|
||||
/**
|
||||
* How to track local mouse cursor.
|
||||
*/
|
||||
public enum LocalPointer {
|
||||
ON,
|
||||
OFF,
|
||||
HIDE,
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol;
|
||||
|
||||
import com.glavsoft.rfb.client.ClientToServerMessage;
|
||||
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public class MessageQueue {
|
||||
private final BlockingQueue<ClientToServerMessage> queue;
|
||||
|
||||
public MessageQueue() {
|
||||
queue = new LinkedBlockingQueue<ClientToServerMessage>();
|
||||
}
|
||||
|
||||
public void put(ClientToServerMessage message) {
|
||||
if ( ! queue.offer(message)) {
|
||||
Logger.getLogger(getClass().getName()).severe("Cannot put message into message queue. Skip: " + 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.poll(1, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol;
|
||||
|
||||
import com.glavsoft.core.SettingsChangedEvent;
|
||||
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.rfb.ClipboardController;
|
||||
import com.glavsoft.rfb.IChangeSettingsListener;
|
||||
import com.glavsoft.rfb.IRepaintController;
|
||||
import com.glavsoft.rfb.IRequestString;
|
||||
import com.glavsoft.rfb.IRfbSessionListener;
|
||||
import com.glavsoft.rfb.RfbCapabilityInfo;
|
||||
import com.glavsoft.rfb.client.ClientMessageType;
|
||||
import com.glavsoft.rfb.client.ClientToServerMessage;
|
||||
import com.glavsoft.rfb.client.FramebufferUpdateRequestMessage;
|
||||
import com.glavsoft.rfb.client.SetEncodingsMessage;
|
||||
import com.glavsoft.rfb.client.SetPixelFormatMessage;
|
||||
import com.glavsoft.rfb.encoding.EncodingType;
|
||||
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||
import com.glavsoft.rfb.encoding.decoder.ByteBuffer;
|
||||
import com.glavsoft.rfb.encoding.decoder.CopyRectDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.CursorPosDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.Decoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.DesctopSizeDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.HextileDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.RREDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.RawDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.RichCursorDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.TightDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.ZRLEDecoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.ZlibDecoder;
|
||||
import com.glavsoft.rfb.protocol.handlers.Handshaker;
|
||||
import com.glavsoft.rfb.protocol.tunnel.TunnelType;
|
||||
import com.glavsoft.transport.BaudrateMeter;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class Protocol implements IChangeSettingsListener {
|
||||
private final ProtocolContext context;
|
||||
private final Logger logger;
|
||||
private final IRequestString passwordRetriever;
|
||||
private MessageQueue messageQueue;
|
||||
private SenderTask senderTask;
|
||||
private ReceiverTask receiverTask;
|
||||
private IRfbSessionListener rfbSessionListener;
|
||||
private IRepaintController repaintController;
|
||||
private Thread senderThread;
|
||||
private Thread receiverThread;
|
||||
private PixelFormat serverPixelFormat;
|
||||
|
||||
private final Map<EncodingType, Decoder> decoders = new LinkedHashMap<EncodingType, Decoder>();
|
||||
private final Set<ClientMessageType> clientMessageTypes = new HashSet<ClientMessageType>();
|
||||
private boolean inCleanUp = false;
|
||||
private boolean isMac;
|
||||
private BaudrateMeter baudrateMeter;
|
||||
private IRequestString connectionIdRetriever;
|
||||
|
||||
public Protocol(Transport transport, IRequestString passwordRetriever, ProtocolSettings settings) {
|
||||
context = new ProtocolContext();
|
||||
context.transport = transport;
|
||||
this.passwordRetriever = passwordRetriever;
|
||||
logger = Logger.getLogger(getClass().getName());
|
||||
context.settings = settings;
|
||||
decoders.put(EncodingType.RAW_ENCODING, RawDecoder.getInstance());
|
||||
}
|
||||
|
||||
public void handshake() throws UnsupportedProtocolVersionException, UnsupportedSecurityTypeException,
|
||||
AuthenticationFailedException, TransportException, FatalException {
|
||||
context.transport = new Handshaker(this).handshake(getTransport());
|
||||
messageQueue = new MessageQueue(); // TODO Why here?
|
||||
}
|
||||
|
||||
public IRequestString getPasswordRetriever() {
|
||||
return passwordRetriever;
|
||||
}
|
||||
|
||||
/**
|
||||
* Following the server initialisation message it's up to the client to send
|
||||
* whichever protocol messages it wants. Typically it will send a
|
||||
* SetPixelFormat message and a SetEncodings message, followed by a
|
||||
* FramebufferUpdateRequest. From then on the server will send
|
||||
* FramebufferUpdate messages in response to the client's
|
||||
* FramebufferUpdateRequest messages. The client should send
|
||||
* FramebufferUpdateRequest messages with incremental set to true when it has
|
||||
* finished processing one FramebufferUpdate and is ready to process another.
|
||||
* With a fast client, the rate at which FramebufferUpdateRequests are sent
|
||||
* should be regulated to avoid hogging the network.
|
||||
*/
|
||||
public void startNormalHandling(IRfbSessionListener rfbSessionListener,
|
||||
IRepaintController repaintController, ClipboardController clipboardController) {
|
||||
this.rfbSessionListener = rfbSessionListener;
|
||||
this.repaintController = repaintController;
|
||||
// if (settings.getColorDepth() == 0) {
|
||||
// settings.setColorDepth(pixelFormat.depth); // the same the server sent when not initialized yet
|
||||
// }
|
||||
correctServerPixelFormat();
|
||||
context.setPixelFormat(createPixelFormat(context.settings));
|
||||
sendMessage(new SetPixelFormatMessage(context.pixelFormat));
|
||||
logger.fine("sent: " + context.pixelFormat);
|
||||
|
||||
sendSupportedEncodingsMessage(context.settings);
|
||||
context.settings.addListener(Protocol.this); // to support pixel format (color depth), and encodings changes
|
||||
context.settings.addListener(repaintController);
|
||||
|
||||
sendRefreshMessage();
|
||||
senderTask = new SenderTask(messageQueue, context.transport, Protocol.this);
|
||||
senderThread = new Thread(senderTask, "RfbSenderTask");
|
||||
senderThread.start();
|
||||
resetDecoders();
|
||||
receiverTask = new ReceiverTask(
|
||||
context.transport, repaintController,
|
||||
clipboardController,
|
||||
Protocol.this, baudrateMeter);
|
||||
receiverThread = new Thread(receiverTask, "RfbReceiverTask");
|
||||
receiverThread.start();
|
||||
}
|
||||
|
||||
private void correctServerPixelFormat() {
|
||||
// correct true color flag
|
||||
if (0 == serverPixelFormat.trueColourFlag) {
|
||||
//we don't support color maps, so always set true color flag up
|
||||
//and select closest convenient value for bpp/depth
|
||||
int depth = serverPixelFormat.depth;
|
||||
if (0 == depth) depth = serverPixelFormat.bitsPerPixel;
|
||||
if (0 == depth) depth = 24;
|
||||
if (depth <= 3) serverPixelFormat = PixelFormat.create3bitColorDepthPixelFormat(serverPixelFormat.bigEndianFlag);
|
||||
else if (depth <= 6) serverPixelFormat = PixelFormat.create6bitColorDepthPixelFormat(serverPixelFormat.bigEndianFlag);
|
||||
else if (depth <= 8) serverPixelFormat = PixelFormat.create8bitColorDepthBGRPixelFormat(serverPixelFormat.bigEndianFlag);
|
||||
else if (depth <= 16) serverPixelFormat = PixelFormat.create16bitColorDepthPixelFormat(serverPixelFormat.bigEndianFlag);
|
||||
else serverPixelFormat = PixelFormat.create24bitColorDepthPixelFormat(serverPixelFormat.bigEndianFlag);
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(ClientToServerMessage message) {
|
||||
messageQueue.put(message);
|
||||
}
|
||||
|
||||
public void sendSupportedEncodingsMessage(ProtocolSettings settings) {
|
||||
final LinkedHashSet<EncodingType> encodings = new LinkedHashSet<EncodingType>();
|
||||
final EncodingType preferredEncoding = settings.getPreferredEncoding();
|
||||
if (preferredEncoding != EncodingType.RAW_ENCODING) {
|
||||
encodings.add(preferredEncoding); // preferred first
|
||||
}
|
||||
for (final EncodingType e : decoders.keySet()) {
|
||||
if (e == preferredEncoding) continue;
|
||||
switch (e) {
|
||||
case RAW_ENCODING: break;
|
||||
case COMPRESS_LEVEL_0 :
|
||||
final int compressionLevel = settings.getCompressionLevel();
|
||||
if (compressionLevel > 0 && compressionLevel < 10) {
|
||||
encodings.add(EncodingType.byId(EncodingType.COMPRESS_LEVEL_0.getId() + compressionLevel));
|
||||
}
|
||||
break;
|
||||
case JPEG_QUALITY_LEVEL_0 :
|
||||
final int jpegQuality = settings.getJpegQuality();
|
||||
final int colorDepth = settings.getColorDepth();
|
||||
if (jpegQuality > 0 && jpegQuality < 10 &&
|
||||
(colorDepth == ProtocolSettings.COLOR_DEPTH_24 ||
|
||||
colorDepth == ProtocolSettings.COLOR_DEPTH_SERVER_SETTINGS)) {
|
||||
encodings.add(EncodingType.byId(EncodingType.JPEG_QUALITY_LEVEL_0.getId() + jpegQuality));
|
||||
}
|
||||
break;
|
||||
case COPY_RECT:
|
||||
if (settings.isAllowCopyRect()) {
|
||||
encodings.add(EncodingType.COPY_RECT);
|
||||
}
|
||||
break;
|
||||
case RICH_CURSOR:
|
||||
if (settings.getMouseCursorTrack() == LocalPointer.HIDE ||
|
||||
settings.getMouseCursorTrack() == LocalPointer.ON) {
|
||||
encodings.add(EncodingType.RICH_CURSOR);
|
||||
}
|
||||
break;
|
||||
case CURSOR_POS:
|
||||
if (settings.getMouseCursorTrack() == LocalPointer.HIDE ||
|
||||
settings.getMouseCursorTrack() == LocalPointer.ON) {
|
||||
encodings.add(EncodingType.CURSOR_POS);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
encodings.add(e);
|
||||
}
|
||||
}
|
||||
SetEncodingsMessage encodingsMessage = new SetEncodingsMessage(encodings);
|
||||
sendMessage(encodingsMessage);
|
||||
logger.fine("sent: " + encodingsMessage.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* create pixel format by bpp
|
||||
*/
|
||||
private PixelFormat createPixelFormat(ProtocolSettings settings) {
|
||||
int serverBigEndianFlag = serverPixelFormat.bigEndianFlag;
|
||||
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 hackForMacOsXScreenSharingServer(PixelFormat.create8bitColorDepthBGRPixelFormat(serverBigEndianFlag));
|
||||
case ProtocolSettings.COLOR_DEPTH_6:
|
||||
return hackForMacOsXScreenSharingServer(PixelFormat.create6bitColorDepthPixelFormat(serverBigEndianFlag));
|
||||
case ProtocolSettings.COLOR_DEPTH_3:
|
||||
return hackForMacOsXScreenSharingServer(PixelFormat.create3bitColorDepthPixelFormat(serverBigEndianFlag));
|
||||
case ProtocolSettings.COLOR_DEPTH_SERVER_SETTINGS:
|
||||
return serverPixelFormat;
|
||||
default:
|
||||
// unsupported bpp, use default
|
||||
return PixelFormat.create24bitColorDepthPixelFormat(serverBigEndianFlag);
|
||||
}
|
||||
}
|
||||
|
||||
private PixelFormat hackForMacOsXScreenSharingServer(PixelFormat pixelFormat) {
|
||||
if (isMac) {
|
||||
pixelFormat.bitsPerPixel = pixelFormat.depth = 16;
|
||||
}
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingsChanged(SettingsChangedEvent e) {
|
||||
ProtocolSettings settings = (ProtocolSettings) e.getSource();
|
||||
if (settings.isChangedEncodings()) {
|
||||
sendSupportedEncodingsMessage(settings);
|
||||
}
|
||||
if (settings.isChangedColorDepth() && receiverTask != null) {
|
||||
receiverTask.queueUpdatePixelFormat(createPixelFormat(settings));
|
||||
}
|
||||
}
|
||||
|
||||
public void sendRefreshMessage() {
|
||||
sendMessage(new FramebufferUpdateRequestMessage(0, 0, context.fbWidth, context.fbHeight, false));
|
||||
logger.fine("sent: full FB Refresh");
|
||||
}
|
||||
|
||||
public void sendFbUpdateMessage() {
|
||||
sendMessage(receiverTask.fullscreenFbUpdateIncrementalRequest);
|
||||
}
|
||||
|
||||
public void cleanUpSession(String message) {
|
||||
cleanUpSession();
|
||||
rfbSessionListener.rfbSessionStopped(message);
|
||||
}
|
||||
|
||||
public void cleanUpSession() {
|
||||
synchronized (this) {
|
||||
if (inCleanUp) return;
|
||||
inCleanUp = true;
|
||||
}
|
||||
if (senderTask != null && senderThread.isAlive()) { senderThread.interrupt(); }
|
||||
if (receiverTask != null && receiverThread.isAlive()) { receiverThread.interrupt(); }
|
||||
if (senderTask != null) {
|
||||
try {
|
||||
senderThread.join(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// nop
|
||||
}
|
||||
senderTask = null;
|
||||
}
|
||||
if (receiverTask != null) {
|
||||
try {
|
||||
receiverThread.join(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// nop
|
||||
}
|
||||
receiverTask = null;
|
||||
}
|
||||
synchronized (this) {
|
||||
inCleanUp = false;
|
||||
}
|
||||
ByteBuffer.removeInstance();
|
||||
}
|
||||
|
||||
public void setServerPixelFormat(PixelFormat serverPixelFormat) {
|
||||
this.serverPixelFormat = serverPixelFormat;
|
||||
}
|
||||
|
||||
public ProtocolSettings getSettings() {
|
||||
return context.getSettings();
|
||||
}
|
||||
|
||||
public Transport getTransport() {
|
||||
return context.getTransport();
|
||||
}
|
||||
|
||||
public int getFbWidth() {
|
||||
return context.getFbWidth();
|
||||
}
|
||||
|
||||
public void setFbWidth(int frameBufferWidth) {
|
||||
context.setFbWidth(frameBufferWidth);
|
||||
}
|
||||
|
||||
public int getFbHeight() {
|
||||
return context.getFbHeight();
|
||||
}
|
||||
|
||||
public void setFbHeight(int frameBufferHeight) {
|
||||
context.setFbHeight(frameBufferHeight);
|
||||
}
|
||||
|
||||
public PixelFormat getPixelFormat() {
|
||||
return context.getPixelFormat();
|
||||
}
|
||||
|
||||
public void setPixelFormat(PixelFormat pixelFormat) {
|
||||
context.setPixelFormat(pixelFormat);
|
||||
if (repaintController != null) {
|
||||
repaintController.setPixelFormat(pixelFormat);
|
||||
}
|
||||
}
|
||||
|
||||
public void setRemoteDesktopName(String name) {
|
||||
context.setRemoteDesktopName(name);
|
||||
}
|
||||
|
||||
public String getRemoteDesktopName() {
|
||||
return context.getRemoteDesktopName();
|
||||
}
|
||||
|
||||
public void setTight(boolean isTight) {
|
||||
context.setTight(isTight);
|
||||
}
|
||||
|
||||
public boolean isTight() {
|
||||
return context.isTight();
|
||||
}
|
||||
|
||||
public void setProtocolVersion(Handshaker.ProtocolVersion protocolVersion) {
|
||||
context.setProtocolVersion(protocolVersion);
|
||||
}
|
||||
|
||||
public Handshaker.ProtocolVersion getProtocolVersion() {
|
||||
return context.getProtocolVersion();
|
||||
}
|
||||
|
||||
public void registerRfbEncodings() {
|
||||
decoders.put(EncodingType.TIGHT, new TightDecoder());
|
||||
decoders.put(EncodingType.HEXTILE, new HextileDecoder());
|
||||
decoders.put(EncodingType.ZRLE, new ZRLEDecoder());
|
||||
decoders.put(EncodingType.ZLIB, new ZlibDecoder());
|
||||
decoders.put(EncodingType.RRE, new RREDecoder());
|
||||
decoders.put(EncodingType.COPY_RECT, new CopyRectDecoder());
|
||||
|
||||
decoders.put(EncodingType.RICH_CURSOR, new RichCursorDecoder());
|
||||
decoders.put(EncodingType.DESKTOP_SIZE, new DesctopSizeDecoder());
|
||||
decoders.put(EncodingType.CURSOR_POS, new CursorPosDecoder());
|
||||
}
|
||||
|
||||
public void resetDecoders() {
|
||||
for (Decoder decoder : decoders.values()) {
|
||||
if (decoder != null) {
|
||||
decoder.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Decoder getDecoderByType(EncodingType type) {
|
||||
return decoders.get(type);
|
||||
}
|
||||
|
||||
public void registerEncoding(RfbCapabilityInfo capInfo) {
|
||||
try {
|
||||
final EncodingType encodingType = EncodingType.byId(capInfo.getCode());
|
||||
if ( ! decoders.containsKey(encodingType)) {
|
||||
final Decoder decoder = encodingType.klass.newInstance();
|
||||
if (decoder != null) {
|
||||
decoders.put(encodingType, decoder);
|
||||
logger.finer("Register encoding: " + encodingType);
|
||||
}
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.finer(e.getMessage());
|
||||
} catch (InstantiationException e) {
|
||||
logger.warning(e.getMessage());
|
||||
} catch (IllegalAccessException e) {
|
||||
logger.warning(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void registerClientMessageType(RfbCapabilityInfo capInfo) {
|
||||
try {
|
||||
final ClientMessageType clientMessageType = ClientMessageType.byId(capInfo.getCode());
|
||||
clientMessageTypes.add(clientMessageType);
|
||||
logger.finer("Register client message type: " + clientMessageType);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.finer(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether server is supported for given client-to-server message
|
||||
*
|
||||
* @param type client-to-server message type to check for
|
||||
* @return true when supported
|
||||
*/
|
||||
public boolean isSupported(ClientMessageType type) {
|
||||
return clientMessageTypes.contains(type) || ClientMessageType.isStandardType(type );
|
||||
}
|
||||
|
||||
public void setTunnelType(TunnelType tunnelType) {
|
||||
context.setTunnelType(tunnelType);
|
||||
}
|
||||
|
||||
public TunnelType getTunnelType() {
|
||||
return context.getTunnelType();
|
||||
}
|
||||
|
||||
public void setMac(boolean isMac) {
|
||||
this.isMac = isMac;
|
||||
}
|
||||
|
||||
public void setBaudrateMeter(BaudrateMeter baudrateMeter) {
|
||||
this.baudrateMeter = baudrateMeter;
|
||||
}
|
||||
|
||||
public int kBPS() {
|
||||
return baudrateMeter == null ? -1 : baudrateMeter.kBPS();
|
||||
}
|
||||
|
||||
public boolean isMac() {
|
||||
return isMac;
|
||||
}
|
||||
|
||||
public void setConnectionIdRetriever(IRequestString connectionIdRetriever) {
|
||||
this.connectionIdRetriever = connectionIdRetriever;
|
||||
}
|
||||
|
||||
public IRequestString getConnectionIdRetriever() {
|
||||
return connectionIdRetriever;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol;
|
||||
|
||||
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||
import com.glavsoft.rfb.protocol.handlers.Handshaker;
|
||||
import com.glavsoft.rfb.protocol.tunnel.TunnelType;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public class ProtocolContext {
|
||||
int fbWidth;
|
||||
int fbHeight;
|
||||
PixelFormat pixelFormat;
|
||||
Transport transport;
|
||||
String remoteDesktopName;
|
||||
boolean isTight;
|
||||
Handshaker.ProtocolVersion protocolVersion;
|
||||
ProtocolSettings settings;
|
||||
private TunnelType tunnelType;
|
||||
|
||||
public PixelFormat getPixelFormat() {
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
public void setPixelFormat(PixelFormat pixelFormat) {
|
||||
this.pixelFormat = pixelFormat;
|
||||
}
|
||||
|
||||
public String getRemoteDesktopName() {
|
||||
return remoteDesktopName;
|
||||
}
|
||||
|
||||
public void setRemoteDesktopName(String name) {
|
||||
remoteDesktopName = name;
|
||||
}
|
||||
|
||||
public int getFbWidth() {
|
||||
return fbWidth;
|
||||
}
|
||||
|
||||
public void setFbWidth(int fbWidth) {
|
||||
this.fbWidth = fbWidth;
|
||||
}
|
||||
|
||||
public int getFbHeight() {
|
||||
return fbHeight;
|
||||
}
|
||||
|
||||
public void setFbHeight(int fbHeight) {
|
||||
this.fbHeight = fbHeight;
|
||||
}
|
||||
|
||||
public ProtocolSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
public Transport getTransport() {
|
||||
return transport;
|
||||
}
|
||||
|
||||
public void setTight(boolean isTight) {
|
||||
this.isTight = isTight;
|
||||
}
|
||||
|
||||
public boolean isTight() {
|
||||
return isTight;
|
||||
}
|
||||
|
||||
public void setProtocolVersion(Handshaker.ProtocolVersion protocolVersion) {
|
||||
this.protocolVersion = protocolVersion;
|
||||
}
|
||||
|
||||
public Handshaker.ProtocolVersion getProtocolVersion() {
|
||||
return protocolVersion;
|
||||
}
|
||||
|
||||
public void setTunnelType(TunnelType tunnelType) {
|
||||
this.tunnelType = tunnelType;
|
||||
}
|
||||
|
||||
public TunnelType getTunnelType() {
|
||||
return tunnelType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol;
|
||||
|
||||
import com.glavsoft.core.SettingsChangedEvent;
|
||||
import com.glavsoft.rfb.IChangeSettingsListener;
|
||||
import com.glavsoft.rfb.encoding.EncodingType;
|
||||
import com.glavsoft.rfb.protocol.tunnel.TunnelType;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Protocol Settings class
|
||||
*/
|
||||
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;
|
||||
|
||||
// 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 COLOR_DEPTH_SERVER_SETTINGS = 0;
|
||||
|
||||
private static final int DEFAULT_COLOR_DEPTH = COLOR_DEPTH_24;
|
||||
|
||||
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;
|
||||
public static final int CHANGED_MOUSE_CURSOR_TRACK = 1 << 4;
|
||||
public static final int CHANGED_COMPRESSION_LEVEL = 1 << 5;
|
||||
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_COLOR_DEPTH = 1 << 9;
|
||||
public static final int CHANGED_SHARED = 1 << 10;
|
||||
|
||||
private static final int MIN_COMPRESSION_LEVEL = 1;
|
||||
private static final int MAX_COMPRESSION_LEVEL = 9;
|
||||
private static final int MIN_JPEG_QUALITY = 1;
|
||||
private static final int MAX_JPEG_QUALITY = 9;
|
||||
|
||||
private transient int changedSettingsMask;
|
||||
|
||||
private boolean sharedFlag;
|
||||
private boolean viewOnly;
|
||||
private EncodingType preferredEncoding;
|
||||
private boolean allowCopyRect;
|
||||
private boolean showRemoteCursor;
|
||||
private LocalPointer mouseCursorTrack;
|
||||
private int compressionLevel;
|
||||
private int jpegQuality;
|
||||
private boolean allowClipboardTransfer;
|
||||
private boolean convertToAscii;
|
||||
private int colorDepth;
|
||||
|
||||
private transient final List<IChangeSettingsListener> listeners;
|
||||
private transient String remoteCharsetName;
|
||||
private TunnelType tunnelType;
|
||||
|
||||
public static ProtocolSettings getDefaultSettings() {
|
||||
return new ProtocolSettings();
|
||||
}
|
||||
|
||||
private ProtocolSettings() {
|
||||
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;
|
||||
|
||||
listeners = new CopyOnWriteArrayList<IChangeSettingsListener>();
|
||||
changedSettingsMask = 0;
|
||||
}
|
||||
|
||||
public ProtocolSettings(ProtocolSettings s) {
|
||||
this();
|
||||
copyDataFrom(s);
|
||||
changedSettingsMask = s.changedSettingsMask;
|
||||
}
|
||||
|
||||
public void copyDataFrom(ProtocolSettings s) {
|
||||
copyDataFrom(s, 0);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void addListener(IChangeSettingsListener listener) {
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
public byte getSharedFlag() {
|
||||
return (byte) (sharedFlag ? 1 : 0);
|
||||
}
|
||||
|
||||
public boolean isShared() {
|
||||
return sharedFlag;
|
||||
}
|
||||
|
||||
public void setSharedFlag(boolean sharedFlag) {
|
||||
if (this.sharedFlag != sharedFlag) {
|
||||
this.sharedFlag = sharedFlag;
|
||||
changedSettingsMask |= CHANGED_SHARED;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isViewOnly() {
|
||||
return viewOnly;
|
||||
}
|
||||
|
||||
public void setViewOnly(boolean viewOnly) {
|
||||
if (this.viewOnly != viewOnly) {
|
||||
this.viewOnly = viewOnly;
|
||||
changedSettingsMask |= CHANGED_VIEW_ONLY;
|
||||
}
|
||||
}
|
||||
|
||||
public int getColorDepth() {
|
||||
return colorDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set depth only in 3, 6, 8, 16, 32. When depth is wrong, it resets to {@link #DEFAULT_COLOR_DEPTH}
|
||||
*/
|
||||
public void setColorDepth(int depth) {
|
||||
if (colorDepth != depth) {
|
||||
changedSettingsMask |= CHANGED_COLOR_DEPTH | CHANGED_ENCODINGS;
|
||||
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:
|
||||
colorDepth = DEFAULT_COLOR_DEPTH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void fireListeners() {
|
||||
if (null == listeners) return;
|
||||
final SettingsChangedEvent event = new SettingsChangedEvent(new ProtocolSettings(this));
|
||||
changedSettingsMask = 0;
|
||||
for (IChangeSettingsListener listener : listeners) {
|
||||
listener.settingsChanged(event);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isRfbSettingsChangedFired(SettingsChangedEvent event) {
|
||||
return event.getSource() instanceof ProtocolSettings;
|
||||
}
|
||||
|
||||
public void setPreferredEncoding(EncodingType preferredEncoding) {
|
||||
if (this.preferredEncoding != preferredEncoding) {
|
||||
this.preferredEncoding = preferredEncoding;
|
||||
changedSettingsMask |= CHANGED_ENCODINGS;
|
||||
}
|
||||
}
|
||||
|
||||
public EncodingType getPreferredEncoding() {
|
||||
return preferredEncoding;
|
||||
}
|
||||
|
||||
public void setAllowCopyRect(boolean allowCopyRect) {
|
||||
if (this.allowCopyRect != allowCopyRect) {
|
||||
this.allowCopyRect = allowCopyRect;
|
||||
changedSettingsMask |= CHANGED_ALLOW_COPY_RECT | CHANGED_ENCODINGS;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAllowCopyRect() {
|
||||
return allowCopyRect;
|
||||
}
|
||||
|
||||
private void setShowRemoteCursor(boolean showRemoteCursor) {
|
||||
if (this.showRemoteCursor != showRemoteCursor) {
|
||||
this.showRemoteCursor = showRemoteCursor;
|
||||
changedSettingsMask |= CHANGED_SHOW_REMOTE_CURSOR | CHANGED_ENCODINGS;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isShowRemoteCursor() {
|
||||
return showRemoteCursor;
|
||||
}
|
||||
|
||||
public void setMouseCursorTrack(LocalPointer mouseCursorTrack) {
|
||||
if (this.mouseCursorTrack != mouseCursorTrack) {
|
||||
this.mouseCursorTrack = mouseCursorTrack;
|
||||
changedSettingsMask |= CHANGED_MOUSE_CURSOR_TRACK | CHANGED_ENCODINGS;
|
||||
setShowRemoteCursor(LocalPointer.ON == mouseCursorTrack);
|
||||
}
|
||||
}
|
||||
|
||||
public LocalPointer getMouseCursorTrack() {
|
||||
return mouseCursorTrack;
|
||||
}
|
||||
|
||||
public int setCompressionLevel(int compressionLevel) {
|
||||
if (compressionLevel >= MIN_COMPRESSION_LEVEL && compressionLevel <= MAX_COMPRESSION_LEVEL &&
|
||||
this.compressionLevel != compressionLevel) {
|
||||
this.compressionLevel = compressionLevel;
|
||||
changedSettingsMask |= CHANGED_COMPRESSION_LEVEL | CHANGED_ENCODINGS;
|
||||
}
|
||||
return this.compressionLevel;
|
||||
}
|
||||
|
||||
public int getCompressionLevel() {
|
||||
return compressionLevel;
|
||||
}
|
||||
|
||||
public int setJpegQuality(int jpegQuality) {
|
||||
if (jpegQuality >= MIN_JPEG_QUALITY && jpegQuality <= MAX_JPEG_QUALITY &&
|
||||
this.jpegQuality != jpegQuality) {
|
||||
this.jpegQuality = jpegQuality;
|
||||
changedSettingsMask |= CHANGED_JPEG_QUALITY | CHANGED_ENCODINGS;
|
||||
}
|
||||
return this.jpegQuality;
|
||||
}
|
||||
|
||||
public int getJpegQuality() {
|
||||
return jpegQuality;
|
||||
}
|
||||
|
||||
public void setAllowClipboardTransfer(boolean enable) {
|
||||
if (this.allowClipboardTransfer != enable) {
|
||||
this.allowClipboardTransfer = enable;
|
||||
changedSettingsMask |= CHANGED_ALLOW_CLIPBOARD_TRANSFER;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAllowClipboardTransfer() {
|
||||
return allowClipboardTransfer;
|
||||
}
|
||||
|
||||
public boolean isConvertToAscii() {
|
||||
return convertToAscii;
|
||||
}
|
||||
|
||||
public void setConvertToAscii(boolean convertToAscii) {
|
||||
if (this.convertToAscii != convertToAscii) {
|
||||
this.convertToAscii = convertToAscii;
|
||||
changedSettingsMask |= CHANGED_CONVERT_TO_ASCII;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isChangedEncodings() {
|
||||
return (changedSettingsMask & CHANGED_ENCODINGS) == CHANGED_ENCODINGS;
|
||||
}
|
||||
|
||||
public boolean isChangedColorDepth() {
|
||||
return (changedSettingsMask & CHANGED_COLOR_DEPTH) == CHANGED_COLOR_DEPTH;
|
||||
}
|
||||
|
||||
public void setRemoteCharsetName(String remoteCharsetName) {
|
||||
this.remoteCharsetName = remoteCharsetName;
|
||||
}
|
||||
|
||||
public String getRemoteCharsetName() {
|
||||
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 +
|
||||
'}';
|
||||
}
|
||||
|
||||
public TunnelType getTunnelType() {
|
||||
return tunnelType;
|
||||
}
|
||||
|
||||
public void setTunnelType(TunnelType tunnelType) {
|
||||
this.tunnelType = tunnelType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol;
|
||||
|
||||
import com.glavsoft.drawing.Renderer;
|
||||
import com.glavsoft.exceptions.CommonException;
|
||||
import com.glavsoft.exceptions.ProtocolException;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.ClipboardController;
|
||||
import com.glavsoft.rfb.IRepaintController;
|
||||
import com.glavsoft.rfb.client.FramebufferUpdateRequestMessage;
|
||||
import com.glavsoft.rfb.client.SetPixelFormatMessage;
|
||||
import com.glavsoft.rfb.encoding.EncodingType;
|
||||
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||
import com.glavsoft.rfb.encoding.decoder.Decoder;
|
||||
import com.glavsoft.rfb.encoding.decoder.FramebufferUpdateRectangle;
|
||||
import com.glavsoft.transport.BaudrateMeter;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class ReceiverTask implements Runnable {
|
||||
private static final byte FRAMEBUFFER_UPDATE = 0;
|
||||
private static final byte SET_COLOR_MAP_ENTRIES = 1;
|
||||
private static final byte BELL = 2;
|
||||
private static final byte SERVER_CUT_TEXT = 3;
|
||||
|
||||
|
||||
private static Logger logger = Logger.getLogger("com.glavsoft.rfb.protocol.ReceiverTask");
|
||||
private final Transport transport;
|
||||
private Renderer renderer;
|
||||
private final IRepaintController repaintController;
|
||||
private final ClipboardController clipboardController;
|
||||
protected FramebufferUpdateRequestMessage fullscreenFbUpdateIncrementalRequest;
|
||||
private final Protocol protocol;
|
||||
private BaudrateMeter baudrateMeter;
|
||||
private PixelFormat pixelFormat;
|
||||
private volatile boolean needSendPixelFormat;
|
||||
|
||||
public ReceiverTask(Transport transport,
|
||||
IRepaintController repaintController, ClipboardController clipboardController,
|
||||
Protocol protocol, BaudrateMeter baudrateMeter) {
|
||||
this.transport = transport;
|
||||
this.repaintController = repaintController;
|
||||
this.clipboardController = clipboardController;
|
||||
this.protocol = protocol;
|
||||
this.baudrateMeter = baudrateMeter;
|
||||
renderer = repaintController.createRenderer(transport, protocol.getFbWidth(), protocol.getFbHeight(),
|
||||
protocol.getPixelFormat());
|
||||
fullscreenFbUpdateIncrementalRequest =
|
||||
new FramebufferUpdateRequestMessage(0, 0, protocol.getFbWidth(), protocol.getFbHeight(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while ( ! Thread.currentThread().isInterrupted()) {
|
||||
byte messageId = transport.readByte();
|
||||
switch (messageId) {
|
||||
case FRAMEBUFFER_UPDATE:
|
||||
// logger.fine("Server message: FramebufferUpdate (0)");
|
||||
framebufferUpdateMessage();
|
||||
break;
|
||||
case SET_COLOR_MAP_ENTRIES:
|
||||
logger.severe("Server message SetColorMapEntries is not implemented. Skip.");
|
||||
setColorMapEntries();
|
||||
break;
|
||||
case BELL:
|
||||
logger.fine("Server message: Bell");
|
||||
System.out.print("\0007");
|
||||
System.out.flush();
|
||||
break;
|
||||
case SERVER_CUT_TEXT:
|
||||
logger.fine("Server message: CutText (3)");
|
||||
serverCutText();
|
||||
break;
|
||||
default:
|
||||
logger.severe("Unsupported server message. Id = " + messageId);
|
||||
}
|
||||
}
|
||||
} catch (TransportException e) {
|
||||
logger.severe("Close session: " + e.getMessage());
|
||||
protocol.cleanUpSession("Connection closed.");
|
||||
} catch (ProtocolException e) {
|
||||
logger.severe(e.getMessage());
|
||||
protocol.cleanUpSession(e.getMessage() + "\nConnection closed.");
|
||||
} catch (CommonException e) {
|
||||
logger.severe(e.getMessage());
|
||||
protocol.cleanUpSession("Connection closed..");
|
||||
} catch (Throwable te) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
te.printStackTrace(pw);
|
||||
protocol.cleanUpSession(te.getMessage() + "\n" + sw.toString());
|
||||
}
|
||||
Logger.getLogger(getClass().getName()).finer("Receiver task stopped");
|
||||
}
|
||||
|
||||
private void setColorMapEntries() throws TransportException {
|
||||
transport.readByte(); // padding
|
||||
transport.readUInt16(); // first color index
|
||||
int length = transport.readUInt16();
|
||||
while (length-- > 0) {
|
||||
transport.readUInt16(); // R
|
||||
transport.readUInt16(); // G
|
||||
transport.readUInt16(); // B
|
||||
}
|
||||
}
|
||||
|
||||
private void serverCutText() throws TransportException, IOException {
|
||||
transport.readByte(); // padding
|
||||
transport.readInt16(); // padding
|
||||
long length = transport.readInt32();
|
||||
if (0 == length) return;
|
||||
if (length > Integer.MAX_VALUE) {
|
||||
clipboardController.updateSystemClipboard(transport.readBytes(Integer.MAX_VALUE));
|
||||
clipboardController.updateSystemClipboard(transport.readBytes((int) (length - Integer.MAX_VALUE)));
|
||||
} else {
|
||||
clipboardController.updateSystemClipboard(transport.readBytes((int) length));
|
||||
}
|
||||
}
|
||||
|
||||
public void framebufferUpdateMessage() throws CommonException {
|
||||
transport.skip(1); // padding
|
||||
int numberOfRectangles = transport.readUInt16();
|
||||
while (numberOfRectangles-- > 0) {
|
||||
FramebufferUpdateRectangle rect = new FramebufferUpdateRectangle();
|
||||
rect.fill(transport);
|
||||
|
||||
Decoder decoder = protocol.getDecoderByType(rect.getEncodingType());
|
||||
// logger.finer(rect.toString() + (0 == numberOfRectangles ? "\n---" : ""));
|
||||
if (decoder != null) {
|
||||
try {
|
||||
if (baudrateMeter != null) baudrateMeter.startMeasuringCycle();
|
||||
decoder.decode(transport, renderer, rect);
|
||||
} finally {
|
||||
if (baudrateMeter != null) baudrateMeter.stopMeasuringCycle();
|
||||
}
|
||||
if (EncodingType.RICH_CURSOR == rect.getEncodingType() ||
|
||||
EncodingType.CURSOR_POS == rect.getEncodingType()) {
|
||||
repaintController.repaintCursor();
|
||||
} else if (rect.getEncodingType() == EncodingType.DESKTOP_SIZE) {
|
||||
synchronized (this) {
|
||||
fullscreenFbUpdateIncrementalRequest =
|
||||
new FramebufferUpdateRequestMessage(0, 0, rect.width, rect.height, true);
|
||||
}
|
||||
renderer = repaintController.createRenderer(transport, rect.width, rect.height,
|
||||
protocol.getPixelFormat());
|
||||
protocol.sendMessage(new FramebufferUpdateRequestMessage(0, 0, rect.width, rect.height, false));
|
||||
return;
|
||||
} else {
|
||||
repaintController.repaintBitmap(rect);
|
||||
}
|
||||
} else {
|
||||
throw new CommonException("Unprocessed encoding: " + rect.toString());
|
||||
}
|
||||
}
|
||||
if (needSendPixelFormat) {
|
||||
synchronized (this) {
|
||||
if (needSendPixelFormat) {
|
||||
needSendPixelFormat = false;
|
||||
protocol.setPixelFormat(pixelFormat);
|
||||
protocol.sendMessage(new SetPixelFormatMessage(pixelFormat));
|
||||
logger.fine("sent: " + pixelFormat);
|
||||
protocol.sendRefreshMessage();
|
||||
logger.fine("sent: nonincremental fb update");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
protocol.sendMessage(fullscreenFbUpdateIncrementalRequest);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void queueUpdatePixelFormat(PixelFormat pf) {
|
||||
pixelFormat = pf;
|
||||
needSendPixelFormat = true;
|
||||
// protocol.sendMessage(new FramebufferUpdateRequestMessage(0, 0, 1, 1, false));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.client.ClientToServerMessage;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class SenderTask implements Runnable {
|
||||
|
||||
private final MessageQueue queue;
|
||||
private final Transport transport;
|
||||
private final Protocol protocol;
|
||||
|
||||
/**
|
||||
* Create sender task
|
||||
* Task runs as thread, receive messages from queue and sends them to transport.
|
||||
* When no messages appears in queue longer than timeout period, sends FramebufferUpdate
|
||||
* request
|
||||
* @param messageQueue queue to poll messages
|
||||
* @param transport transport to send messages out
|
||||
* @param protocol session lifecircle support
|
||||
*/
|
||||
public SenderTask(MessageQueue messageQueue, Transport transport, Protocol protocol) {
|
||||
this.queue = messageQueue;
|
||||
this.transport = transport;
|
||||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ClientToServerMessage message;
|
||||
try {
|
||||
while ( ! Thread.currentThread().isInterrupted()) {
|
||||
message = queue.get();
|
||||
if (message != null) {
|
||||
message.send(transport);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
} catch (TransportException e) {
|
||||
Logger.getLogger(getClass().getName()).severe("Close session: " + e.getMessage());
|
||||
protocol.cleanUpSession("Connection closed");
|
||||
} catch (Throwable te) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
te.printStackTrace(pw);
|
||||
protocol.cleanUpSession(te.getMessage() + "\n" + sw.toString());
|
||||
}
|
||||
Logger.getLogger(getClass().getName()).finer("Sender task stopped");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol.auth;
|
||||
|
||||
import com.glavsoft.exceptions.AuthenticationFailedException;
|
||||
import com.glavsoft.exceptions.ClosedConnectionException;
|
||||
import com.glavsoft.exceptions.FatalException;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.exceptions.UnsupportedSecurityTypeException;
|
||||
import com.glavsoft.rfb.encoding.ServerInitMessage;
|
||||
import com.glavsoft.rfb.protocol.Protocol;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public abstract class AuthHandler {
|
||||
private static final int AUTH_RESULT_OK = 0;
|
||||
// private static final int AUTH_RESULT_FAILED = 1;
|
||||
private Logger logger;
|
||||
|
||||
/**
|
||||
* Not thread safe, no need to be thread safe
|
||||
*/
|
||||
protected Logger logger() {
|
||||
if (null == logger) {
|
||||
logger = Logger.getLogger(getClass().getName());
|
||||
}
|
||||
return logger;
|
||||
}
|
||||
/**
|
||||
* Authenticate using appropriate auth scheme
|
||||
*
|
||||
* @param transport transport for i/o
|
||||
* @param protocol rfb protocol object
|
||||
* @return transport for future i/o using
|
||||
*/
|
||||
public abstract Transport authenticate(Transport transport, Protocol protocol)
|
||||
throws TransportException, FatalException, UnsupportedSecurityTypeException;
|
||||
public abstract SecurityType getType();
|
||||
public int getId() {
|
||||
return getType().getId();
|
||||
}
|
||||
public String getName() {
|
||||
return getType().name();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Security Result received from server
|
||||
* May be:
|
||||
* * 0 - OK
|
||||
* * 1 - Failed
|
||||
*
|
||||
* Do not check on NoneAuthentication
|
||||
*/
|
||||
public void checkSecurityResult(Transport transport) throws TransportException,
|
||||
AuthenticationFailedException {
|
||||
final int securityResult = transport.readInt32();
|
||||
logger().fine("Security result: " + securityResult + (AUTH_RESULT_OK == securityResult ? " (OK)" : " (Failed)"));
|
||||
if (securityResult != AUTH_RESULT_OK) {
|
||||
try {
|
||||
String reason = transport.readString();
|
||||
logger().fine("Security result reason: " + reason);
|
||||
throw new AuthenticationFailedException(reason);
|
||||
} catch (ClosedConnectionException e) {
|
||||
// protocol version 3.3 and 3.7 does not send reason string,
|
||||
// but silently closes the connection
|
||||
throw new AuthenticationFailedException("Authentication failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void initProcedure(Transport transport, Protocol protocol) throws TransportException {
|
||||
sendClientInitMessage(transport, protocol.getSettings().getSharedFlag());
|
||||
ServerInitMessage serverInitMessage = readServerInitMessage(transport);
|
||||
completeContextData(serverInitMessage, protocol);
|
||||
protocol.registerRfbEncodings();
|
||||
}
|
||||
|
||||
protected ServerInitMessage readServerInitMessage(Transport transport) throws TransportException {
|
||||
final ServerInitMessage serverInitMessage = new ServerInitMessage().readFrom(transport);
|
||||
logger().fine("Read: " + serverInitMessage);
|
||||
return serverInitMessage;
|
||||
}
|
||||
|
||||
protected void sendClientInitMessage(Transport transport, byte sharedFlag) throws TransportException {
|
||||
logger().fine("Sent client-init-message: " + sharedFlag);
|
||||
transport.writeByte(sharedFlag).flush();
|
||||
}
|
||||
|
||||
protected void completeContextData(ServerInitMessage serverInitMessage, Protocol protocol) {
|
||||
protocol.setServerPixelFormat(serverInitMessage.getPixelFormat());
|
||||
protocol.setFbWidth(serverInitMessage.getFramebufferWidth());
|
||||
protocol.setFbHeight(serverInitMessage.getFramebufferHeight());
|
||||
protocol.setRemoteDesktopName(serverInitMessage.getName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol.auth;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.protocol.Protocol;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
public class NoneAuthentication extends AuthHandler {
|
||||
|
||||
@Override
|
||||
public Transport authenticate(Transport transport, Protocol protocol) throws TransportException {
|
||||
return transport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityType getType() {
|
||||
return SecurityType.NONE_AUTHENTICATION;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol.auth;
|
||||
|
||||
/**
|
||||
* Security types that implemented
|
||||
*/
|
||||
public enum SecurityType {
|
||||
NONE_AUTHENTICATION(1),
|
||||
VNC_AUTHENTICATION(2),
|
||||
// int RA2_AUTHENTICATION = 5;
|
||||
// int RA2NE_AUTHENTICATION = 6;
|
||||
TIGHT_AUTHENTICATION(16),
|
||||
TIGHT2_AUTHENTICATION(116);
|
||||
// int ULTRA_AUTHENTICATION = 17;
|
||||
// int TLS_AUTHENTICATION = 18;
|
||||
// int VENCRYPT_AUTHENTICATION = 19;
|
||||
|
||||
private int id;
|
||||
private SecurityType(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol.auth;
|
||||
|
||||
import com.glavsoft.exceptions.FatalException;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.exceptions.UnsupportedSecurityTypeException;
|
||||
import com.glavsoft.rfb.RfbCapabilityInfo;
|
||||
import com.glavsoft.rfb.encoding.ServerInitMessage;
|
||||
import com.glavsoft.rfb.protocol.Protocol;
|
||||
import com.glavsoft.rfb.protocol.tunnel.TunnelHandler;
|
||||
import com.glavsoft.rfb.protocol.tunnel.TunnelType;
|
||||
import com.glavsoft.transport.Transport;
|
||||
import com.glavsoft.utils.Strings;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class TightAuthentication extends AuthHandler {
|
||||
private final Map<Integer, AuthHandler> registeredAuthHandlers = new HashMap<Integer, AuthHandler>();
|
||||
private final Map<Integer, TunnelHandler> registeredTunnelHandlers = new HashMap<Integer, TunnelHandler>();
|
||||
|
||||
public TightAuthentication() {
|
||||
}
|
||||
|
||||
public void registerTunnelingHandler(TunnelHandler handler) {
|
||||
registeredTunnelHandlers.put(handler.getId(), handler);
|
||||
}
|
||||
|
||||
public void registerAuthHandler(AuthHandler handler) {
|
||||
registeredAuthHandlers.put(handler.getId(), handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SecurityType getType() {
|
||||
return SecurityType.TIGHT_AUTHENTICATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transport authenticate(Transport transport, Protocol protocol)
|
||||
throws TransportException, FatalException, UnsupportedSecurityTypeException {
|
||||
transport = tunnelingNegotiation(transport, protocol);
|
||||
authorizationNegotiation(transport, protocol);
|
||||
protocol.setTight(true);
|
||||
return transport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initProcedure(Transport transport, Protocol protocol) throws TransportException {
|
||||
capabilitiesNegotiation(transport, protocol);
|
||||
protocol.registerRfbEncodings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Capabilities negotiation consists of server-to-client message, where server introduces its capabilities,
|
||||
* and client-to-server message, which introduces only those client capabilities which are supported by server
|
||||
* and encodings supported by server.
|
||||
*
|
||||
* This data immediately follows the server initialisation message.
|
||||
*
|
||||
* typedef struct _rfbInteractionCapsMsg {
|
||||
* CARD16 nServerMessageTypes;
|
||||
* CARD16 nClientMessageTypes;
|
||||
* CARD16 nEncodingTypes;
|
||||
* CARD16 pad;><------><------>// reserved, must be 0
|
||||
* // followed by nServerMessageTypes * rfbCapabilityInfo structures
|
||||
* // followed by nClientMessageTypes * rfbCapabilityInfo structures
|
||||
* } rfbInteractionCapsMsg;
|
||||
* #define sz_rfbInteractionCapsMsg 8
|
||||
*
|
||||
* nServerMessageTypes | UINT16 | Number of server message types server announces.
|
||||
* nClientMessageTypes | UINT16 | Number of client message types server announces.
|
||||
* nEncodingTypes | UINT16 | Number of encoding types server announces.
|
||||
* ServerMessageTypes | RFBCAPABILITY x nServerMessageTypes | Server side messages which server supports.
|
||||
* ClientMessageTypes | RFBCAPABILITY x nClientMessageTypes | Client side messages which server supports.
|
||||
* Encodings | RFBCAPABILITY x nEncodingTypes | Encoding types which server supports.
|
||||
*
|
||||
* Client replies with message in exactly the same format, listing only those capabilities which are supported
|
||||
* both by server and the client.
|
||||
* Once all three initialization stages are successfully finished, client and server switch to normal protocol flow.
|
||||
*
|
||||
*/
|
||||
void capabilitiesNegotiation(Transport transport, Protocol protocol) throws TransportException {
|
||||
sendClientInitMessage(transport, protocol.getSettings().getSharedFlag());
|
||||
ServerInitMessage serverInitMessage = readServerInitMessage(transport);
|
||||
|
||||
int nServerMessageTypes = transport.readUInt16();
|
||||
int nClientMessageTypes = transport.readUInt16();
|
||||
int nEncodingTypes = transport.readUInt16();
|
||||
transport.readUInt16(); //padding
|
||||
|
||||
logger().fine("nServerMessageTypes: " + nServerMessageTypes + ", nClientMessageTypes: " + nClientMessageTypes +
|
||||
", nEncodingTypes: " + nEncodingTypes);
|
||||
|
||||
registerServerMessagesTypes(transport, protocol, nServerMessageTypes);
|
||||
registerClientMessagesTypes(transport, protocol, nClientMessageTypes);
|
||||
registerEncodings(transport, protocol, nEncodingTypes);
|
||||
completeContextData(serverInitMessage, protocol);
|
||||
}
|
||||
|
||||
private void registerServerMessagesTypes(Transport transport, Protocol protocol, int count) throws TransportException {
|
||||
while (count-- > 0) {
|
||||
RfbCapabilityInfo capInfoReceived = new RfbCapabilityInfo().readFrom(transport);
|
||||
logger().fine("Server message type: " + capInfoReceived.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void registerClientMessagesTypes(Transport transport, Protocol protocol, int count) throws TransportException {
|
||||
while (count-- > 0) {
|
||||
RfbCapabilityInfo capInfoReceived = new RfbCapabilityInfo().readFrom(transport);
|
||||
logger().fine("Client message type: " + capInfoReceived.toString());
|
||||
protocol.registerClientMessageType(capInfoReceived);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerEncodings(Transport transport, Protocol protocol, int count) throws TransportException {
|
||||
while (count-- > 0) {
|
||||
RfbCapabilityInfo capInfoReceived = new RfbCapabilityInfo().readFrom(transport);
|
||||
logger().fine("Encoding: " + capInfoReceived.toString());
|
||||
protocol.registerEncoding(capInfoReceived);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Negotiation of Tunneling Capabilities (protocol versions 3.7t, 3.8t)
|
||||
*
|
||||
* If the chosen security type is rfbSecTypeTight, the server sends a list of
|
||||
* supported tunneling methods ("tunneling" refers to any additional layer of
|
||||
* data transformation, such as encryption or external compression.)
|
||||
*
|
||||
* nTunnelTypes specifies the number of following rfbCapabilityInfo structures
|
||||
* that list all supported tunneling methods in the order of preference.
|
||||
*
|
||||
* NOTE: If nTunnelTypes is 0, that tells the client that no tunneling can be
|
||||
* used, and the client should not send a response requesting a tunneling
|
||||
* method.
|
||||
*
|
||||
* typedef struct _rfbTunnelingCapsMsg {
|
||||
* CARD32 nTunnelTypes;
|
||||
* //followed by nTunnelTypes * rfbCapabilityInfo structures
|
||||
* } rfbTunnelingCapsMsg;
|
||||
* #define sz_rfbTunnelingCapsMsg 4
|
||||
* ----------------------------------------------------------------------------
|
||||
* Tunneling Method Request (protocol versions 3.7t, 3.8t)
|
||||
*
|
||||
* If the list of tunneling capabilities sent by the server was not empty, the
|
||||
* client should reply with a 32-bit code specifying a particular tunneling
|
||||
* method. The following code should be used for no tunneling.
|
||||
*
|
||||
* #define rfbNoTunneling 0
|
||||
* #define sig_rfbNoTunneling "NOTUNNEL"
|
||||
*/
|
||||
Transport tunnelingNegotiation(Transport transport, Protocol protocol)
|
||||
throws TransportException {
|
||||
Transport newTransport = transport;
|
||||
int tunnelsCount;
|
||||
tunnelsCount = (int) transport.readUInt32();
|
||||
logger().fine("Tunneling capabilities: " + tunnelsCount);
|
||||
int [] tunnelCodes = new int[tunnelsCount];
|
||||
if (tunnelsCount > 0) {
|
||||
for (int i = 0; i < tunnelsCount; ++i) {
|
||||
RfbCapabilityInfo rfbCapabilityInfo = new RfbCapabilityInfo().readFrom(transport);
|
||||
tunnelCodes[i] = rfbCapabilityInfo.getCode();
|
||||
logger().fine(rfbCapabilityInfo.toString());
|
||||
}
|
||||
int selectedTunnelCode;
|
||||
if (tunnelsCount > 0) {
|
||||
for (int i = 0; i < tunnelsCount; ++i) {
|
||||
final TunnelHandler tunnelHandler = registeredTunnelHandlers.get(tunnelCodes[i]);
|
||||
if (tunnelHandler != null) {
|
||||
selectedTunnelCode = tunnelCodes[i];
|
||||
transport.writeInt32(selectedTunnelCode).flush();
|
||||
logger().fine("Accepted tunneling type: " + selectedTunnelCode);
|
||||
newTransport = tunnelHandler.createTunnel(transport);
|
||||
logger().fine("Tunnel created: " + TunnelType.byCode(selectedTunnelCode));
|
||||
protocol.setTunnelType(TunnelType.byCode(selectedTunnelCode));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (protocol.getTunnelType() == null) {
|
||||
protocol.setTunnelType(TunnelType.NOTUNNEL);
|
||||
if (tunnelsCount > 0) {
|
||||
transport.writeInt32(TunnelType.NOTUNNEL.code).flush();
|
||||
}
|
||||
logger().fine("Accepted tunneling type: " + TunnelType.NOTUNNEL);
|
||||
}
|
||||
return newTransport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Negotiation of Authentication Capabilities (protocol versions 3.7t, 3.8t)
|
||||
*
|
||||
* After setting up tunneling, the server sends a list of supported
|
||||
* authentication schemes.
|
||||
*
|
||||
* nAuthTypes specifies the number of following rfbCapabilityInfo structures
|
||||
* that list all supported authentication schemes in the order of preference.
|
||||
*
|
||||
* NOTE: If nAuthTypes is 0, that tells the client that no authentication is
|
||||
* necessary, and the client should not send a response requesting an
|
||||
* authentication scheme.
|
||||
*
|
||||
* typedef struct _rfbAuthenticationCapsMsg {
|
||||
* CARD32 nAuthTypes;
|
||||
* // followed by nAuthTypes * rfbCapabilityInfo structures
|
||||
* } rfbAuthenticationCapsMsg;
|
||||
* #define sz_rfbAuthenticationCapsMsg 4
|
||||
*
|
||||
*/
|
||||
void authorizationNegotiation(Transport transport, Protocol protocol)
|
||||
throws UnsupportedSecurityTypeException, TransportException, FatalException {
|
||||
int authCount;
|
||||
authCount = transport.readInt32();
|
||||
logger().fine("Auth capabilities: " + authCount);
|
||||
byte[] cap = new byte[authCount];
|
||||
for (int i = 0; i < authCount; ++i) {
|
||||
RfbCapabilityInfo rfbCapabilityInfo = new RfbCapabilityInfo().readFrom(transport);
|
||||
cap[i] = (byte) rfbCapabilityInfo.getCode();
|
||||
logger().fine(rfbCapabilityInfo.toString());
|
||||
}
|
||||
AuthHandler authHandler = null;
|
||||
if (authCount > 0) {
|
||||
for (int i = 0; i < authCount; ++i) {
|
||||
authHandler = registeredAuthHandlers.get((int) cap[i]);
|
||||
if (authHandler != null) {
|
||||
//sending back RFB capability code
|
||||
transport.writeInt32(authHandler.getId()).flush();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
authHandler = registeredAuthHandlers.get(SecurityType.NONE_AUTHENTICATION.getId());
|
||||
}
|
||||
if (null == authHandler) {
|
||||
throw new UnsupportedSecurityTypeException("Server auth types: " + Strings.toString(cap) +
|
||||
", supported auth types: " + registeredAuthHandlers.values());
|
||||
}
|
||||
logger().fine("Auth capability accepted: " + authHandler.getName());
|
||||
authHandler.authenticate(transport, protocol);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol.auth;
|
||||
|
||||
import com.glavsoft.exceptions.CryptoException;
|
||||
import com.glavsoft.exceptions.FatalException;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.rfb.protocol.Protocol;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.DESKeySpec;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
|
||||
import static com.glavsoft.utils.Strings.getBytesWithCharset;
|
||||
|
||||
public class VncAuthentication extends AuthHandler {
|
||||
@Override
|
||||
public SecurityType getType() {
|
||||
return SecurityType.VNC_AUTHENTICATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transport authenticate(Transport transport, Protocol protocol)
|
||||
throws TransportException, FatalException {
|
||||
byte [] challenge = transport.readBytes(16);
|
||||
String password = protocol.getPasswordRetriever().getResult();
|
||||
if (null == password) password = "";
|
||||
byte [] key = new byte[8];
|
||||
System.arraycopy(getBytesWithCharset(password, Transport.ISO_8859_1), 0, key, 0, Math.min(key.length, getBytesWithCharset(password, Transport.ISO_8859_1).length));
|
||||
transport.write(encrypt(challenge, key)).flush();
|
||||
return transport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt challenge by key using DES
|
||||
* @return encrypted bytes
|
||||
* @throws CryptoException on problem with DES algorithm support or smth about
|
||||
*/
|
||||
public byte[] encrypt(byte[] challenge, byte[] key) throws CryptoException {
|
||||
try {
|
||||
DESKeySpec desKeySpec = new DESKeySpec(mirrorBits(key));
|
||||
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
|
||||
SecretKey secretKey = keyFactory.generateSecret(desKeySpec);
|
||||
Cipher desCipher = Cipher.getInstance("DES/ECB/NoPadding");
|
||||
desCipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||
return desCipher.doFinal(challenge);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new CryptoException("Cannot encrypt challenge", e);
|
||||
} catch (NoSuchPaddingException e) {
|
||||
throw new CryptoException("Cannot encrypt challenge", e);
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
throw new CryptoException("Cannot encrypt challenge", e);
|
||||
} catch (BadPaddingException e) {
|
||||
throw new CryptoException("Cannot encrypt challenge", e);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new CryptoException("Cannot encrypt challenge", e);
|
||||
} catch (InvalidKeySpecException e) {
|
||||
throw new CryptoException("Cannot encrypt challenge", e);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] mirrorBits(byte[] k) {
|
||||
byte[] key = new byte[8];
|
||||
for (int i = 0; i < 8; i++) {
|
||||
byte s = k[i];
|
||||
s = (byte) (((s >> 1) & 0x55) | ((s << 1) & 0xaa));
|
||||
s = (byte) (((s >> 2) & 0x33) | ((s << 2) & 0xcc));
|
||||
s = (byte) (((s >> 4) & 0x0f) | ((s << 4) & 0xf0));
|
||||
key[i] = s;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol.handlers;
|
||||
|
||||
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.rfb.IRequestString;
|
||||
import com.glavsoft.rfb.protocol.Protocol;
|
||||
import com.glavsoft.rfb.protocol.auth.AuthHandler;
|
||||
import com.glavsoft.rfb.protocol.auth.NoneAuthentication;
|
||||
import com.glavsoft.rfb.protocol.auth.SecurityType;
|
||||
import com.glavsoft.rfb.protocol.auth.TightAuthentication;
|
||||
import com.glavsoft.rfb.protocol.auth.VncAuthentication;
|
||||
import com.glavsoft.rfb.protocol.tunnel.SslTunnel;
|
||||
import com.glavsoft.rfb.protocol.tunnel.TunnelType;
|
||||
import com.glavsoft.transport.Transport;
|
||||
import com.glavsoft.utils.Strings;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public class Handshaker {
|
||||
private static final int PROTOCOL_STRING_LENGTH = 12;
|
||||
private static final String RFB_PROTOCOL_STRING_REGEXP = "^RFB (\\d\\d\\d).(\\d\\d\\d)\n$";
|
||||
private static final String DISPATCHER_PROTOCOL_STRING = "TCPDISPATCH\n";
|
||||
|
||||
private static final int MIN_SUPPORTED_VERSION_MAJOR = 3;
|
||||
private static final int MIN_SUPPORTED_VERSION_MINOR = 3;
|
||||
|
||||
private static final int MAX_SUPPORTED_VERSION_MAJOR = 3;
|
||||
private static final int MAX_SUPPORTED_VERSION_MINOR = 8;
|
||||
protected static final int DISPATCHER_PROTOCOL_VERSION = 3;
|
||||
protected static final int KEEP_ALIVE_BYTE = 0;
|
||||
protected static final int START_BYTE = 1;
|
||||
private Protocol protocol;
|
||||
private Logger logger;
|
||||
private final Map<Integer, AuthHandler> registeredAuthHandlers = new HashMap<Integer, AuthHandler>();
|
||||
|
||||
public Handshaker(Protocol protocol) {
|
||||
this.protocol = protocol;
|
||||
logger = Logger.getLogger(getClass().getName());
|
||||
registerAuthHandler(SecurityType.NONE_AUTHENTICATION.getId(), new NoneAuthentication());
|
||||
registerAuthHandler(SecurityType.VNC_AUTHENTICATION.getId(), new VncAuthentication());
|
||||
|
||||
final TightAuthentication tightAuthentication = new TightAuthentication();
|
||||
tightAuthentication.registerAuthHandler(new NoneAuthentication());
|
||||
tightAuthentication.registerAuthHandler(new VncAuthentication());
|
||||
if (protocol.getSettings().getTunnelType() != TunnelType.NOTUNNEL &&
|
||||
SslTunnel.isTransportAvailable()) {
|
||||
tightAuthentication.registerTunnelingHandler(new SslTunnel());
|
||||
registerAuthHandler(SecurityType.TIGHT2_AUTHENTICATION.getId(), tightAuthentication);
|
||||
}
|
||||
registerAuthHandler(SecurityType.TIGHT_AUTHENTICATION.getId(), tightAuthentication);
|
||||
}
|
||||
|
||||
public Transport handshake(Transport transport) throws TransportException, UnsupportedProtocolVersionException, AuthenticationFailedException, FatalException, UnsupportedSecurityTypeException {
|
||||
String protocolString = transport.readString(PROTOCOL_STRING_LENGTH);
|
||||
if (isDispatcherConnection(protocolString)) {
|
||||
handshakeToDispatcher(transport);
|
||||
protocolString = transport.readString(PROTOCOL_STRING_LENGTH);
|
||||
}
|
||||
ProtocolVersion ver = matchProtocolVersion(protocolString);
|
||||
transport.write(Strings.getBytesWithCharset("RFB 00" + ver.major + ".00" + ver.minor + "\n", Transport.ISO_8859_1)).flush();
|
||||
protocol.setProtocolVersion(ver);
|
||||
logger.info("Set protocol version to: " + ver);
|
||||
transport = auth(transport, ver);
|
||||
return transport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make dispatcher connection
|
||||
*
|
||||
* Dispatcher protocol v.3: '<-' means receive from dispatcher, '->' means send to dispatcher
|
||||
* <- "TCPDISPATCH\n" — already received at this point
|
||||
* <- UInt8 numSupportedVersions value
|
||||
* <- numSupportedVersions UInt8 values of supported version num
|
||||
* -> UInt8 value of version accepted
|
||||
* -> UInt8 remoteHostRole value (0 == RFB Server or 1 == RFB Client/viewer)
|
||||
* -> UInt32 connId
|
||||
* <- UInt32 connId (when 0 == connId, then dispatcher generates unique random connId value
|
||||
* and sends it to clients, else it doesn't send the one)
|
||||
* -> UInt8 secret keyword string length
|
||||
* -> String (byte array of ASCII characters) - secret keyword
|
||||
* -> UInt8 dispatcher name string length (may equals to 0)
|
||||
* -> String (byte array of ASCII characters) - dispatcher name
|
||||
* <- UInt8 dispatcher name string length
|
||||
* <- String (byte array of ASCII characters) - dispatcher name
|
||||
* <- 0 'keep alive byte' or non zero 'start byte' (1)
|
||||
* On keep alive byte immediately answer with the same byte, and wain for next byte,
|
||||
* on start byte go to ordinary rfb negotiation.
|
||||
*
|
||||
* @param transport
|
||||
*
|
||||
* @throws TransportException when some io error happens
|
||||
* @throws UnsupportedProtocolVersionException when protocol doesn't match
|
||||
* @throws AuthenticationFailedException when connectionId provided by user is wrong
|
||||
*/
|
||||
private void handshakeToDispatcher(Transport transport) throws TransportException, UnsupportedProtocolVersionException, AuthenticationFailedException {
|
||||
int numSupportedVersions = transport.readUInt8(); // receive num of supported version followed (u8)
|
||||
List<Integer> remoteVersions = new ArrayList<Integer>(numSupportedVersions);
|
||||
for (int i = 0; i < numSupportedVersions; ++i) {
|
||||
remoteVersions.add(transport.readUInt8()); // receive supported protocol versions (numSupportedVersions x u8)
|
||||
}
|
||||
logger.fine("Dispatcher protocol versions: " + Arrays.toString(remoteVersions.toArray()));
|
||||
if (!remoteVersions.contains(DISPATCHER_PROTOCOL_VERSION)) {
|
||||
throw new UnsupportedProtocolVersionException("Dispatcher unsupported protocol versions");
|
||||
}
|
||||
transport.writeByte(DISPATCHER_PROTOCOL_VERSION); // send protocol version we use (u8)
|
||||
transport.writeByte(1).flush(); // send we are the viewer (u8)
|
||||
long connectionId = 0;
|
||||
IRequestString connIdRetriever = protocol.getConnectionIdRetriever();
|
||||
if (null == connIdRetriever) throw new IllegalStateException("ConnectionIdRetriever is null");
|
||||
String sId = connIdRetriever.getResult();
|
||||
if (Strings.isTrimmedEmpty(sId)) throw new AuthenticationFailedException("ConnectionId is empty");
|
||||
try {
|
||||
connectionId = Long.parseLong(sId);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new AuthenticationFailedException("Wrong ConnectionId");
|
||||
}
|
||||
if ( 0 == connectionId) {
|
||||
throw new AuthenticationFailedException("ConnectionId have not be equals to zero");
|
||||
}
|
||||
transport.writeUInt32(connectionId).flush(); // send connectionId (u32)
|
||||
|
||||
transport.writeByte(0); // send UInt8 secret keyword string length. 0 - for none
|
||||
// send String (byte array of ASCII characters) - secret keyword.
|
||||
// Skip if none
|
||||
transport.writeByte(0).flush(); // send UInt8 dispatcher name string length (may equals to 0)
|
||||
// send -> String (byte array of ASCII characters) - dispatcher name.
|
||||
// Skip if none
|
||||
//logger.fine("Sent: version3, viewer, connectionId: " + connectionId + " secret:0, token: 0");
|
||||
int tokenLength = transport.readUInt8(); // receive UInt8 token length
|
||||
// receive byte array - dispatcher token
|
||||
byte [] token = transport.readBytes(tokenLength);
|
||||
//logger.fine("token: #" + tokenLength + " " + (tokenLength>0?token[0]:"") +(tokenLength>1?token[1]:"")+(tokenLength>2?token[2]:""));
|
||||
// receive 0 'keep alive byte' or non zero 'start byte' (1)
|
||||
// on keep alive byte send the same to remote
|
||||
// on start byte go to starting rfb connection
|
||||
int b;
|
||||
do {
|
||||
b = transport.readByte();
|
||||
if (KEEP_ALIVE_BYTE == b) {
|
||||
logger.finer("keep-alive");
|
||||
transport.writeByte(KEEP_ALIVE_BYTE).flush();
|
||||
}
|
||||
} while (b != START_BYTE);
|
||||
logger.info("Dispatcher handshake completed");
|
||||
}
|
||||
|
||||
/**
|
||||
* When first 12 bytes sent by server is "TCPDISPATCH\n" this is dispatcher connection
|
||||
*
|
||||
* @param protocolString string with first 12 bytes sent by server
|
||||
* @return true when we connects to dispatcher, not remote rfb server
|
||||
*/
|
||||
private boolean isDispatcherConnection(String protocolString) {
|
||||
final boolean dispatcherDetected = DISPATCHER_PROTOCOL_STRING.equals(protocolString);
|
||||
if (dispatcherDetected) {
|
||||
logger.info("Dispatcher connection detected");
|
||||
}
|
||||
return dispatcherDetected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take first 12 bytes sent by server and match rfb protocol version.
|
||||
* RFB protocol version string is "RFB MMM.mmm\n". Where MMM is major
|
||||
* protocol version and mmm is minor one.
|
||||
*
|
||||
* Side effect: set protocol.isMac when MacOs at other side is detected
|
||||
*
|
||||
* @param protocolString string with first 12 bytes sent by server
|
||||
* @return version of protocol will be used
|
||||
*/
|
||||
private ProtocolVersion matchProtocolVersion(String protocolString) throws UnsupportedProtocolVersionException {
|
||||
logger.info("Server protocol string: " + protocolString.substring(0, protocolString.length() - 1));
|
||||
Pattern pattern = Pattern.compile(RFB_PROTOCOL_STRING_REGEXP);
|
||||
final Matcher matcher = pattern.matcher(protocolString);
|
||||
if ( ! matcher.matches())
|
||||
throw new UnsupportedProtocolVersionException(
|
||||
"Unsupported protocol version: " + protocolString);
|
||||
int major = Integer.parseInt(matcher.group(1));
|
||||
int minor = Integer.parseInt(matcher.group(2));
|
||||
ProtocolVersion ver;
|
||||
boolean isMac = false;
|
||||
if (889 == minor) {
|
||||
isMac = true;
|
||||
}
|
||||
if (major < MIN_SUPPORTED_VERSION_MAJOR ||
|
||||
MIN_SUPPORTED_VERSION_MAJOR == major && minor < MIN_SUPPORTED_VERSION_MINOR)
|
||||
throw new UnsupportedProtocolVersionException(
|
||||
"Unsupported protocol version: " + major + "." + minor);
|
||||
if (major > MAX_SUPPORTED_VERSION_MAJOR) {
|
||||
// major = MAX_SUPPORTED_VERSION_MAJOR;
|
||||
minor = MAX_SUPPORTED_VERSION_MINOR;
|
||||
}
|
||||
|
||||
if (minor >= MIN_SUPPORTED_VERSION_MINOR && minor < 7) {
|
||||
ver = ProtocolVersion.PROTOCOL_VERSION_3_3;
|
||||
} else if (7 == minor) {
|
||||
ver = ProtocolVersion.PROTOCOL_VERSION_3_7;
|
||||
} else if (minor >= MAX_SUPPORTED_VERSION_MINOR) {
|
||||
ver = ProtocolVersion.PROTOCOL_VERSION_3_8;
|
||||
} else
|
||||
throw new UnsupportedProtocolVersionException("Unsupported protocol version: " + protocolString);
|
||||
protocol.setMac(isMac);
|
||||
return ver;
|
||||
}
|
||||
|
||||
private Transport auth(Transport transport, ProtocolVersion ver) throws UnsupportedSecurityTypeException, TransportException, FatalException, AuthenticationFailedException {
|
||||
AuthHandler handler;
|
||||
switch (ver) {
|
||||
case PROTOCOL_VERSION_3_3:
|
||||
handler = auth33(transport);
|
||||
break;
|
||||
case PROTOCOL_VERSION_3_7:
|
||||
handler = auth37_38(transport);
|
||||
break;
|
||||
case PROTOCOL_VERSION_3_8:
|
||||
handler = auth37_38(transport);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
transport = handler.authenticate(transport, protocol);
|
||||
if (ver == ProtocolVersion.PROTOCOL_VERSION_3_8 ||
|
||||
handler.getType() != SecurityType.NONE_AUTHENTICATION) {
|
||||
handler.checkSecurityResult(transport);
|
||||
}
|
||||
handler.initProcedure(transport, protocol);
|
||||
return transport;
|
||||
}
|
||||
|
||||
private AuthHandler auth33(Transport transport) throws TransportException, UnsupportedSecurityTypeException {
|
||||
int type = transport.readInt32();
|
||||
logger.info("Type received: " + type);
|
||||
if (0 == type)
|
||||
throw new UnsupportedSecurityTypeException(transport.readString());
|
||||
AuthHandler handler = registeredAuthHandlers.get(selectAuthHandlerId((byte) (0xff & type)));
|
||||
return handler;
|
||||
}
|
||||
|
||||
private AuthHandler auth37_38(Transport transport) throws TransportException, UnsupportedSecurityTypeException {
|
||||
int secTypesNum = transport.readUInt8();
|
||||
if (0 == secTypesNum)
|
||||
throw new UnsupportedSecurityTypeException(transport.readString());
|
||||
byte[] secTypes = transport.readBytes(secTypesNum);
|
||||
logger.info("Security Types received (" + secTypesNum + "): " + Strings.toString(secTypes));
|
||||
final int typeIdAccepted = selectAuthHandlerId(secTypes);
|
||||
final AuthHandler authHandler = registeredAuthHandlers.get(typeIdAccepted);
|
||||
transport.writeByte(typeIdAccepted).flush();
|
||||
return authHandler;
|
||||
}
|
||||
|
||||
private int selectAuthHandlerId(byte... secTypes)
|
||||
throws UnsupportedSecurityTypeException, TransportException {
|
||||
AuthHandler handler;
|
||||
// Tight2 Authentication very first
|
||||
for (byte type : secTypes) {
|
||||
if (SecurityType.TIGHT2_AUTHENTICATION.getId() == (0xff & type)) {
|
||||
handler = registeredAuthHandlers.get(SecurityType.TIGHT2_AUTHENTICATION.getId());
|
||||
if (handler != null) {
|
||||
logger.info("Security Type accepted: " + SecurityType.TIGHT2_AUTHENTICATION.name());
|
||||
return SecurityType.TIGHT2_AUTHENTICATION.getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Tight Authentication first
|
||||
for (byte type : secTypes) {
|
||||
if (SecurityType.TIGHT_AUTHENTICATION.getId() == (0xff & type)) {
|
||||
handler = registeredAuthHandlers.get(SecurityType.TIGHT_AUTHENTICATION.getId());
|
||||
if (handler != null) {
|
||||
logger.info("Security Type accepted: " + SecurityType.TIGHT_AUTHENTICATION.name());
|
||||
return SecurityType.TIGHT_AUTHENTICATION.getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
for (byte type : secTypes) {
|
||||
handler = registeredAuthHandlers.get(0xff & type);
|
||||
if (handler != null) {
|
||||
logger.info("Security Type accepted: " + handler.getType());
|
||||
return handler.getType().getId();
|
||||
}
|
||||
}
|
||||
throw new UnsupportedSecurityTypeException(
|
||||
"No security types supported. Server sent '"
|
||||
+ Strings.toString(secTypes)
|
||||
+ "' security types, but we do not support any of their.");
|
||||
}
|
||||
|
||||
private void registerAuthHandler(int id, AuthHandler handler) {
|
||||
registeredAuthHandlers.put(id, handler);
|
||||
}
|
||||
|
||||
public static enum ProtocolVersion {
|
||||
PROTOCOL_VERSION_3_3(3, 3),
|
||||
PROTOCOL_VERSION_3_7(3, 7),
|
||||
PROTOCOL_VERSION_3_8(3, 8);
|
||||
|
||||
public final int minor;
|
||||
public final int major;
|
||||
|
||||
ProtocolVersion(int major, int minor) {
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(major) + "." + String.valueOf(minor);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol.tunnel;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public class SslTunnel implements TunnelHandler {
|
||||
|
||||
/**
|
||||
* Requested protocol (algorithm) name.
|
||||
* May be one of the follows:
|
||||
* SSL Supports some version of SSL; may support other versions
|
||||
* SSLv2 Supports SSL version 2 or later; may support other versions
|
||||
* SSLv3 Supports SSL version 3; may support other versions
|
||||
* TLS Supports some version of TLS; may support other versions
|
||||
* TLSv1 Supports RFC 2246: TLS version 1.0 ; may support other versions
|
||||
* TLSv1.1 Supports RFC 4346: TLS version 1.1 ; may support other versions
|
||||
* TLSv1.2 Supports RFC 5246: TLS version 1.2 ; may support other versions
|
||||
*
|
||||
* Using TLSv1.1 because earlier versions have vulnerabilities.
|
||||
*/
|
||||
private static final String PROTOCOL = "TLSv1.1";
|
||||
private static final String SSL_TRANSPORT = "com.glavsoft.transport.SslTransport";
|
||||
|
||||
public SslTunnel() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return TunnelType.SSL.code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transport createTunnel(Transport transport) throws TransportException {
|
||||
try {
|
||||
SSLContext sslContext = SSLContext.getInstance(PROTOCOL);
|
||||
sslContext.init(null, getTrustAllCertsManager(), null);
|
||||
SSLEngine engine = sslContext.createSSLEngine();
|
||||
engine.setUseClientMode(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
final Class<Transport> sslTransportClass = (Class<Transport>) Class.forName(SSL_TRANSPORT);
|
||||
final Constructor<Transport> constructor = sslTransportClass.getConstructor(Transport.class, SSLEngine.class);
|
||||
return constructor.newInstance(transport, engine);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new TransportException("Cannot create SSL/TLS tunnel", e);
|
||||
} catch (KeyManagementException e) {
|
||||
throw new TransportException("Cannot create SSL/TLS tunnel", e);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new TransportException("Cannot create SSL/TLS tunnel, SSL transport plugin unavailable", e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new TransportException("Cannot create SSL/TLS tunnel, SSL transport plugin unavailable", e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new TransportException("Cannot create SSL/TLS tunnel, SSL transport plugin unavailable", e);
|
||||
} catch (InstantiationException e) {
|
||||
throw new TransportException("Cannot create SSL/TLS tunnel, SSL transport plugin unavailable", e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new TransportException("Cannot create SSL/TLS tunnel, SSL transport plugin unavailable", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private TrustManager[] getTrustAllCertsManager() {
|
||||
return new TrustManager[]{
|
||||
new X509TrustManager() {
|
||||
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
||||
return null;
|
||||
}
|
||||
public void checkClientTrusted(
|
||||
java.security.cert.X509Certificate[] certs, String authType) {
|
||||
}
|
||||
public void checkServerTrusted(
|
||||
java.security.cert.X509Certificate[] certs, String authType) {
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public static boolean isTransportAvailable() {
|
||||
try {
|
||||
Class.forName("com.glavsoft.transport.SslTransport");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol.tunnel;
|
||||
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public interface TunnelHandler {
|
||||
public int getId();
|
||||
|
||||
Transport createTunnel(Transport transport) throws TransportException;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol.tunnel;
|
||||
|
||||
import com.glavsoft.rfb.RfbCapabilityInfo;
|
||||
|
||||
/**
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public enum TunnelType {
|
||||
NOTUNNEL(0, RfbCapabilityInfo.VENDOR_STANDARD, "NOTUNNEL", ""),
|
||||
|
||||
SSL(2, RfbCapabilityInfo.VENDOR_TIGHT, "SSL_____", "SSL/TLS");
|
||||
|
||||
public final int code;
|
||||
public final String vendor;
|
||||
public final String name;
|
||||
public final String hrName;
|
||||
|
||||
TunnelType(int code, String vendor, String name, String humanReadableName) {
|
||||
this.code = code;
|
||||
this.vendor = vendor;
|
||||
this.name = name;
|
||||
hrName = humanReadableName;
|
||||
}
|
||||
|
||||
public static TunnelType byCode(int code) {
|
||||
for (TunnelType type : values()) {
|
||||
if (type.code == code) return type;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// Copyright (C) 2010 - 2014 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.transport;
|
||||
|
||||
/**
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public class BaudrateMeter {
|
||||
|
||||
public static final int MIN_BPS = 10000;
|
||||
private static final int n = 5;
|
||||
private static final double ALPHA = 2. / (n + 1);
|
||||
private double ema = 0;
|
||||
private boolean measure = false;
|
||||
private long start;
|
||||
private long bytes;
|
||||
|
||||
public void count(int bytes) {
|
||||
if (measure) this.bytes += bytes;
|
||||
}
|
||||
|
||||
public int kBPS() {
|
||||
return (int) (ema / 1000);
|
||||
}
|
||||
|
||||
public void startMeasuringCycle() {
|
||||
measure = true;
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void stopMeasuringCycle() {
|
||||
measure = false;
|
||||
long ms = System.currentTimeMillis() - start;
|
||||
if (0 == ms || bytes < 100) return; // skip with too small portion of data
|
||||
double bps = bytes * 8. / (ms / 1000.);
|
||||
// double bpms = bytes * 8. / ms;
|
||||
if (bps < MIN_BPS) { // limit lower value
|
||||
bps = MIN_BPS;
|
||||
}
|
||||
// exponential moving-average smoothing
|
||||
ema = ALPHA * bps + (1. - ALPHA) * (0. == ema ? bps : ema);
|
||||
bytes = 0;
|
||||
}
|
||||
}
|
||||
349
plugins/vnc/src/main/java/com/glavsoft/transport/Transport.java
Normal file
349
plugins/vnc/src/main/java/com/glavsoft/transport/Transport.java
Normal file
@@ -0,0 +1,349 @@
|
||||
// Copyright (C) 2010 - 2014 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.transport;
|
||||
|
||||
import com.glavsoft.exceptions.ClosedConnectionException;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author dime
|
||||
*/
|
||||
public class Transport {
|
||||
public final static Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
|
||||
public final static Charset UTF8 = StandardCharsets.UTF_8;
|
||||
DataInputStream is;
|
||||
DataOutputStream os;
|
||||
InputStream origIs;
|
||||
OutputStream origOs;
|
||||
private BaudrateMeter baudrateMeter;
|
||||
|
||||
public Transport(Socket socket) throws IOException {
|
||||
this(new BufferedInputStream(socket.getInputStream()), new BufferedOutputStream(socket.getOutputStream()));
|
||||
}
|
||||
|
||||
public Transport(InputStream is) {
|
||||
this(is, null);
|
||||
}
|
||||
|
||||
public Transport(OutputStream os) {
|
||||
this(null, os);
|
||||
}
|
||||
|
||||
public Transport(InputStream is, OutputStream os) {
|
||||
init(is, os);
|
||||
}
|
||||
|
||||
void init(InputStream is, OutputStream os) {
|
||||
origIs = is;
|
||||
this.is = is != null ? new DataInputStream(is) : null;
|
||||
origOs = os;
|
||||
this.os = os != null ? new DataOutputStream(os) : null;
|
||||
}
|
||||
|
||||
public Transport() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
void release() {
|
||||
origIs = is = null;
|
||||
origOs = os = null;
|
||||
}
|
||||
|
||||
public byte readByte() throws TransportException {
|
||||
try {
|
||||
if (baudrateMeter != null) baudrateMeter.count(1);
|
||||
return is.readByte();
|
||||
} catch (EOFException e) {
|
||||
throw new ClosedConnectionException(e);
|
||||
} catch (IOException e) {
|
||||
throw new TransportException("Cannot read byte", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void setBaudrateMeter(BaudrateMeter baudrateMeter) {
|
||||
this.baudrateMeter = baudrateMeter;
|
||||
}
|
||||
|
||||
public int readUInt8() throws TransportException {
|
||||
return readByte() & 0x0ff;
|
||||
}
|
||||
|
||||
public int readUInt16() throws TransportException {
|
||||
return readInt16() & 0x0ffff;
|
||||
}
|
||||
|
||||
public short readInt16() throws TransportException {
|
||||
try {
|
||||
if (baudrateMeter != null) baudrateMeter.count(2);
|
||||
return is.readShort();
|
||||
} catch (EOFException e) {
|
||||
throw new ClosedConnectionException(e);
|
||||
} catch (IOException e) {
|
||||
throw new TransportException("Cannot read int16", e);
|
||||
}
|
||||
}
|
||||
|
||||
public long readUInt32() throws TransportException {
|
||||
return readInt32() & 0xffffffffL;
|
||||
}
|
||||
|
||||
public int readInt32() throws TransportException {
|
||||
try {
|
||||
if (baudrateMeter != null) baudrateMeter.count(4);
|
||||
return is.readInt();
|
||||
} catch (EOFException e) {
|
||||
throw new ClosedConnectionException(e);
|
||||
} catch (IOException e) {
|
||||
throw new TransportException("Cannot read int32", e);
|
||||
}
|
||||
}
|
||||
|
||||
public long readInt64() throws TransportException {
|
||||
try {
|
||||
if (baudrateMeter != null) baudrateMeter.count(8);
|
||||
return is.readLong();
|
||||
} catch (EOFException e) {
|
||||
throw new ClosedConnectionException(e);
|
||||
} catch (IOException e) {
|
||||
throw new TransportException("Cannot read int32", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read string by it length.
|
||||
* Use this method only when sure no character accept ASCII will be read.
|
||||
* Use readBytes and character encoding conversion instead.
|
||||
*
|
||||
* @return String read
|
||||
*/
|
||||
public String readString(int length) throws TransportException {
|
||||
// return new String(readBytes(length), ISO_8859_1);
|
||||
return stringWithBytesAndCharset(readBytes(length));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 or {@link #readUtf8String} method
|
||||
* when utf-8 encoding needed.
|
||||
*
|
||||
* @return String read
|
||||
* @throws TransportException
|
||||
*/
|
||||
public String readString() 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 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), UTF8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read @code{length} byte array
|
||||
* Create byte array with length of @code{length}, read @code{length} bytes and return the array
|
||||
*
|
||||
* @param length
|
||||
* @return byte array which contains the data read
|
||||
* @throws TransportException
|
||||
*/
|
||||
public byte[] readBytes(int length) throws TransportException {
|
||||
try {
|
||||
return is.readNBytes(length);
|
||||
} catch (IOException e) {
|
||||
throw new TransportException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] readBytes(byte[] b, int offset, int length) throws TransportException {
|
||||
try {
|
||||
is.readFully(b, offset, length);
|
||||
if (baudrateMeter != null) {
|
||||
baudrateMeter.count(length);
|
||||
}
|
||||
return b;
|
||||
} catch (EOFException e) {
|
||||
throw new ClosedConnectionException(e);
|
||||
} catch (IOException e) {
|
||||
throw new TransportException("Cannot read " + length + " bytes array", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void skip(int length) throws TransportException {
|
||||
try {
|
||||
int rest = length;
|
||||
do {
|
||||
rest -= is.skipBytes(rest);
|
||||
} while (rest > 0);
|
||||
if (baudrateMeter != null) {
|
||||
baudrateMeter.count(length);
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
throw new ClosedConnectionException(e);
|
||||
} catch (IOException e) {
|
||||
throw new TransportException("Cannot skip " + length + " bytes", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForOutputInit() throws TransportException {
|
||||
if (null == os) throw new TransportException("Uninitialized writer");
|
||||
}
|
||||
|
||||
public Transport flush() throws TransportException {
|
||||
checkForOutputInit();
|
||||
try {
|
||||
os.flush();
|
||||
} catch (IOException e) {
|
||||
throw new TransportException("Cannot flush output stream", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Transport writeByte(int b) throws TransportException {
|
||||
return write((byte) (b & 0xff));
|
||||
}
|
||||
|
||||
public Transport write(byte b) throws TransportException {
|
||||
checkForOutputInit();
|
||||
try {
|
||||
os.writeByte(b);
|
||||
} catch (IOException e) {
|
||||
throw new TransportException("Cannot write byte", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Transport writeInt16(int sh) throws TransportException {
|
||||
return write((short) (sh & 0xffff));
|
||||
}
|
||||
|
||||
public Transport write(short sh) throws TransportException {
|
||||
checkForOutputInit();
|
||||
try {
|
||||
os.writeShort(sh);
|
||||
} catch (IOException e) {
|
||||
throw new TransportException("Cannot write short", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Transport writeInt32(int i) throws TransportException {
|
||||
return write(i);
|
||||
}
|
||||
|
||||
public Transport writeUInt32(long i) throws TransportException {
|
||||
return write((int) i & 0xffffffff);
|
||||
}
|
||||
|
||||
public Transport writeInt64(long i) throws TransportException {
|
||||
checkForOutputInit();
|
||||
try {
|
||||
os.writeLong(i);
|
||||
} catch (IOException e) {
|
||||
throw new TransportException("Cannot write long", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Transport write(int i) throws TransportException {
|
||||
checkForOutputInit();
|
||||
try {
|
||||
os.writeInt(i);
|
||||
} catch (IOException e) {
|
||||
throw new TransportException("Cannot write int", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Transport write(byte[] b) throws TransportException {
|
||||
return write(b, 0, b.length);
|
||||
}
|
||||
|
||||
public Transport write(byte[] b, int length) throws TransportException {
|
||||
return write(b, 0, length);
|
||||
}
|
||||
|
||||
public Transport write(byte[] b, int offset, int length) throws TransportException {
|
||||
checkForOutputInit();
|
||||
try {
|
||||
os.write(b, offset, length <= b.length ? length : b.length);
|
||||
} catch (IOException e) {
|
||||
throw new TransportException("Cannot write " + length + " bytes", e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setOutputStreamTo(OutputStream os) {
|
||||
this.os = new DataOutputStream(os);
|
||||
}
|
||||
|
||||
public Transport zero(int count) throws TransportException {
|
||||
while (count-- > 0) {
|
||||
writeByte(0);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private String stringWithBytesAndCharset(byte[] bytes) {
|
||||
String result;
|
||||
try {
|
||||
result = new String(bytes, ISO_8859_1);
|
||||
} catch (NoSuchMethodError error) {
|
||||
try {
|
||||
result = new String(bytes, ISO_8859_1.name());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
904
plugins/vnc/src/main/java/com/glavsoft/utils/Keymap.java
Normal file
904
plugins/vnc/src/main/java/com/glavsoft/utils/Keymap.java
Normal file
@@ -0,0 +1,904 @@
|
||||
// Copyright (C) 2010 - 2014 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.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public class Keymap {
|
||||
|
||||
public static final int K_F1 = 0xffbe;
|
||||
public static final int K_F2 = 0xffbf;
|
||||
public static final int K_F3 = 0xffc0;
|
||||
public static final int K_F4 = 0xffc1;
|
||||
public static final int K_F5 = 0xffc2;
|
||||
public static final int K_F6 = 0xffc3;
|
||||
public static final int K_F7 = 0xffc4;
|
||||
public static final int K_F8 = 0xffc5;
|
||||
public static final int K_F9 = 0xffc6;
|
||||
public static final int K_F10 = 0xffc7;
|
||||
public static final int K_F11 = 0xffc8;
|
||||
public static final int K_F12 = 0xffc9;
|
||||
public static final int K_INSERT = 0xff63;
|
||||
public static final int K_DELETE = 0xffff;
|
||||
public static final int K_HOME = 0xff50;
|
||||
public static final int K_END = 0xff57;
|
||||
public static final int K_PAGE_DOWN = 0xff56;
|
||||
public static final int K_PAGE_UP = 0xff55;
|
||||
public static final int K_DOWN = 0xff54;
|
||||
public static final int K_RIGHT = 0xff53;
|
||||
public static final int K_UP = 0xff52;
|
||||
public static final int K_LEFT = 0xff51;
|
||||
public static final int K_ESCAPE = 0xff1b;
|
||||
public static final int K_ENTER = 0xff0d;
|
||||
public static final int K_TAB = 0xff09;
|
||||
public static final int K_BACK_SPACE = 0xff08;
|
||||
public static final int K_ALT_LEFT = 0xffe9;
|
||||
public static final int K_META_LEFT = 0xffe7;
|
||||
public static final int K_SHIFT_LEFT = 0xffe1;
|
||||
public static final int K_CTRL_LEFT = 0xffe3;
|
||||
public static final int K_SUPER_LEFT = 0xffeb;
|
||||
public static final int K_HYPER_LEFT = 0xffed;
|
||||
|
||||
public static final int K_KP_SPACE = 0xFF80;
|
||||
public static final int K_KP_TAB = 0xFF89;
|
||||
public static final int K_KP_ENTER = 0xFF8D;
|
||||
public static final int K_KP_F1 = 0xFF91;
|
||||
public static final int K_KP_F2 = 0xFF92;
|
||||
public static final int K_KP_F3 = 0xFF93;
|
||||
public static final int K_KP_F4 = 0xFF94;
|
||||
public static final int K_KP_HOME = 0xFF95;
|
||||
public static final int K_KP_LEFT = 0xFF96;
|
||||
public static final int K_KP_UP = 0xFF97;
|
||||
public static final int K_KP_RIGHT = 0xFF98;
|
||||
public static final int K_KP_DOWN = 0xFF99;
|
||||
public static final int K_KP_PRIOR = 0xFF9A;
|
||||
public static final int K_KP_PAGE_UP = 0xFF9A;
|
||||
public static final int K_KP_NEXT = 0xFF9B;
|
||||
public static final int K_KP_PAGE_DOWN = 0xFF9B;
|
||||
public static final int K_KP_END = 0xFF9C;
|
||||
public static final int K_KP_BEGIN = 0xFF9D;
|
||||
public static final int K_KP_INSERT = 0xFF9E;
|
||||
public static final int K_KP_DELETE = 0xFF9F;
|
||||
public static final int K_KP_EQUAL = 0xFFBD;
|
||||
|
||||
public static final int K_KP_MULTIPLY = 0xFFAA;
|
||||
public static final int K_KP_ADD = 0xFFAB;
|
||||
public static final int K_KP_SEPARATOR = 0xFFAC; /* separator, often comma */
|
||||
public static final int K_KP_SUBTRACT = 0xFFAD;
|
||||
public static final int K_KP_DECIMAL = 0xFFAE;
|
||||
public static final int K_KP_DIVIDE = 0xFFAF;
|
||||
|
||||
public static final int K_KP_0 = 0xFFB0;
|
||||
public static final int K_KP_1 = 0xFFB1;
|
||||
public static final int K_KP_2 = 0xFFB2;
|
||||
public static final int K_KP_3 = 0xFFB3;
|
||||
public static final int K_KP_4 = 0xFFB4;
|
||||
public static final int K_KP_5 = 0xFFB5;
|
||||
public static final int K_KP_6 = 0xFFB6;
|
||||
public static final int K_KP_7 = 0xFFB7;
|
||||
public static final int K_KP_8 = 0xFFB8;
|
||||
public static final int K_KP_9 = 0xFFB9;
|
||||
|
||||
private static Map<Integer, Integer> keyMap = toMap(new int[][] {
|
||||
// X Unicode
|
||||
{ 0x01a1, 0x0104 }, /* Aogonek LATIN CAPITAL LETTER A WITH OGONEK */
|
||||
{ 0x01a2, 0x02d8 }, /* breve BREVE */
|
||||
{ 0x01a3, 0x0141 }, /* Lstroke LATIN CAPITAL LETTER L WITH STROKE */
|
||||
{ 0x01a5, 0x013d }, /* Lcaron LATIN CAPITAL LETTER L WITH CARON */
|
||||
{ 0x01a6, 0x015a }, /* Sacute LATIN CAPITAL LETTER S WITH ACUTE */
|
||||
{ 0x01a9, 0x0160 }, /* Scaron LATIN CAPITAL LETTER S WITH CARON */
|
||||
{ 0x01aa, 0x015e }, /* Scedilla LATIN CAPITAL LETTER S WITH CEDILLA */
|
||||
{ 0x01ab, 0x0164 }, /* Tcaron LATIN CAPITAL LETTER T WITH CARON */
|
||||
{ 0x01ac, 0x0179 }, /* Zacute LATIN CAPITAL LETTER Z WITH ACUTE */
|
||||
{ 0x01ae, 0x017d }, /* Zcaron LATIN CAPITAL LETTER Z WITH CARON */
|
||||
{ 0x01af, 0x017b }, /* Zabovedot LATIN CAPITAL LETTER Z WITH DOT ABOVE */
|
||||
{ 0x01b1, 0x0105 }, /* aogonek LATIN SMALL LETTER A WITH OGONEK */
|
||||
{ 0x01b2, 0x02db }, /* ogonek OGONEK */
|
||||
{ 0x01b3, 0x0142 }, /* lstroke LATIN SMALL LETTER L WITH STROKE */
|
||||
{ 0x01b5, 0x013e }, /* lcaron LATIN SMALL LETTER L WITH CARON */
|
||||
{ 0x01b6, 0x015b }, /* sacute LATIN SMALL LETTER S WITH ACUTE */
|
||||
{ 0x01b7, 0x02c7 }, /* caron CARON */
|
||||
{ 0x01b9, 0x0161 }, /* scaron LATIN SMALL LETTER S WITH CARON */
|
||||
{ 0x01ba, 0x015f }, /* scedilla LATIN SMALL LETTER S WITH CEDILLA */
|
||||
{ 0x01bb, 0x0165 }, /* tcaron LATIN SMALL LETTER T WITH CARON */
|
||||
{ 0x01bc, 0x017a }, /* zacute LATIN SMALL LETTER Z WITH ACUTE */
|
||||
{ 0x01bd, 0x02dd }, /* doubleacute DOUBLE ACUTE ACCENT */
|
||||
{ 0x01be, 0x017e }, /* zcaron LATIN SMALL LETTER Z WITH CARON */
|
||||
{ 0x01bf, 0x017c }, /* zabovedot LATIN SMALL LETTER Z WITH DOT ABOVE */
|
||||
{ 0x01c0, 0x0154 }, /* Racute LATIN CAPITAL LETTER R WITH ACUTE */
|
||||
{ 0x01c3, 0x0102 }, /* Abreve LATIN CAPITAL LETTER A WITH BREVE */
|
||||
{ 0x01c5, 0x0139 }, /* Lacute LATIN CAPITAL LETTER L WITH ACUTE */
|
||||
{ 0x01c6, 0x0106 }, /* Cacute LATIN CAPITAL LETTER C WITH ACUTE */
|
||||
{ 0x01c8, 0x010c }, /* Ccaron LATIN CAPITAL LETTER C WITH CARON */
|
||||
{ 0x01ca, 0x0118 }, /* Eogonek LATIN CAPITAL LETTER E WITH OGONEK */
|
||||
{ 0x01cc, 0x011a }, /* Ecaron LATIN CAPITAL LETTER E WITH CARON */
|
||||
{ 0x01cf, 0x010e }, /* Dcaron LATIN CAPITAL LETTER D WITH CARON */
|
||||
{ 0x01d0, 0x0110 }, /* Dstroke LATIN CAPITAL LETTER D WITH STROKE */
|
||||
{ 0x01d1, 0x0143 }, /* Nacute LATIN CAPITAL LETTER N WITH ACUTE */
|
||||
{ 0x01d2, 0x0147 }, /* Ncaron LATIN CAPITAL LETTER N WITH CARON */
|
||||
{ 0x01d5, 0x0150 }, /* Odoubleacute LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */
|
||||
{ 0x01d8, 0x0158 }, /* Rcaron LATIN CAPITAL LETTER R WITH CARON */
|
||||
{ 0x01d9, 0x016e }, /* Uring LATIN CAPITAL LETTER U WITH RING ABOVE */
|
||||
{ 0x01db, 0x0170 }, /* Udoubleacute LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */
|
||||
{ 0x01de, 0x0162 }, /* Tcedilla LATIN CAPITAL LETTER T WITH CEDILLA */
|
||||
{ 0x01e0, 0x0155 }, /* racute LATIN SMALL LETTER R WITH ACUTE */
|
||||
{ 0x01e3, 0x0103 }, /* abreve LATIN SMALL LETTER A WITH BREVE */
|
||||
{ 0x01e5, 0x013a }, /* lacute LATIN SMALL LETTER L WITH ACUTE */
|
||||
{ 0x01e6, 0x0107 }, /* cacute LATIN SMALL LETTER C WITH ACUTE */
|
||||
{ 0x01e8, 0x010d }, /* ccaron LATIN SMALL LETTER C WITH CARON */
|
||||
{ 0x01ea, 0x0119 }, /* eogonek LATIN SMALL LETTER E WITH OGONEK */
|
||||
{ 0x01ec, 0x011b }, /* ecaron LATIN SMALL LETTER E WITH CARON */
|
||||
{ 0x01ef, 0x010f }, /* dcaron LATIN SMALL LETTER D WITH CARON */
|
||||
{ 0x01f0, 0x0111 }, /* dstroke LATIN SMALL LETTER D WITH STROKE */
|
||||
{ 0x01f1, 0x0144 }, /* nacute LATIN SMALL LETTER N WITH ACUTE */
|
||||
{ 0x01f2, 0x0148 }, /* ncaron LATIN SMALL LETTER N WITH CARON */
|
||||
{ 0x01f5, 0x0151 }, /* odoubleacute LATIN SMALL LETTER O WITH DOUBLE ACUTE */
|
||||
{ 0x01f8, 0x0159 }, /* rcaron LATIN SMALL LETTER R WITH CARON */
|
||||
{ 0x01f9, 0x016f }, /* uring LATIN SMALL LETTER U WITH RING ABOVE */
|
||||
{ 0x01fb, 0x0171 }, /* udoubleacute LATIN SMALL LETTER U WITH DOUBLE ACUTE */
|
||||
{ 0x01fe, 0x0163 }, /* tcedilla LATIN SMALL LETTER T WITH CEDILLA */
|
||||
{ 0x01ff, 0x02d9 }, /* abovedot DOT ABOVE */
|
||||
{ 0x02a1, 0x0126 }, /* Hstroke LATIN CAPITAL LETTER H WITH STROKE */
|
||||
{ 0x02a6, 0x0124 }, /* Hcircumflex LATIN CAPITAL LETTER H WITH CIRCUMFLEX */
|
||||
{ 0x02a9, 0x0130 }, /* Iabovedot LATIN CAPITAL LETTER I WITH DOT ABOVE */
|
||||
{ 0x02ab, 0x011e }, /* Gbreve LATIN CAPITAL LETTER G WITH BREVE */
|
||||
{ 0x02ac, 0x0134 }, /* Jcircumflex LATIN CAPITAL LETTER J WITH CIRCUMFLEX */
|
||||
{ 0x02b1, 0x0127 }, /* hstroke LATIN SMALL LETTER H WITH STROKE */
|
||||
{ 0x02b6, 0x0125 }, /* hcircumflex LATIN SMALL LETTER H WITH CIRCUMFLEX */
|
||||
{ 0x02b9, 0x0131 }, /* idotless LATIN SMALL LETTER DOTLESS I */
|
||||
{ 0x02bb, 0x011f }, /* gbreve LATIN SMALL LETTER G WITH BREVE */
|
||||
{ 0x02bc, 0x0135 }, /* jcircumflex LATIN SMALL LETTER J WITH CIRCUMFLEX */
|
||||
{ 0x02c5, 0x010a }, /* Cabovedot LATIN CAPITAL LETTER C WITH DOT ABOVE */
|
||||
{ 0x02c6, 0x0108 }, /* Ccircumflex LATIN CAPITAL LETTER C WITH CIRCUMFLEX */
|
||||
{ 0x02d5, 0x0120 }, /* Gabovedot LATIN CAPITAL LETTER G WITH DOT ABOVE */
|
||||
{ 0x02d8, 0x011c }, /* Gcircumflex LATIN CAPITAL LETTER G WITH CIRCUMFLEX */
|
||||
{ 0x02dd, 0x016c }, /* Ubreve LATIN CAPITAL LETTER U WITH BREVE */
|
||||
{ 0x02de, 0x015c }, /* Scircumflex LATIN CAPITAL LETTER S WITH CIRCUMFLEX */
|
||||
{ 0x02e5, 0x010b }, /* cabovedot LATIN SMALL LETTER C WITH DOT ABOVE */
|
||||
{ 0x02e6, 0x0109 }, /* ccircumflex LATIN SMALL LETTER C WITH CIRCUMFLEX */
|
||||
{ 0x02f5, 0x0121 }, /* gabovedot LATIN SMALL LETTER G WITH DOT ABOVE */
|
||||
{ 0x02f8, 0x011d }, /* gcircumflex LATIN SMALL LETTER G WITH CIRCUMFLEX */
|
||||
{ 0x02fd, 0x016d }, /* ubreve LATIN SMALL LETTER U WITH BREVE */
|
||||
{ 0x02fe, 0x015d }, /* scircumflex LATIN SMALL LETTER S WITH CIRCUMFLEX */
|
||||
{ 0x03a2, 0x0138 }, /* kra LATIN SMALL LETTER KRA */
|
||||
{ 0x03a3, 0x0156 }, /* Rcedilla LATIN CAPITAL LETTER R WITH CEDILLA */
|
||||
{ 0x03a5, 0x0128 }, /* Itilde LATIN CAPITAL LETTER I WITH TILDE */
|
||||
{ 0x03a6, 0x013b }, /* Lcedilla LATIN CAPITAL LETTER L WITH CEDILLA */
|
||||
{ 0x03aa, 0x0112 }, /* Emacron LATIN CAPITAL LETTER E WITH MACRON */
|
||||
{ 0x03ab, 0x0122 }, /* Gcedilla LATIN CAPITAL LETTER G WITH CEDILLA */
|
||||
{ 0x03ac, 0x0166 }, /* Tslash LATIN CAPITAL LETTER T WITH STROKE */
|
||||
{ 0x03b3, 0x0157 }, /* rcedilla LATIN SMALL LETTER R WITH CEDILLA */
|
||||
{ 0x03b5, 0x0129 }, /* itilde LATIN SMALL LETTER I WITH TILDE */
|
||||
{ 0x03b6, 0x013c }, /* lcedilla LATIN SMALL LETTER L WITH CEDILLA */
|
||||
{ 0x03ba, 0x0113 }, /* emacron LATIN SMALL LETTER E WITH MACRON */
|
||||
{ 0x03bb, 0x0123 }, /* gcedilla LATIN SMALL LETTER G WITH CEDILLA */
|
||||
{ 0x03bc, 0x0167 }, /* tslash LATIN SMALL LETTER T WITH STROKE */
|
||||
{ 0x03bd, 0x014a }, /* ENG LATIN CAPITAL LETTER ENG */
|
||||
{ 0x03bf, 0x014b }, /* eng LATIN SMALL LETTER ENG */
|
||||
{ 0x03c0, 0x0100 }, /* Amacron LATIN CAPITAL LETTER A WITH MACRON */
|
||||
{ 0x03c7, 0x012e }, /* Iogonek LATIN CAPITAL LETTER I WITH OGONEK */
|
||||
{ 0x03cc, 0x0116 }, /* Eabovedot LATIN CAPITAL LETTER E WITH DOT ABOVE */
|
||||
{ 0x03cf, 0x012a }, /* Imacron LATIN CAPITAL LETTER I WITH MACRON */
|
||||
{ 0x03d1, 0x0145 }, /* Ncedilla LATIN CAPITAL LETTER N WITH CEDILLA */
|
||||
{ 0x03d2, 0x014c }, /* Omacron LATIN CAPITAL LETTER O WITH MACRON */
|
||||
{ 0x03d3, 0x0136 }, /* Kcedilla LATIN CAPITAL LETTER K WITH CEDILLA */
|
||||
{ 0x03d9, 0x0172 }, /* Uogonek LATIN CAPITAL LETTER U WITH OGONEK */
|
||||
{ 0x03dd, 0x0168 }, /* Utilde LATIN CAPITAL LETTER U WITH TILDE */
|
||||
{ 0x03de, 0x016a }, /* Umacron LATIN CAPITAL LETTER U WITH MACRON */
|
||||
{ 0x03e0, 0x0101 }, /* amacron LATIN SMALL LETTER A WITH MACRON */
|
||||
{ 0x03e7, 0x012f }, /* iogonek LATIN SMALL LETTER I WITH OGONEK */
|
||||
{ 0x03ec, 0x0117 }, /* eabovedot LATIN SMALL LETTER E WITH DOT ABOVE */
|
||||
{ 0x03ef, 0x012b }, /* imacron LATIN SMALL LETTER I WITH MACRON */
|
||||
{ 0x03f1, 0x0146 }, /* ncedilla LATIN SMALL LETTER N WITH CEDILLA */
|
||||
{ 0x03f2, 0x014d }, /* omacron LATIN SMALL LETTER O WITH MACRON */
|
||||
{ 0x03f3, 0x0137 }, /* kcedilla LATIN SMALL LETTER K WITH CEDILLA */
|
||||
{ 0x03f9, 0x0173 }, /* uogonek LATIN SMALL LETTER U WITH OGONEK */
|
||||
{ 0x03fd, 0x0169 }, /* utilde LATIN SMALL LETTER U WITH TILDE */
|
||||
{ 0x03fe, 0x016b }, /* umacron LATIN SMALL LETTER U WITH MACRON */
|
||||
{ 0x047e, 0x203e }, /* overline OVERLINE */
|
||||
{ 0x04a1, 0x3002 }, /* kana_fullstop IDEOGRAPHIC FULL STOP */
|
||||
{ 0x04a2, 0x300c }, /* kana_openingbracket LEFT CORNER BRACKET */
|
||||
{ 0x04a3, 0x300d }, /* kana_closingbracket RIGHT CORNER BRACKET */
|
||||
{ 0x04a4, 0x3001 }, /* kana_comma IDEOGRAPHIC COMMA */
|
||||
{ 0x04a5, 0x30fb }, /* kana_conjunctive KATAKANA MIDDLE DOT */
|
||||
{ 0x04a6, 0x30f2 }, /* kana_WO KATAKANA LETTER WO */
|
||||
{ 0x04a7, 0x30a1 }, /* kana_a KATAKANA LETTER SMALL A */
|
||||
{ 0x04a8, 0x30a3 }, /* kana_i KATAKANA LETTER SMALL I */
|
||||
{ 0x04a9, 0x30a5 }, /* kana_u KATAKANA LETTER SMALL U */
|
||||
{ 0x04aa, 0x30a7 }, /* kana_e KATAKANA LETTER SMALL E */
|
||||
{ 0x04ab, 0x30a9 }, /* kana_o KATAKANA LETTER SMALL O */
|
||||
{ 0x04ac, 0x30e3 }, /* kana_ya KATAKANA LETTER SMALL YA */
|
||||
{ 0x04ad, 0x30e5 }, /* kana_yu KATAKANA LETTER SMALL YU */
|
||||
{ 0x04ae, 0x30e7 }, /* kana_yo KATAKANA LETTER SMALL YO */
|
||||
{ 0x04af, 0x30c3 }, /* kana_tsu KATAKANA LETTER SMALL TU */
|
||||
{ 0x04b0, 0x30fc }, /* prolongedsound KATAKANA-HIRAGANA PROLONGED SOUND MARK */
|
||||
{ 0x04b1, 0x30a2 }, /* kana_A KATAKANA LETTER A */
|
||||
{ 0x04b2, 0x30a4 }, /* kana_I KATAKANA LETTER I */
|
||||
{ 0x04b3, 0x30a6 }, /* kana_U KATAKANA LETTER U */
|
||||
{ 0x04b4, 0x30a8 }, /* kana_E KATAKANA LETTER E */
|
||||
{ 0x04b5, 0x30aa }, /* kana_O KATAKANA LETTER O */
|
||||
{ 0x04b6, 0x30ab }, /* kana_KA KATAKANA LETTER KA */
|
||||
{ 0x04b7, 0x30ad }, /* kana_KI KATAKANA LETTER KI */
|
||||
{ 0x04b8, 0x30af }, /* kana_KU KATAKANA LETTER KU */
|
||||
{ 0x04b9, 0x30b1 }, /* kana_KE KATAKANA LETTER KE */
|
||||
{ 0x04ba, 0x30b3 }, /* kana_KO KATAKANA LETTER KO */
|
||||
{ 0x04bb, 0x30b5 }, /* kana_SA KATAKANA LETTER SA */
|
||||
{ 0x04bc, 0x30b7 }, /* kana_SHI KATAKANA LETTER SI */
|
||||
{ 0x04bd, 0x30b9 }, /* kana_SU KATAKANA LETTER SU */
|
||||
{ 0x04be, 0x30bb }, /* kana_SE KATAKANA LETTER SE */
|
||||
{ 0x04bf, 0x30bd }, /* kana_SO KATAKANA LETTER SO */
|
||||
{ 0x04c0, 0x30bf }, /* kana_TA KATAKANA LETTER TA */
|
||||
{ 0x04c1, 0x30c1 }, /* kana_CHI KATAKANA LETTER TI */
|
||||
{ 0x04c2, 0x30c4 }, /* kana_TSU KATAKANA LETTER TU */
|
||||
{ 0x04c3, 0x30c6 }, /* kana_TE KATAKANA LETTER TE */
|
||||
{ 0x04c4, 0x30c8 }, /* kana_TO KATAKANA LETTER TO */
|
||||
{ 0x04c5, 0x30ca }, /* kana_NA KATAKANA LETTER NA */
|
||||
{ 0x04c6, 0x30cb }, /* kana_NI KATAKANA LETTER NI */
|
||||
{ 0x04c7, 0x30cc }, /* kana_NU KATAKANA LETTER NU */
|
||||
{ 0x04c8, 0x30cd }, /* kana_NE KATAKANA LETTER NE */
|
||||
{ 0x04c9, 0x30ce }, /* kana_NO KATAKANA LETTER NO */
|
||||
{ 0x04ca, 0x30cf }, /* kana_HA KATAKANA LETTER HA */
|
||||
{ 0x04cb, 0x30d2 }, /* kana_HI KATAKANA LETTER HI */
|
||||
{ 0x04cc, 0x30d5 }, /* kana_FU KATAKANA LETTER HU */
|
||||
{ 0x04cd, 0x30d8 }, /* kana_HE KATAKANA LETTER HE */
|
||||
{ 0x04ce, 0x30db }, /* kana_HO KATAKANA LETTER HO */
|
||||
{ 0x04cf, 0x30de }, /* kana_MA KATAKANA LETTER MA */
|
||||
{ 0x04d0, 0x30df }, /* kana_MI KATAKANA LETTER MI */
|
||||
{ 0x04d1, 0x30e0 }, /* kana_MU KATAKANA LETTER MU */
|
||||
{ 0x04d2, 0x30e1 }, /* kana_ME KATAKANA LETTER ME */
|
||||
{ 0x04d3, 0x30e2 }, /* kana_MO KATAKANA LETTER MO */
|
||||
{ 0x04d4, 0x30e4 }, /* kana_YA KATAKANA LETTER YA */
|
||||
{ 0x04d5, 0x30e6 }, /* kana_YU KATAKANA LETTER YU */
|
||||
{ 0x04d6, 0x30e8 }, /* kana_YO KATAKANA LETTER YO */
|
||||
{ 0x04d7, 0x30e9 }, /* kana_RA KATAKANA LETTER RA */
|
||||
{ 0x04d8, 0x30ea }, /* kana_RI KATAKANA LETTER RI */
|
||||
{ 0x04d9, 0x30eb }, /* kana_RU KATAKANA LETTER RU */
|
||||
{ 0x04da, 0x30ec }, /* kana_RE KATAKANA LETTER RE */
|
||||
{ 0x04db, 0x30ed }, /* kana_RO KATAKANA LETTER RO */
|
||||
{ 0x04dc, 0x30ef }, /* kana_WA KATAKANA LETTER WA */
|
||||
{ 0x04dd, 0x30f3 }, /* kana_N KATAKANA LETTER N */
|
||||
{ 0x04de, 0x309b }, /* voicedsound KATAKANA-HIRAGANA VOICED SOUND MARK */
|
||||
{ 0x04df, 0x309c }, /* semivoicedsound KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
|
||||
{ 0x05ac, 0x060c }, /* Arabic_comma ARABIC COMMA */
|
||||
{ 0x05bb, 0x061b }, /* Arabic_semicolon ARABIC SEMICOLON */
|
||||
{ 0x05bf, 0x061f }, /* Arabic_question_mark ARABIC QUESTION MARK */
|
||||
{ 0x05c1, 0x0621 }, /* Arabic_hamza ARABIC LETTER HAMZA */
|
||||
{ 0x05c2, 0x0622 }, /* Arabic_maddaonalef ARABIC LETTER ALEF WITH MADDA ABOVE */
|
||||
{ 0x05c3, 0x0623 }, /* Arabic_hamzaonalef ARABIC LETTER ALEF WITH HAMZA ABOVE */
|
||||
{ 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ARABIC LETTER WAW WITH HAMZA ABOVE */
|
||||
{ 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef ARABIC LETTER ALEF WITH HAMZA BELOW */
|
||||
{ 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ARABIC LETTER YEH WITH HAMZA ABOVE */
|
||||
{ 0x05c7, 0x0627 }, /* Arabic_alef ARABIC LETTER ALEF */
|
||||
{ 0x05c8, 0x0628 }, /* Arabic_beh ARABIC LETTER BEH */
|
||||
{ 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ARABIC LETTER TEH MARBUTA */
|
||||
{ 0x05ca, 0x062a }, /* Arabic_teh ARABIC LETTER TEH */
|
||||
{ 0x05cb, 0x062b }, /* Arabic_theh ARABIC LETTER THEH */
|
||||
{ 0x05cc, 0x062c }, /* Arabic_jeem ARABIC LETTER JEEM */
|
||||
{ 0x05cd, 0x062d }, /* Arabic_hah ARABIC LETTER HAH */
|
||||
{ 0x05ce, 0x062e }, /* Arabic_khah ARABIC LETTER KHAH */
|
||||
{ 0x05cf, 0x062f }, /* Arabic_dal ARABIC LETTER DAL */
|
||||
{ 0x05d0, 0x0630 }, /* Arabic_thal ARABIC LETTER THAL */
|
||||
{ 0x05d1, 0x0631 }, /* Arabic_ra ARABIC LETTER REH */
|
||||
{ 0x05d2, 0x0632 }, /* Arabic_zain ARABIC LETTER ZAIN */
|
||||
{ 0x05d3, 0x0633 }, /* Arabic_seen ARABIC LETTER SEEN */
|
||||
{ 0x05d4, 0x0634 }, /* Arabic_sheen ARABIC LETTER SHEEN */
|
||||
{ 0x05d5, 0x0635 }, /* Arabic_sad ARABIC LETTER SAD */
|
||||
{ 0x05d6, 0x0636 }, /* Arabic_dad ARABIC LETTER DAD */
|
||||
{ 0x05d7, 0x0637 }, /* Arabic_tah ARABIC LETTER TAH */
|
||||
{ 0x05d8, 0x0638 }, /* Arabic_zah ARABIC LETTER ZAH */
|
||||
{ 0x05d9, 0x0639 }, /* Arabic_ain ARABIC LETTER AIN */
|
||||
{ 0x05da, 0x063a }, /* Arabic_ghain ARABIC LETTER GHAIN */
|
||||
{ 0x05e0, 0x0640 }, /* Arabic_tatweel ARABIC TATWEEL */
|
||||
{ 0x05e1, 0x0641 }, /* Arabic_feh ARABIC LETTER FEH */
|
||||
{ 0x05e2, 0x0642 }, /* Arabic_qaf ARABIC LETTER QAF */
|
||||
{ 0x05e3, 0x0643 }, /* Arabic_kaf ARABIC LETTER KAF */
|
||||
{ 0x05e4, 0x0644 }, /* Arabic_lam ARABIC LETTER LAM */
|
||||
{ 0x05e5, 0x0645 }, /* Arabic_meem ARABIC LETTER MEEM */
|
||||
{ 0x05e6, 0x0646 }, /* Arabic_noon ARABIC LETTER NOON */
|
||||
{ 0x05e7, 0x0647 }, /* Arabic_ha ARABIC LETTER HEH */
|
||||
{ 0x05e8, 0x0648 }, /* Arabic_waw ARABIC LETTER WAW */
|
||||
{ 0x05e9, 0x0649 }, /* Arabic_alefmaksura ARABIC LETTER ALEF MAKSURA */
|
||||
{ 0x05ea, 0x064a }, /* Arabic_yeh ARABIC LETTER YEH */
|
||||
{ 0x05eb, 0x064b }, /* Arabic_fathatan ARABIC FATHATAN */
|
||||
{ 0x05ec, 0x064c }, /* Arabic_dammatan ARABIC DAMMATAN */
|
||||
{ 0x05ed, 0x064d }, /* Arabic_kasratan ARABIC KASRATAN */
|
||||
{ 0x05ee, 0x064e }, /* Arabic_fatha ARABIC FATHA */
|
||||
{ 0x05ef, 0x064f }, /* Arabic_damma ARABIC DAMMA */
|
||||
{ 0x05f0, 0x0650 }, /* Arabic_kasra ARABIC KASRA */
|
||||
{ 0x05f1, 0x0651 }, /* Arabic_shadda ARABIC SHADDA */
|
||||
{ 0x05f2, 0x0652 }, /* Arabic_sukun ARABIC SUKUN */
|
||||
{ 0x06a1, 0x0452 }, /* Serbian_dje CYRILLIC SMALL LETTER DJE */
|
||||
{ 0x06a2, 0x0453 }, /* Macedonia_gje CYRILLIC SMALL LETTER GJE */
|
||||
{ 0x06a3, 0x0451 }, /* Cyrillic_io CYRILLIC SMALL LETTER IO */
|
||||
{ 0x06a4, 0x0454 }, /* Ukrainian_ie CYRILLIC SMALL LETTER UKRAINIAN IE */
|
||||
{ 0x06a5, 0x0455 }, /* Macedonia_dse CYRILLIC SMALL LETTER DZE */
|
||||
{ 0x06a6, 0x0456 }, /* Ukrainian_i CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */
|
||||
{ 0x06a7, 0x0457 }, /* Ukrainian_yi CYRILLIC SMALL LETTER YI */
|
||||
{ 0x06a8, 0x0458 }, /* Cyrillic_je CYRILLIC SMALL LETTER JE */
|
||||
{ 0x06a9, 0x0459 }, /* Cyrillic_lje CYRILLIC SMALL LETTER LJE */
|
||||
{ 0x06aa, 0x045a }, /* Cyrillic_nje CYRILLIC SMALL LETTER NJE */
|
||||
{ 0x06ab, 0x045b }, /* Serbian_tshe CYRILLIC SMALL LETTER TSHE */
|
||||
{ 0x06ac, 0x045c }, /* Macedonia_kje CYRILLIC SMALL LETTER KJE */
|
||||
{ 0x06ae, 0x045e }, /* Byelorussian_shortu CYRILLIC SMALL LETTER SHORT U */
|
||||
{ 0x06af, 0x045f }, /* Cyrillic_dzhe CYRILLIC SMALL LETTER DZHE */
|
||||
{ 0x06b0, 0x2116 }, /* numerosign NUMERO SIGN */
|
||||
{ 0x06b1, 0x0402 }, /* Serbian_DJE CYRILLIC CAPITAL LETTER DJE */
|
||||
{ 0x06b2, 0x0403 }, /* Macedonia_GJE CYRILLIC CAPITAL LETTER GJE */
|
||||
{ 0x06b3, 0x0401 }, /* Cyrillic_IO CYRILLIC CAPITAL LETTER IO */
|
||||
{ 0x06b4, 0x0404 }, /* Ukrainian_IE CYRILLIC CAPITAL LETTER UKRAINIAN IE */
|
||||
{ 0x06b5, 0x0405 }, /* Macedonia_DSE CYRILLIC CAPITAL LETTER DZE */
|
||||
{ 0x06b6, 0x0406 }, /* Ukrainian_I CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */
|
||||
{ 0x06b7, 0x0407 }, /* Ukrainian_YI CYRILLIC CAPITAL LETTER YI */
|
||||
{ 0x06b8, 0x0408 }, /* Cyrillic_JE CYRILLIC CAPITAL LETTER JE */
|
||||
{ 0x06b9, 0x0409 }, /* Cyrillic_LJE CYRILLIC CAPITAL LETTER LJE */
|
||||
{ 0x06ba, 0x040a }, /* Cyrillic_NJE CYRILLIC CAPITAL LETTER NJE */
|
||||
{ 0x06bb, 0x040b }, /* Serbian_TSHE CYRILLIC CAPITAL LETTER TSHE */
|
||||
{ 0x06bc, 0x040c }, /* Macedonia_KJE CYRILLIC CAPITAL LETTER KJE */
|
||||
{ 0x06be, 0x040e }, /* Byelorussian_SHORTU CYRILLIC CAPITAL LETTER SHORT U */
|
||||
{ 0x06bf, 0x040f }, /* Cyrillic_DZHE CYRILLIC CAPITAL LETTER DZHE */
|
||||
{ 0x06c0, 0x044e }, /* Cyrillic_yu CYRILLIC SMALL LETTER YU */
|
||||
{ 0x06c1, 0x0430 }, /* Cyrillic_a CYRILLIC SMALL LETTER A */
|
||||
{ 0x06c2, 0x0431 }, /* Cyrillic_be CYRILLIC SMALL LETTER BE */
|
||||
{ 0x06c3, 0x0446 }, /* Cyrillic_tse CYRILLIC SMALL LETTER TSE */
|
||||
{ 0x06c4, 0x0434 }, /* Cyrillic_de CYRILLIC SMALL LETTER DE */
|
||||
{ 0x06c5, 0x0435 }, /* Cyrillic_ie CYRILLIC SMALL LETTER IE */
|
||||
{ 0x06c6, 0x0444 }, /* Cyrillic_ef CYRILLIC SMALL LETTER EF */
|
||||
{ 0x06c7, 0x0433 }, /* Cyrillic_ghe CYRILLIC SMALL LETTER GHE */
|
||||
{ 0x06c8, 0x0445 }, /* Cyrillic_ha CYRILLIC SMALL LETTER HA */
|
||||
{ 0x06c9, 0x0438 }, /* Cyrillic_i CYRILLIC SMALL LETTER I */
|
||||
{ 0x06ca, 0x0439 }, /* Cyrillic_shorti CYRILLIC SMALL LETTER SHORT I */
|
||||
{ 0x06cb, 0x043a }, /* Cyrillic_ka CYRILLIC SMALL LETTER KA */
|
||||
{ 0x06cc, 0x043b }, /* Cyrillic_el CYRILLIC SMALL LETTER EL */
|
||||
{ 0x06cd, 0x043c }, /* Cyrillic_em CYRILLIC SMALL LETTER EM */
|
||||
{ 0x06ce, 0x043d }, /* Cyrillic_en CYRILLIC SMALL LETTER EN */
|
||||
{ 0x06cf, 0x043e }, /* Cyrillic_o CYRILLIC SMALL LETTER O */
|
||||
{ 0x06d0, 0x043f }, /* Cyrillic_pe CYRILLIC SMALL LETTER PE */
|
||||
{ 0x06d1, 0x044f }, /* Cyrillic_ya CYRILLIC SMALL LETTER YA */
|
||||
{ 0x06d2, 0x0440 }, /* Cyrillic_er CYRILLIC SMALL LETTER ER */
|
||||
{ 0x06d3, 0x0441 }, /* Cyrillic_es CYRILLIC SMALL LETTER ES */
|
||||
{ 0x06d4, 0x0442 }, /* Cyrillic_te CYRILLIC SMALL LETTER TE */
|
||||
{ 0x06d5, 0x0443 }, /* Cyrillic_u CYRILLIC SMALL LETTER U */
|
||||
{ 0x06d6, 0x0436 }, /* Cyrillic_zhe CYRILLIC SMALL LETTER ZHE */
|
||||
{ 0x06d7, 0x0432 }, /* Cyrillic_ve CYRILLIC SMALL LETTER VE */
|
||||
{ 0x06d8, 0x044c }, /* Cyrillic_softsign CYRILLIC SMALL LETTER SOFT SIGN */
|
||||
{ 0x06d9, 0x044b }, /* Cyrillic_yeru CYRILLIC SMALL LETTER YERU */
|
||||
{ 0x06da, 0x0437 }, /* Cyrillic_ze CYRILLIC SMALL LETTER ZE */
|
||||
{ 0x06db, 0x0448 }, /* Cyrillic_sha CYRILLIC SMALL LETTER SHA */
|
||||
{ 0x06dc, 0x044d }, /* Cyrillic_e CYRILLIC SMALL LETTER E */
|
||||
{ 0x06dd, 0x0449 }, /* Cyrillic_shcha CYRILLIC SMALL LETTER SHCHA */
|
||||
{ 0x06de, 0x0447 }, /* Cyrillic_che CYRILLIC SMALL LETTER CHE */
|
||||
{ 0x06df, 0x044a }, /* Cyrillic_hardsign CYRILLIC SMALL LETTER HARD SIGN */
|
||||
{ 0x06e0, 0x042e }, /* Cyrillic_YU CYRILLIC CAPITAL LETTER YU */
|
||||
{ 0x06e1, 0x0410 }, /* Cyrillic_A CYRILLIC CAPITAL LETTER A */
|
||||
{ 0x06e2, 0x0411 }, /* Cyrillic_BE CYRILLIC CAPITAL LETTER BE */
|
||||
{ 0x06e3, 0x0426 }, /* Cyrillic_TSE CYRILLIC CAPITAL LETTER TSE */
|
||||
{ 0x06e4, 0x0414 }, /* Cyrillic_DE CYRILLIC CAPITAL LETTER DE */
|
||||
{ 0x06e5, 0x0415 }, /* Cyrillic_IE CYRILLIC CAPITAL LETTER IE */
|
||||
{ 0x06e6, 0x0424 }, /* Cyrillic_EF CYRILLIC CAPITAL LETTER EF */
|
||||
{ 0x06e7, 0x0413 }, /* Cyrillic_GHE CYRILLIC CAPITAL LETTER GHE */
|
||||
{ 0x06e8, 0x0425 }, /* Cyrillic_HA CYRILLIC CAPITAL LETTER HA */
|
||||
{ 0x06e9, 0x0418 }, /* Cyrillic_I CYRILLIC CAPITAL LETTER I */
|
||||
{ 0x06ea, 0x0419 }, /* Cyrillic_SHORTI CYRILLIC CAPITAL LETTER SHORT I */
|
||||
{ 0x06eb, 0x041a }, /* Cyrillic_KA CYRILLIC CAPITAL LETTER KA */
|
||||
{ 0x06ec, 0x041b }, /* Cyrillic_EL CYRILLIC CAPITAL LETTER EL */
|
||||
{ 0x06ed, 0x041c }, /* Cyrillic_EM CYRILLIC CAPITAL LETTER EM */
|
||||
{ 0x06ee, 0x041d }, /* Cyrillic_EN CYRILLIC CAPITAL LETTER EN */
|
||||
{ 0x06ef, 0x041e }, /* Cyrillic_O CYRILLIC CAPITAL LETTER O */
|
||||
{ 0x06f0, 0x041f }, /* Cyrillic_PE CYRILLIC CAPITAL LETTER PE */
|
||||
{ 0x06f1, 0x042f }, /* Cyrillic_YA CYRILLIC CAPITAL LETTER YA */
|
||||
{ 0x06f2, 0x0420 }, /* Cyrillic_ER CYRILLIC CAPITAL LETTER ER */
|
||||
{ 0x06f3, 0x0421 }, /* Cyrillic_ES CYRILLIC CAPITAL LETTER ES */
|
||||
{ 0x06f4, 0x0422 }, /* Cyrillic_TE CYRILLIC CAPITAL LETTER TE */
|
||||
{ 0x06f5, 0x0423 }, /* Cyrillic_U CYRILLIC CAPITAL LETTER U */
|
||||
{ 0x06f6, 0x0416 }, /* Cyrillic_ZHE CYRILLIC CAPITAL LETTER ZHE */
|
||||
{ 0x06f7, 0x0412 }, /* Cyrillic_VE CYRILLIC CAPITAL LETTER VE */
|
||||
{ 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN CYRILLIC CAPITAL LETTER SOFT SIGN */
|
||||
{ 0x06f9, 0x042b }, /* Cyrillic_YERU CYRILLIC CAPITAL LETTER YERU */
|
||||
{ 0x06fa, 0x0417 }, /* Cyrillic_ZE CYRILLIC CAPITAL LETTER ZE */
|
||||
{ 0x06fb, 0x0428 }, /* Cyrillic_SHA CYRILLIC CAPITAL LETTER SHA */
|
||||
{ 0x06fc, 0x042d }, /* Cyrillic_E CYRILLIC CAPITAL LETTER E */
|
||||
{ 0x06fd, 0x0429 }, /* Cyrillic_SHCHA CYRILLIC CAPITAL LETTER SHCHA */
|
||||
{ 0x06fe, 0x0427 }, /* Cyrillic_CHE CYRILLIC CAPITAL LETTER CHE */
|
||||
{ 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN CYRILLIC CAPITAL LETTER HARD SIGN */
|
||||
{ 0x07a1, 0x0386 }, /* Greek_ALPHAaccent GREEK CAPITAL LETTER ALPHA WITH TONOS */
|
||||
{ 0x07a2, 0x0388 }, /* Greek_EPSILONaccent GREEK CAPITAL LETTER EPSILON WITH TONOS */
|
||||
{ 0x07a3, 0x0389 }, /* Greek_ETAaccent GREEK CAPITAL LETTER ETA WITH TONOS */
|
||||
{ 0x07a4, 0x038a }, /* Greek_IOTAaccent GREEK CAPITAL LETTER IOTA WITH TONOS */
|
||||
{ 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */
|
||||
{ 0x07a7, 0x038c }, /* Greek_OMICRONaccent GREEK CAPITAL LETTER OMICRON WITH TONOS */
|
||||
{ 0x07a8, 0x038e }, /* Greek_UPSILONaccent GREEK CAPITAL LETTER UPSILON WITH TONOS */
|
||||
{ 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */
|
||||
{ 0x07ab, 0x038f }, /* Greek_OMEGAaccent GREEK CAPITAL LETTER OMEGA WITH TONOS */
|
||||
{ 0x07ae, 0x0385 }, /* Greek_accentdieresis GREEK DIALYTIKA TONOS */
|
||||
{ 0x07af, 0x2015 }, /* Greek_horizbar HORIZONTAL BAR */
|
||||
{ 0x07b1, 0x03ac }, /* Greek_alphaaccent GREEK SMALL LETTER ALPHA WITH TONOS */
|
||||
{ 0x07b2, 0x03ad }, /* Greek_epsilonaccent GREEK SMALL LETTER EPSILON WITH TONOS */
|
||||
{ 0x07b3, 0x03ae }, /* Greek_etaaccent GREEK SMALL LETTER ETA WITH TONOS */
|
||||
{ 0x07b4, 0x03af }, /* Greek_iotaaccent GREEK SMALL LETTER IOTA WITH TONOS */
|
||||
{ 0x07b5, 0x03ca }, /* Greek_iotadieresis GREEK SMALL LETTER IOTA WITH DIALYTIKA */
|
||||
{ 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */
|
||||
{ 0x07b7, 0x03cc }, /* Greek_omicronaccent GREEK SMALL LETTER OMICRON WITH TONOS */
|
||||
{ 0x07b8, 0x03cd }, /* Greek_upsilonaccent GREEK SMALL LETTER UPSILON WITH TONOS */
|
||||
{ 0x07b9, 0x03cb }, /* Greek_upsilondieresis GREEK SMALL LETTER UPSILON WITH DIALYTIKA */
|
||||
{ 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */
|
||||
{ 0x07bb, 0x03ce }, /* Greek_omegaaccent GREEK SMALL LETTER OMEGA WITH TONOS */
|
||||
{ 0x07c1, 0x0391 }, /* Greek_ALPHA GREEK CAPITAL LETTER ALPHA */
|
||||
{ 0x07c2, 0x0392 }, /* Greek_BETA GREEK CAPITAL LETTER BETA */
|
||||
{ 0x07c3, 0x0393 }, /* Greek_GAMMA GREEK CAPITAL LETTER GAMMA */
|
||||
{ 0x07c4, 0x0394 }, /* Greek_DELTA GREEK CAPITAL LETTER DELTA */
|
||||
{ 0x07c5, 0x0395 }, /* Greek_EPSILON GREEK CAPITAL LETTER EPSILON */
|
||||
{ 0x07c6, 0x0396 }, /* Greek_ZETA GREEK CAPITAL LETTER ZETA */
|
||||
{ 0x07c7, 0x0397 }, /* Greek_ETA GREEK CAPITAL LETTER ETA */
|
||||
{ 0x07c8, 0x0398 }, /* Greek_THETA GREEK CAPITAL LETTER THETA */
|
||||
{ 0x07c9, 0x0399 }, /* Greek_IOTA GREEK CAPITAL LETTER IOTA */
|
||||
{ 0x07ca, 0x039a }, /* Greek_KAPPA GREEK CAPITAL LETTER KAPPA */
|
||||
{ 0x07cb, 0x039b }, /* Greek_LAMBDA GREEK CAPITAL LETTER LAMDA */
|
||||
{ 0x07cc, 0x039c }, /* Greek_MU GREEK CAPITAL LETTER MU */
|
||||
{ 0x07cd, 0x039d }, /* Greek_NU GREEK CAPITAL LETTER NU */
|
||||
{ 0x07ce, 0x039e }, /* Greek_XI GREEK CAPITAL LETTER XI */
|
||||
{ 0x07cf, 0x039f }, /* Greek_OMICRON GREEK CAPITAL LETTER OMICRON */
|
||||
{ 0x07d0, 0x03a0 }, /* Greek_PI GREEK CAPITAL LETTER PI */
|
||||
{ 0x07d1, 0x03a1 }, /* Greek_RHO GREEK CAPITAL LETTER RHO */
|
||||
{ 0x07d2, 0x03a3 }, /* Greek_SIGMA GREEK CAPITAL LETTER SIGMA */
|
||||
{ 0x07d4, 0x03a4 }, /* Greek_TAU GREEK CAPITAL LETTER TAU */
|
||||
{ 0x07d5, 0x03a5 }, /* Greek_UPSILON GREEK CAPITAL LETTER UPSILON */
|
||||
{ 0x07d6, 0x03a6 }, /* Greek_PHI GREEK CAPITAL LETTER PHI */
|
||||
{ 0x07d7, 0x03a7 }, /* Greek_CHI GREEK CAPITAL LETTER CHI */
|
||||
{ 0x07d8, 0x03a8 }, /* Greek_PSI GREEK CAPITAL LETTER PSI */
|
||||
{ 0x07d9, 0x03a9 }, /* Greek_OMEGA GREEK CAPITAL LETTER OMEGA */
|
||||
{ 0x07e1, 0x03b1 }, /* Greek_alpha GREEK SMALL LETTER ALPHA */
|
||||
{ 0x07e2, 0x03b2 }, /* Greek_beta GREEK SMALL LETTER BETA */
|
||||
{ 0x07e3, 0x03b3 }, /* Greek_gamma GREEK SMALL LETTER GAMMA */
|
||||
{ 0x07e4, 0x03b4 }, /* Greek_delta GREEK SMALL LETTER DELTA */
|
||||
{ 0x07e5, 0x03b5 }, /* Greek_epsilon GREEK SMALL LETTER EPSILON */
|
||||
{ 0x07e6, 0x03b6 }, /* Greek_zeta GREEK SMALL LETTER ZETA */
|
||||
{ 0x07e7, 0x03b7 }, /* Greek_eta GREEK SMALL LETTER ETA */
|
||||
{ 0x07e8, 0x03b8 }, /* Greek_theta GREEK SMALL LETTER THETA */
|
||||
{ 0x07e9, 0x03b9 }, /* Greek_iota GREEK SMALL LETTER IOTA */
|
||||
{ 0x07ea, 0x03ba }, /* Greek_kappa GREEK SMALL LETTER KAPPA */
|
||||
{ 0x07eb, 0x03bb }, /* Greek_lambda GREEK SMALL LETTER LAMDA */
|
||||
{ 0x07ec, 0x03bc }, /* Greek_mu GREEK SMALL LETTER MU */
|
||||
{ 0x07ed, 0x03bd }, /* Greek_nu GREEK SMALL LETTER NU */
|
||||
{ 0x07ee, 0x03be }, /* Greek_xi GREEK SMALL LETTER XI */
|
||||
{ 0x07ef, 0x03bf }, /* Greek_omicron GREEK SMALL LETTER OMICRON */
|
||||
{ 0x07f0, 0x03c0 }, /* Greek_pi GREEK SMALL LETTER PI */
|
||||
{ 0x07f1, 0x03c1 }, /* Greek_rho GREEK SMALL LETTER RHO */
|
||||
{ 0x07f2, 0x03c3 }, /* Greek_sigma GREEK SMALL LETTER SIGMA */
|
||||
{ 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma GREEK SMALL LETTER FINAL SIGMA */
|
||||
{ 0x07f4, 0x03c4 }, /* Greek_tau GREEK SMALL LETTER TAU */
|
||||
{ 0x07f5, 0x03c5 }, /* Greek_upsilon GREEK SMALL LETTER UPSILON */
|
||||
{ 0x07f6, 0x03c6 }, /* Greek_phi GREEK SMALL LETTER PHI */
|
||||
{ 0x07f7, 0x03c7 }, /* Greek_chi GREEK SMALL LETTER CHI */
|
||||
{ 0x07f8, 0x03c8 }, /* Greek_psi GREEK SMALL LETTER PSI */
|
||||
{ 0x07f9, 0x03c9 }, /* Greek_omega GREEK SMALL LETTER OMEGA */
|
||||
{ 0x08a1, 0x23b7 }, /* leftradical ??? */
|
||||
{ 0x08a2, 0x250c }, /* topleftradical BOX DRAWINGS LIGHT DOWN AND RIGHT */
|
||||
{ 0x08a3, 0x2500 }, /* horizconnector BOX DRAWINGS LIGHT HORIZONTAL */
|
||||
{ 0x08a4, 0x2320 }, /* topintegral TOP HALF INTEGRAL */
|
||||
{ 0x08a5, 0x2321 }, /* botintegral BOTTOM HALF INTEGRAL */
|
||||
{ 0x08a6, 0x2502 }, /* vertconnector BOX DRAWINGS LIGHT VERTICAL */
|
||||
{ 0x08a7, 0x23a1 }, /* topleftsqbracket ??? */
|
||||
{ 0x08a8, 0x23a3 }, /* botleftsqbracket ??? */
|
||||
{ 0x08a9, 0x23a4 }, /* toprightsqbracket ??? */
|
||||
{ 0x08aa, 0x23a6 }, /* botrightsqbracket ??? */
|
||||
{ 0x08ab, 0x239b }, /* topleftparens ??? */
|
||||
{ 0x08ac, 0x239d }, /* botleftparens ??? */
|
||||
{ 0x08ad, 0x239e }, /* toprightparens ??? */
|
||||
{ 0x08ae, 0x23a0 }, /* botrightparens ??? */
|
||||
{ 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ??? */
|
||||
{ 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ??? */
|
||||
/* 0x08b1 topleftsummation ??? */
|
||||
/* 0x08b2 botleftsummation ??? */
|
||||
/* 0x08b3 topvertsummationconnector ??? */
|
||||
/* 0x08b4 botvertsummationconnector ??? */
|
||||
/* 0x08b5 toprightsummation ??? */
|
||||
/* 0x08b6 botrightsummation ??? */
|
||||
/* 0x08b7 rightmiddlesummation ??? */
|
||||
{ 0x08bc, 0x2264 }, /* lessthanequal LESS-THAN OR EQUAL TO */
|
||||
{ 0x08bd, 0x2260 }, /* notequal NOT EQUAL TO */
|
||||
{ 0x08be, 0x2265 }, /* greaterthanequal GREATER-THAN OR EQUAL TO */
|
||||
{ 0x08bf, 0x222b }, /* integral INTEGRAL */
|
||||
{ 0x08c0, 0x2234 }, /* therefore THEREFORE */
|
||||
{ 0x08c1, 0x221d }, /* variation PROPORTIONAL TO */
|
||||
{ 0x08c2, 0x221e }, /* infinity INFINITY */
|
||||
{ 0x08c5, 0x2207 }, /* nabla NABLA */
|
||||
{ 0x08c8, 0x223c }, /* approximate TILDE OPERATOR */
|
||||
{ 0x08c9, 0x2243 }, /* similarequal ASYMPTOTICALLY EQUAL TO */
|
||||
{ 0x08cd, 0x21d4 }, /* ifonlyif LEFT RIGHT DOUBLE ARROW */
|
||||
{ 0x08ce, 0x21d2 }, /* implies RIGHTWARDS DOUBLE ARROW */
|
||||
{ 0x08cf, 0x2261 }, /* identical IDENTICAL TO */
|
||||
{ 0x08d6, 0x221a }, /* radical SQUARE ROOT */
|
||||
{ 0x08da, 0x2282 }, /* includedin SUBSET OF */
|
||||
{ 0x08db, 0x2283 }, /* includes SUPERSET OF */
|
||||
{ 0x08dc, 0x2229 }, /* intersection INTERSECTION */
|
||||
{ 0x08dd, 0x222a }, /* union UNION */
|
||||
{ 0x08de, 0x2227 }, /* logicaland LOGICAL AND */
|
||||
{ 0x08df, 0x2228 }, /* logicalor LOGICAL OR */
|
||||
{ 0x08ef, 0x2202 }, /* partialderivative PARTIAL DIFFERENTIAL */
|
||||
{ 0x08f6, 0x0192 }, /* function LATIN SMALL LETTER F WITH HOOK */
|
||||
{ 0x08fb, 0x2190 }, /* leftarrow LEFTWARDS ARROW */
|
||||
{ 0x08fc, 0x2191 }, /* uparrow UPWARDS ARROW */
|
||||
{ 0x08fd, 0x2192 }, /* rightarrow RIGHTWARDS ARROW */
|
||||
{ 0x08fe, 0x2193 }, /* downarrow DOWNWARDS ARROW */
|
||||
/* 0x09df blank ??? */
|
||||
{ 0x09e0, 0x25c6 }, /* soliddiamond BLACK DIAMOND */
|
||||
{ 0x09e1, 0x2592 }, /* checkerboard MEDIUM SHADE */
|
||||
{ 0x09e2, 0x2409 }, /* ht SYMBOL FOR HORIZONTAL TABULATION */
|
||||
{ 0x09e3, 0x240c }, /* ff SYMBOL FOR FORM FEED */
|
||||
{ 0x09e4, 0x240d }, /* cr SYMBOL FOR CARRIAGE RETURN */
|
||||
{ 0x09e5, 0x240a }, /* lf SYMBOL FOR LINE FEED */
|
||||
{ 0x09e8, 0x2424 }, /* nl SYMBOL FOR NEWLINE */
|
||||
{ 0x09e9, 0x240b }, /* vt SYMBOL FOR VERTICAL TABULATION */
|
||||
{ 0x09ea, 0x2518 }, /* lowrightcorner BOX DRAWINGS LIGHT UP AND LEFT */
|
||||
{ 0x09eb, 0x2510 }, /* uprightcorner BOX DRAWINGS LIGHT DOWN AND LEFT */
|
||||
{ 0x09ec, 0x250c }, /* upleftcorner BOX DRAWINGS LIGHT DOWN AND RIGHT */
|
||||
{ 0x09ed, 0x2514 }, /* lowleftcorner BOX DRAWINGS LIGHT UP AND RIGHT */
|
||||
{ 0x09ee, 0x253c }, /* crossinglines BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
|
||||
{ 0x09ef, 0x23ba }, /* horizlinescan1 HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */
|
||||
{ 0x09f0, 0x23bb }, /* horizlinescan3 HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */
|
||||
{ 0x09f1, 0x2500 }, /* horizlinescan5 BOX DRAWINGS LIGHT HORIZONTAL */
|
||||
{ 0x09f2, 0x23bc }, /* horizlinescan7 HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */
|
||||
{ 0x09f3, 0x23bd }, /* horizlinescan9 HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */
|
||||
{ 0x09f4, 0x251c }, /* leftt BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
|
||||
{ 0x09f5, 0x2524 }, /* rightt BOX DRAWINGS LIGHT VERTICAL AND LEFT */
|
||||
{ 0x09f6, 0x2534 }, /* bott BOX DRAWINGS LIGHT UP AND HORIZONTAL */
|
||||
{ 0x09f7, 0x252c }, /* topt BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
|
||||
{ 0x09f8, 0x2502 }, /* vertbar BOX DRAWINGS LIGHT VERTICAL */
|
||||
{ 0x0aa1, 0x2003 }, /* emspace EM SPACE */
|
||||
{ 0x0aa2, 0x2002 }, /* enspace EN SPACE */
|
||||
{ 0x0aa3, 0x2004 }, /* em3space THREE-PER-EM SPACE */
|
||||
{ 0x0aa4, 0x2005 }, /* em4space FOUR-PER-EM SPACE */
|
||||
{ 0x0aa5, 0x2007 }, /* digitspace FIGURE SPACE */
|
||||
{ 0x0aa6, 0x2008 }, /* punctspace PUNCTUATION SPACE */
|
||||
{ 0x0aa7, 0x2009 }, /* thinspace THIN SPACE */
|
||||
{ 0x0aa8, 0x200a }, /* hairspace HAIR SPACE */
|
||||
{ 0x0aa9, 0x2014 }, /* emdash EM DASH */
|
||||
{ 0x0aaa, 0x2013 }, /* endash EN DASH */
|
||||
/* 0x0aac signifblank ??? */
|
||||
{ 0x0aae, 0x2026 }, /* ellipsis HORIZONTAL ELLIPSIS */
|
||||
{ 0x0aaf, 0x2025 }, /* doubbaselinedot TWO DOT LEADER */
|
||||
{ 0x0ab0, 0x2153 }, /* onethird VULGAR FRACTION ONE THIRD */
|
||||
{ 0x0ab1, 0x2154 }, /* twothirds VULGAR FRACTION TWO THIRDS */
|
||||
{ 0x0ab2, 0x2155 }, /* onefifth VULGAR FRACTION ONE FIFTH */
|
||||
{ 0x0ab3, 0x2156 }, /* twofifths VULGAR FRACTION TWO FIFTHS */
|
||||
{ 0x0ab4, 0x2157 }, /* threefifths VULGAR FRACTION THREE FIFTHS */
|
||||
{ 0x0ab5, 0x2158 }, /* fourfifths VULGAR FRACTION FOUR FIFTHS */
|
||||
{ 0x0ab6, 0x2159 }, /* onesixth VULGAR FRACTION ONE SIXTH */
|
||||
{ 0x0ab7, 0x215a }, /* fivesixths VULGAR FRACTION FIVE SIXTHS */
|
||||
{ 0x0ab8, 0x2105 }, /* careof CARE OF */
|
||||
{ 0x0abb, 0x2012 }, /* figdash FIGURE DASH */
|
||||
{ 0x0abc, 0x2329 }, /* leftanglebracket LEFT-POINTING ANGLE BRACKET */
|
||||
/* 0x0abd decimalpoint ??? */
|
||||
{ 0x0abe, 0x232a }, /* rightanglebracket RIGHT-POINTING ANGLE BRACKET */
|
||||
/* 0x0abf marker ??? */
|
||||
{ 0x0ac3, 0x215b }, /* oneeighth VULGAR FRACTION ONE EIGHTH */
|
||||
{ 0x0ac4, 0x215c }, /* threeeighths VULGAR FRACTION THREE EIGHTHS */
|
||||
{ 0x0ac5, 0x215d }, /* fiveeighths VULGAR FRACTION FIVE EIGHTHS */
|
||||
{ 0x0ac6, 0x215e }, /* seveneighths VULGAR FRACTION SEVEN EIGHTHS */
|
||||
{ 0x0ac9, 0x2122 }, /* trademark TRADE MARK SIGN */
|
||||
{ 0x0aca, 0x2613 }, /* signaturemark SALTIRE */
|
||||
/* 0x0acb trademarkincircle ??? */
|
||||
{ 0x0acc, 0x25c1 }, /* leftopentriangle WHITE LEFT-POINTING TRIANGLE */
|
||||
{ 0x0acd, 0x25b7 }, /* rightopentriangle WHITE RIGHT-POINTING TRIANGLE */
|
||||
{ 0x0ace, 0x25cb }, /* emopencircle WHITE CIRCLE */
|
||||
{ 0x0acf, 0x25af }, /* emopenrectangle WHITE VERTICAL RECTANGLE */
|
||||
{ 0x0ad0, 0x2018 }, /* leftsinglequotemark LEFT SINGLE QUOTATION MARK */
|
||||
{ 0x0ad1, 0x2019 }, /* rightsinglequotemark RIGHT SINGLE QUOTATION MARK */
|
||||
{ 0x0ad2, 0x201c }, /* leftdoublequotemark LEFT DOUBLE QUOTATION MARK */
|
||||
{ 0x0ad3, 0x201d }, /* rightdoublequotemark RIGHT DOUBLE QUOTATION MARK */
|
||||
{ 0x0ad4, 0x211e }, /* prescription PRESCRIPTION TAKE */
|
||||
{ 0x0ad6, 0x2032 }, /* minutes PRIME */
|
||||
{ 0x0ad7, 0x2033 }, /* seconds DOUBLE PRIME */
|
||||
{ 0x0ad9, 0x271d }, /* latincross LATIN CROSS */
|
||||
/* 0x0ada hexagram ??? */
|
||||
{ 0x0adb, 0x25ac }, /* filledrectbullet BLACK RECTANGLE */
|
||||
{ 0x0adc, 0x25c0 }, /* filledlefttribullet BLACK LEFT-POINTING TRIANGLE */
|
||||
{ 0x0add, 0x25b6 }, /* filledrighttribullet BLACK RIGHT-POINTING TRIANGLE */
|
||||
{ 0x0ade, 0x25cf }, /* emfilledcircle BLACK CIRCLE */
|
||||
{ 0x0adf, 0x25ae }, /* emfilledrect BLACK VERTICAL RECTANGLE */
|
||||
{ 0x0ae0, 0x25e6 }, /* enopencircbullet WHITE BULLET */
|
||||
{ 0x0ae1, 0x25ab }, /* enopensquarebullet WHITE SMALL SQUARE */
|
||||
{ 0x0ae2, 0x25ad }, /* openrectbullet WHITE RECTANGLE */
|
||||
{ 0x0ae3, 0x25b3 }, /* opentribulletup WHITE UP-POINTING TRIANGLE */
|
||||
{ 0x0ae4, 0x25bd }, /* opentribulletdown WHITE DOWN-POINTING TRIANGLE */
|
||||
{ 0x0ae5, 0x2606 }, /* openstar WHITE STAR */
|
||||
{ 0x0ae6, 0x2022 }, /* enfilledcircbullet BULLET */
|
||||
{ 0x0ae7, 0x25aa }, /* enfilledsqbullet BLACK SMALL SQUARE */
|
||||
{ 0x0ae8, 0x25b2 }, /* filledtribulletup BLACK UP-POINTING TRIANGLE */
|
||||
{ 0x0ae9, 0x25bc }, /* filledtribulletdown BLACK DOWN-POINTING TRIANGLE */
|
||||
{ 0x0aea, 0x261c }, /* leftpointer WHITE LEFT POINTING INDEX */
|
||||
{ 0x0aeb, 0x261e }, /* rightpointer WHITE RIGHT POINTING INDEX */
|
||||
{ 0x0aec, 0x2663 }, /* club BLACK CLUB SUIT */
|
||||
{ 0x0aed, 0x2666 }, /* diamond BLACK DIAMOND SUIT */
|
||||
{ 0x0aee, 0x2665 }, /* heart BLACK HEART SUIT */
|
||||
{ 0x0af0, 0x2720 }, /* maltesecross MALTESE CROSS */
|
||||
{ 0x0af1, 0x2020 }, /* dagger DAGGER */
|
||||
{ 0x0af2, 0x2021 }, /* doubledagger DOUBLE DAGGER */
|
||||
{ 0x0af3, 0x2713 }, /* checkmark CHECK MARK */
|
||||
{ 0x0af4, 0x2717 }, /* ballotcross BALLOT X */
|
||||
{ 0x0af5, 0x266f }, /* musicalsharp MUSIC SHARP SIGN */
|
||||
{ 0x0af6, 0x266d }, /* musicalflat MUSIC FLAT SIGN */
|
||||
{ 0x0af7, 0x2642 }, /* malesymbol MALE SIGN */
|
||||
{ 0x0af8, 0x2640 }, /* femalesymbol FEMALE SIGN */
|
||||
{ 0x0af9, 0x260e }, /* telephone BLACK TELEPHONE */
|
||||
{ 0x0afa, 0x2315 }, /* telephonerecorder TELEPHONE RECORDER */
|
||||
{ 0x0afb, 0x2117 }, /* phonographcopyright SOUND RECORDING COPYRIGHT */
|
||||
{ 0x0afc, 0x2038 }, /* caret CARET */
|
||||
{ 0x0afd, 0x201a }, /* singlelowquotemark SINGLE LOW-9 QUOTATION MARK */
|
||||
{ 0x0afe, 0x201e }, /* doublelowquotemark DOUBLE LOW-9 QUOTATION MARK */
|
||||
/* 0x0aff cursor ??? */
|
||||
{ 0x0ba3, 0x003c }, /* leftcaret LESS-THAN SIGN */
|
||||
{ 0x0ba6, 0x003e }, /* rightcaret GREATER-THAN SIGN */
|
||||
{ 0x0ba8, 0x2228 }, /* downcaret LOGICAL OR */
|
||||
{ 0x0ba9, 0x2227 }, /* upcaret LOGICAL AND */
|
||||
{ 0x0bc0, 0x00af }, /* overbar MACRON */
|
||||
{ 0x0bc2, 0x22a5 }, /* downtack UP TACK */
|
||||
{ 0x0bc3, 0x2229 }, /* upshoe INTERSECTION */
|
||||
{ 0x0bc4, 0x230a }, /* downstile LEFT FLOOR */
|
||||
{ 0x0bc6, 0x005f }, /* underbar LOW LINE */
|
||||
{ 0x0bca, 0x2218 }, /* jot RING OPERATOR */
|
||||
{ 0x0bcc, 0x2395 }, /* quad APL FUNCTIONAL SYMBOL QUAD */
|
||||
{ 0x0bce, 0x22a4 }, /* uptack DOWN TACK */
|
||||
{ 0x0bcf, 0x25cb }, /* circle WHITE CIRCLE */
|
||||
{ 0x0bd3, 0x2308 }, /* upstile LEFT CEILING */
|
||||
{ 0x0bd6, 0x222a }, /* downshoe UNION */
|
||||
{ 0x0bd8, 0x2283 }, /* rightshoe SUPERSET OF */
|
||||
{ 0x0bda, 0x2282 }, /* leftshoe SUBSET OF */
|
||||
{ 0x0bdc, 0x22a2 }, /* lefttack RIGHT TACK */
|
||||
{ 0x0bfc, 0x22a3 }, /* righttack LEFT TACK */
|
||||
{ 0x0cdf, 0x2017 }, /* hebrew_doublelowline DOUBLE LOW LINE */
|
||||
{ 0x0ce0, 0x05d0 }, /* hebrew_aleph HEBREW LETTER ALEF */
|
||||
{ 0x0ce1, 0x05d1 }, /* hebrew_bet HEBREW LETTER BET */
|
||||
{ 0x0ce2, 0x05d2 }, /* hebrew_gimel HEBREW LETTER GIMEL */
|
||||
{ 0x0ce3, 0x05d3 }, /* hebrew_dalet HEBREW LETTER DALET */
|
||||
{ 0x0ce4, 0x05d4 }, /* hebrew_he HEBREW LETTER HE */
|
||||
{ 0x0ce5, 0x05d5 }, /* hebrew_waw HEBREW LETTER VAV */
|
||||
{ 0x0ce6, 0x05d6 }, /* hebrew_zain HEBREW LETTER ZAYIN */
|
||||
{ 0x0ce7, 0x05d7 }, /* hebrew_chet HEBREW LETTER HET */
|
||||
{ 0x0ce8, 0x05d8 }, /* hebrew_tet HEBREW LETTER TET */
|
||||
{ 0x0ce9, 0x05d9 }, /* hebrew_yod HEBREW LETTER YOD */
|
||||
{ 0x0cea, 0x05da }, /* hebrew_finalkaph HEBREW LETTER FINAL KAF */
|
||||
{ 0x0ceb, 0x05db }, /* hebrew_kaph HEBREW LETTER KAF */
|
||||
{ 0x0cec, 0x05dc }, /* hebrew_lamed HEBREW LETTER LAMED */
|
||||
{ 0x0ced, 0x05dd }, /* hebrew_finalmem HEBREW LETTER FINAL MEM */
|
||||
{ 0x0cee, 0x05de }, /* hebrew_mem HEBREW LETTER MEM */
|
||||
{ 0x0cef, 0x05df }, /* hebrew_finalnun HEBREW LETTER FINAL NUN */
|
||||
{ 0x0cf0, 0x05e0 }, /* hebrew_nun HEBREW LETTER NUN */
|
||||
{ 0x0cf1, 0x05e1 }, /* hebrew_samech HEBREW LETTER SAMEKH */
|
||||
{ 0x0cf2, 0x05e2 }, /* hebrew_ayin HEBREW LETTER AYIN */
|
||||
{ 0x0cf3, 0x05e3 }, /* hebrew_finalpe HEBREW LETTER FINAL PE */
|
||||
{ 0x0cf4, 0x05e4 }, /* hebrew_pe HEBREW LETTER PE */
|
||||
{ 0x0cf5, 0x05e5 }, /* hebrew_finalzade HEBREW LETTER FINAL TSADI */
|
||||
{ 0x0cf6, 0x05e6 }, /* hebrew_zade HEBREW LETTER TSADI */
|
||||
{ 0x0cf7, 0x05e7 }, /* hebrew_qoph HEBREW LETTER QOF */
|
||||
{ 0x0cf8, 0x05e8 }, /* hebrew_resh HEBREW LETTER RESH */
|
||||
{ 0x0cf9, 0x05e9 }, /* hebrew_shin HEBREW LETTER SHIN */
|
||||
{ 0x0cfa, 0x05ea }, /* hebrew_taw HEBREW LETTER TAV */
|
||||
{ 0x0da1, 0x0e01 }, /* Thai_kokai THAI CHARACTER KO KAI */
|
||||
{ 0x0da2, 0x0e02 }, /* Thai_khokhai THAI CHARACTER KHO KHAI */
|
||||
{ 0x0da3, 0x0e03 }, /* Thai_khokhuat THAI CHARACTER KHO KHUAT */
|
||||
{ 0x0da4, 0x0e04 }, /* Thai_khokhwai THAI CHARACTER KHO KHWAI */
|
||||
{ 0x0da5, 0x0e05 }, /* Thai_khokhon THAI CHARACTER KHO KHON */
|
||||
{ 0x0da6, 0x0e06 }, /* Thai_khorakhang THAI CHARACTER KHO RAKHANG */
|
||||
{ 0x0da7, 0x0e07 }, /* Thai_ngongu THAI CHARACTER NGO NGU */
|
||||
{ 0x0da8, 0x0e08 }, /* Thai_chochan THAI CHARACTER CHO CHAN */
|
||||
{ 0x0da9, 0x0e09 }, /* Thai_choching THAI CHARACTER CHO CHING */
|
||||
{ 0x0daa, 0x0e0a }, /* Thai_chochang THAI CHARACTER CHO CHANG */
|
||||
{ 0x0dab, 0x0e0b }, /* Thai_soso THAI CHARACTER SO SO */
|
||||
{ 0x0dac, 0x0e0c }, /* Thai_chochoe THAI CHARACTER CHO CHOE */
|
||||
{ 0x0dad, 0x0e0d }, /* Thai_yoying THAI CHARACTER YO YING */
|
||||
{ 0x0dae, 0x0e0e }, /* Thai_dochada THAI CHARACTER DO CHADA */
|
||||
{ 0x0daf, 0x0e0f }, /* Thai_topatak THAI CHARACTER TO PATAK */
|
||||
{ 0x0db0, 0x0e10 }, /* Thai_thothan THAI CHARACTER THO THAN */
|
||||
{ 0x0db1, 0x0e11 }, /* Thai_thonangmontho THAI CHARACTER THO NANGMONTHO */
|
||||
{ 0x0db2, 0x0e12 }, /* Thai_thophuthao THAI CHARACTER THO PHUTHAO */
|
||||
{ 0x0db3, 0x0e13 }, /* Thai_nonen THAI CHARACTER NO NEN */
|
||||
{ 0x0db4, 0x0e14 }, /* Thai_dodek THAI CHARACTER DO DEK */
|
||||
{ 0x0db5, 0x0e15 }, /* Thai_totao THAI CHARACTER TO TAO */
|
||||
{ 0x0db6, 0x0e16 }, /* Thai_thothung THAI CHARACTER THO THUNG */
|
||||
{ 0x0db7, 0x0e17 }, /* Thai_thothahan THAI CHARACTER THO THAHAN */
|
||||
{ 0x0db8, 0x0e18 }, /* Thai_thothong THAI CHARACTER THO THONG */
|
||||
{ 0x0db9, 0x0e19 }, /* Thai_nonu THAI CHARACTER NO NU */
|
||||
{ 0x0dba, 0x0e1a }, /* Thai_bobaimai THAI CHARACTER BO BAIMAI */
|
||||
{ 0x0dbb, 0x0e1b }, /* Thai_popla THAI CHARACTER PO PLA */
|
||||
{ 0x0dbc, 0x0e1c }, /* Thai_phophung THAI CHARACTER PHO PHUNG */
|
||||
{ 0x0dbd, 0x0e1d }, /* Thai_fofa THAI CHARACTER FO FA */
|
||||
{ 0x0dbe, 0x0e1e }, /* Thai_phophan THAI CHARACTER PHO PHAN */
|
||||
{ 0x0dbf, 0x0e1f }, /* Thai_fofan THAI CHARACTER FO FAN */
|
||||
{ 0x0dc0, 0x0e20 }, /* Thai_phosamphao THAI CHARACTER PHO SAMPHAO */
|
||||
{ 0x0dc1, 0x0e21 }, /* Thai_moma THAI CHARACTER MO MA */
|
||||
{ 0x0dc2, 0x0e22 }, /* Thai_yoyak THAI CHARACTER YO YAK */
|
||||
{ 0x0dc3, 0x0e23 }, /* Thai_rorua THAI CHARACTER RO RUA */
|
||||
{ 0x0dc4, 0x0e24 }, /* Thai_ru THAI CHARACTER RU */
|
||||
{ 0x0dc5, 0x0e25 }, /* Thai_loling THAI CHARACTER LO LING */
|
||||
{ 0x0dc6, 0x0e26 }, /* Thai_lu THAI CHARACTER LU */
|
||||
{ 0x0dc7, 0x0e27 }, /* Thai_wowaen THAI CHARACTER WO WAEN */
|
||||
{ 0x0dc8, 0x0e28 }, /* Thai_sosala THAI CHARACTER SO SALA */
|
||||
{ 0x0dc9, 0x0e29 }, /* Thai_sorusi THAI CHARACTER SO RUSI */
|
||||
{ 0x0dca, 0x0e2a }, /* Thai_sosua THAI CHARACTER SO SUA */
|
||||
{ 0x0dcb, 0x0e2b }, /* Thai_hohip THAI CHARACTER HO HIP */
|
||||
{ 0x0dcc, 0x0e2c }, /* Thai_lochula THAI CHARACTER LO CHULA */
|
||||
{ 0x0dcd, 0x0e2d }, /* Thai_oang THAI CHARACTER O ANG */
|
||||
{ 0x0dce, 0x0e2e }, /* Thai_honokhuk THAI CHARACTER HO NOKHUK */
|
||||
{ 0x0dcf, 0x0e2f }, /* Thai_paiyannoi THAI CHARACTER PAIYANNOI */
|
||||
{ 0x0dd0, 0x0e30 }, /* Thai_saraa THAI CHARACTER SARA A */
|
||||
{ 0x0dd1, 0x0e31 }, /* Thai_maihanakat THAI CHARACTER MAI HAN-AKAT */
|
||||
{ 0x0dd2, 0x0e32 }, /* Thai_saraaa THAI CHARACTER SARA AA */
|
||||
{ 0x0dd3, 0x0e33 }, /* Thai_saraam THAI CHARACTER SARA AM */
|
||||
{ 0x0dd4, 0x0e34 }, /* Thai_sarai THAI CHARACTER SARA I */
|
||||
{ 0x0dd5, 0x0e35 }, /* Thai_saraii THAI CHARACTER SARA II */
|
||||
{ 0x0dd6, 0x0e36 }, /* Thai_saraue THAI CHARACTER SARA UE */
|
||||
{ 0x0dd7, 0x0e37 }, /* Thai_sarauee THAI CHARACTER SARA UEE */
|
||||
{ 0x0dd8, 0x0e38 }, /* Thai_sarau THAI CHARACTER SARA U */
|
||||
{ 0x0dd9, 0x0e39 }, /* Thai_sarauu THAI CHARACTER SARA UU */
|
||||
{ 0x0dda, 0x0e3a }, /* Thai_phinthu THAI CHARACTER PHINTHU */
|
||||
/* 0x0dde Thai_maihanakat_maitho ??? */
|
||||
{ 0x0ddf, 0x0e3f }, /* Thai_baht THAI CURRENCY SYMBOL BAHT */
|
||||
{ 0x0de0, 0x0e40 }, /* Thai_sarae THAI CHARACTER SARA E */
|
||||
{ 0x0de1, 0x0e41 }, /* Thai_saraae THAI CHARACTER SARA AE */
|
||||
{ 0x0de2, 0x0e42 }, /* Thai_sarao THAI CHARACTER SARA O */
|
||||
{ 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan THAI CHARACTER SARA AI MAIMUAN */
|
||||
{ 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai THAI CHARACTER SARA AI MAIMALAI */
|
||||
{ 0x0de5, 0x0e45 }, /* Thai_lakkhangyao THAI CHARACTER LAKKHANGYAO */
|
||||
{ 0x0de6, 0x0e46 }, /* Thai_maiyamok THAI CHARACTER MAIYAMOK */
|
||||
{ 0x0de7, 0x0e47 }, /* Thai_maitaikhu THAI CHARACTER MAITAIKHU */
|
||||
{ 0x0de8, 0x0e48 }, /* Thai_maiek THAI CHARACTER MAI EK */
|
||||
{ 0x0de9, 0x0e49 }, /* Thai_maitho THAI CHARACTER MAI THO */
|
||||
{ 0x0dea, 0x0e4a }, /* Thai_maitri THAI CHARACTER MAI TRI */
|
||||
{ 0x0deb, 0x0e4b }, /* Thai_maichattawa THAI CHARACTER MAI CHATTAWA */
|
||||
{ 0x0dec, 0x0e4c }, /* Thai_thanthakhat THAI CHARACTER THANTHAKHAT */
|
||||
{ 0x0ded, 0x0e4d }, /* Thai_nikhahit THAI CHARACTER NIKHAHIT */
|
||||
{ 0x0df0, 0x0e50 }, /* Thai_leksun THAI DIGIT ZERO */
|
||||
{ 0x0df1, 0x0e51 }, /* Thai_leknung THAI DIGIT ONE */
|
||||
{ 0x0df2, 0x0e52 }, /* Thai_leksong THAI DIGIT TWO */
|
||||
{ 0x0df3, 0x0e53 }, /* Thai_leksam THAI DIGIT THREE */
|
||||
{ 0x0df4, 0x0e54 }, /* Thai_leksi THAI DIGIT FOUR */
|
||||
{ 0x0df5, 0x0e55 }, /* Thai_lekha THAI DIGIT FIVE */
|
||||
{ 0x0df6, 0x0e56 }, /* Thai_lekhok THAI DIGIT SIX */
|
||||
{ 0x0df7, 0x0e57 }, /* Thai_lekchet THAI DIGIT SEVEN */
|
||||
{ 0x0df8, 0x0e58 }, /* Thai_lekpaet THAI DIGIT EIGHT */
|
||||
{ 0x0df9, 0x0e59 }, /* Thai_lekkao THAI DIGIT NINE */
|
||||
{ 0x0ea1, 0x3131 }, /* Hangul_Kiyeog HANGUL LETTER KIYEOK */
|
||||
{ 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog HANGUL LETTER SSANGKIYEOK */
|
||||
{ 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios HANGUL LETTER KIYEOK-SIOS */
|
||||
{ 0x0ea4, 0x3134 }, /* Hangul_Nieun HANGUL LETTER NIEUN */
|
||||
{ 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj HANGUL LETTER NIEUN-CIEUC */
|
||||
{ 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh HANGUL LETTER NIEUN-HIEUH */
|
||||
{ 0x0ea7, 0x3137 }, /* Hangul_Dikeud HANGUL LETTER TIKEUT */
|
||||
{ 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud HANGUL LETTER SSANGTIKEUT */
|
||||
{ 0x0ea9, 0x3139 }, /* Hangul_Rieul HANGUL LETTER RIEUL */
|
||||
{ 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog HANGUL LETTER RIEUL-KIYEOK */
|
||||
{ 0x0eab, 0x313b }, /* Hangul_RieulMieum HANGUL LETTER RIEUL-MIEUM */
|
||||
{ 0x0eac, 0x313c }, /* Hangul_RieulPieub HANGUL LETTER RIEUL-PIEUP */
|
||||
{ 0x0ead, 0x313d }, /* Hangul_RieulSios HANGUL LETTER RIEUL-SIOS */
|
||||
{ 0x0eae, 0x313e }, /* Hangul_RieulTieut HANGUL LETTER RIEUL-THIEUTH */
|
||||
{ 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf HANGUL LETTER RIEUL-PHIEUPH */
|
||||
{ 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh HANGUL LETTER RIEUL-HIEUH */
|
||||
{ 0x0eb1, 0x3141 }, /* Hangul_Mieum HANGUL LETTER MIEUM */
|
||||
{ 0x0eb2, 0x3142 }, /* Hangul_Pieub HANGUL LETTER PIEUP */
|
||||
{ 0x0eb3, 0x3143 }, /* Hangul_SsangPieub HANGUL LETTER SSANGPIEUP */
|
||||
{ 0x0eb4, 0x3144 }, /* Hangul_PieubSios HANGUL LETTER PIEUP-SIOS */
|
||||
{ 0x0eb5, 0x3145 }, /* Hangul_Sios HANGUL LETTER SIOS */
|
||||
{ 0x0eb6, 0x3146 }, /* Hangul_SsangSios HANGUL LETTER SSANGSIOS */
|
||||
{ 0x0eb7, 0x3147 }, /* Hangul_Ieung HANGUL LETTER IEUNG */
|
||||
{ 0x0eb8, 0x3148 }, /* Hangul_Jieuj HANGUL LETTER CIEUC */
|
||||
{ 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj HANGUL LETTER SSANGCIEUC */
|
||||
{ 0x0eba, 0x314a }, /* Hangul_Cieuc HANGUL LETTER CHIEUCH */
|
||||
{ 0x0ebb, 0x314b }, /* Hangul_Khieuq HANGUL LETTER KHIEUKH */
|
||||
{ 0x0ebc, 0x314c }, /* Hangul_Tieut HANGUL LETTER THIEUTH */
|
||||
{ 0x0ebd, 0x314d }, /* Hangul_Phieuf HANGUL LETTER PHIEUPH */
|
||||
{ 0x0ebe, 0x314e }, /* Hangul_Hieuh HANGUL LETTER HIEUH */
|
||||
{ 0x0ebf, 0x314f }, /* Hangul_A HANGUL LETTER A */
|
||||
{ 0x0ec0, 0x3150 }, /* Hangul_AE HANGUL LETTER AE */
|
||||
{ 0x0ec1, 0x3151 }, /* Hangul_YA HANGUL LETTER YA */
|
||||
{ 0x0ec2, 0x3152 }, /* Hangul_YAE HANGUL LETTER YAE */
|
||||
{ 0x0ec3, 0x3153 }, /* Hangul_EO HANGUL LETTER EO */
|
||||
{ 0x0ec4, 0x3154 }, /* Hangul_E HANGUL LETTER E */
|
||||
{ 0x0ec5, 0x3155 }, /* Hangul_YEO HANGUL LETTER YEO */
|
||||
{ 0x0ec6, 0x3156 }, /* Hangul_YE HANGUL LETTER YE */
|
||||
{ 0x0ec7, 0x3157 }, /* Hangul_O HANGUL LETTER O */
|
||||
{ 0x0ec8, 0x3158 }, /* Hangul_WA HANGUL LETTER WA */
|
||||
{ 0x0ec9, 0x3159 }, /* Hangul_WAE HANGUL LETTER WAE */
|
||||
{ 0x0eca, 0x315a }, /* Hangul_OE HANGUL LETTER OE */
|
||||
{ 0x0ecb, 0x315b }, /* Hangul_YO HANGUL LETTER YO */
|
||||
{ 0x0ecc, 0x315c }, /* Hangul_U HANGUL LETTER U */
|
||||
{ 0x0ecd, 0x315d }, /* Hangul_WEO HANGUL LETTER WEO */
|
||||
{ 0x0ece, 0x315e }, /* Hangul_WE HANGUL LETTER WE */
|
||||
{ 0x0ecf, 0x315f }, /* Hangul_WI HANGUL LETTER WI */
|
||||
{ 0x0ed0, 0x3160 }, /* Hangul_YU HANGUL LETTER YU */
|
||||
{ 0x0ed1, 0x3161 }, /* Hangul_EU HANGUL LETTER EU */
|
||||
{ 0x0ed2, 0x3162 }, /* Hangul_YI HANGUL LETTER YI */
|
||||
{ 0x0ed3, 0x3163 }, /* Hangul_I HANGUL LETTER I */
|
||||
{ 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog HANGUL JONGSEONG KIYEOK */
|
||||
{ 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog HANGUL JONGSEONG SSANGKIYEOK */
|
||||
{ 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios HANGUL JONGSEONG KIYEOK-SIOS */
|
||||
{ 0x0ed7, 0x11ab }, /* Hangul_J_Nieun HANGUL JONGSEONG NIEUN */
|
||||
{ 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj HANGUL JONGSEONG NIEUN-CIEUC */
|
||||
{ 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh HANGUL JONGSEONG NIEUN-HIEUH */
|
||||
{ 0x0eda, 0x11ae }, /* Hangul_J_Dikeud HANGUL JONGSEONG TIKEUT */
|
||||
{ 0x0edb, 0x11af }, /* Hangul_J_Rieul HANGUL JONGSEONG RIEUL */
|
||||
{ 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog HANGUL JONGSEONG RIEUL-KIYEOK */
|
||||
{ 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum HANGUL JONGSEONG RIEUL-MIEUM */
|
||||
{ 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub HANGUL JONGSEONG RIEUL-PIEUP */
|
||||
{ 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios HANGUL JONGSEONG RIEUL-SIOS */
|
||||
{ 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut HANGUL JONGSEONG RIEUL-THIEUTH */
|
||||
{ 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf HANGUL JONGSEONG RIEUL-PHIEUPH */
|
||||
{ 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh HANGUL JONGSEONG RIEUL-HIEUH */
|
||||
{ 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum HANGUL JONGSEONG MIEUM */
|
||||
{ 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub HANGUL JONGSEONG PIEUP */
|
||||
{ 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios HANGUL JONGSEONG PIEUP-SIOS */
|
||||
{ 0x0ee6, 0x11ba }, /* Hangul_J_Sios HANGUL JONGSEONG SIOS */
|
||||
{ 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios HANGUL JONGSEONG SSANGSIOS */
|
||||
{ 0x0ee8, 0x11bc }, /* Hangul_J_Ieung HANGUL JONGSEONG IEUNG */
|
||||
{ 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj HANGUL JONGSEONG CIEUC */
|
||||
{ 0x0eea, 0x11be }, /* Hangul_J_Cieuc HANGUL JONGSEONG CHIEUCH */
|
||||
{ 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq HANGUL JONGSEONG KHIEUKH */
|
||||
{ 0x0eec, 0x11c0 }, /* Hangul_J_Tieut HANGUL JONGSEONG THIEUTH */
|
||||
{ 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf HANGUL JONGSEONG PHIEUPH */
|
||||
{ 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh HANGUL JONGSEONG HIEUH */
|
||||
{ 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh HANGUL LETTER RIEUL-YEORINHIEUH */
|
||||
{ 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum HANGUL LETTER KAPYEOUNMIEUM */
|
||||
{ 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub HANGUL LETTER KAPYEOUNPIEUP */
|
||||
{ 0x0ef2, 0x317f }, /* Hangul_PanSios HANGUL LETTER PANSIOS */
|
||||
{ 0x0ef3, 0x3181 }, /* Hangul_KkogjiDalrinIeung HANGUL LETTER YESIEUNG */
|
||||
{ 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf HANGUL LETTER KAPYEOUNPHIEUPH */
|
||||
{ 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh HANGUL LETTER YEORINHIEUH */
|
||||
{ 0x0ef6, 0x318d }, /* Hangul_AraeA HANGUL LETTER ARAEA */
|
||||
{ 0x0ef7, 0x318e }, /* Hangul_AraeAE HANGUL LETTER ARAEAE */
|
||||
{ 0x0ef8, 0x11eb }, /* Hangul_J_PanSios HANGUL JONGSEONG PANSIOS */
|
||||
{ 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung HANGUL JONGSEONG YESIEUNG */
|
||||
{ 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh HANGUL JONGSEONG YEORINHIEUH */
|
||||
{ 0x0eff, 0x20a9 }, /* Korean_Won WON SIGN */
|
||||
{ 0x13a4, 0x20ac }, /* Euro EURO SIGN */
|
||||
{ 0x13bc, 0x0152 }, /* OE LATIN CAPITAL LIGATURE OE */
|
||||
{ 0x13bd, 0x0153 }, /* oe LATIN SMALL LIGATURE OE */
|
||||
{ 0x13be, 0x0178 }, /* Ydiaeresis LATIN CAPITAL LETTER Y WITH DIAERESIS */
|
||||
{ 0x20ac, 0x20ac }, /* EuroSign EURO SIGN */
|
||||
});
|
||||
|
||||
private static Map<Integer, Integer> toMap(int[][] keys) {
|
||||
Map<Integer, Integer> keyMap = new HashMap<Integer, Integer>();
|
||||
for (int[] km: keys) {
|
||||
keyMap.put(km[1], km[0]);
|
||||
}
|
||||
return keyMap;
|
||||
}
|
||||
|
||||
public static int unicode2keysym(int ch) {
|
||||
if (ch >= 32 && ch <= 126 || ch >= 160 && ch <= 255)
|
||||
return ch;
|
||||
Integer converted = keyMap.get(ch);
|
||||
return converted != null ?
|
||||
converted :
|
||||
// No variants has been found for the unicode symbol then pass as unicode
|
||||
// with the special flag.
|
||||
// Note this method is valid only for 0x100-0x10ffff unicode value range.
|
||||
ch | 0x01000000;
|
||||
}
|
||||
|
||||
}
|
||||
63
plugins/vnc/src/main/java/com/glavsoft/utils/LazyLoaded.java
Normal file
63
plugins/vnc/src/main/java/com/glavsoft/utils/LazyLoaded.java
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright (C) 2010 - 2014 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.utils;
|
||||
|
||||
/**
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public class LazyLoaded<T> {
|
||||
private boolean isLoaded;
|
||||
private T lazyObj;
|
||||
private Loader<T> loader;
|
||||
|
||||
private LazyLoaded() {
|
||||
}
|
||||
|
||||
public T get() {
|
||||
if (isLoaded) {
|
||||
return lazyObj;
|
||||
} else {
|
||||
try {
|
||||
lazyObj = loader.load();
|
||||
isLoaded = true;
|
||||
} catch (Throwable ignore) {
|
||||
return null;
|
||||
}
|
||||
return lazyObj;
|
||||
}
|
||||
}
|
||||
|
||||
public LazyLoaded(Loader<T> loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
public interface Loader<T> {
|
||||
/**
|
||||
* Loads the lazy loaded object
|
||||
*
|
||||
* @return object loaded
|
||||
*/
|
||||
T load() throws Throwable;
|
||||
}
|
||||
}
|
||||
62
plugins/vnc/src/main/java/com/glavsoft/utils/Strings.java
Normal file
62
plugins/vnc/src/main/java/com/glavsoft/utils/Strings.java
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (C) 2010 - 2014 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.utils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class Strings {
|
||||
public static String toString(byte[] byteArray) {
|
||||
StringBuilder sb = new StringBuilder("[");
|
||||
boolean notFirst = false;
|
||||
for (byte b : byteArray) {
|
||||
if (notFirst) {
|
||||
sb.append(", ");
|
||||
} else {
|
||||
notFirst = true;
|
||||
}
|
||||
sb.append(b);
|
||||
}
|
||||
return sb.append("]").toString();
|
||||
}
|
||||
|
||||
public static boolean isTrimmedEmpty(String s) {
|
||||
return null == s || (s.trim().length() == 0);
|
||||
}
|
||||
|
||||
public static byte[] getBytesWithCharset(String string, Charset charset) {
|
||||
byte[] result;
|
||||
try {
|
||||
result = string.getBytes(charset);
|
||||
} catch (NoSuchMethodError error) {
|
||||
try {
|
||||
result = string.getBytes(charset.name());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright (C) 2010 - 2014 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.utils;
|
||||
|
||||
import com.glavsoft.rfb.client.ClientMessageType;
|
||||
import com.glavsoft.rfb.client.ClientToServerMessage;
|
||||
import com.glavsoft.rfb.protocol.Protocol;
|
||||
import com.glavsoft.rfb.protocol.ProtocolSettings;
|
||||
import com.glavsoft.transport.BaudrateMeter;
|
||||
|
||||
/**
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public class ViewerControlApi {
|
||||
private final Protocol protocol;
|
||||
private BaudrateMeter baudrateMeter;
|
||||
|
||||
public ViewerControlApi(Protocol protocol, BaudrateMeter baudrateMeter) {
|
||||
this.protocol = protocol;
|
||||
this.baudrateMeter = baudrateMeter;
|
||||
protocol.setBaudrateMeter(baudrateMeter);
|
||||
}
|
||||
|
||||
public void sendMessage(ClientToServerMessage message) {
|
||||
protocol.sendMessage(message);
|
||||
}
|
||||
|
||||
public void sendKeepAlive() {
|
||||
protocol.sendSupportedEncodingsMessage(protocol.getSettings());
|
||||
}
|
||||
|
||||
public void setCompressionLevelTo(int compressionLevel) {
|
||||
final ProtocolSettings settings = protocol.getSettings();
|
||||
settings.setCompressionLevel(compressionLevel);
|
||||
settings.fireListeners();
|
||||
}
|
||||
|
||||
public void setJpegQualityTo(int jpegQuality) {
|
||||
final ProtocolSettings settings = protocol.getSettings();
|
||||
settings.setJpegQuality(jpegQuality);
|
||||
settings.fireListeners();
|
||||
}
|
||||
|
||||
public void setViewOnly(boolean isViewOnly) {
|
||||
final ProtocolSettings settings = protocol.getSettings();
|
||||
settings.setViewOnly(isViewOnly);
|
||||
settings.fireListeners();
|
||||
}
|
||||
|
||||
|
||||
public int getBaudrate() {
|
||||
return baudrateMeter.kBPS();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether remote server is supported for given client-to-server message
|
||||
*
|
||||
* @param type client-to-server message type to check for
|
||||
* @return true when supported
|
||||
*/
|
||||
public boolean isSupported(ClientMessageType type) {
|
||||
return protocol.isSupported(type);
|
||||
}
|
||||
|
||||
}
|
||||
119
plugins/vnc/src/main/java/com/glavsoft/viewer/cli/Parser.java
Normal file
119
plugins/vnc/src/main/java/com/glavsoft/viewer/cli/Parser.java
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright (C) 2010 - 2014 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.cli;
|
||||
|
||||
import com.glavsoft.utils.Strings;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Command line interface parameters parser
|
||||
*/
|
||||
public class Parser {
|
||||
private final Map<String, Option> options = new LinkedHashMap<String, Option>();
|
||||
private final List<String> plainOptions = new ArrayList<String>();
|
||||
private boolean isSetPlainOptions = false;
|
||||
|
||||
public void addOption(String opName, String defaultValue, String desc) {
|
||||
Option op = new Option(opName, defaultValue, desc);
|
||||
options.put(opName.toLowerCase(), op);
|
||||
}
|
||||
|
||||
public void parse(String [] args) {
|
||||
for (String p : args) {
|
||||
if (p.startsWith("-")) {
|
||||
int skipMinuses = p.startsWith("--") ? 2 : 1;
|
||||
String[] params = p.split("=", 2);
|
||||
Option op = options.get(params[0].toLowerCase().substring(skipMinuses));
|
||||
if (op != null) {
|
||||
op.isSet = true;
|
||||
if (params.length > 1 && ! Strings.isTrimmedEmpty(params[1])) {
|
||||
op.value = params[1];
|
||||
}
|
||||
}
|
||||
} else if ( ! p.startsWith("-")) {
|
||||
isSetPlainOptions = true;
|
||||
plainOptions.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getValueFor(String param) {
|
||||
Option op = options.get(param.toLowerCase());
|
||||
return op != null ? op.value : null;
|
||||
}
|
||||
|
||||
public boolean isSet(String param) {
|
||||
Option op = options.get(param.toLowerCase());
|
||||
return op != null && op.isSet;
|
||||
}
|
||||
|
||||
public boolean isSetPlainOptions() {
|
||||
return isSetPlainOptions;
|
||||
}
|
||||
|
||||
public String getPlainOptionAt(int index) {
|
||||
return plainOptions.get(index);
|
||||
}
|
||||
|
||||
public int getPlainOptionsNumber() {
|
||||
return plainOptions.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Command line interface option
|
||||
*/
|
||||
private static class Option {
|
||||
String opName;
|
||||
String desc;
|
||||
String value;
|
||||
boolean isSet = false;
|
||||
Option(String opName, String defaultValue, String desc) {
|
||||
this.opName = opName;
|
||||
this.desc = desc;
|
||||
this.value = defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
public String optionsUsage() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
int maxNameLength = 0;
|
||||
for (Option op : options.values()) {
|
||||
if (op.desc.isEmpty()) continue;
|
||||
maxNameLength = Math.max(maxNameLength, op.opName.length());
|
||||
}
|
||||
for (Option op : options.values()) {
|
||||
if (op.desc.isEmpty()) continue;
|
||||
sb.append(" -").append(op.opName);
|
||||
for (int i=0; i<maxNameLength - op.opName.length(); ++i) {
|
||||
sb.append(' ');
|
||||
}
|
||||
sb.append(" : ").append(op.desc).append('\n');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
37
plugins/vnc/src/main/java/com/glavsoft/viewer/mvp/Model.java
Normal file
37
plugins/vnc/src/main/java/com/glavsoft/viewer/mvp/Model.java
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (C) 2010 - 2014 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;
|
||||
|
||||
/**
|
||||
* Model layer for Model-View-Presenter architecture
|
||||
*
|
||||
* A Model is a data representation object. The Presenter layer of architecture provides data flow between views and models.
|
||||
* The Model must have a set of getters and setters for data properties it represents.
|
||||
*
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public interface Model {
|
||||
|
||||
|
||||
}
|
||||
281
plugins/vnc/src/main/java/com/glavsoft/viewer/mvp/Presenter.java
Normal file
281
plugins/vnc/src/main/java/com/glavsoft/viewer/mvp/Presenter.java
Normal file
@@ -0,0 +1,281 @@
|
||||
// Copyright (C) 2010 - 2014 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 java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Presenter layer for Model-View-Presenter architecture
|
||||
*
|
||||
* @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>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register View at the Presenter
|
||||
*
|
||||
* @param name - name (id) of View
|
||||
* @param view - the View
|
||||
*/
|
||||
public void addView(String name, View view) {
|
||||
registeredViews.put(name, view);
|
||||
}
|
||||
|
||||
public Set<Map.Entry<String,View>> removeAllViews() {
|
||||
final Set<Map.Entry<String, View>> save = registeredViews.entrySet();
|
||||
registeredViews.clear();
|
||||
return save;
|
||||
}
|
||||
/**
|
||||
* Register Model at the Presenter
|
||||
*
|
||||
* @param name - name (id) of Model
|
||||
* @param model - the Model
|
||||
*/
|
||||
public void addModel(String name, Model model) {
|
||||
registeredModels.put(name, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over Models and pass available model properties of each model into Views
|
||||
* @see #populateFrom(String) for more details
|
||||
*/
|
||||
protected void populate() {
|
||||
savedInvocationTargetException = null;
|
||||
for (Map.Entry<String, Model> entry : registeredModels.entrySet()) {
|
||||
String modelName = entry.getKey();
|
||||
Model model = entry.getValue();
|
||||
populateFrom(modelName, model);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over model's getters, gets model property (name, value and its value type), then tries to set view's property
|
||||
* with the same name and value type. Skip property getting or setting on any access errors
|
||||
*
|
||||
* @param modelName name of Model
|
||||
*/
|
||||
public void populateFrom(String modelName) {
|
||||
Model model = registeredModels.get(modelName);
|
||||
if (model != 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 ignore) {
|
||||
// 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 ignore) {
|
||||
// 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 ignore) {
|
||||
// nop
|
||||
} catch (InvocationTargetException e) {
|
||||
savedInvocationTargetException = e.getCause();
|
||||
break;
|
||||
} catch (IllegalAccessException ignore) {
|
||||
// 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 ignore) {
|
||||
// nop
|
||||
} catch (InvocationTargetException e) {
|
||||
savedInvocationTargetException = e.getCause();
|
||||
break;
|
||||
} catch (IllegalAccessException ignore) {
|
||||
// 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 ignore) {
|
||||
// nop
|
||||
} catch (InvocationTargetException e) {
|
||||
e.getCause().printStackTrace();
|
||||
savedInvocationTargetException = e.getCause();
|
||||
break;
|
||||
} catch (IllegalAccessException ignore) {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void throwPossiblyHappenedException() throws Throwable {
|
||||
if (savedInvocationTargetException != null) {
|
||||
Throwable tmp = savedInvocationTargetException;
|
||||
savedInvocationTargetException = null;
|
||||
throw tmp;
|
||||
}
|
||||
}
|
||||
|
||||
protected View getView(String name) {
|
||||
return registeredViews.get(name);
|
||||
}
|
||||
|
||||
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 ignore) {
|
||||
// nop
|
||||
} catch (InvocationTargetException e) {
|
||||
savedInvocationTargetException = e.getCause();
|
||||
break;
|
||||
} catch (IllegalAccessException ignore) {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright (C) 2010 - 2014 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;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public class PropertyNotFoundException extends CommonException {
|
||||
public PropertyNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
45
plugins/vnc/src/main/java/com/glavsoft/viewer/mvp/View.java
Normal file
45
plugins/vnc/src/main/java/com/glavsoft/viewer/mvp/View.java
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (C) 2010 - 2014 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;
|
||||
|
||||
/**
|
||||
* View layer for Model-View-Presenter architecture
|
||||
*
|
||||
* The View is a visible and user interacted object. The Presenter layer of architecture determines when to show
|
||||
* the View and provide data flow between views and models.
|
||||
* The View must have a set of getters and setters for data properties it represents.
|
||||
*
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public interface View {
|
||||
/**
|
||||
* Make the view visible
|
||||
*/
|
||||
void showView();
|
||||
|
||||
/**
|
||||
* Close the view
|
||||
*/
|
||||
void closeView();
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
// Copyright (C) 2010 - 2014 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.settings;
|
||||
|
||||
import com.glavsoft.utils.Strings;
|
||||
import com.glavsoft.viewer.mvp.Model;
|
||||
|
||||
/**
|
||||
* Object that represents parameters needed for establishing network connection to remote host.
|
||||
* This is used to pass a number of parameters into connection establishing module and
|
||||
* to provide a Connection History interface feature.
|
||||
*
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* A name of remote host.
|
||||
* Rather symbolic (dns) name or ip address
|
||||
* Ex. remote.host.mydomain.com or localhost or 192.168.0.2 etc.
|
||||
*/
|
||||
public String hostName;
|
||||
/**
|
||||
* A port number of remote host.
|
||||
* Default is 5900
|
||||
*/
|
||||
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 ConnectionParams(String hostName, String portNumber) {
|
||||
this.hostName = hostName;
|
||||
try {
|
||||
setPortNumber(portNumber);
|
||||
} catch (WrongParameterException ignore) {
|
||||
// use default
|
||||
this.portNumber = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check host name empty
|
||||
* @return true if host name is empty
|
||||
*/
|
||||
public boolean isHostNameEmpty() {
|
||||
return Strings.isTrimmedEmpty(hostName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse port number from string specified.
|
||||
* Thrown WrongParameterException on error.
|
||||
*
|
||||
* @param port string representation of port number
|
||||
* @throws WrongParameterException when parsing error occurs or port number is out of range
|
||||
* @return portNubmer parsed
|
||||
*/
|
||||
private int parsePortNumber(String port) throws WrongParameterException {
|
||||
int portNumber;
|
||||
if (null == port) return 0;
|
||||
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");
|
||||
}
|
||||
return portNumber;
|
||||
}
|
||||
|
||||
public void setHostName(String hostName) {
|
||||
this.hostName = hostName;
|
||||
}
|
||||
public String getHostName() {
|
||||
return this.hostName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse port number from string specified.
|
||||
* Thrown WrongParameterException on error.
|
||||
* Set portNumber property when port on success.
|
||||
* throws WrongParameterException when parsing error occurs or port number is out of range
|
||||
*
|
||||
* @param port string representation of port number
|
||||
*/
|
||||
public void setPortNumber(String port) throws WrongParameterException {
|
||||
portNumber = this.parsePortNumber(port);
|
||||
}
|
||||
|
||||
public void setPortNumber(int port) {
|
||||
this.portNumber = port;
|
||||
}
|
||||
|
||||
public int getPortNumber() {
|
||||
return 0 == portNumber ? DEFAULT_RFB_PORT : portNumber;
|
||||
}
|
||||
|
||||
public void setSshPortNumber(String port) throws WrongParameterException {
|
||||
try {
|
||||
sshPortNumber = this.parsePortNumber(port);
|
||||
} catch (WrongParameterException e) {
|
||||
throw new WrongParameterException("SSH port number error. " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void setSshPortNumber(int port) {
|
||||
this.sshPortNumber = port;
|
||||
}
|
||||
|
||||
public int getSshPortNumber() {
|
||||
return 0 == sshPortNumber ? DEFAULT_SSH_PORT: sshPortNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set flag to use SSH port forwarding when connect to host
|
||||
* @param useSsh
|
||||
*/
|
||||
public void setUseSsh(boolean useSsh) {
|
||||
this.useSsh = useSsh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to use SSH port forwarding when connect to host
|
||||
* @return true if user wants to use SSH
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy and complete only field that are empty (null, zerro or empty string) in `this' object from the other one
|
||||
*
|
||||
* @param other ConnectionParams object to copy fields from
|
||||
*/
|
||||
public void completeEmptyFieldsFrom(ConnectionParams other) {
|
||||
if (null == other) return;
|
||||
if (Strings.isTrimmedEmpty(hostName) && ! Strings.isTrimmedEmpty(other.hostName)) {
|
||||
hostName = other.hostName;
|
||||
}
|
||||
if ( 0 == portNumber && other.portNumber != 0) {
|
||||
portNumber = other.portNumber;
|
||||
}
|
||||
if (Strings.isTrimmedEmpty(sshUserName) && ! Strings.isTrimmedEmpty(other.sshUserName)) {
|
||||
sshUserName = other.sshUserName;
|
||||
}
|
||||
if (Strings.isTrimmedEmpty(sshHostName) && ! Strings.isTrimmedEmpty(other.sshHostName)) {
|
||||
sshHostName = other.sshHostName;
|
||||
}
|
||||
if ( 0 == sshPortNumber && other.sshPortNumber != 0) {
|
||||
sshPortNumber = other.sshPortNumber;
|
||||
}
|
||||
useSsh |= other.useSsh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return hostName != null ? hostName : "";
|
||||
// return (hostName != null ? hostName : "") + ":" + portNumber + " " + useSsh + " " + sshUserName + "@" + sshHostName + ":" + sshPortNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* For logging purpose
|
||||
*
|
||||
* @return string representation of object
|
||||
*/
|
||||
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) {
|
||||
return (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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2010 - 2014 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.settings;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
// Copyright (C) 2010 - 2014 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.settings;
|
||||
|
||||
import com.glavsoft.core.SettingsChangedEvent;
|
||||
import com.glavsoft.rfb.IChangeSettingsListener;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* @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 CopyOnWriteArrayList<IChangeSettingsListener>();
|
||||
private int changedSettingsMask = 0;
|
||||
|
||||
private final UiSettingsData uiSettingsData;
|
||||
public boolean showControls = true;
|
||||
public boolean showConnectionDialog = false;
|
||||
|
||||
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() +
|
||||
'}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright (C) 2010 - 2014 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.settings;
|
||||
|
||||
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 +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright (C) 2010 - 2014 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.settings;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2010 - 2014 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 CancelConnectionException extends CommonException {
|
||||
|
||||
public CancelConnectionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (C) 2010 - 2014 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;
|
||||
|
||||
/**
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public class CancelConnectionQuietlyException extends CancelConnectionException {
|
||||
|
||||
public CancelConnectionQuietlyException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// Copyright (C) 2010 - 2014 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.ClipboardController;
|
||||
import com.glavsoft.rfb.client.ClientCutTextMessage;
|
||||
import com.glavsoft.rfb.protocol.Protocol;
|
||||
import com.glavsoft.rfb.protocol.ProtocolSettings;
|
||||
import com.glavsoft.utils.Strings;
|
||||
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.datatransfer.UnsupportedFlavorException;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
public class ClipboardControllerImpl implements ClipboardController, Runnable {
|
||||
private static final String STANDARD_CHARSET = "ISO-8859-1"; // aka Latin-1
|
||||
private static final long CLIPBOARD_UPDATE_CHECK_INTERVAL_MILS = 1000L;
|
||||
private Clipboard clipboard;
|
||||
private String clipboardText = null;
|
||||
private volatile boolean isRunning;
|
||||
private boolean isEnabled;
|
||||
private final Protocol protocol;
|
||||
private Charset charset;
|
||||
|
||||
public ClipboardControllerImpl(Protocol protocol, String charsetName) {
|
||||
this.protocol = protocol;
|
||||
try {
|
||||
clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
updateSavedClipboardContent(); // prevent onstart clipboard content sending
|
||||
} catch (SecurityException e) { /*nop*/ }
|
||||
|
||||
if (Strings.isTrimmedEmpty(charsetName)) {
|
||||
charset = Charset.defaultCharset();
|
||||
} else if ("standard".equalsIgnoreCase(charsetName)) {
|
||||
charset = Charset.forName(STANDARD_CHARSET);
|
||||
} else {
|
||||
charset = Charset.isSupported(charsetName) ? Charset.forName(charsetName) : Charset.defaultCharset();
|
||||
}
|
||||
// not supported UTF-charsets as they are multibytes.
|
||||
// add others multibytes charsets on need
|
||||
if (charset.name().startsWith("UTF")) {
|
||||
charset = Charset.forName(STANDARD_CHARSET);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSystemClipboard(byte[] bytes) {
|
||||
if (clipboard != null) {
|
||||
StringSelection stringSelection = new StringSelection(new String(bytes, charset));
|
||||
if (isEnabled) {
|
||||
clipboard.setContents(stringSelection, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for clipboard changes listeners
|
||||
* Retrieves text content from system clipboard which then available
|
||||
* through getClipboardText().
|
||||
*/
|
||||
private void updateSavedClipboardContent() {
|
||||
if (clipboard != null && clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
|
||||
try {
|
||||
clipboardText = (String)clipboard.getData(DataFlavor.stringFlavor);
|
||||
} catch (UnsupportedFlavorException e) {
|
||||
// ignore
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
} else {
|
||||
clipboardText = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClipboardText() {
|
||||
return clipboardText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get text clipboard contents when needed send to remote, or null vise versa
|
||||
*
|
||||
* @return clipboard string contents if it is changed from last method call
|
||||
* or null when clipboard contains non text object or clipboard contents didn't changed
|
||||
*/
|
||||
@Override
|
||||
public String getRenewedClipboardText() {
|
||||
String old = clipboardText;
|
||||
updateSavedClipboardContent();
|
||||
if (clipboardText != null && ! clipboardText.equals(old))
|
||||
return clipboardText;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enable) {
|
||||
if (! enable) {
|
||||
isRunning = false;
|
||||
}
|
||||
if (enable && ! isEnabled) {
|
||||
new Thread(this).start();
|
||||
}
|
||||
isEnabled = enable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
isRunning = true;
|
||||
while (isRunning) {
|
||||
String clipboardText = getRenewedClipboardText();
|
||||
if (clipboardText != null) {
|
||||
protocol.sendMessage(new ClientCutTextMessage(clipboardText, charset));
|
||||
}
|
||||
try {
|
||||
Thread.sleep(CLIPBOARD_UPDATE_CHECK_INTERVAL_MILS);
|
||||
} catch (InterruptedException ignore) { }
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingsChanged(SettingsChangedEvent e) {
|
||||
ProtocolSettings settings = (ProtocolSettings) e.getSource();
|
||||
setEnabled(settings.isAllowClipboardTransfer());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (C) 2010 - 2014 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 ConnectionErrorException extends CommonException {
|
||||
|
||||
public ConnectionErrorException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol.ProtocolSettings;
|
||||
import com.glavsoft.utils.Strings;
|
||||
import com.glavsoft.utils.ViewerControlApi;
|
||||
import com.glavsoft.viewer.mvp.Presenter;
|
||||
import com.glavsoft.viewer.mvp.PropertyNotFoundException;
|
||||
import com.glavsoft.viewer.settings.ConnectionParams;
|
||||
import com.glavsoft.viewer.settings.UiSettings;
|
||||
import com.glavsoft.viewer.settings.WrongParameterException;
|
||||
import com.glavsoft.viewer.swing.gui.ConnectionView;
|
||||
import com.glavsoft.viewer.swing.gui.ConnectionsHistory;
|
||||
import com.glavsoft.viewer.swing.ssh.SshConnectionManager;
|
||||
import com.glavsoft.viewer.workers.AbstractConnectionWorkerFactory;
|
||||
import com.glavsoft.viewer.workers.NetworkConnectionWorker;
|
||||
import com.glavsoft.viewer.workers.RfbConnectionWorker;
|
||||
|
||||
import java.net.Socket;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A Presenter (controller) that presents business logic for connection establishing interactions
|
||||
*
|
||||
* Before starting connection process you have to add View(s) and Model(s), and need to set @see ConnectionWorkerFactory
|
||||
*
|
||||
* @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 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;
|
||||
private ViewerControlApi viewerControlApi;
|
||||
|
||||
public ConnectionPresenter() {
|
||||
this.hasSshSupport = SshConnectionManager.checkForSshSupport();
|
||||
|
||||
logger = Logger.getLogger(getClass().getName());
|
||||
}
|
||||
|
||||
public void startConnection(ProtocolSettings rfbSettings, UiSettings uiSettings)
|
||||
throws IllegalStateException {
|
||||
startConnection(rfbSettings, uiSettings, 0);
|
||||
}
|
||||
|
||||
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);
|
||||
show();
|
||||
populate();
|
||||
if ( ! uiSettings.showConnectionDialog) {
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
public void setUseSsh(boolean useSsh) {
|
||||
setModelProperty(PROPERTY_USE_SSH, useSsh, boolean.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate connection process from View
|
||||
* ex. by press "Connect" button
|
||||
*
|
||||
* @param hostName name of host to connect
|
||||
*/
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cause ConnectionHistory prop to be saved
|
||||
*/
|
||||
void saveHistory() {
|
||||
final ConnectionParams cp = (ConnectionParams) getModel(CONNECTION_PARAMS_MODEL);
|
||||
connectionsHistory.reorder(cp, rfbSettings, uiSettings);
|
||||
connectionsHistory.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares async network connection worker and starts to execute it
|
||||
* Network connection worker tries to establish tcp connection with remote host
|
||||
*/
|
||||
public void connect() {
|
||||
final ConnectionParams connectionParams = (ConnectionParams) getModel(CONNECTION_PARAMS_MODEL);
|
||||
if (null == connectionWorkerFactory) {
|
||||
throw new IllegalStateException("connectionWorkerFactory is not set");
|
||||
}
|
||||
networkConnectionWorker = connectionWorkerFactory.createNetworkConnectionWorker();
|
||||
networkConnectionWorker.setConnectionParams(connectionParams);
|
||||
networkConnectionWorker.setPresenter(this);
|
||||
networkConnectionWorker.setHasSshSupport(hasSshSupport);
|
||||
networkConnectionWorker.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for connection worker, invoked on failed connection attempt.
|
||||
* Both for tcp network connection worker and for rfb connection worker.
|
||||
*/
|
||||
void connectionFailed() {
|
||||
cancelConnection();
|
||||
reconnect(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for connection worker, invoked when connection is cancelled
|
||||
*/
|
||||
void connectionCancelled() {
|
||||
cancelConnection();
|
||||
enableConnectionDialog();
|
||||
}
|
||||
|
||||
private void enableConnectionDialog() {
|
||||
setViewProperty(PROPERTY_CONNECTION_IN_PROGRESS, false, boolean.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for tcp network connection worker.
|
||||
* Invoked on successful connection.
|
||||
* Invoked in EDT
|
||||
*
|
||||
* @param workingSocket a socket binded with established connection
|
||||
*/
|
||||
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();
|
||||
viewerControlApi = rfbConnectionWorker.getViewerControlApi();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for rfb connection worker
|
||||
* Invoked on successful connection
|
||||
*/
|
||||
void successfulRfbConnection() {
|
||||
enableConnectionDialog();
|
||||
getView(CONNECTION_VIEW).closeView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully cancel active connection workers
|
||||
*/
|
||||
public void cancelConnection() {
|
||||
logger.finer("Cancel connection");
|
||||
if (networkConnectionWorker != null) {
|
||||
networkConnectionWorker.cancel();
|
||||
}
|
||||
if (rfbConnectionWorker != null) {
|
||||
rfbConnectionWorker.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask ConnectionView to show dialog
|
||||
*
|
||||
* @param errorMessage message to show
|
||||
*/
|
||||
void showConnectionErrorDialog(String errorMessage) {
|
||||
final ConnectionView connectionView = (ConnectionView) getView(CONNECTION_VIEW);
|
||||
if (connectionView != null) {
|
||||
connectionView.showConnectionErrorDialog(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask ConnectionView to show dialog whether to reconnect or close app
|
||||
*
|
||||
* @param errorTitle dialog title to show
|
||||
* @param errorMessage message to show
|
||||
*/
|
||||
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), String.class);
|
||||
setModelProperty(PROPERTY_SSH_HOST_NAME, getViewPropertyOrNull(PROPERTY_SSH_HOST_NAME), String.class);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* populate Model(s) props with data from param passed (network connection related props)
|
||||
* populate View(s) props from model(s)
|
||||
* get rfb and ui settings from History
|
||||
*
|
||||
* @param connectionParams connection parameters
|
||||
*/
|
||||
public void populateFromHistoryItem(ConnectionParams connectionParams) {
|
||||
setModelProperty(PROPERTY_HOST_NAME, connectionParams.hostName, String.class);
|
||||
setModelProperty(PROPERTY_RFB_PORT_NUMBER, connectionParams.getPortNumber(), int.class);
|
||||
setModelProperty(PROPERTY_USE_SSH, connectionParams.useSsh(), boolean.class);
|
||||
setModelProperty(PROPERTY_SSH_HOST_NAME, connectionParams.sshHostName, String.class);
|
||||
setModelProperty(PROPERTY_SSH_PORT_NUMBER, connectionParams.getSshPortNumber(), int.class);
|
||||
setModelProperty(PROPERTY_SSH_USER_NAME, connectionParams.sshUserName, String.class);
|
||||
populateFrom(CONNECTION_PARAMS_MODEL);
|
||||
rfbSettings.copyDataFrom(connectionsHistory.getProtocolSettings(connectionParams));
|
||||
uiSettings.copyDataFrom(connectionsHistory.getUiSettingsData(connectionParams));
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget history records at all
|
||||
*/
|
||||
public void clearHistory() {
|
||||
connectionsHistory.clear();
|
||||
connectionsHistory.reorder((ConnectionParams) getModel(CONNECTION_PARAMS_MODEL), rfbSettings, uiSettings);
|
||||
populateFrom(CONNECTIONS_HISTORY_MODEL);
|
||||
clearMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show status info about currently executed operation
|
||||
*
|
||||
* @param message status message
|
||||
*/
|
||||
void showMessage(String message) {
|
||||
setViewProperty(PROPERTY_STATUS_BAR_MESSAGE, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show empty status bar
|
||||
*/
|
||||
void clearMessage() {
|
||||
showMessage("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set connection worker factory
|
||||
*
|
||||
* @param connectionWorkerFactory factory
|
||||
*/
|
||||
public void setConnectionWorkerFactory(AbstractConnectionWorkerFactory connectionWorkerFactory) {
|
||||
this.connectionWorkerFactory = connectionWorkerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset presenter and try to start new connection establishing process
|
||||
*
|
||||
* @param predefinedPassword password
|
||||
*/
|
||||
void reconnect(String predefinedPassword) {
|
||||
if (predefinedPassword != null && ! predefinedPassword.isEmpty())
|
||||
connectionWorkerFactory.setPredefinedPassword(predefinedPassword);
|
||||
clearMessage();
|
||||
enableConnectionDialog();
|
||||
show();
|
||||
populate();
|
||||
if ( ! uiSettings.showConnectionDialog) {
|
||||
connect();
|
||||
}
|
||||
}
|
||||
|
||||
void clearPredefinedPassword() {
|
||||
connectionWorkerFactory.setPredefinedPassword(null);
|
||||
}
|
||||
|
||||
public UiSettings getUiSettings() {
|
||||
return uiSettings;
|
||||
}
|
||||
|
||||
public ProtocolSettings getRfbSettings() {
|
||||
return rfbSettings;
|
||||
}
|
||||
|
||||
boolean needReconnection() {
|
||||
return needReconnection;
|
||||
}
|
||||
|
||||
public void setNeedReconnection(boolean need) {
|
||||
needReconnection = need;
|
||||
}
|
||||
|
||||
public ViewerControlApi getViewerControlApi() {
|
||||
return viewerControlApi;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.client.KeyEventMessage;
|
||||
import com.glavsoft.rfb.protocol.Protocol;
|
||||
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.KeyListener;
|
||||
|
||||
import static com.glavsoft.utils.Keymap.K_ALT_LEFT;
|
||||
import static com.glavsoft.utils.Keymap.K_BACK_SPACE;
|
||||
import static com.glavsoft.utils.Keymap.K_CTRL_LEFT;
|
||||
import static com.glavsoft.utils.Keymap.K_DELETE;
|
||||
import static com.glavsoft.utils.Keymap.K_DOWN;
|
||||
import static com.glavsoft.utils.Keymap.K_END;
|
||||
import static com.glavsoft.utils.Keymap.K_ENTER;
|
||||
import static com.glavsoft.utils.Keymap.K_ESCAPE;
|
||||
import static com.glavsoft.utils.Keymap.K_F1;
|
||||
import static com.glavsoft.utils.Keymap.K_F10;
|
||||
import static com.glavsoft.utils.Keymap.K_F11;
|
||||
import static com.glavsoft.utils.Keymap.K_F12;
|
||||
import static com.glavsoft.utils.Keymap.K_F2;
|
||||
import static com.glavsoft.utils.Keymap.K_F3;
|
||||
import static com.glavsoft.utils.Keymap.K_F4;
|
||||
import static com.glavsoft.utils.Keymap.K_F5;
|
||||
import static com.glavsoft.utils.Keymap.K_F6;
|
||||
import static com.glavsoft.utils.Keymap.K_F7;
|
||||
import static com.glavsoft.utils.Keymap.K_F8;
|
||||
import static com.glavsoft.utils.Keymap.K_F9;
|
||||
import static com.glavsoft.utils.Keymap.K_HOME;
|
||||
import static com.glavsoft.utils.Keymap.K_HYPER_LEFT;
|
||||
import static com.glavsoft.utils.Keymap.K_INSERT;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_0;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_1;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_2;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_3;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_4;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_5;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_6;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_7;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_8;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_9;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_ADD;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_DECIMAL;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_DIVIDE;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_DOWN;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_END;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_ENTER;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_HOME;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_INSERT;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_LEFT;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_MULTIPLY;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_PAGE_DOWN;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_PAGE_UP;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_RIGHT;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_SEPARATOR;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_SUBTRACT;
|
||||
import static com.glavsoft.utils.Keymap.K_KP_UP;
|
||||
import static com.glavsoft.utils.Keymap.K_LEFT;
|
||||
import static com.glavsoft.utils.Keymap.K_META_LEFT;
|
||||
import static com.glavsoft.utils.Keymap.K_PAGE_DOWN;
|
||||
import static com.glavsoft.utils.Keymap.K_PAGE_UP;
|
||||
import static com.glavsoft.utils.Keymap.K_RIGHT;
|
||||
import static com.glavsoft.utils.Keymap.K_SHIFT_LEFT;
|
||||
import static com.glavsoft.utils.Keymap.K_SUPER_LEFT;
|
||||
import static com.glavsoft.utils.Keymap.K_TAB;
|
||||
import static com.glavsoft.utils.Keymap.K_UP;
|
||||
import static com.glavsoft.utils.Keymap.unicode2keysym;
|
||||
|
||||
public class KeyEventListener implements KeyListener {
|
||||
|
||||
|
||||
private ModifierButtonEventListener modifierButtonListener;
|
||||
private boolean convertToAscii;
|
||||
private final Protocol protocol;
|
||||
private KeyboardConvertor convertor;
|
||||
|
||||
public KeyEventListener(Protocol protocol) {
|
||||
this.protocol = protocol;
|
||||
this.convertToAscii = false;
|
||||
}
|
||||
|
||||
private void processKeyEvent(KeyEvent e) {
|
||||
if (processModifierKeys(e)) return;
|
||||
if (processSpecialKeys(e)) return;
|
||||
if (processActionKey(e)) return;
|
||||
|
||||
int keyChar = e.getKeyChar();
|
||||
final int location = e.getKeyLocation();
|
||||
if (0xffff == keyChar) { keyChar = convertToAscii? convertor.convert(keyChar, e) : 0; }
|
||||
if (keyChar < 0x20) {
|
||||
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;
|
||||
case KeyEvent.VK_TAB: keyChar = K_TAB; break;
|
||||
case KeyEvent.VK_ESCAPE: keyChar = K_ESCAPE; break;
|
||||
case KeyEvent.VK_ENTER:
|
||||
keyChar = KeyEvent.KEY_LOCATION_NUMPAD == location ? K_KP_ENTER : K_ENTER;
|
||||
break;
|
||||
default: break; //fall through
|
||||
}
|
||||
|
||||
}
|
||||
} else if (KeyEvent.VK_DELETE == keyChar) {
|
||||
keyChar = K_DELETE;
|
||||
} else if (convertToAscii) {
|
||||
keyChar = convertor.convert(keyChar, e);
|
||||
} else {
|
||||
keyChar = unicode2keysym(keyChar);
|
||||
}
|
||||
|
||||
sendKeyEvent(keyChar, e);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process AltGraph, num pad keys...
|
||||
*/
|
||||
private boolean processSpecialKeys(KeyEvent e) {
|
||||
int keyCode = e.getKeyCode();
|
||||
if (KeyEvent.VK_ALT_GRAPH == keyCode) {
|
||||
sendKeyEvent(K_CTRL_LEFT, e);
|
||||
sendKeyEvent(K_ALT_LEFT, e);
|
||||
return true;
|
||||
}
|
||||
switch (keyCode) {
|
||||
case KeyEvent.VK_NUMPAD0: keyCode = K_KP_0;break;
|
||||
case KeyEvent.VK_NUMPAD1: keyCode = K_KP_1;break;
|
||||
case KeyEvent.VK_NUMPAD2: keyCode = K_KP_2;break;
|
||||
case KeyEvent.VK_NUMPAD3: keyCode = K_KP_3;break;
|
||||
case KeyEvent.VK_NUMPAD4: keyCode = K_KP_4;break;
|
||||
case KeyEvent.VK_NUMPAD5: keyCode = K_KP_5;break;
|
||||
case KeyEvent.VK_NUMPAD6: keyCode = K_KP_6;break;
|
||||
case KeyEvent.VK_NUMPAD7: keyCode = K_KP_7;break;
|
||||
case KeyEvent.VK_NUMPAD8: keyCode = K_KP_8;break;
|
||||
case KeyEvent.VK_NUMPAD9: keyCode = K_KP_9;break;
|
||||
|
||||
case KeyEvent.VK_MULTIPLY: keyCode = K_KP_MULTIPLY;break;
|
||||
case KeyEvent.VK_ADD: keyCode = K_KP_ADD;break;
|
||||
case KeyEvent.VK_SEPARATOR: keyCode = K_KP_SEPARATOR;break;
|
||||
case KeyEvent.VK_SUBTRACT: keyCode = K_KP_SUBTRACT;break;
|
||||
case KeyEvent.VK_DECIMAL: keyCode = K_KP_DECIMAL;break;
|
||||
case KeyEvent.VK_DIVIDE: keyCode = K_KP_DIVIDE;break;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
sendKeyEvent(keyCode, e);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean processActionKey(KeyEvent e) {
|
||||
int keyCode = e.getKeyCode();
|
||||
final int location = e.getKeyLocation();
|
||||
if (e.isActionKey()) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.VK_HOME: keyCode = KeyEvent.KEY_LOCATION_NUMPAD == location? K_KP_HOME: K_HOME; break;
|
||||
case KeyEvent.VK_LEFT: keyCode = KeyEvent.KEY_LOCATION_NUMPAD == location? K_KP_LEFT: K_LEFT; break;
|
||||
case KeyEvent.VK_UP: keyCode = KeyEvent.KEY_LOCATION_NUMPAD == location? K_KP_UP: K_UP; break;
|
||||
case KeyEvent.VK_RIGHT: keyCode = KeyEvent.KEY_LOCATION_NUMPAD == location? K_KP_RIGHT: K_RIGHT; break;
|
||||
case KeyEvent.VK_DOWN: keyCode = KeyEvent.KEY_LOCATION_NUMPAD == location? K_KP_DOWN: K_DOWN; break;
|
||||
case KeyEvent.VK_PAGE_UP: keyCode = KeyEvent.KEY_LOCATION_NUMPAD == location? K_KP_PAGE_UP: K_PAGE_UP; break;
|
||||
case KeyEvent.VK_PAGE_DOWN: keyCode = KeyEvent.KEY_LOCATION_NUMPAD == location? K_KP_PAGE_DOWN: K_PAGE_DOWN; break;
|
||||
case KeyEvent.VK_END: keyCode = KeyEvent.KEY_LOCATION_NUMPAD == location? K_KP_END: K_END; break;
|
||||
case KeyEvent.VK_INSERT: keyCode = KeyEvent.KEY_LOCATION_NUMPAD == location? K_KP_INSERT: K_INSERT; break;
|
||||
case KeyEvent.VK_F1: keyCode = K_F1; break;
|
||||
case KeyEvent.VK_F2: keyCode = K_F2; break;
|
||||
case KeyEvent.VK_F3: keyCode = K_F3; break;
|
||||
case KeyEvent.VK_F4: keyCode = K_F4; break;
|
||||
case KeyEvent.VK_F5: keyCode = K_F5; break;
|
||||
case KeyEvent.VK_F6: keyCode = K_F6; break;
|
||||
case KeyEvent.VK_F7: keyCode = K_F7; break;
|
||||
case KeyEvent.VK_F8: keyCode = K_F8; break;
|
||||
case KeyEvent.VK_F9: keyCode = K_F9; break;
|
||||
case KeyEvent.VK_F10: keyCode = K_F10; break;
|
||||
case KeyEvent.VK_F11: keyCode = K_F11; break;
|
||||
case KeyEvent.VK_F12: keyCode = K_F12; break;
|
||||
|
||||
case KeyEvent.VK_KP_LEFT: keyCode = K_KP_LEFT; break;
|
||||
case KeyEvent.VK_KP_UP: keyCode = K_KP_UP; break;
|
||||
case KeyEvent.VK_KP_RIGHT: keyCode = K_KP_RIGHT; break;
|
||||
case KeyEvent.VK_KP_DOWN: keyCode = K_KP_DOWN; break;
|
||||
|
||||
default: return false; // ignore other 'action' keys
|
||||
}
|
||||
sendKeyEvent(keyCode, e);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean processModifierKeys(KeyEvent e) {
|
||||
int keyCode = e.getKeyCode();
|
||||
switch (keyCode) {
|
||||
case KeyEvent.VK_CONTROL: keyCode = K_CTRL_LEFT; break;
|
||||
case KeyEvent.VK_SHIFT: keyCode = K_SHIFT_LEFT; break;
|
||||
case KeyEvent.VK_ALT: keyCode = K_ALT_LEFT; break;
|
||||
case KeyEvent.VK_META: keyCode = K_META_LEFT; break;
|
||||
// follow two are 'action' keys in java terms but modifier keys actualy
|
||||
case KeyEvent.VK_WINDOWS: keyCode = K_SUPER_LEFT; break;
|
||||
case KeyEvent.VK_CONTEXT_MENU: keyCode = K_HYPER_LEFT; break;
|
||||
default: return false;
|
||||
}
|
||||
if (modifierButtonListener != null) {
|
||||
modifierButtonListener.fireEvent(e);
|
||||
}
|
||||
sendKeyEvent(keyCode +
|
||||
(e.getKeyLocation() == KeyEvent.KEY_LOCATION_RIGHT ? 1 : 0), // "Right" Ctrl/Alt/Shift/Meta deffers frim "Left" ones by +1
|
||||
e);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void sendKeyEvent(int keyChar, KeyEvent e) {
|
||||
protocol.sendMessage(new KeyEventMessage(keyChar, e.getID() == KeyEvent.KEY_PRESSED));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyTyped(KeyEvent e) {
|
||||
e.consume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
processKeyEvent(e);
|
||||
e.consume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
processKeyEvent(e);
|
||||
e.consume();
|
||||
}
|
||||
|
||||
public void addModifierListener(ModifierButtonEventListener modifierButtonListener) {
|
||||
this.modifierButtonListener = modifierButtonListener;
|
||||
}
|
||||
|
||||
public void setConvertToAscii(boolean convertToAscii) {
|
||||
this.convertToAscii = convertToAscii;
|
||||
if (convertToAscii && null == convertor) {
|
||||
convertor = new KeyboardConvertor();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
// Copyright (C) 2010 - 2014 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 java.awt.Toolkit;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class KeyboardConvertor {
|
||||
private static final boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
|
||||
private static final String PATTERN_STRING_FOR_SCANCODE = "scancode=(\\d+)";
|
||||
private Pattern patternForScancode;
|
||||
@SuppressWarnings("serial")
|
||||
private static final Map<Integer, CodePair> keyMap = new HashMap<Integer, CodePair>() {{
|
||||
put(192 /* Back Quote */, new CodePair('`' /*96*/, '~' /*126*/));
|
||||
put(49 /* 1 */, new CodePair('1' /*49*/, '!' /*33*/));
|
||||
put(50 /* 2 */, new CodePair('2' /*50*/, '@' /*64*/));
|
||||
put(51 /* 3 */, new CodePair('3' /*51*/, '#' /*35*/));
|
||||
put(52 /* 4 */, new CodePair('4' /*52*/, '$' /*36*/));
|
||||
put(53 /* 5 */, new CodePair('5' /*53*/, '%' /*37*/));
|
||||
put(54 /* 6 */, new CodePair('6' /*54*/, '^' /*94*/));
|
||||
put(55 /* 7 */, new CodePair('7' /*55*/, '&' /*38*/));
|
||||
put(56 /* 8 */, new CodePair('8' /*56*/, '*' /*42*/));
|
||||
put(57 /* 9 */, new CodePair('9' /*57*/, '(' /*40*/));
|
||||
put(48 /* 0 */, new CodePair('0' /*48*/, ')' /*41*/));
|
||||
put(45 /* Minus */, new CodePair('-' /*45*/, '_' /*95*/));
|
||||
put(61 /* Equals */, new CodePair('=' /*61*/, '+' /*43*/));
|
||||
put(92 /* Back Slash */, new CodePair('\\' /*92*/, '|' /*124*/));
|
||||
|
||||
put(81 /* Q */, new CodePair('q' /*113*/, 'Q' /*81*/));
|
||||
put(87 /* W */, new CodePair('w' /*119*/, 'W' /*87*/));
|
||||
put(69 /* E */, new CodePair('e' /*101*/, 'E' /*69*/));
|
||||
put(82 /* R */, new CodePair('r' /*114*/, 'R' /*82*/));
|
||||
put(84 /* T */, new CodePair('t' /*116*/, 'T' /*84*/));
|
||||
put(89 /* Y */, new CodePair('y' /*121*/, 'Y' /*89*/));
|
||||
put(85 /* U */, new CodePair('u' /*117*/, 'U' /*85*/));
|
||||
put(73 /* I */, new CodePair('i' /*105*/, 'I' /*73*/));
|
||||
put(79 /* O */, new CodePair('o' /*111*/, 'O' /*79*/));
|
||||
put(80 /* P */, new CodePair('p' /*112*/, 'P' /*80*/));
|
||||
put(91 /* Open Bracket */, new CodePair('[' /*91*/, '{' /*123*/));
|
||||
put(93 /* Close Bracket */, new CodePair(']' /*93*/, '}' /*125*/));
|
||||
|
||||
put(65 /* A */, new CodePair('a' /*97*/, 'A' /*65*/));
|
||||
put(83 /* S */, new CodePair('s' /*115*/, 'S' /*83*/));
|
||||
put(68 /* D */, new CodePair('d' /*100*/, 'D' /*68*/));
|
||||
put(70 /* F */, new CodePair('f' /*102*/, 'F' /*70*/));
|
||||
put(71 /* G */, new CodePair('g' /*103*/, 'G' /*71*/));
|
||||
put(72 /* H */, new CodePair('h' /*104*/, 'H' /*72*/));
|
||||
put(74 /* J */, new CodePair('j' /*106*/, 'J' /*74*/));
|
||||
put(75 /* K */, new CodePair('k' /*107*/, 'K' /*75*/));
|
||||
put(76 /* L */, new CodePair('l' /*108*/, 'L' /*76*/));
|
||||
put(59 /* Semicolon */, new CodePair(';' /*59*/, ':' /*58*/));
|
||||
put(222 /* Quote */, new CodePair('\'' /*39*/, '"' /*34*/));
|
||||
|
||||
put(90 /* Z */, new CodePair('z' /*122*/, 'Z' /*90*/));
|
||||
put(88 /* X */, new CodePair('x' /*120*/, 'X' /*88*/));
|
||||
put(67 /* C */, new CodePair('c' /*99*/, 'C' /*67*/));
|
||||
put(86 /* V */, new CodePair('v' /*118*/, 'V' /*86*/));
|
||||
put(66 /* B */, new CodePair('b' /*98*/, 'B' /*66*/));
|
||||
put(78 /* N */, new CodePair('n' /*110*/, 'N' /*78*/));
|
||||
put(77 /* M */, new CodePair('m' /*109*/, 'M' /*77*/));
|
||||
put(44 /* Comma */, new CodePair(',' /*44*/, '<' /*60*/));
|
||||
put(46 /* Period */, new CodePair('.' /*46*/, '>' /*62*/));
|
||||
put(47 /* Slash */, new CodePair('/' /*47*/, '?' /*63*/));
|
||||
|
||||
// put(60 /* Less */, new CodePair('<' /*60*/, '>')); // 105-th key on 105-keys keyboard (less/greather/bar)
|
||||
put(KeyEvent.VK_LESS /* Less */, new CodePair('<' /*60*/, '>')); // 105-th key on 105-keys keyboard (less/greather/bar)
|
||||
// put(KeyEvent.VK_GREATER /* Greater */, new CodePair('<' /*60*/, '>')); // 105-th key on 105-keys keyboard (less/greather/bar)
|
||||
}};
|
||||
|
||||
private boolean canCheckCapsWithToolkit;
|
||||
|
||||
public KeyboardConvertor() {
|
||||
try {
|
||||
Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_CAPS_LOCK);
|
||||
canCheckCapsWithToolkit = true;
|
||||
} catch (Exception e) {
|
||||
canCheckCapsWithToolkit = false;
|
||||
}
|
||||
if (isWindows) {
|
||||
patternForScancode = Pattern.compile(PATTERN_STRING_FOR_SCANCODE);
|
||||
}
|
||||
}
|
||||
|
||||
public int convert(int keyChar, KeyEvent ev) {
|
||||
int keyCode = ev.getKeyCode();
|
||||
boolean isShiftDown = ev.isShiftDown();
|
||||
CodePair codePair = keyMap.get(keyCode);
|
||||
if (null == codePair)
|
||||
return keyChar;
|
||||
if (isWindows) {
|
||||
final Matcher matcher = patternForScancode.matcher(ev.paramString());
|
||||
if (matcher.matches()) {
|
||||
try {
|
||||
int scancode = Integer.parseInt(matcher.group(1));
|
||||
if (90 == keyCode && 21 == scancode) { // deutsch z->y
|
||||
codePair = keyMap.get(89); // y
|
||||
} else if (89 == keyCode && 44 == scancode) { // deutsch y->z
|
||||
codePair = keyMap.get(90); // z
|
||||
}
|
||||
} catch (NumberFormatException e) { /*nop*/ }
|
||||
}
|
||||
}
|
||||
boolean isCapsLock = false;
|
||||
if (Character.isLetter(codePair.code)) {
|
||||
if (canCheckCapsWithToolkit) {
|
||||
try {
|
||||
isCapsLock =
|
||||
Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_CAPS_LOCK);
|
||||
} catch (Exception ex) { /* nop */ }
|
||||
}
|
||||
}
|
||||
return isShiftDown && ! isCapsLock || ! isShiftDown && isCapsLock ?
|
||||
codePair.codeShifted :
|
||||
codePair.code;
|
||||
}
|
||||
|
||||
private static class CodePair {
|
||||
public int code, codeShifted;
|
||||
public CodePair(int code, int codeShifted) {
|
||||
this.code = code;
|
||||
this.codeShifted = codeShifted;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright (C) 2010 - 2014 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 javax.swing.JToggleButton;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ModifierButtonEventListener {
|
||||
Map<Integer, JToggleButton> buttons = new HashMap<Integer, JToggleButton>();
|
||||
public void addButton(int keyCode, JToggleButton button) {
|
||||
buttons.put(keyCode, button);
|
||||
}
|
||||
public void fireEvent(KeyEvent e) {
|
||||
int code = e.getKeyCode();
|
||||
if (buttons.containsKey(code)) {
|
||||
buttons.get(code).setSelected(e.getID() == KeyEvent.KEY_PRESSED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2010 - 2014 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 java.awt.event.MouseEvent;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public interface MouseEnteredListener {
|
||||
void mouseEnteredEvent(MouseEvent mouseEvent);
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.IRepaintController;
|
||||
import com.glavsoft.rfb.client.PointerEventMessage;
|
||||
import com.glavsoft.rfb.protocol.Protocol;
|
||||
|
||||
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;
|
||||
|
||||
public class MouseEventListener extends MouseInputAdapter
|
||||
implements MouseWheelListener {
|
||||
private static final byte BUTTON_LEFT = 1;
|
||||
private static final byte BUTTON_MIDDLE = 1 << 1;
|
||||
private static final byte BUTTON_RIGHT = 1 << 2;
|
||||
private static final byte WHEEL_UP = 1 << 3;
|
||||
private static final byte WHEEL_DOWN = 1 << 4;
|
||||
|
||||
private final IRepaintController repaintController;
|
||||
private final Protocol protocol;
|
||||
private volatile double scaleFactor;
|
||||
|
||||
public MouseEventListener(IRepaintController repaintController, Protocol protocol,
|
||||
double scaleFactor) {
|
||||
this.repaintController = repaintController;
|
||||
this.protocol = protocol;
|
||||
this.scaleFactor = scaleFactor;
|
||||
}
|
||||
|
||||
public void processMouseEvent(MouseEvent mouseEvent,
|
||||
MouseWheelEvent mouseWheelEvent, boolean moved) {
|
||||
byte buttonMask = 0;
|
||||
if (null == mouseEvent && mouseWheelEvent != null) {
|
||||
mouseEvent = mouseWheelEvent;
|
||||
}
|
||||
assert mouseEvent != null;
|
||||
short x = (short) (mouseEvent.getX() / scaleFactor);
|
||||
short y = (short) (mouseEvent.getY() / scaleFactor);
|
||||
if (moved) {
|
||||
repaintController.updateCursorPosition(x, y);
|
||||
}
|
||||
|
||||
int modifiersEx = mouseEvent.getModifiersEx();
|
||||
// left
|
||||
buttonMask |= (modifiersEx & InputEvent.BUTTON1_DOWN_MASK) != 0 ?
|
||||
BUTTON_LEFT : 0;
|
||||
// middle
|
||||
buttonMask |= (modifiersEx & InputEvent.BUTTON2_DOWN_MASK) != 0 ?
|
||||
BUTTON_MIDDLE : 0;
|
||||
// right
|
||||
buttonMask |= (modifiersEx & InputEvent.BUTTON3_DOWN_MASK) != 0 ?
|
||||
BUTTON_RIGHT : 0;
|
||||
|
||||
// wheel
|
||||
if (mouseWheelEvent != null) {
|
||||
int notches = mouseWheelEvent.getWheelRotation();
|
||||
byte wheelMask = notches < 0 ? WHEEL_UP : WHEEL_DOWN;
|
||||
// handle more then 1 notches
|
||||
notches = Math.abs(notches);
|
||||
for (int i = 1; i < notches; ++i) {
|
||||
protocol.sendMessage(new PointerEventMessage((byte) (buttonMask | wheelMask), x, y));
|
||||
protocol.sendMessage(new PointerEventMessage(buttonMask, x, y));
|
||||
}
|
||||
protocol.sendMessage(new PointerEventMessage((byte) (buttonMask | wheelMask), x, y));
|
||||
}
|
||||
protocol.sendMessage(new PointerEventMessage(buttonMask, x, y));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent mouseEvent) {
|
||||
mouseEvent.getComponent().requestFocusInWindow();
|
||||
processMouseEvent(mouseEvent, null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent mouseEvent) {
|
||||
processMouseEvent(mouseEvent, null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent mouseEvent) {
|
||||
processMouseEvent(mouseEvent, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent mouseEvent) {
|
||||
processMouseEvent(mouseEvent, null, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseWheelMoved(MouseWheelEvent emouseWheelEvent) {
|
||||
processMouseEvent(null, emouseWheelEvent, false);
|
||||
}
|
||||
|
||||
public void setScaleFactor(double scaleFactor) {
|
||||
this.scaleFactor = scaleFactor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
// Copyright (C) 2010 - 2014 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.drawing.Renderer;
|
||||
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||
import com.glavsoft.rfb.encoding.decoder.FramebufferUpdateRectangle;
|
||||
import com.glavsoft.transport.Transport;
|
||||
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.DataBuffer;
|
||||
import java.awt.image.DataBufferInt;
|
||||
import java.awt.image.DirectColorModel;
|
||||
import java.awt.image.ImageObserver;
|
||||
import java.awt.image.Raster;
|
||||
import java.awt.image.SampleModel;
|
||||
import java.awt.image.WritableRaster;
|
||||
import java.util.concurrent.BrokenBarrierException;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
public class RendererImpl extends Renderer implements ImageObserver {
|
||||
CyclicBarrier barrier = new CyclicBarrier(2);
|
||||
private final Image offscreenImage;
|
||||
|
||||
public RendererImpl(Transport transport, int width, int height, PixelFormat pixelFormat) {
|
||||
if (0 == width) width = 1;
|
||||
if (0 == height) height = 1;
|
||||
init(width, height, pixelFormat);
|
||||
ColorModel colorModel = new DirectColorModel(24, 0xff0000, 0xff00, 0xff);
|
||||
SampleModel sampleModel = colorModel.createCompatibleSampleModel(width,
|
||||
height);
|
||||
|
||||
DataBuffer dataBuffer = new DataBufferInt(pixels, width * height);
|
||||
WritableRaster raster = Raster.createWritableRaster(sampleModel,
|
||||
dataBuffer, null);
|
||||
offscreenImage = new BufferedImage(colorModel, raster, false, null);
|
||||
cursor = new SoftCursorImpl(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw jpeg image data
|
||||
*
|
||||
* @param bytes jpeg image data array
|
||||
* @param offset start offset at data array
|
||||
* @param jpegBufferLength jpeg image data array length
|
||||
* @param rect image location and dimensions
|
||||
*/
|
||||
@Override
|
||||
public void drawJpegImage(byte[] bytes, int offset, int jpegBufferLength,
|
||||
FramebufferUpdateRectangle rect) {
|
||||
Image jpegImage = Toolkit.getDefaultToolkit().createImage(bytes,
|
||||
offset, jpegBufferLength);
|
||||
Toolkit.getDefaultToolkit().prepareImage(jpegImage, -1, -1, this);
|
||||
try {
|
||||
barrier.await(3, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
// nop
|
||||
} catch (BrokenBarrierException e) {
|
||||
// nop
|
||||
} catch (TimeoutException e) {
|
||||
// nop
|
||||
}
|
||||
Graphics graphics = offscreenImage.getGraphics();
|
||||
graphics.drawImage(jpegImage, rect.x, rect.y, rect.width, rect.height, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean imageUpdate(Image img, int infoflags, int x, int y,
|
||||
int width, int height) {
|
||||
boolean isReady = (infoflags & (ALLBITS | ABORT)) != 0;
|
||||
if (isReady) {
|
||||
try {
|
||||
barrier.await();
|
||||
} catch (InterruptedException e) {
|
||||
// nop
|
||||
} catch (BrokenBarrierException e) {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
return !isReady;
|
||||
}
|
||||
|
||||
/* Swing specific interface */
|
||||
public Image getOffscreenImage() {
|
||||
return offscreenImage;
|
||||
}
|
||||
|
||||
public void paintImageOn(Graphics g, int width, int height, double scaleFactor) {
|
||||
lock.lock();
|
||||
try {
|
||||
g.drawImage(offscreenImage, 0, 0, null);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void paintCursorOn(Graphics g, boolean force) {
|
||||
synchronized (cursor.getLock()) {
|
||||
Image cursorImage = ((SoftCursorImpl) cursor).getImage();
|
||||
if (cursorImage != null && (force ||
|
||||
g.getClipBounds().intersects(cursor.rX, cursor.rY, cursor.width, cursor.height))) {
|
||||
g.drawImage(cursorImage, cursor.rX, cursor.rY, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public SoftCursorImpl getCursor() {
|
||||
return (SoftCursorImpl) cursor;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// Copyright (C) 2010 - 2014 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.drawing.SoftCursor;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.image.MemoryImageSource;
|
||||
|
||||
public class SoftCursorImpl extends SoftCursor {
|
||||
private Image cursorImage;
|
||||
|
||||
public SoftCursorImpl(int hotX, int hotY, int width, int height) {
|
||||
super(hotX, hotY, width, height);
|
||||
}
|
||||
|
||||
public Image getImage() {
|
||||
return cursorImage;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createNewCursorImage(int[] cursorPixels, int hotX, int hotY, int width, int height) {
|
||||
cursorImage = Toolkit.getDefaultToolkit().createImage(
|
||||
new MemoryImageSource(width, height, cursorPixels, 0, width));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
272
plugins/vnc/src/main/java/com/glavsoft/viewer/swing/Surface.java
Normal file
272
plugins/vnc/src/main/java/com/glavsoft/viewer/swing/Surface.java
Normal file
@@ -0,0 +1,272 @@
|
||||
// Copyright (C) 2010 - 2014 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.drawing.Renderer;
|
||||
import com.glavsoft.rfb.IChangeSettingsListener;
|
||||
import com.glavsoft.rfb.IRepaintController;
|
||||
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||
import com.glavsoft.rfb.encoding.decoder.FramebufferUpdateRectangle;
|
||||
import com.glavsoft.rfb.protocol.Protocol;
|
||||
import com.glavsoft.rfb.protocol.ProtocolSettings;
|
||||
import com.glavsoft.transport.Transport;
|
||||
import com.glavsoft.viewer.settings.LocalMouseCursorShape;
|
||||
import com.glavsoft.viewer.settings.UiSettings;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.RenderingHints;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class Surface extends JPanel implements IRepaintController, IChangeSettingsListener {
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
private SoftCursorImpl cursor;
|
||||
private volatile RendererImpl renderer;
|
||||
private MouseEventListener mouseEventListener;
|
||||
private KeyEventListener keyEventListener;
|
||||
private boolean showCursor;
|
||||
private ModifierButtonEventListener modifierButtonListener;
|
||||
private boolean isUserInputEnabled = false;
|
||||
private final Protocol protocol;
|
||||
private double scaleFactor;
|
||||
public Dimension oldSize;
|
||||
|
||||
@Override
|
||||
public boolean isDoubleBuffered() {
|
||||
// TODO returning false in some reason may speed ups drawing, but may
|
||||
// not. Needed in challenging.
|
||||
return false;
|
||||
}
|
||||
|
||||
public Surface(Protocol protocol, double scaleFactor, LocalMouseCursorShape mouseCursorShape) {
|
||||
this.protocol = protocol;
|
||||
this.scaleFactor = scaleFactor;
|
||||
this.setFocusable(true);
|
||||
init(protocol.getFbWidth(), protocol.getFbHeight());
|
||||
oldSize = getPreferredSize();
|
||||
|
||||
if (!protocol.getSettings().isViewOnly()) {
|
||||
setUserInputEnabled(true, protocol.getSettings().isConvertToAscii());
|
||||
}
|
||||
showCursor = protocol.getSettings().isShowRemoteCursor();
|
||||
setLocalCursorShape(mouseCursorShape);
|
||||
}
|
||||
|
||||
public void setViewerWindow(SwingViewerWindow viewerWindow) {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
private void setUserInputEnabled(boolean enable, boolean convertToAscii) {
|
||||
if (enable == isUserInputEnabled) return;
|
||||
isUserInputEnabled = enable;
|
||||
if (enable) {
|
||||
if (null == mouseEventListener) {
|
||||
mouseEventListener = new MouseEventListener(this, protocol, scaleFactor);
|
||||
}
|
||||
addMouseListener(mouseEventListener);
|
||||
addMouseMotionListener(mouseEventListener);
|
||||
addMouseWheelListener(mouseEventListener);
|
||||
|
||||
setFocusTraversalKeysEnabled(false);
|
||||
if (null == keyEventListener) {
|
||||
keyEventListener = new KeyEventListener(protocol);
|
||||
if (modifierButtonListener != null) {
|
||||
keyEventListener.addModifierListener(modifierButtonListener);
|
||||
}
|
||||
}
|
||||
keyEventListener.setConvertToAscii(convertToAscii);
|
||||
addKeyListener(keyEventListener);
|
||||
enableInputMethods(false);
|
||||
} else {
|
||||
removeMouseListener(mouseEventListener);
|
||||
removeMouseMotionListener(mouseEventListener);
|
||||
removeMouseWheelListener(mouseEventListener);
|
||||
removeKeyListener(keyEventListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Renderer createRenderer(Transport transport, int width, int height, PixelFormat pixelFormat) {
|
||||
renderer = new RendererImpl(transport, width, height, pixelFormat);
|
||||
cursor = renderer.getCursor();
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
init(renderer.getWidth(), renderer.getHeight());
|
||||
updateFrameSize();
|
||||
} else {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
init(renderer.getWidth(), renderer.getHeight());
|
||||
updateFrameSize();
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
Logger.getLogger(getClass().getName()).severe("Interrupted: " + e.getMessage());
|
||||
protocol.cleanUpSession("Interrupted: " + e.getMessage());
|
||||
} catch (InvocationTargetException e) {
|
||||
Logger.getLogger(getClass().getName()).severe("Fatal error: " + e.getCause().getMessage());
|
||||
protocol.cleanUpSession("Fatal error: " + e.getCause().getMessage());
|
||||
}
|
||||
}
|
||||
return renderer;
|
||||
}
|
||||
|
||||
private void init(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
setSize(getPreferredSize());
|
||||
}
|
||||
|
||||
private void updateFrameSize() {
|
||||
setSize(getPreferredSize());
|
||||
// viewerWindow.pack();
|
||||
requestFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g) { // EDT
|
||||
if (null == renderer) return;
|
||||
|
||||
if (scaleFactor != 1.0) {
|
||||
((Graphics2D) g).scale(scaleFactor, scaleFactor);
|
||||
}
|
||||
|
||||
|
||||
final Object appleContentScaleFactor = getToolkit().getDesktopProperty("apple.awt.contentScaleFactor");
|
||||
((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING,
|
||||
(appleContentScaleFactor != null && (Integer) appleContentScaleFactor != 1) ?
|
||||
RenderingHints.VALUE_RENDER_SPEED : // speed for Apple Retina display
|
||||
RenderingHints.VALUE_RENDER_DEFAULT); // quality for others
|
||||
renderer.paintImageOn(g, super.getWidth(), super.getHeight(),scaleFactor); // internally locked with renderer.lock
|
||||
if (showCursor) {
|
||||
renderer.paintCursorOn(g, scaleFactor != 1);// internally locked with cursor.lock
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return new Dimension((int) (this.width * scaleFactor), (int) (this.height * scaleFactor));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMinimumSize() {
|
||||
return getPreferredSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getMaximumSize() {
|
||||
return getPreferredSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves protocol and simply invokes native JPanel repaint method which
|
||||
* asyncroniously register repaint request using invokeLater to repaint be
|
||||
* runned in Swing event dispatcher thread. So may be called from other
|
||||
* threads.
|
||||
*/
|
||||
@Override
|
||||
public void repaintBitmap(FramebufferUpdateRectangle rect) {
|
||||
repaintBitmap(rect.x, rect.y, rect.width, rect.height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void repaintBitmap(int x, int y, int width, int height) {
|
||||
repaint((int) (x * scaleFactor), (int) (y * scaleFactor),
|
||||
(int) Math.ceil(width * scaleFactor), (int) Math.ceil(height * scaleFactor));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void repaintCursor() {
|
||||
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),
|
||||
(int) Math.ceil(cursor.width * scaleFactor) + 1, (int) Math.ceil(cursor.height * scaleFactor) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCursorPosition(short x, short y) {
|
||||
synchronized (cursor.getLock()) {
|
||||
cursor.updatePosition(x, y);
|
||||
repaintCursor();
|
||||
}
|
||||
}
|
||||
|
||||
private void showCursor(boolean show) {
|
||||
synchronized (cursor.getLock()) {
|
||||
showCursor = show;
|
||||
}
|
||||
}
|
||||
|
||||
public void addModifierListener(ModifierButtonEventListener modifierButtonListener) {
|
||||
this.modifierButtonListener = modifierButtonListener;
|
||||
if (keyEventListener != null) {
|
||||
keyEventListener.addModifierListener(modifierButtonListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingsChanged(SettingsChangedEvent e) {
|
||||
if (ProtocolSettings.isRfbSettingsChangedFired(e)) {
|
||||
ProtocolSettings settings = (ProtocolSettings) e.getSource();
|
||||
setUserInputEnabled(!settings.isViewOnly(), settings.isConvertToAscii());
|
||||
showCursor(settings.isShowRemoteCursor());
|
||||
} else if (UiSettings.isUiSettingsChangedFired(e)) {
|
||||
UiSettings uiSettings = (UiSettings) e.getSource();
|
||||
oldSize = getPreferredSize();
|
||||
scaleFactor = uiSettings.getScaleFactor();
|
||||
if (uiSettings.isChangedMouseCursorShape()) {
|
||||
setLocalCursorShape(uiSettings.getMouseCursorShape());
|
||||
}
|
||||
}
|
||||
mouseEventListener.setScaleFactor(scaleFactor);
|
||||
updateFrameSize();
|
||||
}
|
||||
|
||||
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.initColorDecoder(pixelFormat);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright (C) 2010 - 2014 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.mvp.Presenter;
|
||||
import com.glavsoft.viewer.workers.AbstractConnectionWorkerFactory;
|
||||
import com.glavsoft.viewer.workers.NetworkConnectionWorker;
|
||||
import com.glavsoft.viewer.workers.RfbConnectionWorker;
|
||||
|
||||
import java.awt.Component;
|
||||
|
||||
/**
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public class SwingConnectionWorkerFactory extends AbstractConnectionWorkerFactory {
|
||||
|
||||
private Component parent;
|
||||
private String predefinedPassword;
|
||||
private final ConnectionPresenter presenter;
|
||||
private final SwingViewerWindowFactory viewerWindowFactory;
|
||||
|
||||
public SwingConnectionWorkerFactory(Component parent, String predefinedPassword, Presenter presenter,
|
||||
SwingViewerWindowFactory viewerWindowFactory) {
|
||||
this.parent = parent;
|
||||
this.predefinedPassword = predefinedPassword;
|
||||
this.presenter = (ConnectionPresenter) presenter;
|
||||
this.viewerWindowFactory = viewerWindowFactory;
|
||||
}
|
||||
|
||||
public SwingConnectionWorkerFactory(Component parent, Presenter connectionPresenter, SwingViewerWindowFactory viewerWindowFactory) {
|
||||
this(parent, "", connectionPresenter, viewerWindowFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkConnectionWorker createNetworkConnectionWorker() {
|
||||
return new SwingNetworkConnectionWorker(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RfbConnectionWorker createRfbConnectionWorker() {
|
||||
return new SwingRfbConnectionWorker(predefinedPassword, presenter, parent, viewerWindowFactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPredefinedPassword(String predefinedPassword) {
|
||||
this.predefinedPassword = predefinedPassword;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright (C) 2010 - 2014 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.mvp.Presenter;
|
||||
import com.glavsoft.viewer.settings.ConnectionParams;
|
||||
import com.glavsoft.viewer.swing.ssh.SshConnectionManager;
|
||||
import com.glavsoft.viewer.workers.NetworkConnectionWorker;
|
||||
|
||||
import javax.swing.SwingWorker;
|
||||
import java.awt.Component;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
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 Component parent;
|
||||
private Logger logger;
|
||||
private boolean hasSshSupport;
|
||||
private ConnectionParams connectionParams;
|
||||
private ConnectionPresenter presenter;
|
||||
private SshConnectionManager sshConnectionManager;
|
||||
|
||||
|
||||
public SwingNetworkConnectionWorker(Component parent) {
|
||||
this.parent = parent;
|
||||
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 com.glavsoft.viewer.swing.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()) {
|
||||
try {
|
||||
sshConnectionManager = SshConnectionManager.createManager(parent);
|
||||
} catch (ConnectionErrorException e) {
|
||||
hasSshSupport = false; // TODO propogate into upper level?
|
||||
throw e;
|
||||
}
|
||||
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: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
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 (CancelConnectionQuietlyException cce) {
|
||||
logger.warning("Cancelled by user: " + cce.getMessage());
|
||||
// errorMessage = null; // exit without dialog showing
|
||||
} catch (CancelConnectionException cce) {
|
||||
logger.severe("Cancelled: " + cce.getMessage());
|
||||
errorMessage = cce.getMessage();
|
||||
} catch (SecurityException 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();
|
||||
}
|
||||
if (errorMessage != null) {
|
||||
presenter.showConnectionErrorDialog(errorMessage);
|
||||
}
|
||||
presenter.clearMessage();
|
||||
presenter.connectionFailed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConnectionParams(ConnectionParams connectionParams) {
|
||||
this.connectionParams = connectionParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPresenter(Presenter presenter) {
|
||||
this.presenter = (ConnectionPresenter) presenter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHasSshSupport(boolean hasSshSupport) {
|
||||
this.hasSshSupport = hasSshSupport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel() {
|
||||
if (hasSshSupport && sshConnectionManager != null && sshConnectionManager.isConnected()) {
|
||||
sshConnectionManager.closeConnection();
|
||||
sshConnectionManager = null;
|
||||
}
|
||||
return super.cancel(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
// Copyright (C) 2010 - 2014 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.AuthenticationFailedException;
|
||||
import com.glavsoft.exceptions.FatalException;
|
||||
import com.glavsoft.exceptions.TransportException;
|
||||
import com.glavsoft.exceptions.UnsupportedProtocolVersionException;
|
||||
import com.glavsoft.exceptions.UnsupportedSecurityTypeException;
|
||||
import com.glavsoft.rfb.IRequestString;
|
||||
import com.glavsoft.rfb.IRfbSessionListener;
|
||||
import com.glavsoft.rfb.protocol.Protocol;
|
||||
import com.glavsoft.rfb.protocol.ProtocolSettings;
|
||||
import com.glavsoft.transport.BaudrateMeter;
|
||||
import com.glavsoft.transport.Transport;
|
||||
import com.glavsoft.utils.Strings;
|
||||
import com.glavsoft.utils.ViewerControlApi;
|
||||
import com.glavsoft.viewer.settings.UiSettings;
|
||||
import com.glavsoft.viewer.swing.gui.RequestSomethingDialog;
|
||||
import com.glavsoft.viewer.workers.ConnectionWorker;
|
||||
import com.glavsoft.viewer.workers.RfbConnectionWorker;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import java.awt.Component;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
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 Component parent;
|
||||
private SwingViewerWindowFactory viewerWindowFactory;
|
||||
private Logger logger;
|
||||
private volatile boolean isStoppingProcess;
|
||||
private SwingViewerWindow viewerWindow;
|
||||
private String connectionString;
|
||||
private Protocol workingProtocol;
|
||||
private Socket workingSocket;
|
||||
private ProtocolSettings rfbSettings;
|
||||
private UiSettings uiSettings;
|
||||
private ViewerControlApi viewerControlApi;
|
||||
|
||||
@Override
|
||||
public Void doInBackground() throws Exception {
|
||||
if (null == workingSocket) throw new ConnectionErrorException("Null socket");
|
||||
workingSocket.setTcpNoDelay(true); // disable Nagle algorithm
|
||||
Transport transport = new Transport(workingSocket);
|
||||
final BaudrateMeter baudrateMeter = new BaudrateMeter();
|
||||
transport.setBaudrateMeter(baudrateMeter);
|
||||
workingProtocol = new Protocol(transport,
|
||||
new PasswordChooser(connectionString, parent, this),
|
||||
rfbSettings);
|
||||
workingProtocol.setConnectionIdRetriever(new ConnectionIdChooser(parent, this));
|
||||
viewerControlApi = new ViewerControlApi(workingProtocol, baudrateMeter);
|
||||
String message = "Handshaking with remote host";
|
||||
logger.info(message);
|
||||
publish(message);
|
||||
|
||||
workingProtocol.handshake();
|
||||
return null;
|
||||
}
|
||||
|
||||
public SwingRfbConnectionWorker(String predefinedPassword, ConnectionPresenter presenter, Component parent,
|
||||
SwingViewerWindowFactory viewerWindowFactory) {
|
||||
this.predefinedPassword = predefinedPassword;
|
||||
this.presenter = presenter;
|
||||
this.parent = parent;
|
||||
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.getRepaintController(), 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(errorTitle + ": " + errorMessage);
|
||||
} catch (UnsupportedSecurityTypeException e) {
|
||||
errorTitle = "Unsupported Security Type";
|
||||
errorMessage = e.getMessage();
|
||||
logger.severe(errorTitle + ": " + errorMessage);
|
||||
} catch (AuthenticationFailedException e) {
|
||||
errorTitle = "Authentication Failed";
|
||||
errorMessage = e.getMessage();
|
||||
logger.severe(errorTitle + ": " + errorMessage);
|
||||
presenter.clearPredefinedPassword();
|
||||
} catch (TransportException e) {
|
||||
errorTitle = "Connection Error";
|
||||
final Throwable cause = e.getCause();
|
||||
errorMessage = errorTitle + " : " + e.getMessage();
|
||||
if (cause != null) {
|
||||
if (cause instanceof EOFException)
|
||||
errorMessage += ", possible reason: remote host not responding.";
|
||||
logger.throwing("", "", cause);
|
||||
}
|
||||
logger.severe(errorMessage);
|
||||
} catch (EOFException e) {
|
||||
errorTitle = "Connection Error";
|
||||
errorMessage = errorTitle + ": " + e.getMessage();
|
||||
logger.severe(errorMessage);
|
||||
} catch (IOException e) {
|
||||
errorTitle = "Connection Error";
|
||||
errorMessage = errorTitle + ": " + e.getMessage();
|
||||
logger.severe(errorMessage);
|
||||
} catch (FatalException e) {
|
||||
errorTitle = "Connection Error";
|
||||
errorMessage = errorTitle + ": " + e.getMessage();
|
||||
logger.severe(errorMessage);
|
||||
} catch (Throwable e) {
|
||||
errorTitle = "Error";
|
||||
errorMessage = errorTitle + ": " + 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 void cleanUpUISessionAndConnection() {
|
||||
synchronized (this) {
|
||||
isStoppingProcess = true;
|
||||
}
|
||||
if (workingSocket != null && workingSocket.isConnected()) {
|
||||
try {
|
||||
workingSocket.close();
|
||||
} catch (IOException e) { /*nop*/ }
|
||||
}
|
||||
if (viewerWindow != null) {
|
||||
viewerWindow.close();
|
||||
}
|
||||
synchronized (this) {
|
||||
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 IRequestString {
|
||||
private String connectionString;
|
||||
private final Component parent;
|
||||
private final ConnectionWorker onCancel;
|
||||
|
||||
private PasswordChooser(String connectionString, Component parent, ConnectionWorker onCancel) {
|
||||
this.connectionString = connectionString;
|
||||
this.parent = parent;
|
||||
this.onCancel = onCancel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResult() {
|
||||
return Strings.isTrimmedEmpty(predefinedPassword) ?
|
||||
askPassword() :
|
||||
predefinedPassword;
|
||||
}
|
||||
|
||||
private String askPassword() {
|
||||
RequestSomethingDialog dialog =
|
||||
new RequestSomethingDialog(parent, "VNC Authentication", true,
|
||||
"Server '" + connectionString + "' requires VNC authentication", "Password:")
|
||||
.setOkLabel("Login")
|
||||
.setInputFieldLength(12);
|
||||
if (!dialog.askResult()) {
|
||||
onCancel.cancel();
|
||||
}
|
||||
return dialog.getResult();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewerControlApi getViewerControlApi() {
|
||||
return viewerControlApi;
|
||||
}
|
||||
|
||||
private class ConnectionIdChooser implements IRequestString {
|
||||
private final Component parent;
|
||||
private final ConnectionWorker<Void> onCancel;
|
||||
|
||||
public ConnectionIdChooser(Component parent, ConnectionWorker<Void> onCancel) {
|
||||
this.parent = parent;
|
||||
this.onCancel = onCancel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResult() {
|
||||
RequestSomethingDialog dialog =
|
||||
new RequestSomethingDialog(parent, "TcpDispatcher ConnectionId", false,
|
||||
"TcpDispatcher requires Connection Id.",
|
||||
"Please get the Connection Id from you peer by any other communication channel\n(ex. phone call or IM) and insert it into the form field below.",
|
||||
"Connection Id:")
|
||||
.setInputFieldLength(18);
|
||||
if (!dialog.askResult()) {
|
||||
onCancel.cancel();
|
||||
}
|
||||
return dialog.getResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,993 @@
|
||||
// Copyright (C) 2010 - 2014 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.IRepaintController;
|
||||
import com.glavsoft.rfb.client.KeyEventMessage;
|
||||
import com.glavsoft.rfb.protocol.Protocol;
|
||||
import com.glavsoft.rfb.protocol.ProtocolSettings;
|
||||
import com.glavsoft.rfb.protocol.tunnel.TunnelType;
|
||||
import com.glavsoft.utils.Keymap;
|
||||
import com.glavsoft.utils.Strings;
|
||||
import com.glavsoft.viewer.settings.UiSettings;
|
||||
import com.glavsoft.viewer.swing.gui.OptionsDialog;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLayeredPane;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JToggleButton;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.BevelBorder;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Container;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.GraphicsConfiguration;
|
||||
import java.awt.Insets;
|
||||
import java.awt.MouseInfo;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.event.ItemEvent;
|
||||
import java.awt.event.ItemListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.event.MouseMotionListener;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.util.ArrayList;
|
||||
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, MouseEnteredListener {
|
||||
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 JScrollPane scroller;
|
||||
private JFrame frame;
|
||||
private boolean forceResizable = true;
|
||||
private ButtonsBar buttonsBar;
|
||||
private Surface surface;
|
||||
private boolean isSeparateFrame;
|
||||
private ViewerEventsListener viewerEventsListener;
|
||||
private final String appName;
|
||||
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;
|
||||
private Container container;
|
||||
private static Logger logger = Logger.getLogger(SwingViewerWindow.class.getName());
|
||||
|
||||
public SwingViewerWindow(Protocol workingProtocol, ProtocolSettings rfbSettings, UiSettings uiSettings, Surface surface,
|
||||
boolean isSeparateFrame, boolean isApplet, ViewerEventsListener viewerEventsListener,
|
||||
String appName, String connectionString,
|
||||
ConnectionPresenter presenter, Container externalContainer) {
|
||||
this.workingProtocol = workingProtocol;
|
||||
this.rfbSettings = rfbSettings;
|
||||
this.uiSettings = uiSettings;
|
||||
this.surface = surface;
|
||||
this.isSeparateFrame = isSeparateFrame;
|
||||
this.viewerEventsListener = viewerEventsListener;
|
||||
this.appName = appName;
|
||||
this.connectionString = connectionString;
|
||||
this.presenter = presenter;
|
||||
createContainer(surface, externalContainer);
|
||||
|
||||
if (uiSettings.showControls) {
|
||||
createButtonsPanel(workingProtocol, isSeparateFrame? frame: externalContainer, isApplet);
|
||||
if (isSeparateFrame) registerResizeListener(frame);
|
||||
updateZoomButtonsState();
|
||||
}
|
||||
if (uiSettings.isFullScreen()) {
|
||||
switchOnFullscreenMode();
|
||||
}
|
||||
setSurfaceToHandleKbdFocus();
|
||||
}
|
||||
|
||||
private void createContainer(final Surface surface, Container externalContainer) {
|
||||
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);
|
||||
scroller = new JScrollPane();
|
||||
scroller.getViewport().setBackground(Color.DARK_GRAY);
|
||||
scroller.setViewportView(lpane);
|
||||
|
||||
if (isSeparateFrame) {
|
||||
frame = new JFrame();
|
||||
frame.addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent windowEvent) {
|
||||
super.windowClosing(windowEvent);
|
||||
fireCloseApp();
|
||||
}
|
||||
});
|
||||
frame.setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE);
|
||||
Utils.setApplicationIconsForWindow(frame);
|
||||
frame.setLayout(new BorderLayout(0, 0));
|
||||
frame.add(scroller, BorderLayout.CENTER);
|
||||
|
||||
// frame.pack();
|
||||
lpane.setSize(surface.getPreferredSize());
|
||||
|
||||
internalPack(null);
|
||||
container = frame;
|
||||
fireContainerCompleted();
|
||||
} else {
|
||||
if (null == externalContainer) throw new IllegalArgumentException("External Container is null"); // TODO catch it somewhere
|
||||
externalContainer.setLayout(new BorderLayout(0, 0));
|
||||
externalContainer.add(scroller, BorderLayout.CENTER);
|
||||
container = externalContainer;
|
||||
fireContainerCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
private void fireContainerCompleted() {
|
||||
if (viewerEventsListener != null) {
|
||||
viewerEventsListener.onViewerComponentContainerBuilt(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void pack() {
|
||||
final Dimension oldSize = lpane.getSize();
|
||||
lpane.setSize(surface.getPreferredSize());
|
||||
if (isSeparateFrame && ! isZoomToFitSelected()) {
|
||||
internalPack(oldSize);
|
||||
}
|
||||
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();
|
||||
buttonsBar.setNoFullScreenGroupVisible(false);
|
||||
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;
|
||||
buttonsBar.setNoFullScreenGroupVisible(true);
|
||||
setButtonsBarVisible(true);
|
||||
try {
|
||||
frame.dispose();
|
||||
frame.setUndecorated(false);
|
||||
frame.setResizable(true);
|
||||
frame.getGraphicsConfiguration().getDevice().setFullScreenWindow(null);
|
||||
} catch (Exception ignore) {
|
||||
// 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);
|
||||
lpane.repaint();
|
||||
lpane.validate();
|
||||
buttonsBar.bar.validate();
|
||||
}
|
||||
|
||||
public IRepaintController getRepaintController() {
|
||||
return surface;
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (isSeparateFrame && frame != null) {
|
||||
frame.setVisible(false);
|
||||
frame.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEnteredEvent(MouseEvent mouseEvent) {
|
||||
setSurfaceToHandleKbdFocus();
|
||||
}
|
||||
|
||||
public void addMouseListener(MouseListener mouseListener) {
|
||||
surface.addMouseListener(mouseListener);
|
||||
}
|
||||
|
||||
public JFrame getFrame() {
|
||||
return frame;
|
||||
}
|
||||
|
||||
public void setVisible() {
|
||||
container.setVisible(true);
|
||||
}
|
||||
public void validate() {
|
||||
container.validate();
|
||||
}
|
||||
|
||||
public static class ButtonsBar {
|
||||
private static final Insets BUTTONS_MARGIN = new Insets(2, 2, 2, 2);
|
||||
private JPanel bar;
|
||||
private boolean isVisible;
|
||||
private ArrayList<Component> noFullScreenGroup = new ArrayList<Component>();
|
||||
|
||||
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 Component createStrut() {
|
||||
return 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());
|
||||
}
|
||||
|
||||
public void addToNoFullScreenGroup(Component component) {
|
||||
noFullScreenGroup.add(component);
|
||||
}
|
||||
|
||||
public void setNoFullScreenGroupVisible(boolean isVisible) {
|
||||
for (Component c : noFullScreenGroup) {
|
||||
c.setVisible(isVisible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 < lpane.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 > lpane.getSize().width) viewPosition.x =
|
||||
lpane.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 < lpane.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 > lpane.getSize().height) viewPosition.y =
|
||||
lpane.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 Protocol protocol, Container container, boolean isApplet) {
|
||||
final SwingViewerWindow.ButtonsBar buttonsBar = createButtonsBar();
|
||||
|
||||
buttonsBar.addToNoFullScreenGroup(
|
||||
buttonsBar.createButton("options", "Set Options", new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
showOptionsDialog();
|
||||
setSurfaceToHandleKbdFocus();
|
||||
}
|
||||
}));
|
||||
|
||||
buttonsBar.addToNoFullScreenGroup(
|
||||
buttonsBar.createButton("info", "Show connection info", new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
showConnectionInfoMessage();
|
||||
setSurfaceToHandleKbdFocus();
|
||||
}
|
||||
}));
|
||||
|
||||
buttonsBar.addToNoFullScreenGroup(
|
||||
buttonsBar.createStrut());
|
||||
|
||||
buttonsBar.createButton("refresh", "Refresh screen", new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
protocol.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(protocol);
|
||||
setSurfaceToHandleKbdFocus();
|
||||
}
|
||||
});
|
||||
kbdButtons.add(ctrlAltDelButton);
|
||||
|
||||
JButton winButton = buttonsBar.createButton("win", "Send 'Win' key as 'Ctrl-Esc'", new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
sendWinKey(protocol);
|
||||
setSurfaceToHandleKbdFocus();
|
||||
}
|
||||
});
|
||||
kbdButtons.add(winButton);
|
||||
|
||||
JToggleButton ctrlButton = buttonsBar.createToggleButton("ctrl", "Ctrl Lock",
|
||||
new ItemListener() {
|
||||
@Override
|
||||
public void itemStateChanged(ItemEvent e) {
|
||||
if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
protocol.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true));
|
||||
} else {
|
||||
protocol.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) {
|
||||
protocol.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, true));
|
||||
} else {
|
||||
protocol.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();
|
||||
//
|
||||
// final JToggleButton viewOnlyButton = buttonsBar.createToggleButton("viewonly", "View Only",
|
||||
// new ItemListener() {
|
||||
// @Override
|
||||
// public void itemStateChanged(ItemEvent e) {
|
||||
// if (e.getStateChange() == ItemEvent.SELECTED) {
|
||||
// rfbSettings.setViewOnly(true);
|
||||
// rfbSettings.fireListeners();
|
||||
// } else {
|
||||
// rfbSettings.setViewOnly(false);
|
||||
// rfbSettings.fireListeners();
|
||||
// }
|
||||
// setSurfaceToHandleKbdFocus();
|
||||
// }
|
||||
// });
|
||||
// viewOnlyButton.setSelected(rfbSettings.isViewOnly());
|
||||
// rfbSettings.addListener(new IChangeSettingsListener() {
|
||||
// @Override
|
||||
// public void settingsChanged(SettingsChangedEvent event) {
|
||||
// if (ProtocolSettings.isRfbSettingsChangedFired(event)) {
|
||||
// ProtocolSettings settings = (ProtocolSettings) event.getSource();
|
||||
// viewOnlyButton.setSelected(settings.isViewOnly());
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// kbdButtons.add(viewOnlyButton);
|
||||
|
||||
buttonsBar.createStrut();
|
||||
|
||||
buttonsBar.createButton("close", isApplet ? "Disconnect" : "Close", new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
close();
|
||||
presenter.setNeedReconnection(false);
|
||||
presenter.cancelConnection();
|
||||
fireCloseApp();
|
||||
}
|
||||
}).setAlignmentX(JComponent.RIGHT_ALIGNMENT);
|
||||
|
||||
setButtonsBarVisible(true, container);
|
||||
}
|
||||
|
||||
private void fireCloseApp() {
|
||||
if (viewerEventsListener != null) {
|
||||
viewerEventsListener.onViewerComponentClosing();
|
||||
}
|
||||
}
|
||||
|
||||
private void sendCtrlAltDel(Protocol protocol) {
|
||||
protocol.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true));
|
||||
protocol.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, true));
|
||||
protocol.sendMessage(new KeyEventMessage(Keymap.K_DELETE, true));
|
||||
protocol.sendMessage(new KeyEventMessage(Keymap.K_DELETE, false));
|
||||
protocol.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, false));
|
||||
protocol.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, false));
|
||||
}
|
||||
|
||||
private void sendWinKey(Protocol protocol) {
|
||||
protocol.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true));
|
||||
protocol.sendMessage(new KeyEventMessage(Keymap.K_ESCAPE, true));
|
||||
protocol.sendMessage(new KeyEventMessage(Keymap.K_ESCAPE, false));
|
||||
protocol.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();
|
||||
if ( ! Strings.isTrimmedEmpty(appName)) {
|
||||
message.append(appName).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");
|
||||
if (workingProtocol.getTunnelType() != null && workingProtocol.getTunnelType() != TunnelType.NOTUNNEL) {
|
||||
message.append(" using ").append(workingProtocol.getTunnelType().hrName).append(" tunneling");
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
// Copyright (C) 2010 - 2014 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.rfb.protocol.Protocol;
|
||||
import com.glavsoft.rfb.protocol.ProtocolSettings;
|
||||
import com.glavsoft.viewer.settings.UiSettings;
|
||||
|
||||
import java.awt.Container;
|
||||
|
||||
/**
|
||||
* Factory that creates SwingViewerWindow with a number of params
|
||||
*
|
||||
* @author dime at tightvnc.com
|
||||
*/
|
||||
public class SwingViewerWindowFactory {
|
||||
|
||||
private final boolean isSeparateFrame;
|
||||
private boolean isApplet = false;
|
||||
private final ViewerEventsListener viewerEventsListener;
|
||||
private Container externalContainer;
|
||||
private String appName;
|
||||
|
||||
/**
|
||||
* Construct factory for creating SwingViewerWindow at separate frame
|
||||
*
|
||||
* @param viewerEventsListener the listener for closing app events
|
||||
*/
|
||||
public SwingViewerWindowFactory(ViewerEventsListener viewerEventsListener) {
|
||||
this(true, viewerEventsListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct factory for creating SwingViewerWindow
|
||||
*
|
||||
* @param isSeparateFrame if true, creates at the separate frame, else use external container
|
||||
* @see #setExternalContainer(Container) and put viewer window into the container
|
||||
*
|
||||
* @param viewerEventsListener the listener for closing app events
|
||||
*/
|
||||
public SwingViewerWindowFactory(boolean isSeparateFrame,
|
||||
ViewerEventsListener viewerEventsListener) {
|
||||
this.isSeparateFrame = isSeparateFrame;
|
||||
this.viewerEventsListener = viewerEventsListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates SwingViewerWindow
|
||||
* @see SwingViewerWindow
|
||||
*
|
||||
* @param workingProtocol Protocol object, represents network session that is 'connected' to remote host
|
||||
* @param rfbSettings rfb protocol settings currently used at the session
|
||||
* @param uiSettings gui settings currently used to for displaying viewer window
|
||||
* @param connectionString used to show window title string for displaying remote host name
|
||||
* @param presenter ConnectionPresenter that response for reconnection and connection history manipulation
|
||||
* @return the SwingViewerWindow
|
||||
*/
|
||||
public SwingViewerWindow createViewerWindow(Protocol workingProtocol,
|
||||
ProtocolSettings rfbSettings, UiSettings uiSettings,
|
||||
String connectionString, ConnectionPresenter presenter) {
|
||||
// TODO do we need in presenter here? or split to to history handling and reconnection ability
|
||||
Surface surface = new Surface(workingProtocol, uiSettings.getScaleFactor(), uiSettings.getMouseCursorShape());
|
||||
final SwingViewerWindow viewerWindow = new SwingViewerWindow(workingProtocol, rfbSettings, uiSettings,
|
||||
surface, isSeparateFrame, isApplet, viewerEventsListener, appName, connectionString, presenter, externalContainer);
|
||||
surface.setViewerWindow(viewerWindow);
|
||||
viewerWindow.setRemoteDesktopName(workingProtocol.getRemoteDesktopName());
|
||||
rfbSettings.addListener(viewerWindow);
|
||||
uiSettings.addListener(surface);
|
||||
return viewerWindow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets external container that will be used to contain viewer window
|
||||
*
|
||||
* @param externalContainer
|
||||
*/
|
||||
public void setExternalContainer(Container externalContainer) {
|
||||
this.externalContainer = externalContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the application starts as browser applet
|
||||
*
|
||||
* @param isApplet
|
||||
*/
|
||||
public void setIsApplet(boolean isApplet) {
|
||||
this.isApplet = isApplet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the application name string to show it at info dialog
|
||||
* @param appName application name
|
||||
*/
|
||||
public void setAppName(String appName) {
|
||||
this.appName = appName;
|
||||
}
|
||||
}
|
||||
141
plugins/vnc/src/main/java/com/glavsoft/viewer/swing/Utils.java
Normal file
141
plugins/vnc/src/main/java/com/glavsoft/viewer/swing/Utils.java
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright (C) 2010 - 2014 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.settings.LocalMouseCursorShape;
|
||||
import com.glavsoft.viewer.swing.mac.MacUtils;
|
||||
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JDialog;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.awt.Image;
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.Window;
|
||||
import java.awt.image.ImageObserver;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Utils for Swing GUI
|
||||
*/
|
||||
public class Utils {
|
||||
private static List<Image> icons = new LinkedList<Image>();
|
||||
|
||||
public static List<Image> getApplicationIcons() {
|
||||
if ( ! icons.isEmpty()) return icons;
|
||||
for (String icoSize : new String[]{"16x16", "32x32", "48x48", "128x128"}) {
|
||||
URL resource = Utils.class.getResource("/com/glavsoft/viewer/images/tightvnc-logo-" + icoSize + ".png");
|
||||
Image image = resource != null ?
|
||||
Toolkit.getDefaultToolkit().createImage(resource) :
|
||||
null;
|
||||
if (image != null) {
|
||||
icons.add(image);
|
||||
}
|
||||
}
|
||||
return icons;
|
||||
}
|
||||
|
||||
public static ImageIcon getButtonIcon(String name) {
|
||||
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) {
|
||||
if (!MacUtils.isMac()) {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2010 - 2014 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;
|
||||
|
||||
/**
|
||||
* @author dime at glavsoft.com
|
||||
*/
|
||||
public interface ViewerEventsListener {
|
||||
|
||||
/**
|
||||
* Invoked when close application event fires.
|
||||
* Ex. when 'Close' button on Connection dialog pressed, when closes viewer window...
|
||||
*/
|
||||
void onViewerComponentClosing();
|
||||
|
||||
/**
|
||||
* Invoked when (separate) frame contained remote viewport, scroller etc layers created.
|
||||
*/
|
||||
void onViewerComponentContainerBuilt(SwingViewerWindow viewerWindow);
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright (C) 2010 - 2014 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 javax.swing.ComboBoxModel;
|
||||
import javax.swing.JComboBox;
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user