// 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(); } }