mirror of https://github.com/apache/cloudstack.git
336 lines
10 KiB
Java
336 lines
10 KiB
Java
// Licensed to the Apache Software Foundation (ASF) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you under the Apache License, Version 2.0 (the
|
|
// "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
package com.cloud.consoleproxy;
|
|
|
|
import java.awt.event.InputEvent;
|
|
import java.awt.event.KeyEvent;
|
|
import java.awt.event.MouseEvent;
|
|
import java.net.InetSocketAddress;
|
|
|
|
import org.apache.log4j.Logger;
|
|
|
|
import rdpclient.RdpClient;
|
|
import streamer.Pipeline;
|
|
import streamer.PipelineImpl;
|
|
import streamer.SocketWrapper;
|
|
import streamer.apr.AprSocketWrapperImpl;
|
|
import streamer.ssl.SSLState;
|
|
|
|
import com.cloud.consoleproxy.rdp.KeysymToKeycode;
|
|
import com.cloud.consoleproxy.rdp.RdpBufferedImageCanvas;
|
|
import com.cloud.consoleproxy.vnc.FrameBufferCanvas;
|
|
|
|
import common.AwtKeyEventSource;
|
|
import common.AwtMouseEventSource;
|
|
import common.ScreenDescription;
|
|
import common.SizeChangeListener;
|
|
|
|
public class ConsoleProxyRdpClient extends ConsoleProxyClientBase {
|
|
|
|
private static final Logger s_logger = Logger.getLogger(ConsoleProxyRdpClient.class);
|
|
|
|
private static final int SHIFT_KEY_MASK = 64;
|
|
private static final int CTRL_KEY_MASK = 128;
|
|
private static final int META_KEY_MASK = 256;
|
|
private static final int ALT_KEY_MASK = 512;
|
|
|
|
private RdpClient _client;
|
|
private ScreenDescription _screen;
|
|
private SocketWrapper _socket = null;
|
|
private RdpBufferedImageCanvas _canvas = null;
|
|
|
|
private Thread _worker;
|
|
private volatile boolean _workerDone = true;
|
|
private volatile long _threadStopTime;
|
|
|
|
private AwtMouseEventSource _mouseEventSource = null;
|
|
private AwtKeyEventSource _keyEventSource = null;
|
|
|
|
public RdpBufferedImageCanvas getCanvas() {
|
|
return _canvas;
|
|
}
|
|
|
|
public void setCanvas(RdpBufferedImageCanvas canvas) {
|
|
_canvas = canvas;
|
|
}
|
|
|
|
@Override
|
|
public void onClientConnected() {
|
|
// TODO Auto-generated method stub
|
|
}
|
|
|
|
@Override
|
|
public void onClientClose() {
|
|
s_logger.info("Received client close indication. remove viewer from map.");
|
|
ConsoleProxy.removeViewer(this);
|
|
}
|
|
|
|
@Override
|
|
public boolean isHostConnected() {
|
|
//FIXME
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean isFrontEndAlive() {
|
|
if (_socket != null) {
|
|
if (_workerDone || System.currentTimeMillis() - getClientLastFrontEndActivityTime() > ConsoleProxy.VIEWER_LINGER_SECONDS * 1000) {
|
|
s_logger.info("Front end has been idle for too long");
|
|
_socket.shutdown();
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void sendClientRawKeyboardEvent(InputEventType event, int code, int modifiers) {
|
|
if (_client == null)
|
|
return;
|
|
|
|
updateFrontEndActivityTime();
|
|
|
|
KeyEvent keyEvent = map(event, code, modifiers);
|
|
switch (event) {
|
|
case KEY_DOWN:
|
|
_keyEventSource.keyPressed(keyEvent);
|
|
break;
|
|
|
|
case KEY_UP:
|
|
_keyEventSource.keyReleased(keyEvent);
|
|
break;
|
|
|
|
case KEY_PRESS:
|
|
break;
|
|
|
|
default:
|
|
assert (false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
private KeyEvent map(InputEventType event, int code, int modifiers) {
|
|
int keycode = KeysymToKeycode.getKeycode(code);
|
|
char keyChar = (char)keycode;
|
|
|
|
KeyEvent keyEvent = null;
|
|
int modifier = mapModifier(modifiers);
|
|
|
|
switch (event) {
|
|
case KEY_DOWN:
|
|
keyEvent = new KeyEvent(_canvas, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), modifier, keycode, keyChar);
|
|
break;
|
|
|
|
case KEY_UP:
|
|
keyEvent = new KeyEvent(_canvas, KeyEvent.KEY_RELEASED, System.currentTimeMillis(), modifier, keycode, keyChar);
|
|
break;
|
|
|
|
case KEY_PRESS:
|
|
break;
|
|
|
|
default:
|
|
assert (false);
|
|
break;
|
|
}
|
|
return keyEvent;
|
|
}
|
|
|
|
@Override
|
|
public void sendClientMouseEvent(InputEventType event, int x, int y, int code, int modifiers) {
|
|
if (_client == null)
|
|
return;
|
|
updateFrontEndActivityTime();
|
|
|
|
int mousecode = mapMouseButton(code);
|
|
|
|
if (event == InputEventType.MOUSE_DOWN) {
|
|
_mouseEventSource.mousePressed(new MouseEvent(_canvas, MouseEvent.MOUSE_PRESSED, System.currentTimeMillis(), mapMouseDownModifier(code, modifiers), x, y, 1, false,
|
|
mousecode));
|
|
}
|
|
|
|
if (event == InputEventType.MOUSE_UP) {
|
|
_mouseEventSource.mouseReleased((new MouseEvent(_canvas, MouseEvent.MOUSE_RELEASED, System.currentTimeMillis(), mapMouseUpModifier(code, modifiers), x, y, 1, false,
|
|
mousecode)));
|
|
}
|
|
|
|
if (event == InputEventType.MOUSE_MOVE) {
|
|
_mouseEventSource.mouseMoved(new MouseEvent(_canvas, MouseEvent.MOUSE_MOVED, System.currentTimeMillis(), mapModifier(modifiers), x, y, 0, false));
|
|
}
|
|
}
|
|
|
|
public int mapMouseDownModifier(int code, int modifiers) {
|
|
int mod = mapModifier(modifiers);
|
|
switch (code) {
|
|
case 0:
|
|
mod = mod | MouseEvent.BUTTON1_DOWN_MASK;
|
|
break;
|
|
case 2:
|
|
mod = mod | MouseEvent.BUTTON3_DOWN_MASK;
|
|
break;
|
|
default:
|
|
}
|
|
return mod;
|
|
}
|
|
|
|
public int mapMouseUpModifier(int code, int modifiers) {
|
|
int mod = mapModifier(modifiers);
|
|
switch (code) {
|
|
case 0:
|
|
mod = mod | MouseEvent.BUTTON1_MASK;
|
|
break;
|
|
case 2:
|
|
mod = mod | MouseEvent.BUTTON3_MASK;
|
|
break;
|
|
default:
|
|
}
|
|
return mod;
|
|
}
|
|
|
|
private int mapModifier(int modifiers) {
|
|
int mod = 0;
|
|
|
|
if ((modifiers & SHIFT_KEY_MASK) != 0)
|
|
mod = mod | InputEvent.SHIFT_DOWN_MASK;
|
|
|
|
if ((modifiers & CTRL_KEY_MASK) != 0)
|
|
mod = mod | InputEvent.CTRL_DOWN_MASK;
|
|
|
|
if ((modifiers & META_KEY_MASK) != 0)
|
|
mod = mod | InputEvent.META_DOWN_MASK;
|
|
|
|
if ((modifiers & ALT_KEY_MASK) != 0)
|
|
mod = mod | InputEvent.ALT_DOWN_MASK;
|
|
|
|
return mod;
|
|
}
|
|
|
|
public int mapMouseButton(int code) {
|
|
switch (code) {
|
|
case 0:
|
|
return MouseEvent.BUTTON1;
|
|
case 2:
|
|
return MouseEvent.BUTTON3;
|
|
default:
|
|
return MouseEvent.BUTTON2;
|
|
}
|
|
|
|
}
|
|
|
|
@Override
|
|
public void initClient(final ConsoleProxyClientParam param) {
|
|
if ((System.currentTimeMillis() - _threadStopTime) < 1000) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
int canvasWidth = 1024;
|
|
int canvasHeight = 768;
|
|
setClientParam(param);
|
|
|
|
final String host = param.getHypervHost();
|
|
final String password = param.getPassword();
|
|
final String instanceId = param.getClientHostAddress();
|
|
final int port = param.getClientHostPort();
|
|
|
|
final SSLState sslState = new SSLState();
|
|
|
|
final String username = param.getUsername();
|
|
String name = null;
|
|
String domain = null;
|
|
if (username.contains("\\")) {
|
|
String[] tokens = username.split("\\\\");
|
|
name = tokens[1];
|
|
domain = tokens[0];
|
|
} else {
|
|
name = username;
|
|
domain = "Workgroup";
|
|
}
|
|
|
|
_screen = new ScreenDescription();
|
|
_canvas = new RdpBufferedImageCanvas(this, canvasWidth, canvasHeight);
|
|
onFramebufferSizeChange(canvasWidth, canvasHeight);
|
|
|
|
_screen.addSizeChangeListener(new SizeChangeListener() {
|
|
@Override
|
|
public void sizeChanged(int width, int height) {
|
|
if (_canvas != null) {
|
|
_canvas.setCanvasSize(width, height);
|
|
}
|
|
}
|
|
});
|
|
|
|
s_logger.info("connecting to instance " + instanceId + " on host " + host);
|
|
_client = new RdpClient("client", host, domain, name, password, instanceId, _screen, _canvas, sslState);
|
|
|
|
_mouseEventSource = _client.getMouseEventSource();
|
|
_keyEventSource = _client.getKeyEventSource();
|
|
|
|
_worker = new Thread(new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
_socket = new AprSocketWrapperImpl("socket", sslState);
|
|
Pipeline pipeline = new PipelineImpl("Client");
|
|
pipeline.add(_socket, _client);
|
|
pipeline.link("socket", _client.getId(), "socket");
|
|
pipeline.validate();
|
|
|
|
InetSocketAddress address = new InetSocketAddress(host, port);
|
|
ConsoleProxy.ensureRoute(host);
|
|
|
|
try {
|
|
_workerDone = false;
|
|
s_logger.info("Connecting socket to remote server and run main loop(s)");
|
|
_socket.connect(address);
|
|
} catch (Exception e) {
|
|
s_logger.info(" error occurred in connecting to socket " + e.getMessage());
|
|
} finally {
|
|
shutdown();
|
|
}
|
|
|
|
_threadStopTime = System.currentTimeMillis();
|
|
s_logger.info("Receiver thread stopped.");
|
|
_workerDone = true;
|
|
}
|
|
});
|
|
_worker.setDaemon(true);
|
|
_worker.start();
|
|
} catch (Exception e) {
|
|
_workerDone = true;
|
|
s_logger.info("error occurred in initializing rdp client " + e.getMessage());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void closeClient() {
|
|
_workerDone = true;
|
|
shutdown();
|
|
}
|
|
|
|
@Override
|
|
protected FrameBufferCanvas getFrameBufferCavas() {
|
|
return _canvas;
|
|
}
|
|
|
|
protected void shutdown() {
|
|
if (_socket != null)
|
|
_socket.shutdown();
|
|
}
|
|
}
|