/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright (C) 2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
package com.tigervnc.rfb;
public class AuthFailureException extends Exception {
- public AuthFailureException(String s) { super(s); }
+ public AuthFailureException() { super("Authentication failure"); }
+ public AuthFailureException(String reason) {
+ super("Authentication failure: "+reason);
+ }
}
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright (C) 2011-2012 Brian P. Hinz
+ * Copyright (C) 2011-2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
abstract public class CConnection extends CMsgHandler {
+ static LogWriter vlog = new LogWriter("CConnection");
+
+ private static final String osName =
+ System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
+
public CConnection()
{
super();
- csecurity = null; is = null; os = null; reader_ = null; writer_ = null;
+ csecurity = null;
+ supportsLocalCursor = false; supportsDesktopResize = false;
+ is = null; os = null; reader_ = null; writer_ = null;
shared = false;
- state_ = RFBSTATE_UNINITIALISED;
+ state_ = stateEnum.RFBSTATE_UNINITIALISED;
+ pendingPFChange = false; preferredEncoding = Encodings.encodingTight;
+ compressLevel = 2; qualityLevel = -1;
+ formatChange = false; encodingChange = false;
+ firstUpdate = true; pendingUpdate = false; continuousUpdates = false;
+ forceNonincremental = true;
framebuffer = null; decoder = new DecodeManager(this);
security = new SecurityClient();
}
// which we are connected. This might be the result of getPeerEndpoint on
// a TcpSocket, for example, or a host specified by DNS name & port.
// The serverName is used when verifying the Identity of a host (see RA2).
- public final void setServerName(String name) {
- serverName = name;
- }
+ public void setServerName(String name_) { serverName = name_; }
- // setShared sets the value of the shared flag which will be sent to the
- // server upon initialisation.
- public final void setShared(boolean s) { shared = s; }
+ public void setServerPort(int port_) { serverPort = port_; }
// setStreams() sets the streams to be used for the connection. These must
// be set before initialiseProtocol() and processMsg() are called. The
os = os_;
}
+ // setShared sets the value of the shared flag which will be sent to the
+ // server upon initialisation.
+ public final void setShared(boolean s) { shared = s; }
+
// setFramebuffer configures the PixelBuffer that the CConnection
// should render all pixel data in to. Note that the CConnection
// takes ownership of the PixelBuffer and it must not be deleted by
{
decoder.flush();
+ if (fb != null) {
+ assert(fb.width() == server.width());
+ assert(fb.height() == server.height());
+ }
+
if ((framebuffer != null) && (fb != null)) {
Rect rect = new Rect();
// there is data to read on the InStream.
public final void initialiseProtocol()
{
- state_ = RFBSTATE_PROTOCOL_VERSION;
+ state_ = stateEnum.RFBSTATE_PROTOCOL_VERSION;
}
// processMsg() should be called whenever there is data to read on the
private void processVersionMsg()
{
+ ByteBuffer verStr = ByteBuffer.allocate(12);
+ int majorVersion;
+ int minorVersion;
+
vlog.debug("reading protocol version");
- AtomicBoolean done = new AtomicBoolean();
- if (!cp.readVersion(is, done)) {
- state_ = RFBSTATE_INVALID;
+
+ if (!is.checkNoWait(12))
+ return;
+
+ is.readBytes(verStr, 12);
+
+ if ((new String(verStr.array())).matches("RFB \\d{3}\\.\\d{3}\\n")) {
+ majorVersion =
+ Integer.parseInt((new String(verStr.array())).substring(4,7));
+ minorVersion =
+ Integer.parseInt((new String(verStr.array())).substring(8,11));
+ } else {
+ state_ = stateEnum.RFBSTATE_INVALID;
throw new Exception("reading version failed: not an RFB server?");
}
- if (!done.get()) return;
+
+ server.setVersion(majorVersion, minorVersion);
vlog.info("Server supports RFB protocol version "
- +cp.majorVersion+"."+ cp.minorVersion);
+ +server.majorVersion+"."+ server.minorVersion);
// The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
- if (cp.beforeVersion(3,3)) {
+ if (server.beforeVersion(3,3)) {
String msg = ("Server gave unsupported RFB protocol version "+
- cp.majorVersion+"."+cp.minorVersion);
+ server.majorVersion+"."+server.minorVersion);
vlog.error(msg);
- state_ = RFBSTATE_INVALID;
+ state_ = stateEnum.RFBSTATE_INVALID;
throw new Exception(msg);
- } else if (cp.beforeVersion(3,7)) {
- cp.setVersion(3,3);
- } else if (cp.afterVersion(3,8)) {
- cp.setVersion(3,8);
+ } else if (server.beforeVersion(3,7)) {
+ server.setVersion(3,3);
+ } else if (server.afterVersion(3,8)) {
+ server.setVersion(3,8);
}
- cp.writeVersion(os);
- state_ = RFBSTATE_SECURITY_TYPES;
+ verStr.clear();
+ verStr.put(String.format("RFB %03d.%03d\n",
+ majorVersion, minorVersion).getBytes()).flip();
+ os.writeBytes(verStr.array(), 0, 12);
+ os.flush();
+
+ state_ = stateEnum.RFBSTATE_SECURITY_TYPES;
vlog.info("Using RFB protocol version "+
- cp.majorVersion+"."+cp.minorVersion);
+ server.majorVersion+"."+server.minorVersion);
}
private void processSecurityTypesMsg()
List<Integer> secTypes = new ArrayList<Integer>();
secTypes = security.GetEnabledSecTypes();
- if (cp.isVersion(3,3)) {
+ if (server.isVersion(3,3)) {
// legacy 3.3 server may only offer "vnc authentication" or "none"
}
if (secType == Security.secTypeInvalid) {
- state_ = RFBSTATE_INVALID;
+ state_ = stateEnum.RFBSTATE_INVALID;
vlog.error("No matching security types");
throw new Exception("No matching security types");
}
- state_ = RFBSTATE_SECURITY;
+ state_ = stateEnum.RFBSTATE_SECURITY;
csecurity = security.GetCSecurity(secType);
processSecurityMsg();
}
private void processSecurityMsg() {
vlog.debug("processing security message");
if (csecurity.processMsg(this)) {
- state_ = RFBSTATE_SECURITY_RESULT;
+ state_ = stateEnum.RFBSTATE_SECURITY_RESULT;
processSecurityResultMsg();
}
}
private void processSecurityResultMsg() {
vlog.debug("processing security result message");
int result;
- if (cp.beforeVersion(3,8) && csecurity.getType() == Security.secTypeNone) {
+ if (server.beforeVersion(3,8) && csecurity.getType() == Security.secTypeNone) {
result = Security.secResultOK;
} else {
if (!is.checkNoWait(1)) return;
default:
throw new Exception("Unknown security result from server");
}
- String reason;
- if (cp.beforeVersion(3,8))
- reason = "Authentication failure";
- else
- reason = is.readString();
- state_ = RFBSTATE_INVALID;
+ state_ = stateEnum.RFBSTATE_INVALID;
+ if (server.beforeVersion(3,8))
+ throw new AuthFailureException();
+ String reason = is.readString();
throw new AuthFailureException(reason);
}
}
private void throwConnFailedException() {
- state_ = RFBSTATE_INVALID;
+ state_ = stateEnum.RFBSTATE_INVALID;
String reason;
reason = is.readString();
throw new ConnFailedException(reason);
}
private void securityCompleted() {
- state_ = RFBSTATE_INITIALISATION;
+ state_ = stateEnum.RFBSTATE_INITIALISATION;
reader_ = new CMsgReader(this, is);
- writer_ = new CMsgWriter(cp, os);
+ writer_ = new CMsgWriter(server, os);
vlog.debug("Authentication success!");
authSuccess();
writer_.writeClientInit(shared);
}
- // Methods to be overridden in a derived class
+ // Methods overridden from CMsgHandler
// Note: These must be called by any deriving classes
decoder.flush();
super.setDesktopSize(w,h);
+
+ if (continuousUpdates)
+ writer().writeEnableContinuousUpdates(true, 0, 0,
+ server.width(),
+ server.height());
+
+ resizeFramebuffer();
+ assert(framebuffer != null);
+ assert(framebuffer.width() == server.width());
+ assert(framebuffer.height() == server.height());
}
public void setExtendedDesktopSize(int reason,
- int result, int w, int h,
+ int result,
+ int w, int h,
ScreenSet layout) {
decoder.flush();
super.setExtendedDesktopSize(reason, result, w, h, layout);
+
+ if (continuousUpdates)
+ writer().writeEnableContinuousUpdates(true, 0, 0,
+ server.width(),
+ server.height());
+
+ resizeFramebuffer();
+ assert(framebuffer != null);
+ assert(framebuffer.width() == server.width());
+ assert(framebuffer.height() == server.height());
+ }
+
+ public void endOfContinuousUpdates()
+ {
+ super.endOfContinuousUpdates();
+
+ // We've gotten the marker for a format change, so make the pending
+ // one active
+ if (pendingPFChange) {
+ server.setPF(pendingPF);
+ pendingPFChange = false;
+
+ // We might have another change pending
+ if (formatChange)
+ requestNewUpdate();
+ }
+ }
+ // serverInit() is called when the ServerInit message is received. The
+ // derived class must call on to CConnection::serverInit().
+ public void serverInit(int width, int height,
+ PixelFormat pf, String name)
+ {
+ super.serverInit(width, height, pf, name);
+
+ state_ = stateEnum.RFBSTATE_NORMAL;
+ vlog.debug("initialisation done");
+
+ initDone();
+ assert(framebuffer != null);
+ // FIXME: even if the client is scaling?
+ assert(framebuffer.width() == server.width());
+ assert(framebuffer.height() == server.height());
+
+ // We want to make sure we call SetEncodings at least once
+ encodingChange = true;
+
+ requestNewUpdate();
+
+ // This initial update request is a bit of a corner case, so we need
+ // to help out setting the correct format here.
+ if (pendingPFChange) {
+ server.setPF(pendingPF);
+ pendingPFChange = false;
+ }
}
public void readAndDecodeRect(Rect r, int encoding,
decoder.flush();
}
- // getIdVerifier() returns the identity verifier associated with the connection.
- // Ownership of the IdentityVerifier is retained by the CConnection instance.
- //public IdentityVerifier getIdentityVerifier() { return 0; }
-
public void framebufferUpdateStart()
{
super.framebufferUpdateStart();
+
+ assert(framebuffer != null);
+
+ // Note: This might not be true if continuous updates are supported
+ pendingUpdate = false;
+
+ requestNewUpdate();
}
public void framebufferUpdateEnd()
decoder.flush();
super.framebufferUpdateEnd();
+
+ // A format change has been scheduled and we are now past the update
+ // with the old format. Time to active the new one.
+ if (pendingPFChange && !continuousUpdates) {
+ server.setPF(pendingPF);
+ pendingPFChange = false;
+ }
+
+ if (firstUpdate) {
+ if (server.supportsContinuousUpdates) {
+ vlog.info("Enabling continuous updates");
+ continuousUpdates = true;
+ writer().writeEnableContinuousUpdates(true, 0, 0,
+ server.width(),
+ server.height());
+ }
+
+ firstUpdate = false;
+ }
}
public void dataRect(Rect r, int encoding)
decoder.decodeRect(r, encoding, framebuffer);
}
+ // Methods to be overridden in a derived class
+
// authSuccess() is called when authentication has succeeded.
- public void authSuccess()
+ public void authSuccess() { }
+
+ // initDone() is called when the connection is fully established
+ // and standard messages can be sent. This is called before the
+ // initial FramebufferUpdateRequest giving a derived class the
+ // chance to modify pixel format and settings. The derived class
+ // must also make sure it has provided a valid framebuffer before
+ // returning.
+ public void initDone() { }
+
+ // resizeFramebuffer() is called whenever the framebuffer
+ // dimensions or the screen layout changes. A subclass must make
+ // sure the pixel buffer has been updated once this call returns.
+ public void resizeFramebuffer()
{
+ assert(false);
}
- // serverInit() is called when the ServerInit message is received. The
- // derived class must call on to CConnection::serverInit().
- public void serverInit()
+ // refreshFramebuffer() forces a complete refresh of the entire
+ // framebuffer
+ public void refreshFramebuffer()
{
- state_ = RFBSTATE_NORMAL;
- vlog.debug("initialisation done");
+ forceNonincremental = true;
+
+ // Without fences, we cannot safely trigger an update request directly
+ // but must wait for the next update to arrive.
+ if (continuousUpdates)
+ requestNewUpdate();
}
- // Other methods
+ // setPreferredEncoding()/getPreferredEncoding() adjusts which
+ // encoding is listed first as a hint to the server that it is the
+ // preferred one
+ public void setPreferredEncoding(int encoding)
+ {
+ if (preferredEncoding == encoding)
+ return;
+
+ preferredEncoding = encoding;
+ encodingChange = true;
+ }
+
+ public int getPreferredEncoding()
+ {
+ return preferredEncoding;
+ }
+
+ // setCompressLevel()/setQualityLevel() controls the encoding hints
+ // sent to the server
+ public void setCompressLevel(int level)
+ {
+ if (compressLevel == level)
+ return;
+
+ compressLevel = level;
+ encodingChange = true;
+ }
+
+ public void setQualityLevel(int level)
+ {
+ if (qualityLevel == level)
+ return;
+
+ qualityLevel = level;
+ encodingChange = true;
+ }
+
+ // setPF() controls the pixel format requested from the server.
+ // server.pf() will automatically be adjusted once the new format
+ // is active.
+ public void setPF(PixelFormat pf)
+ {
+ if (server.pf().equal(pf) && !formatChange)
+ return;
+
+ nextPF = pf;
+ formatChange = true;
+ }
public CMsgReader reader() { return reader_; }
public CMsgWriter writer() { return writer_; }
// Access method used by SSecurity implementations that can verify servers'
// Identities, to determine the unique(ish) name of the server.
public String getServerName() { return serverName; }
+ public int getServerPort() { return serverPort; }
- public boolean isSecure() { return csecurity != null ? csecurity.isSecure() : false; }
-
- public static final int RFBSTATE_UNINITIALISED = 0;
- public static final int RFBSTATE_PROTOCOL_VERSION = 1;
- public static final int RFBSTATE_SECURITY_TYPES = 2;
- public static final int RFBSTATE_SECURITY = 3;
- public static final int RFBSTATE_SECURITY_RESULT = 4;
- public static final int RFBSTATE_INITIALISATION = 5;
- public static final int RFBSTATE_NORMAL = 6;
- public static final int RFBSTATE_INVALID = 7;
+ boolean isSecure() { return csecurity != null ? csecurity.isSecure() : false; }
- public int state() { return state_; }
+ public enum stateEnum {
+ RFBSTATE_UNINITIALISED,
+ RFBSTATE_PROTOCOL_VERSION,
+ RFBSTATE_SECURITY_TYPES,
+ RFBSTATE_SECURITY,
+ RFBSTATE_SECURITY_RESULT,
+ RFBSTATE_INITIALISATION,
+ RFBSTATE_NORMAL,
+ RFBSTATE_INVALID
+ };
- public int getServerPort() { return serverPort; }
- public void setServerPort(int port) {
- serverPort = port;
- }
+ public stateEnum state() { return state_; }
- protected void setState(int s) { state_ = s; }
+ protected void setState(stateEnum s) { state_ = s; }
protected void setReader(CMsgReader r) { reader_ = r; }
protected void setWriter(CMsgWriter w) { writer_ = w; }
// We cannot guarantee any synchronisation at this level
flags = 0;
- synchronized(this) {
- writer().writeFence(flags, len, data);
+ writer().writeFence(flags, len, data);
+ }
+
+ // requestNewUpdate() requests an update from the server, having set the
+ // format and encoding appropriately.
+ private void requestNewUpdate()
+ {
+ if (formatChange && !pendingPFChange) {
+ /* Catch incorrect requestNewUpdate calls */
+ assert(!pendingUpdate || continuousUpdates);
+
+ // We have to make sure we switch the internal format at a safe
+ // time. For continuous updates we temporarily disable updates and
+ // look for a EndOfContinuousUpdates message to see when to switch.
+ // For classical updates we just got a new update right before this
+ // function was called, so we need to make sure we finish that
+ // update before we can switch.
+
+ pendingPFChange = true;
+ pendingPF = nextPF;
+
+ if (continuousUpdates)
+ writer().writeEnableContinuousUpdates(false, 0, 0, 0, 0);
+
+ writer().writeSetPixelFormat(pendingPF);
+
+ if (continuousUpdates)
+ writer().writeEnableContinuousUpdates(true, 0, 0,
+ server.width(),
+ server.height());
+ formatChange = false;
+ }
+
+ if (encodingChange) {
+ updateEncodings();
+ encodingChange = false;
+ }
+
+ if (forceNonincremental || !continuousUpdates) {
+ pendingUpdate = true;
+ writer().writeFramebufferUpdateRequest(new Rect(0, 0,
+ server.width(),
+ server.height()),
+ !forceNonincremental);
}
+
+ forceNonincremental = false;
+ }
+
+ // Ask for encodings based on which decoders are supported. Assumes higher
+ // encoding numbers are more desirable.
+
+ private void updateEncodings()
+ {
+ List<Integer> encodings = new ArrayList<Integer>();
+
+ if (server.supportsLocalCursor) {
+ // JRE on Windows does not support alpha cursor
+ if (!osName.contains("windows"))
+ encodings.add(Encodings.pseudoEncodingCursorWithAlpha);
+ encodings.add(Encodings.pseudoEncodingCursor);
+ encodings.add(Encodings.pseudoEncodingXCursor);
+ }
+ if (server.supportsDesktopResize) {
+ encodings.add(Encodings.pseudoEncodingDesktopSize);
+ encodings.add(Encodings.pseudoEncodingExtendedDesktopSize);
+ }
+ if (server.supportsClientRedirect)
+ encodings.add(Encodings.pseudoEncodingClientRedirect);
+
+ encodings.add(Encodings.pseudoEncodingDesktopName);
+ encodings.add(Encodings.pseudoEncodingLastRect);
+ encodings.add(Encodings.pseudoEncodingContinuousUpdates);
+ encodings.add(Encodings.pseudoEncodingFence);
+
+ if (Decoder.supported(preferredEncoding)) {
+ encodings.add(preferredEncoding);
+ }
+
+ encodings.add(Encodings.encodingCopyRect);
+
+ for (int i = Encodings.encodingMax; i >= 0; i--) {
+ if ((i != preferredEncoding) && Decoder.supported(i))
+ encodings.add(i);
+ }
+
+ if (compressLevel >= 0 && compressLevel <= 9)
+ encodings.add(Encodings.pseudoEncodingCompressLevel0 + compressLevel);
+ if (qualityLevel >= 0 && qualityLevel <= 9)
+ encodings.add(Encodings.pseudoEncodingQualityLevel0 + qualityLevel);
+
+ writer().writeSetEncodings(encodings);
}
private void throwAuthFailureException() {
String reason;
- vlog.debug("state="+state()+", ver="+cp.majorVersion+"."+cp.minorVersion);
- if (state() == RFBSTATE_SECURITY_RESULT && !cp.beforeVersion(3,8)) {
+ vlog.debug("state="+state()+", ver="+server.majorVersion+"."+server.minorVersion);
+ if (state() == stateEnum.RFBSTATE_SECURITY_RESULT && !server.beforeVersion(3,8)) {
reason = is.readString();
} else {
reason = "Authentication failure";
}
- state_ = RFBSTATE_INVALID;
+ state_ = stateEnum.RFBSTATE_INVALID;
vlog.error(reason);
throw new AuthFailureException(reason);
}
+ public CSecurity csecurity;
+ public SecurityClient security;
+
+ protected boolean supportsLocalCursor;
+ protected boolean supportsDesktopResize;
+
private InStream is;
private OutStream os;
private CMsgReader reader_;
private CMsgWriter writer_;
private boolean deleteStreamsWhenDone;
private boolean shared;
- private int state_ = RFBSTATE_UNINITIALISED;
+ private stateEnum state_;
private String serverName;
+ private int serverPort;
- protected ModifiablePixelBuffer framebuffer;
- private DecodeManager decoder;
+ private boolean pendingPFChange;
+ private PixelFormat pendingPF;
- public CSecurity csecurity;
- public SecurityClient security;
- public static final int maxSecTypes = 8;
- int nSecTypes;
- int[] secTypes;
- int serverPort;
- boolean clientSecTypeOrder;
+ private int preferredEncoding;
+ private int compressLevel;
+ private int qualityLevel;
- static LogWriter vlog = new LogWriter("CConnection");
+ private boolean formatChange;
+ private PixelFormat nextPF;
+ private boolean encodingChange;
+
+ private boolean firstUpdate;
+ private boolean pendingUpdate;
+ private boolean continuousUpdates;
+
+ private boolean forceNonincremental;
+
+ protected ModifiablePixelBuffer framebuffer;
+ private DecodeManager decoder;
}
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2011 Pierre Ossman for Cendio AB
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- * Copyright (C) 2011 Brian P. Hinz
+ * Copyright (C) 2011-2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
abstract public class CMsgHandler {
+ static LogWriter vlog = new LogWriter("CMsgHandler");
+
public CMsgHandler() {
- cp = new ConnParams();
+ server = new ServerParams();
}
public void setDesktopSize(int width, int height)
{
- cp.width = width;
- cp.height = height;
+ server.setDimensions(width, height);
}
public void setExtendedDesktopSize(int reason, int result,
int width, int height,
ScreenSet layout)
{
- cp.supportsSetDesktopSize = true;
+ server.supportsSetDesktopSize = true;
if ((reason == screenTypes.reasonClient) && (result != screenTypes.resultSuccess))
return;
- if (!layout.validate(width, height))
- vlog.error("Server sent us an invalid screen layout");
-
- cp.width = width;
- cp.height = height;
- cp.screenLayout = layout;
+ server.setDimensions(width, height, layout);
}
+ abstract public void setCursor(int width, int height, Point hotspot,
+ byte[] data);
+
public void setPixelFormat(PixelFormat pf)
{
- cp.setPF(pf);
+ server.setPF(pf);
}
public void setName(String name)
{
- cp.setName(name);
+ server.setName(name);
}
public void fence(int flags, int len, byte[] data)
{
- cp.supportsFence = true;
+ server.supportsFence = true;
}
public void endOfContinuousUpdates()
{
- cp.supportsContinuousUpdates = true;
+ server.supportsContinuousUpdates = true;
}
abstract public void clientRedirect(int port, String host,
String x509subject);
- abstract public void setCursor(int width, int height, Point hotspot,
- byte[] data);
- abstract public void serverInit();
+ public void serverInit(int width, int height,
+ PixelFormat pf, String name)
+ {
+ server.setDimensions(width, height);
+ server.setPF(pf);
+ server.setName(name);
+ }
abstract public void readAndDecodeRect(Rect r, int encoding,
ModifiablePixelBuffer pb);
abstract public void bell();
abstract public void serverCutText(String str, int len);
- public ConnParams cp;
-
- static LogWriter vlog = new LogWriter("CMsgHandler");
+ public ServerParams server;
}
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright (C) 2011-2017 Brian P. Hinz
+ * Copyright (C) 2011-2019 Brian P. Hinz
* Copyright (C) 2017 Pierre Ossman for Cendio AB
*
* This is free software; you can redistribute it and/or modify
public class CMsgReader {
+ static LogWriter vlog = new LogWriter("CMsgReader");
+
protected CMsgReader(CMsgHandler handler_, InStream is_)
{
imageBufIdealSize = 0;
handler = handler_;
is = is_;
+ nUpdateRectsLeft = 0;
imageBuf = null;
imageBufSize = 0;
- nUpdateRectsLeft = 0;
}
public void readServerInit()
{
int width = is.readU16();
int height = is.readU16();
- handler.setDesktopSize(width, height);
PixelFormat pf = new PixelFormat();
pf.read(is);
- handler.setPixelFormat(pf);
String name = is.readString();
- handler.setName(name);
- handler.serverInit();
+ handler.serverInit(width, height, pf, name);
}
public void readMsg()
readEndOfContinuousUpdates();
break;
default:
+ vlog.error("unknown message type "+type);
throw new Exception("unknown message type");
}
} else {
{
is.skip(3);
int len = is.readU32();
+
if (len > 256*1024) {
is.skip(len);
vlog.error("cut text too long ("+len+" bytes) - ignoring");
return;
}
+
ByteBuffer buf = ByteBuffer.allocate(len);
is.readBytes(buf, len);
Charset latin1 = Charset.forName("ISO-8859-1");
protected void readRect(Rect r, int encoding)
{
- if ((r.br.x > handler.cp.width) || (r.br.y > handler.cp.height)) {
+ if ((r.br.x > handler.server.width()) || (r.br.y > handler.server.height())) {
vlog.error("Rect too big: "+r.width()+"x"+r.height()+" at "+
- r.tl.x+","+r.tl.y+" exceeds "+handler.cp.width+"x"+
- handler.cp.height);
+ r.tl.x+","+r.tl.y+" exceeds "+handler.server.width()+"x"+
+ handler.server.height());
throw new Exception("Rect too big");
}
protected void readSetCursor(int width, int height, Point hotspot)
{
- int data_len = width * height * (handler.cp.pf().bpp/8);
+ int data_len = width * height * (handler.server.pf().bpp/8);
int mask_len = ((width+7)/8) * height;
ByteBuffer data = ByteBuffer.allocate(data_len);
ByteBuffer mask = ByteBuffer.allocate(mask_len);
else
out.put((byte)0);
- handler.cp.pf().rgbFromBuffer(out.duplicate(), in.duplicate(), 1);
+ handler.server.pf().rgbFromBuffer(out.duplicate(), in.duplicate(), 1);
- in.position(in.position() + handler.cp.pf().bpp/8);
+ in.position(in.position() + handler.server.pf().bpp/8);
out.position(out.position() + 3);
}
}
encoding = is.readS32();
- origPF = handler.cp.pf();
- handler.cp.setPF(rgbaPF);
+ origPF = handler.server.pf();
+ handler.server.setPF(rgbaPF);
handler.readAndDecodeRect(pb.getRect(), encoding, pb);
- handler.cp.setPF(origPF);
+ handler.server.setPF(origPF);
// ARGB with pre-multiplied alpha works best for BufferedImage
if (pb.area() > 0) {
imageBuf = new int[imageBufSize];
}
if (nPixels != 0)
- nPixels = imageBufSize / (handler.cp.pf().bpp / 8);
+ nPixels = imageBufSize / (handler.server.pf().bpp / 8);
return imageBuf;
}
protected int nUpdateRectsLeft;
protected int[] imageBuf;
protected int imageBufSize;
-
- static LogWriter vlog = new LogWriter("CMsgReader");
}
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2011 Pierre Ossman for Cendio AB
- * Copyright (C) 2011 Brian P. Hinz
+ * Copyright (C) 2011-2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
import com.tigervnc.rdr.*;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
-import java.util.Iterator;
+import java.util.*;
public class CMsgWriter {
- protected CMsgWriter(ConnParams cp_, OutStream os_)
+ protected CMsgWriter(ServerParams server_, OutStream os_)
{
- cp = cp_;
+ server = server_;
os = os_;
}
endMsg();
}
- synchronized public void writeSetEncodings(int nEncodings, int[] encodings)
+ synchronized public void writeSetEncodings(List<Integer> encodings)
{
startMsg(MsgTypes.msgTypeSetEncodings);
os.skip(1);
- os.writeU16(nEncodings);
- for (int i = 0; i < nEncodings; i++)
- os.writeU32(encodings[i]);
+ os.writeU16(encodings.size());
+ for (Iterator<Integer> i = encodings.iterator(); i.hasNext();)
+ os.writeU32(i.next());
endMsg();
}
- // Ask for encodings based on which decoders are supported. Assumes higher
- // encoding numbers are more desirable.
-
- synchronized public void writeSetEncodings(int preferredEncoding, boolean useCopyRect)
- {
- int nEncodings = 0;
- int[] encodings = new int[Encodings.encodingMax+3];
-
- if (cp.supportsLocalCursor) {
- if (cp.supportsLocalCursorWithAlpha)
- encodings[nEncodings++] = Encodings.pseudoEncodingCursorWithAlpha;
- encodings[nEncodings++] = Encodings.pseudoEncodingCursor;
- encodings[nEncodings++] = Encodings.pseudoEncodingXCursor;
- }
- if (cp.supportsDesktopResize)
- encodings[nEncodings++] = Encodings.pseudoEncodingDesktopSize;
- if (cp.supportsExtendedDesktopSize)
- encodings[nEncodings++] = Encodings.pseudoEncodingExtendedDesktopSize;
- if (cp.supportsDesktopRename)
- encodings[nEncodings++] = Encodings.pseudoEncodingDesktopName;
- if (cp.supportsClientRedirect)
- encodings[nEncodings++] = Encodings.pseudoEncodingClientRedirect;
-
- encodings[nEncodings++] = Encodings.pseudoEncodingLastRect;
- encodings[nEncodings++] = Encodings.pseudoEncodingContinuousUpdates;
- encodings[nEncodings++] = Encodings.pseudoEncodingFence;
-
- if (Decoder.supported(preferredEncoding)) {
- encodings[nEncodings++] = preferredEncoding;
- }
-
- if (useCopyRect) {
- encodings[nEncodings++] = Encodings.encodingCopyRect;
- }
-
- /*
- * Prefer encodings in this order:
- *
- * Tight, ZRLE, Hextile, *
- */
-
- if ((preferredEncoding != Encodings.encodingTight) &&
- Decoder.supported(Encodings.encodingTight))
- encodings[nEncodings++] = Encodings.encodingTight;
-
- if ((preferredEncoding != Encodings.encodingZRLE) &&
- Decoder.supported(Encodings.encodingZRLE))
- encodings[nEncodings++] = Encodings.encodingZRLE;
-
- if ((preferredEncoding != Encodings.encodingHextile) &&
- Decoder.supported(Encodings.encodingHextile))
- encodings[nEncodings++] = Encodings.encodingHextile;
-
- // Remaining encodings
- for (int i = Encodings.encodingMax; i >= 0; i--) {
- switch (i) {
- case Encodings.encodingCopyRect:
- case Encodings.encodingTight:
- case Encodings.encodingZRLE:
- case Encodings.encodingHextile:
- /* These have already been sent earlier */
- break;
- default:
- if ((i != preferredEncoding) && Decoder.supported(i))
- encodings[nEncodings++] = i;
- }
- }
-
- if (cp.compressLevel >= 0 && cp.compressLevel <= 9)
- encodings[nEncodings++] =
- Encodings.pseudoEncodingCompressLevel0 + cp.compressLevel;
- if (cp.qualityLevel >= 0 && cp.qualityLevel <= 9)
- encodings[nEncodings++] =
- Encodings.pseudoEncodingQualityLevel0 + cp.qualityLevel;
-
- writeSetEncodings(nEncodings, encodings);
- }
-
synchronized public void writeSetDesktopSize(int width, int height,
ScreenSet layout)
{
- if (!cp.supportsSetDesktopSize)
+ if (!server.supportsSetDesktopSize)
throw new Exception("Server does not support SetDesktopSize");
startMsg(MsgTypes.msgTypeSetDesktopSize);
synchronized public void writeEnableContinuousUpdates(boolean enable,
int x, int y, int w, int h)
{
- if (!cp.supportsContinuousUpdates)
+ if (!server.supportsContinuousUpdates)
throw new Exception("Server does not support continuous updates");
startMsg(MsgTypes.msgTypeEnableContinuousUpdates);
synchronized public void writeFence(int flags, int len, byte[] data)
{
- if (!cp.supportsFence)
+ if (!server.supportsFence)
throw new Exception("Server does not support fences");
if (len > 64)
throw new Exception("Too large fence payload");
endMsg();
}
- synchronized public void keyEvent(int keysym, boolean down)
+ synchronized public void writeKeyEvent(int keysym, boolean down)
{
startMsg(MsgTypes.msgTypeKeyEvent);
os.writeU8(down?1:0);
endMsg();
}
- synchronized public void pointerEvent(Point pos, int buttonMask)
+ synchronized public void writePointerEvent(Point pos, int buttonMask)
{
Point p = new Point(pos.x,pos.y);
if (p.x < 0) p.x = 0;
if (p.y < 0) p.y = 0;
- if (p.x >= cp.width) p.x = cp.width - 1;
- if (p.y >= cp.height) p.y = cp.height - 1;
+ if (p.x >= server.width()) p.x = server.width() - 1;
+ if (p.y >= server.height()) p.y = server.height() - 1;
startMsg(MsgTypes.msgTypePointerEvent);
os.writeU8(buttonMask);
endMsg();
}
- synchronized public void startMsg(int type) {
+ synchronized protected void startMsg(int type) {
os.writeU8(type);
}
- synchronized public void endMsg() {
+ synchronized protected void endMsg() {
os.flush();
}
- ConnParams cp;
- OutStream os;
+ protected ServerParams server;
+ protected OutStream os;
}
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright (C) 2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
package com.tigervnc.rfb;
public class ConnFailedException extends Exception {
- public ConnFailedException(String s) { super(s); }
+ public ConnFailedException() { super("Connection failed"); }
+ public ConnFailedException(String reason) {
+ super("Connection faied: "+reason);
+ }
}
+++ /dev/null
-/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- * Copyright (C) 2012-2016 Brian P. Hinz
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- */
-
-package com.tigervnc.rfb;
-
-import com.tigervnc.rdr.*;
-import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-public class ConnParams {
-
- private static final int subsampleUndefined = -1;
- private static final int subsampleNone = 0;
- private static final int subsampleGray = 1;
- private static final int subsample2X = 2;
- private static final int subsample4X = 3;
- private static final int subsample8X = 4;
- private static final int subsample16X = 5;
-
- public ConnParams()
- {
- majorVersion = 0; minorVersion = 0;
- width = 0; height = 0; useCopyRect = false;
- supportsLocalCursor = false; supportsLocalXCursor = false;
- supportsLocalCursorWithAlpha = false;
- supportsDesktopResize = false; supportsExtendedDesktopSize = false;
- supportsDesktopRename = false; supportsLastRect = false;
- supportsSetDesktopSize = false; supportsFence = false;
- supportsContinuousUpdates = false;
- supportsClientRedirect = false;
- compressLevel = 6; qualityLevel = -1; fineQualityLevel = -1;
- subsampling = subsampleUndefined; name_ = null; verStrPos = 0;
-
- encodings_ = new ArrayList();
- screenLayout = new ScreenSet();
-
- setName("");
- }
-
- public boolean readVersion(InStream is, AtomicBoolean done)
- {
- if (verStrPos >= 12) return false;
- verStr = new StringBuilder(13);
- while (is.checkNoWait(1) && verStrPos < 12) {
- verStr.insert(verStrPos++,(char)is.readU8());
- }
-
- if (verStrPos < 12) {
- done.set(false);
- return true;
- }
- done.set(true);
- verStr.insert(12,'0');
- verStrPos = 0;
- if (verStr.toString().matches("RFB \\d{3}\\.\\d{3}\\n0")) {
- majorVersion = Integer.parseInt(verStr.substring(4,7));
- minorVersion = Integer.parseInt(verStr.substring(8,11));
- return true;
- }
- return false;
- }
-
- public void writeVersion(OutStream os)
- {
- String str = String.format("RFB %03d.%03d\n", majorVersion, minorVersion);
- os.writeBytes(str.getBytes(), 0, 12);
- os.flush();
- }
-
- public int majorVersion;
- public int minorVersion;
-
- public void setVersion(int major, int minor) {
- majorVersion = major; minorVersion = minor;
- }
- public boolean isVersion(int major, int minor) {
- return majorVersion == major && minorVersion == minor;
- }
- public boolean beforeVersion(int major, int minor) {
- return (majorVersion < major ||
- (majorVersion == major && minorVersion < minor));
- }
- public boolean afterVersion(int major, int minor) {
- return !beforeVersion(major,minor+1);
- }
-
- public int width;
- public int height;
- public ScreenSet screenLayout;
-
- public PixelFormat pf() { return pf_; }
- public void setPF(PixelFormat pf) {
-
- pf_ = pf;
-
- if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32)
- throw new Exception("setPF: not 8, 16 or 32 bpp?");
- }
-
- public String name() { return name_; }
- public void setName(String name)
- {
- name_ = name;
- }
-
- public boolean supportsEncoding(int encoding)
- {
- return encodings_.indexOf(encoding) != -1;
- }
-
- public void setEncodings(int nEncodings, int[] encodings)
- {
- useCopyRect = false;
- supportsLocalCursor = false;
- supportsLocalCursorWithAlpha = false;
- supportsDesktopResize = false;
- supportsExtendedDesktopSize = false;
- supportsLocalXCursor = false;
- supportsLastRect = false;
- compressLevel = -1;
- qualityLevel = -1;
- fineQualityLevel = -1;
- subsampling = subsampleUndefined;
-
- encodings_.clear();
- encodings_.add(Encodings.encodingRaw);
-
- for (int i = nEncodings-1; i >= 0; i--) {
- switch (encodings[i]) {
- case Encodings.encodingCopyRect:
- useCopyRect = true;
- break;
- case Encodings.pseudoEncodingCursor:
- supportsLocalCursor = true;
- break;
- case Encodings.pseudoEncodingXCursor:
- supportsLocalXCursor = true;
- break;
- case Encodings.pseudoEncodingCursorWithAlpha:
- supportsLocalCursorWithAlpha = true;
- break;
- case Encodings.pseudoEncodingDesktopSize:
- supportsDesktopResize = true;
- break;
- case Encodings.pseudoEncodingExtendedDesktopSize:
- supportsExtendedDesktopSize = true;
- break;
- case Encodings.pseudoEncodingDesktopName:
- supportsDesktopRename = true;
- break;
- case Encodings.pseudoEncodingLastRect:
- supportsLastRect = true;
- break;
- case Encodings.pseudoEncodingFence:
- supportsFence = true;
- break;
- case Encodings.pseudoEncodingContinuousUpdates:
- supportsContinuousUpdates = true;
- break;
- case Encodings.pseudoEncodingClientRedirect:
- supportsClientRedirect = true;
- break;
- case Encodings.pseudoEncodingSubsamp1X:
- subsampling = subsampleNone;
- break;
- case Encodings.pseudoEncodingSubsampGray:
- subsampling = subsampleGray;
- break;
- case Encodings.pseudoEncodingSubsamp2X:
- subsampling = subsample2X;
- break;
- case Encodings.pseudoEncodingSubsamp4X:
- subsampling = subsample4X;
- break;
- case Encodings.pseudoEncodingSubsamp8X:
- subsampling = subsample8X;
- break;
- case Encodings.pseudoEncodingSubsamp16X:
- subsampling = subsample16X;
- break;
- }
-
- if (encodings[i] >= Encodings.pseudoEncodingCompressLevel0 &&
- encodings[i] <= Encodings.pseudoEncodingCompressLevel9)
- compressLevel = encodings[i] - Encodings.pseudoEncodingCompressLevel0;
-
- if (encodings[i] >= Encodings.pseudoEncodingQualityLevel0 &&
- encodings[i] <= Encodings.pseudoEncodingQualityLevel9)
- qualityLevel = encodings[i] - Encodings.pseudoEncodingQualityLevel0;
-
- if (encodings[i] >= Encodings.pseudoEncodingFineQualityLevel0 &&
- encodings[i] <= Encodings.pseudoEncodingFineQualityLevel100)
- fineQualityLevel = encodings[i] - Encodings.pseudoEncodingFineQualityLevel0;
-
- if (encodings[i] > 0)
- encodings_.add(encodings[i]);
- }
- }
-
- public boolean useCopyRect;
-
- public boolean supportsLocalCursor;
- public boolean supportsLocalXCursor;
- public boolean supportsLocalCursorWithAlpha;
- public boolean supportsDesktopResize;
- public boolean supportsExtendedDesktopSize;
- public boolean supportsDesktopRename;
- public boolean supportsLastRect;
- public boolean supportsClientRedirect;
-
- public boolean supportsSetDesktopSize;
- public boolean supportsFence;
- public boolean supportsContinuousUpdates;
-
- public int compressLevel;
- public int qualityLevel;
- public int fineQualityLevel;
- public int subsampling;
-
- private PixelFormat pf_;
- private String name_;
- private Cursor cursor_;
- private ArrayList encodings_;
- private StringBuilder verStr;
- private int verStrPos;
-}
/* Copyright 2014 Pierre Ossman <ossman@cendio.se> for Cendio AB
- * Copyright 2016 Brian P. Hinz
+ * Copyright 2016-2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
public CopyRectDecoder() { super(DecoderFlags.DecoderPlain); }
public void readRect(Rect r, InStream is,
- ConnParams cp, OutStream os)
+ ServerParams server, OutStream os)
{
os.copyBytes(is, 4);
}
public void getAffectedRegion(Rect rect, Object buffer,
- int buflen, ConnParams cp,
+ int buflen, ServerParams server,
Region region)
{
MemInStream is = new MemInStream((byte[])buffer, 0, buflen);
int srcX = is.readU16();
int srcY = is.readU16();
- super.getAffectedRegion(rect, buffer, buflen, cp, region);
+ super.getAffectedRegion(rect, buffer, buflen, server, region);
region.assign_union(new Region(rect.translate(new Point(srcX-rect.tl.x,
srcY-rect.tl.y))));
}
public void decodeRect(Rect r, Object buffer,
- int buflen, ConnParams cp,
+ int buflen, ServerParams server,
ModifiablePixelBuffer pb)
{
MemInStream is = new MemInStream((byte[])buffer, 0, buflen);
/* Copyright 2015 Pierre Ossman for Cendio AB
- * Copyright 2016 Brian P. Hinz
+ * Copyright 2016-2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
if (threads.size() == 1) {
bufferStream = freeBuffers.getFirst();
bufferStream.clear();
- decoder.readRect(r, conn.getInStream(), conn.cp, bufferStream);
+ decoder.readRect(r, conn.getInStream(), conn.server, bufferStream);
decoder.decodeRect(r, (Object)bufferStream.data(), bufferStream.length(),
- conn.cp, pb);
+ conn.server, pb);
return;
}
// Read the rect
bufferStream.clear();
- decoder.readRect(r, conn.getInStream(), conn.cp, bufferStream);
+ decoder.readRect(r, conn.getInStream(), conn.server, bufferStream);
// Then try to put it on the queue
entry = new QueueEntry();
entry.rect = r;
entry.encoding = encoding;
entry.decoder = decoder;
- entry.cp = conn.cp;
+ entry.server = conn.server;
entry.pb = pb;
entry.bufferStream = bufferStream;
decoder.getAffectedRegion(r, bufferStream.data(),
- bufferStream.length(), conn.cp,
+ bufferStream.length(), conn.server,
entry.affectedRegion);
queueMutex.lock();
public Rect rect;
public int encoding;
public Decoder decoder;
- public ConnParams cp;
+ public ServerParams server;
public ModifiablePixelBuffer pb;
public MemOutStream bufferStream;
public Region affectedRegion;
try {
entry.decoder.decodeRect(entry.rect, entry.bufferStream.data(),
entry.bufferStream.length(),
- entry.cp, entry.pb);
+ entry.server, entry.pb);
} catch (com.tigervnc.rdr.Exception e) {
manager.setThreadException(e);
} catch(java.lang.Exception e) {
entry2.rect,
entry2.bufferStream.data(),
entry2.bufferStream.length(),
- entry.cp))
+ entry.server))
lockedRegion.assign_union(entry.affectedRegion);
continue next;
}
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright (C) 2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
}
abstract public void readRect(Rect r, InStream is,
- ConnParams cp, OutStream os);
+ ServerParams server, OutStream os);
abstract public void decodeRect(Rect r, Object buffer,
- int buflen, ConnParams cp,
+ int buflen, ServerParams server,
ModifiablePixelBuffer pb);
public void getAffectedRegion(Rect rect, Object buffer,
- int buflen, ConnParams cp,
+ int buflen, ServerParams server,
Region region)
{
region.reset(rect);
public boolean doRectsConflict(Rect rectA, Object bufferA,
int buflenA, Rect rectB,
Object bufferB, int buflenB,
- ConnParams cp)
+ ServerParams server)
{
return false;
}
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright (C) 2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
public HextileDecoder() { super(DecoderFlags.DecoderPlain); }
public void readRect(Rect r, InStream is,
- ConnParams cp, OutStream os)
+ ServerParams server, OutStream os)
{
Rect t = new Rect();
int bytesPerPixel;
- bytesPerPixel = cp.pf().bpp/8;
+ bytesPerPixel = server.pf().bpp/8;
for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {
}
public void decodeRect(Rect r, Object buffer,
- int buflen, ConnParams cp,
+ int buflen, ServerParams server,
ModifiablePixelBuffer pb)
{
MemInStream is = new MemInStream((byte[])buffer, 0, buflen);
- PixelFormat pf = cp.pf();
+ PixelFormat pf = server.pf();
switch (pf.bpp) {
case 8: hextileDecode8(r, is, pf, pb); break;
case 16: hextileDecode16(r, is, pf, pb); break;
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
- * Copyright 2016 Brian P. Hinz
+ * Copyright 2016-2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
public RREDecoder() { super(DecoderFlags.DecoderPlain); }
public void readRect(Rect r, InStream is,
- ConnParams cp, OutStream os)
+ ServerParams server, OutStream os)
{
int numRects;
numRects = is.readU32();
os.writeU32(numRects);
- os.copyBytes(is, cp.pf().bpp/8 + numRects * (cp.pf().bpp/8 + 8));
+ os.copyBytes(is, server.pf().bpp/8 + numRects * (server.pf().bpp/8 + 8));
}
public void decodeRect(Rect r, Object buffer,
- int buflen, ConnParams cp,
+ int buflen, ServerParams server,
ModifiablePixelBuffer pb)
{
MemInStream is = new MemInStream((byte[])buffer, 0, buflen);
- PixelFormat pf = cp.pf();
+ PixelFormat pf = server.pf();
switch (pf.bpp) {
case 8: rreDecode8 (r, is, pf, pb); break;
case 16: rreDecode16(r, is, pf, pb); break;
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright (C) 2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
public RawDecoder() { super(DecoderFlags.DecoderPlain); }
public void readRect(Rect r, InStream is,
- ConnParams cp, OutStream os)
+ ServerParams server, OutStream os)
{
- os.copyBytes(is, r.area() * cp.pf().bpp/8);
+ os.copyBytes(is, r.area() * server.pf().bpp/8);
}
static LogWriter vlog = new LogWriter("RawDecoder");
public void decodeRect(Rect r, Object buffer,
- int buflen, ConnParams cp,
+ int buflen, ServerParams server,
ModifiablePixelBuffer pb)
{
- assert(buflen >= r.area() * cp.pf().bpp/8);
- pb.imageRect(cp.pf(), r, (byte[])buffer);
+ assert(buflen >= r.area() * server.pf().bpp/8);
+ pb.imageRect(server.pf(), r, (byte[])buffer);
}
}
}
}
- // FIXME: List order shouldn't matter
- //inline bool operator(const ScreenSet& r) const { return screens == r.screens; }
- //inline bool operator(const ScreenSet& r) const { return screens != r.screens; }
-
public ArrayList<Screen> screens;
static LogWriter vlog = new LogWriter("ScreenSet");
--- /dev/null
+/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright (C) 2011 D. R. Commander. All Rights Reserved.
+ * Copyright (C) 2012-2019 Brian P. Hinz
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+package com.tigervnc.rfb;
+
+import com.tigervnc.rdr.*;
+import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class ServerParams {
+
+ private static final int subsampleUndefined = -1;
+ private static final int subsampleNone = 0;
+ private static final int subsampleGray = 1;
+ private static final int subsample2X = 2;
+ private static final int subsample4X = 3;
+ private static final int subsample8X = 4;
+ private static final int subsample16X = 5;
+
+ public ServerParams()
+ {
+ majorVersion = 0; minorVersion = 0;
+ width_ = 0; height_ = 0; useCopyRect = false;
+ supportsLocalCursor = false; supportsLocalXCursor = false;
+ supportsLocalCursorWithAlpha = false;
+ supportsDesktopResize = false; supportsExtendedDesktopSize = false;
+ supportsDesktopRename = false; supportsLastRect = false;
+ supportsSetDesktopSize = false; supportsFence = false;
+ supportsContinuousUpdates = false;
+ supportsClientRedirect = false;
+ compressLevel = 6; qualityLevel = -1; fineQualityLevel = -1;
+ subsampling = subsampleUndefined; name_ = null; verStrPos = 0;
+
+ encodings_ = new ArrayList();
+ screenLayout_ = new ScreenSet();
+
+ setName("");
+ }
+
+ public int majorVersion;
+ public int minorVersion;
+
+ public void setVersion(int major, int minor) {
+ majorVersion = major; minorVersion = minor;
+ }
+ public boolean isVersion(int major, int minor) {
+ return majorVersion == major && minorVersion == minor;
+ }
+ public boolean beforeVersion(int major, int minor) {
+ return (majorVersion < major ||
+ (majorVersion == major && minorVersion < minor));
+ }
+ public boolean afterVersion(int major, int minor) {
+ return !beforeVersion(major,minor+1);
+ }
+
+ public int width() { return width_; }
+ public int height() { return height_; }
+ public ScreenSet screenLayout() { return screenLayout_; }
+
+ public void setDimensions(int width, int height)
+ {
+ ScreenSet layout = new ScreenSet();
+ layout.add_screen(new Screen(0, 0, 0, width, height, 0));
+ setDimensions(width, height, layout);
+ }
+
+ public void setDimensions(int width, int height, ScreenSet layout)
+ {
+ if (!layout.validate(width, height))
+ throw new Exception("Attempted to configure an invalid screen layout");
+
+ width_ = width;
+ height_ = height;
+ screenLayout_ = layout;
+ }
+
+ public PixelFormat pf() { return pf_; }
+ public void setPF(PixelFormat pf) {
+
+ pf_ = pf;
+
+ if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32)
+ throw new Exception("setPF: not 8, 16 or 32 bpp?");
+ }
+
+ public String name() { return name_; }
+ public void setName(String name)
+ {
+ name_ = name;
+ }
+
+ public boolean supportsEncoding(int encoding)
+ {
+ return encodings_.indexOf(encoding) != -1;
+ }
+
+ public void setEncodings(int nEncodings, int[] encodings)
+ {
+ useCopyRect = false;
+ supportsLocalCursor = false;
+ supportsLocalCursorWithAlpha = false;
+ supportsDesktopResize = false;
+ supportsExtendedDesktopSize = false;
+ supportsLocalXCursor = false;
+ supportsLastRect = false;
+ compressLevel = -1;
+ qualityLevel = -1;
+ fineQualityLevel = -1;
+ subsampling = subsampleUndefined;
+
+ encodings_.clear();
+ encodings_.add(Encodings.encodingRaw);
+
+ for (int i = nEncodings-1; i >= 0; i--) {
+ switch (encodings[i]) {
+ case Encodings.encodingCopyRect:
+ useCopyRect = true;
+ break;
+ case Encodings.pseudoEncodingCursor:
+ supportsLocalCursor = true;
+ break;
+ case Encodings.pseudoEncodingXCursor:
+ supportsLocalXCursor = true;
+ break;
+ case Encodings.pseudoEncodingCursorWithAlpha:
+ supportsLocalCursorWithAlpha = true;
+ break;
+ case Encodings.pseudoEncodingDesktopSize:
+ supportsDesktopResize = true;
+ break;
+ case Encodings.pseudoEncodingExtendedDesktopSize:
+ supportsExtendedDesktopSize = true;
+ break;
+ case Encodings.pseudoEncodingDesktopName:
+ supportsDesktopRename = true;
+ break;
+ case Encodings.pseudoEncodingLastRect:
+ supportsLastRect = true;
+ break;
+ case Encodings.pseudoEncodingFence:
+ supportsFence = true;
+ break;
+ case Encodings.pseudoEncodingContinuousUpdates:
+ supportsContinuousUpdates = true;
+ break;
+ case Encodings.pseudoEncodingClientRedirect:
+ supportsClientRedirect = true;
+ break;
+ case Encodings.pseudoEncodingSubsamp1X:
+ subsampling = subsampleNone;
+ break;
+ case Encodings.pseudoEncodingSubsampGray:
+ subsampling = subsampleGray;
+ break;
+ case Encodings.pseudoEncodingSubsamp2X:
+ subsampling = subsample2X;
+ break;
+ case Encodings.pseudoEncodingSubsamp4X:
+ subsampling = subsample4X;
+ break;
+ case Encodings.pseudoEncodingSubsamp8X:
+ subsampling = subsample8X;
+ break;
+ case Encodings.pseudoEncodingSubsamp16X:
+ subsampling = subsample16X;
+ break;
+ }
+
+ if (encodings[i] >= Encodings.pseudoEncodingCompressLevel0 &&
+ encodings[i] <= Encodings.pseudoEncodingCompressLevel9)
+ compressLevel = encodings[i] - Encodings.pseudoEncodingCompressLevel0;
+
+ if (encodings[i] >= Encodings.pseudoEncodingQualityLevel0 &&
+ encodings[i] <= Encodings.pseudoEncodingQualityLevel9)
+ qualityLevel = encodings[i] - Encodings.pseudoEncodingQualityLevel0;
+
+ if (encodings[i] >= Encodings.pseudoEncodingFineQualityLevel0 &&
+ encodings[i] <= Encodings.pseudoEncodingFineQualityLevel100)
+ fineQualityLevel = encodings[i] - Encodings.pseudoEncodingFineQualityLevel0;
+
+ if (encodings[i] > 0)
+ encodings_.add(encodings[i]);
+ }
+ }
+
+ public boolean useCopyRect;
+
+ public boolean supportsLocalCursor;
+ public boolean supportsLocalXCursor;
+ public boolean supportsLocalCursorWithAlpha;
+ public boolean supportsDesktopResize;
+ public boolean supportsExtendedDesktopSize;
+ public boolean supportsDesktopRename;
+ public boolean supportsLastRect;
+ public boolean supportsClientRedirect;
+
+ public boolean supportsSetDesktopSize;
+ public boolean supportsFence;
+ public boolean supportsContinuousUpdates;
+
+ public int compressLevel;
+ public int qualityLevel;
+ public int fineQualityLevel;
+ public int subsampling;
+
+ private int width_;
+ private int height_;
+ private ScreenSet screenLayout_;
+
+ private PixelFormat pf_;
+ private String name_;
+ private Cursor cursor_;
+ private ArrayList encodings_;
+ private StringBuilder verStr;
+ private int verStrPos;
+}
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
* Copyright 2004-2005 Cendio AB.
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
- * Copyright (C) 2011-2012 Brian P. Hinz
+ * Copyright (C) 2011-2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
}
public void readRect(Rect r, InStream is,
- ConnParams cp, OutStream os)
+ ServerParams server, OutStream os)
{
int comp_ctl;
// "Fill" compression type.
if (comp_ctl == tightFill) {
- if (cp.pf().is888())
+ if (server.pf().is888())
os.copyBytes(is, 3);
else
- os.copyBytes(is, cp.pf().bpp/8);
+ os.copyBytes(is, server.pf().bpp/8);
return;
}
palSize = is.readU8() + 1;
os.writeU32(palSize - 1);
- if (cp.pf().is888())
+ if (server.pf().is888())
os.copyBytes(is, palSize * 3);
else
- os.copyBytes(is, palSize * cp.pf().bpp/8);
+ os.copyBytes(is, palSize * server.pf().bpp/8);
break;
case tightFilterGradient:
- if (cp.pf().bpp == 8)
+ if (server.pf().bpp == 8)
throw new Exception("TightDecoder: invalid BPP for gradient filter");
break;
case tightFilterCopy:
rowSize = (r.width() + 7) / 8;
else
rowSize = r.width();
- } else if (cp.pf().is888()) {
+ } else if (server.pf().is888()) {
rowSize = r.width() * 3;
} else {
- rowSize = r.width() * cp.pf().bpp/8;
+ rowSize = r.width() * server.pf().bpp/8;
}
dataSize = r.height() * rowSize;
Rect rectB,
Object bufferB,
int buflenB,
- ConnParams cp)
+ ServerParams server)
{
byte comp_ctl_a, comp_ctl_b;
}
public void decodeRect(Rect r, Object buffer,
- int buflen, ConnParams cp,
+ int buflen, ServerParams server,
ModifiablePixelBuffer pb)
{
ByteBuffer bufptr;
- PixelFormat pf = cp.pf();
+ PixelFormat pf = server.pf();
int comp_ctl;
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
+ * Copyright (C) 2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
}
public void readRect(Rect r, InStream is,
- ConnParams cp, OutStream os)
+ ServerParams server, OutStream os)
{
int len;
}
public void decodeRect(Rect r, Object buffer,
- int buflen, ConnParams cp,
+ int buflen, ServerParams server,
ModifiablePixelBuffer pb)
{
MemInStream is = new MemInStream((byte[])buffer, 0, buflen);
- PixelFormat pf = cp.pf();
+ PixelFormat pf = server.pf();
switch (pf.bpp) {
case 8: zrleDecode8(r, is, zis, pf, pb); break;
case 16: zrleDecode16(r, is, zis, pf, pb); break;
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2009-2013 Pierre Ossman <ossman@cendio.se> for Cendio AB
* Copyright (C) 2011-2013 D. R. Commander. All Rights Reserved.
- * Copyright (C) 2011-2017 Brian P. Hinz
+ * Copyright (C) 2011-2019 Brian P. Hinz
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
public CConn(String vncServerName, Socket socket)
{
serverHost = null; serverPort = 0; desktop = null;
- pendingPFChange = false;
- currentEncoding = Encodings.encodingTight; lastServerEncoding = -1;
- formatChange = false; encodingChange = false;
- firstUpdate = true; pendingUpdate = false; continuousUpdates = false;
- forceNonincremental = true; supportsSyncFence = false;
+ updateCount = 0; pixelCount = 0;
+ lastServerEncoding = -1;
setShared(shared.getValue());
sock = socket;
- int encNum = Encodings.encodingNum(preferredEncoding.getValue());
- if (encNum != -1)
- currentEncoding = encNum;
-
- cp.supportsLocalCursor = true;
- if (VncViewer.os.contains("windows"))
- // JRE on Windows does not support alpha cursor
- cp.supportsLocalCursorWithAlpha = false;
- else
- cp.supportsLocalCursorWithAlpha = true;
-
- cp.supportsDesktopResize = true;
- cp.supportsExtendedDesktopSize = true;
- cp.supportsDesktopRename = true;
-
- cp.supportsSetDesktopSize = false;
- cp.supportsClientRedirect = true;
+ server.supportsLocalCursor = true;
+ server.supportsDesktopResize = true;
+ server.supportsClientRedirect = true;
if (customCompressLevel.getValue())
- cp.compressLevel = compressLevel.getValue();
- else
- cp.compressLevel = -1;
+ setCompressLevel(compressLevel.getValue());
if (!noJpeg.getValue())
- cp.qualityLevel = qualityLevel.getValue();
- else
- cp.qualityLevel = -1;
+ setQualityLevel(qualityLevel.getValue());
if (sock == null) {
setServerName(Hostname.getHost(vncServerName));
OptionsDialog.addCallback("handleOptions", this);
}
- public void refreshFramebuffer()
- {
- forceNonincremental = true;
-
- // Without fences, we cannot safely trigger an update request directly
- // but must wait for the next update to arrive.
- if (supportsSyncFence)
- requestNewUpdate();
- }
-
public String connectionInfo() {
String info = new String("Desktop name: %s%n"+
"Host: %s:%d%n"+
"Protocol version: %d.%d%n"+
"Security method: %s [%s]%n");
String infoText =
- String.format(info, cp.name(),
+ String.format(info, server.name(),
sock.getPeerName(), sock.getPeerPort(),
- cp.width, cp.height,
- cp.pf().print(),
+ server.width(), server.height(),
+ server.pf().print(),
serverPF.print(),
- Encodings.encodingName(currentEncoding),
+ Encodings.encodingName(getPreferredEncoding()),
Encodings.encodingName(lastServerEncoding),
sock.inStream().kbitsPerSecond(),
- cp.majorVersion, cp.minorVersion,
+ server.majorVersion, server.minorVersion,
Security.secTypeName(csecurity.getType()),
csecurity.description());
return infoText;
}
+ public int getUpdateCount()
+ {
+ return updateCount;
+ }
+
+ public int getPixelCount()
+ {
+ return pixelCount;
+ }
+
// The RFB core is not properly asynchronous, so it calls this callback
// whenever it needs to block to wait for more data. Since FLTK is
// monitoring the socket, we just make sure FLTK gets to run.
// serverInit() is called when the serverInit message has been received. At
// this point we create the desktop window and display it. We also tell the
// server the pixel format and encodings to use and request the first update.
- public void serverInit()
+ public void initDone()
{
- super.serverInit();
-
// If using AutoSelect with old servers, start in FullColor
// mode. See comment in autoSelectFormatAndEncoding.
- if (cp.beforeVersion(3, 8) && autoSelect.getValue())
+ if (server.beforeVersion(3, 8) && autoSelect.getValue())
fullColor.setParam(true);
- serverPF = cp.pf();
+ serverPF = server.pf();
- desktop = new DesktopWindow(cp.width, cp.height, cp.name(), serverPF, this);
+ desktop = new DesktopWindow(server.width(), server.height(),
+ server.name(), serverPF, this);
fullColorPF = desktop.getPreferredPF();
// Force a switch to the format and encoding we'd like
- formatChange = true; encodingChange = true;
-
- // And kick off the update cycle
- requestNewUpdate();
-
- // This initial update request is a bit of a corner case, so we need
- // to help out setting the correct format here.
- assert(pendingPFChange);
- cp.setPF(pendingPF);
- pendingPFChange = false;
+ updatePixelFormat();
+ int encNum = Encodings.encodingNum(preferredEncoding.getValue());
+ if (encNum != -1)
+ setPreferredEncoding(encNum);
}
// setDesktopSize() is called when the desktop size changes (including when
}
// setExtendedDesktopSize() is a more advanced version of setDesktopSize()
- public void setExtendedDesktopSize(int reason, int result, int w, int h,
- ScreenSet layout)
+ public void setExtendedDesktopSize(int reason, int result,
+ int w, int h, ScreenSet layout)
{
super.setExtendedDesktopSize(reason, result, w, h, layout);
// one.
public void framebufferUpdateStart()
{
- ModifiablePixelBuffer pb;
- PlatformPixelBuffer ppb;
super.framebufferUpdateStart();
- // Note: This might not be true if sync fences are supported
- pendingUpdate = false;
-
- requestNewUpdate();
-
- // We might still be rendering the previous update
- pb = getFramebuffer();
- assert(pb != null);
- ppb = (PlatformPixelBuffer)pb;
- assert(ppb != null);
-
- //FIXME
}
// framebufferUpdateEnd() is called at the end of an update.
{
super.framebufferUpdateEnd();
- desktop.updateWindow();
-
- if (firstUpdate) {
- // We need fences to make extra update requests and continuous
- // updates "safe". See fence() for the next step.
- if (cp.supportsFence)
- writer().writeFence(fenceTypes.fenceFlagRequest | fenceTypes.fenceFlagSyncNext, 0, null);
+ updateCount++;
- firstUpdate = false;
- }
-
- // A format change has been scheduled and we are now past the update
- // with the old format. Time to active the new one.
- if (pendingPFChange) {
- cp.setPF(pendingPF);
- pendingPFChange = false;
- }
+ desktop.updateWindow();
// Compute new settings based on updated bandwidth values
if (autoSelect.getValue())
super.dataRect(r, encoding);
sock.inStream().stopTiming();
+
+ pixelCount += r.area();
}
public void setCursor(int width, int height, Point hotspot,
public void fence(int flags, int len, byte[] data)
{
// can't call super.super.fence(flags, len, data);
- cp.supportsFence = true;
+ server.supportsFence = true;
if ((flags & fenceTypes.fenceFlagRequest) != 0) {
// We handle everything synchronously so we trivially honor these modes
writer().writeFence(flags, len, data);
return;
}
-
- if (len == 0) {
- // Initial probe
- if ((flags & fenceTypes.fenceFlagSyncNext) != 0) {
- supportsSyncFence = true;
-
- if (cp.supportsContinuousUpdates) {
- vlog.info("Enabling continuous updates");
- continuousUpdates = true;
- writer().writeEnableContinuousUpdates(true, 0, 0, cp.width, cp.height);
- }
- }
- } else {
- // Pixel format change
- MemInStream memStream = new MemInStream(data, 0, len);
- PixelFormat pf = new PixelFormat();
-
- pf.read(memStream);
-
- cp.setPF(pf);
- }
}
////////////////////// Internal methods //////////////////////
- private void resizeFramebuffer()
+ public void resizeFramebuffer()
{
if (desktop == null)
return;
- if (continuousUpdates)
- writer().writeEnableContinuousUpdates(true, 0, 0, cp.width, cp.height);
-
- desktop.resizeFramebuffer(cp.width, cp.height);
+ desktop.resizeFramebuffer(server.width(), server.height());
}
// autoSelectFormatAndEncoding() chooses the format and encoding appropriate
{
long kbitsPerSecond = sock.inStream().kbitsPerSecond();
long timeWaited = sock.inStream().timeWaited();
- boolean newFullColor = fullColor.getValue();
+ boolean newFullColour = fullColor.getValue();
int newQualityLevel = qualityLevel.getValue();
// Always use Tight
- if (currentEncoding != Encodings.encodingTight) {
- currentEncoding = Encodings.encodingTight;
- encodingChange = true;
- }
+ setPreferredEncoding(Encodings.encodingTight);
// Check that we have a decent bandwidth measurement
if ((kbitsPerSecond == 0) || (timeWaited < 100))
if (newQualityLevel != qualityLevel.getValue()) {
vlog.info("Throughput "+kbitsPerSecond+
" kbit/s - changing to quality "+newQualityLevel);
- cp.qualityLevel = newQualityLevel;
qualityLevel.setParam(newQualityLevel);
- encodingChange = true;
+ setQualityLevel(newQualityLevel);
}
}
- if (cp.beforeVersion(3, 8)) {
+ if (server.beforeVersion(3, 8)) {
// Xvnc from TightVNC 1.2.9 sends out FramebufferUpdates with
// cursors "asynchronously". If this happens in the middle of a
// pixel format change, the server will encode the cursor with
}
// Select best color level
- newFullColor = (kbitsPerSecond > 256);
- if (newFullColor != fullColor.getValue()) {
- vlog.info("Throughput "+kbitsPerSecond+
- " kbit/s - full color is now "+
- (newFullColor ? "enabled" : "disabled"));
- fullColor.setParam(newFullColor);
- formatChange = true;
- }
- }
-
- // checkEncodings() sends a setEncodings message if one is needed.
- private void checkEncodings()
- {
- if (encodingChange && (writer() != null)) {
- vlog.info("Using " + Encodings.encodingName(currentEncoding) +
- " encoding");
- writer().writeSetEncodings(currentEncoding, true);
- encodingChange = false;
+ newFullColour = (kbitsPerSecond > 256);
+ if (newFullColour != fullColor.getValue()) {
+ if (newFullColour)
+ vlog.info("Throughput "+kbitsPerSecond+ " kbit/s - full color is now enabled");
+ else
+ vlog.info("Throughput "+kbitsPerSecond+ " kbit/s - full color is now disabled");
+ fullColor.setParam(newFullColour);
+ updatePixelFormat();
}
}
- // requestNewUpdate() requests an update from the server, having set the
+ // updatePixelFormat() requests an update from the server, having set the
// format and encoding appropriately.
- private void requestNewUpdate()
+ private void updatePixelFormat()
{
- if (formatChange) {
- PixelFormat pf;
-
- /* Catch incorrect requestNewUpdate calls */
- assert(!pendingUpdate || supportsSyncFence);
-
- if (fullColor.getValue()) {
- pf = fullColorPF;
- } else {
- if (lowColorLevel.getValue() == 0) {
- pf = verylowColorPF;
- } else if (lowColorLevel.getValue() == 1) {
- pf = lowColorPF;
- } else {
- pf = mediumColorPF;
- }
- }
-
- if (supportsSyncFence) {
- // We let the fence carry the pixel format and switch once we
- // get the response back. That way we will be synchronised with
- // when the server switches.
- MemOutStream memStream = new MemOutStream();
-
- pf.write(memStream);
+ PixelFormat pf;
- writer().writeFence(fenceTypes.fenceFlagRequest | fenceTypes.fenceFlagSyncNext,
- memStream.length(), (byte[])memStream.data());
+ if (fullColor.getValue()) {
+ pf = fullColorPF;
+ } else {
+ if (lowColorLevel.getValue() == 0) {
+ pf = verylowColorPF;
+ } else if (lowColorLevel.getValue() == 1) {
+ pf = lowColorPF;
} else {
- // New requests are sent out at the start of processing the last
- // one, so we cannot switch our internal format right now (doing so
- // would mean misdecoding the current update).
- pendingPFChange = true;
- pendingPF = pf;
+ pf = mediumColorPF;
}
-
- String str = pf.print();
- vlog.info("Using pixel format " + str);
- writer().writeSetPixelFormat(pf);
-
- formatChange = false;
- }
-
- checkEncodings();
-
- if (forceNonincremental || !continuousUpdates) {
- pendingUpdate = true;
- writer().writeFramebufferUpdateRequest(new Rect(0, 0, cp.width, cp.height),
- !forceNonincremental);
}
- forceNonincremental = false;
+ String str = pf.print();
+ vlog.info("Using pixel format " + str);
+ setPF(pf);
}
public void handleOptions()
int encNum = Encodings.encodingNum(preferredEncoding.getValue());
if (encNum != -1)
- this.currentEncoding = encNum;
+ this.setPreferredEncoding(encNum);
}
- this.cp.supportsLocalCursor = true;
-
if (customCompressLevel.getValue())
- this.cp.compressLevel = compressLevel.getValue();
+ this.setCompressLevel(compressLevel.getValue());
else
- this.cp.compressLevel = -1;
+ this.setCompressLevel(-1);
if (!noJpeg.getValue() && !autoSelect.getValue())
- this.cp.qualityLevel = qualityLevel.getValue();
+ this.setQualityLevel(qualityLevel.getValue());
else
- this.cp.qualityLevel = -1;
-
- this.encodingChange = true;
-
- // Format changes refreshes the entire screen though and are therefore
- // very costly. It's probably worth the effort to see if it is necessary
- // here.
- PixelFormat pf;
-
- if (fullColor.getValue()) {
- pf = fullColorPF;
- } else {
- if (lowColorLevel.getValue() == 0)
- pf = verylowColorPF;
- else if (lowColorLevel.getValue() == 1)
- pf = lowColorPF;
- else
- pf = mediumColorPF;
- }
-
- if (!pf.equal(this.cp.pf())) {
- this.formatChange = true;
-
- // Without fences, we cannot safely trigger an update request directly
- // but must wait for the next update to arrive.
- if (this.supportsSyncFence)
- this.requestNewUpdate();
- }
+ this.setQualityLevel(-1);
+ this.updatePixelFormat();
}
////////////////////////////////////////////////////////////////////
// writeClientCutText() is called from the clipboard dialog
public void writeClientCutText(String str, int len) {
- if (state() != RFBSTATE_NORMAL || shuttingDown)
+ if ((state() != stateEnum.RFBSTATE_NORMAL) || shuttingDown)
return;
writer().writeClientCutText(str, len);
}
protected DesktopWindow desktop;
+ private int updateCount;
+ private int pixelCount;
+
private PixelFormat serverPF;
private PixelFormat fullColorPF;
- private boolean pendingPFChange;
- private PixelFormat pendingPF;
-
- private int currentEncoding, lastServerEncoding;
-
- private boolean formatChange;
- private boolean encodingChange;
-
- private boolean firstUpdate;
- private boolean pendingUpdate;
- private boolean continuousUpdates;
-
- private boolean forceNonincremental;
-
- private boolean supportsSyncFence;
+ private int lastServerEncoding;
public ActionListener closeListener = null;
// c) We're not still waiting for a chance to handle DesktopSize
// d) We're not still waiting for startup fullscreen to kick in
if (!firstUpdate && !delayedFullscreen &&
- remoteResize.getValue() && cc.cp.supportsSetDesktopSize)
+ remoteResize.getValue() && cc.server.supportsSetDesktopSize)
timer.start();
} else {
String scaleString = scalingFactor.getValue();
setExtendedState(JFrame.MAXIMIZED_BOTH);
}
- if (cc.cp.supportsSetDesktopSize && !desktopSize.getValue().equals("")) {
+ if (cc.server.supportsSetDesktopSize && !desktopSize.getValue().equals("")) {
// Hack: Wait until we're in the proper mode and position until
// resizing things, otherwise we might send the wrong thing.
if (delayedFullscreen)
// to scroll) we just report a single virtual screen that covers
// the entire framebuffer.
- layout = cc.cp.screenLayout;
+ layout = cc.server.screenLayout();
// Not sure why we have no screens, but adding a new one should be
// safe as there is nothing to conflict with...
sy -= viewport_rect.tl.y;
// Look for perfectly matching existing screen...
- for (iter = cc.cp.screenLayout.begin();
- iter != cc.cp.screenLayout.end(); iter.next()) {
+ for (iter = cc.server.screenLayout().begin();
+ iter != cc.server.screenLayout().end(); iter.next()) {
Screen screen = iter.next(); iter.previous();
if ((screen.dimensions.tl.x == sx) &&
(screen.dimensions.tl.y == sy) &&
}
// Found it?
- if (iter != cc.cp.screenLayout.end()) {
+ if (iter != cc.server.screenLayout().end()) {
layout.add_screen(iter.next());
continue;
}
Random rng = new Random();
while (true) {
id = rng.nextInt();
- for (iter = cc.cp.screenLayout.begin();
- iter != cc.cp.screenLayout.end(); iter.next()) {
+ for (iter = cc.server.screenLayout().begin();
+ iter != cc.server.screenLayout().end(); iter.next()) {
Screen screen = iter.next(); iter.previous();
if (screen.id == id)
break;
}
- if (iter == cc.cp.screenLayout.end())
+ if (iter == cc.server.screenLayout().end())
break;
}
}
// Do we actually change anything?
- if ((width == cc.cp.width) &&
- (height == cc.cp.height) &&
- (layout == cc.cp.screenLayout))
+ if ((width == cc.server.width()) &&
+ (height == cc.server.height()) &&
+ (layout == cc.server.screenLayout()))
return;
String buffer;
vlog.debug(String.format("Requesting framebuffer resize from %dx%d to %dx%d",
- cc.cp.width, cc.cp.height, width, height));
+ cc.server.width(), cc.server.height(), width, height));
layout.debug_print();
if (!layout.validate(width, height)) {
if (scaleString.matches("^[0-9]+$")) {
scroll.setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
- viewport.setScaledSize(cc.cp.width, cc.cp.height);
+ viewport.setScaledSize(cc.server.width(), cc.server.height());
} else {
scroll.setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_NEVER);
scroll.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_NEVER);
private void handleRfbState()
{
CConn cc = VncViewer.cc;
- if (cc != null && cc.state() == CConnection.RFBSTATE_NORMAL) {
+ if (cc != null && cc.state() == CConnection.stateEnum.RFBSTATE_NORMAL) {
JComponent[] components = {
encNoneCheckbox, encTLSCheckbox, encX509Checkbox, authNoneCheckbox,
authVncCheckbox, authVncCheckbox, authIdentCheckbox, authPlainCheckbox,
public void updateWindow() {
Rect r = frameBuffer.getDamage();
if (!r.is_empty()) {
- if (cc.cp.width != scaledWidth ||
- cc.cp.height != scaledHeight) {
+ if (cc.server.width() != scaledWidth ||
+ cc.server.height() != scaledHeight) {
AffineTransform t = new AffineTransform();
t.scale((double)scaleRatioX, (double)scaleRatioY);
Rectangle s = new Rectangle(r.tl.x, r.tl.y, r.width(), r.height());
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
synchronized(frameBuffer.getImage()) {
- if (cc.cp.width != scaledWidth ||
- cc.cp.height != scaledHeight) {
+ if (cc.server.width() != scaledWidth ||
+ cc.server.height() != scaledHeight) {
g2.setRenderingHint(RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY);
g2.drawImage(frameBuffer.getImage(), 0, 0,
scaledWidth = width;
scaledHeight = height;
} else {
- float widthRatio = (float)width / (float)cc.cp.width;
- float heightRatio = (float)height / (float)cc.cp.height;
+ float widthRatio = (float)width / (float)cc.server.width();
+ float heightRatio = (float)height / (float)cc.server.height();
float ratio = Math.min(widthRatio, heightRatio);
- scaledWidth = (int)Math.floor(cc.cp.width * ratio);
- scaledHeight = (int)Math.floor(cc.cp.height * ratio);
+ scaledWidth = (int)Math.floor(cc.server.width() * ratio);
+ scaledHeight = (int)Math.floor(cc.server.height() * ratio);
}
- scaleRatioX = (float)scaledWidth / (float)cc.cp.width;
- scaleRatioY = (float)scaledHeight / (float)cc.cp.height;
+ scaleRatioX = (float)scaledWidth / (float)cc.server.width();
+ scaleRatioY = (float)scaledHeight / (float)cc.server.height();
}
if (scaledWidth != getWidth() || scaledHeight != getHeight())
setSize(new Dimension(scaledWidth, scaledHeight));
if (!viewOnly.getValue()) {
if (buttonMask != lastButtonMask || !pos.equals(lastPointerPos)) {
try {
- if (cc.cp.width != scaledWidth ||
- cc.cp.height != scaledHeight) {
+ if (cc.server.width() != scaledWidth ||
+ cc.server.height() != scaledHeight) {
int sx = (scaleRatioX == 1.00) ?
pos.x : (int)Math.floor(pos.x / scaleRatioX);
int sy = (scaleRatioY == 1.00) ?
pos.y : (int)Math.floor(pos.y / scaleRatioY);
pos = pos.translate(new Point(sx - pos.x, sy - pos.y));
}
- cc.writer().pointerEvent(pos, buttonMask);
+ cc.writer().writePointerEvent(pos, buttonMask);
} catch (Exception e) {
vlog.error("%s", e.getMessage());
cc.close();
downKeySym.containsValue(XK_Alt_R)) {
vlog.debug("Faking release of AltGr (Ctrl_L+Alt_R)");
try {
- cc.writer().keyEvent(XK_Control_L, false);
- cc.writer().keyEvent(XK_Alt_R, false);
+ cc.writer().writeKeyEvent(XK_Control_L, false);
+ cc.writer().writeKeyEvent(XK_Alt_R, false);
} catch (Exception e) {
vlog.error("%s", e.getMessage());
cc.close();
try {
// Fake keycode?
if (keyCode > 0xffff)
- cc.writer().keyEvent(keySym, true);
+ cc.writer().writeKeyEvent(keySym, true);
else
- cc.writer().keyEvent(keySym, true);
+ cc.writer().writeKeyEvent(keySym, true);
} catch (Exception e) {
vlog.error("%s", e.getMessage());
cc.close();
downKeySym.containsValue(XK_Alt_R)) {
vlog.debug("Restoring AltGr state");
try {
- cc.writer().keyEvent(XK_Control_L, true);
- cc.writer().keyEvent(XK_Alt_R, true);
+ cc.writer().writeKeyEvent(XK_Control_L, true);
+ cc.writer().writeKeyEvent(XK_Alt_R, true);
} catch (Exception e) {
vlog.error("%s", e.getMessage());
cc.close();
try {
if (keyCode > 0xffff)
- cc.writer().keyEvent(iter, false);
+ cc.writer().writeKeyEvent(iter, false);
else
- cc.writer().keyEvent(iter, false);
+ cc.writer().writeKeyEvent(iter, false);
} catch (Exception e) {
vlog.error("%s", e.getMessage());
cc.close();
{
setMenuKey();
/*
- setScaledSize(cc.cp.width, cc.cp.height);
+ setScaledSize(cc.server.width(), cc.server.height());
if (!oldSize.equals(new Dimension(scaledWidth, scaledHeight))) {
// Re-layout the DesktopWindow when the scaled size changes.
// Ideally we'd do this with a ComponentListener, but unfortunately