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