/* Copyright (c) 2017, Sikuli.org, sikulix.com
*
* 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 org.sikuli.vnc;
import com.tigervnc.network.TcpSocket;
import com.tigervnc.rdr.FdInStreamBlockCallback;
import com.tigervnc.rfb.*;
import com.tigervnc.rfb.Exception;
import com.tigervnc.rfb.Point;
import com.tigervnc.vncviewer.CConn;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.Closeable;
import java.io.IOException;
class VNCClient extends CConnection implements FdInStreamBlockCallback, Closeable
{
static ThreadLocal<UserPasswdGetter> UPG = new ThreadLocal<>();
static {
CConn.upg = new UserPasswdGetter()
{
@Override
public boolean getUserPasswd(StringBuffer stringBuffer, StringBuffer stringBuffer1)
{
UserPasswdGetter upg = UPG.get();
return false;
}
};
}
private final TcpSocket sock;
private boolean shuttingDown = false;
private PixelFormat serverPF;
private int currentEncoding;
private VNCFrameBuffer frameBuffer;
public static VNCClient connect(String address, int port, String password, boolean shareConnection) throws IOException
{
VNCClient client = new VNCClient(address, port, password, shareConnection);
while (client.state() != VNCClient.RFBSTATE_NORMAL) {
client.processMsg();
}
return client;
}
private VNCClient(String address, int port, final String password, boolean shareConnection) throws IOException
{
this.security = new ThreadLocalSecurityClient(new BasicUserPasswdGetter(password));
this.currentEncoding = Encodings.encodingTight;
this.setShared(shareConnection);
setServerName(address);
setServerPort(port);
this.sock = new TcpSocket(this.getServerName(), this.getServerPort());
this.sock.inStream().setBlockCallback(this);
this.setStreams(this.sock.inStream(), this.sock.outStream());
this.initialiseProtocol();
}
@Override
public PixelFormat getPreferredPF()
{
return new PixelFormat();
}
@Override
public void serverInit()
{
super.serverInit();
this.serverPF = this.cp.pf();
this.frameBuffer = new VNCFrameBuffer(this.cp.width, this.cp.height, this.serverPF);
this.writer().writeSetEncodings(this.currentEncoding, true);
}
public void setDesktopSize(int var1, int var2)
{
super.setDesktopSize(var1, var2);
this.resizeFramebuffer();
}
public void setColourMapEntries(int offset, int nbColors, int[] rgb) {
frameBuffer.setColourMapEntries(offset, nbColors, rgb);
}
private void resizeFramebuffer()
{
if (this.frameBuffer != null) {
if (this.cp.width != 0 || this.cp.height != 0) {
if (this.frameBuffer.width() != this.cp.width || this.frameBuffer.height() != this.cp.height) {
this.frameBuffer.resize(cp.width, cp.height);
}
}
}
}
public void refreshFramebuffer()
{
refreshFramebuffer(0, 0, cp.width, cp.height, false);
}
/**
* Sends FramebufferUpdateRequest message to server.
*
* @param x X coordinate of desired region
* @param y Y coordinate of desired region
* @param w Width of desired region
* @param h Height of desired region
* @param incremental Zero sends entire desktop, One sends changes only.
* @throws IOException If there is a socket error
*/
public void refreshFramebuffer(int x, int y, int w, int h, boolean incremental)
{
writer().writeFramebufferUpdateRequest(new Rect(x, y, w, h), incremental);
}
@Override
public void framebufferUpdateStart()
{
refreshFramebuffer(0, 0, cp.width, cp.height, true);
}
@Override
public void framebufferUpdateEnd()
{
}
public void fillRect(Rect r, int p)
{
this.frameBuffer.fillRect(r.tl.x, r.tl.y, r.width(), r.height(), p);
}
public void imageRect(Rect r, Object p)
{
this.frameBuffer.imageRect(r.tl.x, r.tl.y, r.width(), r.height(), p);
}
public void copyRect(Rect r, int sx, int sy)
{
this.frameBuffer.copyRect(r.tl.x, r.tl.y, r.width(), r.height(), sx, sy);
}
/**
* Tells VNC server to depress key.
*
* @param key X Window System Keysym for key.
* @throws IOException If there is a socket error.
*/
protected void keyDown(int key) throws IOException
{
writer().writeKeyEvent(key, true);
}
/**
* Tells VNC server to release key.
*
* @param key X Window System Keysym for key.
* @throws IOException If there is a socket error.
*/
protected void keyUp(int key) throws IOException
{
writer().writeKeyEvent(key, false);
}
/**
* Tells VNC server to perform a mouse event. bOne through bEight are mouse
* buttons one through eight respectively. A zero means release that
* button, and a one means depress that button.
*
* @param buttonState logical or of BUTTON_N_DOWN
* @param x X coordinate of action
* @param y Y coordinate of action
* @throws IOException If there is a socket error.
*/
protected void mouseEvent(int buttonState, int x, int y) throws IOException
{
writer().writePointerEvent(new Point(x, y), buttonState);
}
/**
* Closes the connection
*
* @throws IOException
*/
public void close() throws IOException
{
this.shuttingDown = true;
if (this.sock != null) {
this.sock.shutdown();
}
}
/**
* Returns the VNCClient Object as a string
*/
public String toString()
{
return "VNCClient: " + getServerName() + ":" + getServerPort();
}
public Rectangle getBounds()
{
return new Rectangle(0, 0, this.cp.width, this.cp.height);
}
public BufferedImage getFrameBuffer(int x, int y, int w, int h)
{
return frameBuffer.getImage(x, y, w, h);
}
@Override
public void blockCallback()
{
try {
synchronized (this) {
this.wait(1L);
}
} catch (InterruptedException var4) {
throw new Exception(var4.getMessage());
}
}
public void processMessages()
{
while (!shuttingDown) {
processMsg();
}
}
}