// Copyright 2012 Citrix Systems, Inc. Licensed under the // Apache License, Version 2.0 (the "License"); you may not use this // file except in compliance with the License. Citrix Systems, Inc. // reserves all rights not expressly granted by 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. // // Automatically generated by addcopyright.py at 04/03/2012 package com.cloud.consoleproxy.vnc; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.io.DataOutputStream; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import com.cloud.consoleproxy.util.Logger; import com.cloud.consoleproxy.vnc.packet.client.ClientPacket; import com.cloud.consoleproxy.vnc.packet.client.FramebufferUpdateRequestPacket; import com.cloud.consoleproxy.vnc.packet.client.KeyboardEventPacket; import com.cloud.consoleproxy.vnc.packet.client.MouseEventPacket; import com.cloud.consoleproxy.vnc.packet.client.SetEncodingsPacket; import com.cloud.consoleproxy.vnc.packet.client.SetPixelFormatPacket; public class VncClientPacketSender implements Runnable, PaintNotificationListener, KeyListener, MouseListener, MouseMotionListener, FrameBufferUpdateListener { private static final Logger s_logger = Logger.getLogger(VncClientPacketSender.class); // Queue for outgoing packets private final BlockingQueue<ClientPacket> queue = new ArrayBlockingQueue<ClientPacket>(30); private final DataOutputStream os; private final VncScreenDescription screen; private final VncClient vncConnection; private boolean connectionAlive = true; // Don't send update request again until we receive next frame buffer update private boolean updateRequestSent = false; public VncClientPacketSender(DataOutputStream os, VncScreenDescription screen, VncClient vncConnection) { this.os = os; this.screen = screen; this.vncConnection = vncConnection; sendSetPixelFormat(); sendSetEncodings(); requestFullScreenUpdate(); } public void sendClientPacket(ClientPacket packet) { queue.add(packet); } @Override public void run() { try { while (connectionAlive) { ClientPacket packet = queue.poll(1, TimeUnit.SECONDS); if (packet != null) { packet.write(os); os.flush(); } } } catch (Throwable e) { s_logger.error("Unexpected exception: ", e); if (connectionAlive) { closeConnection(); vncConnection.shutdown(); } } } private void sendSetEncodings() { queue.add(new SetEncodingsPacket(RfbConstants.SUPPORTED_ENCODINGS_ARRAY)); } private void sendSetPixelFormat() { if (!screen.isRGB888_32_LE()) { queue.add(new SetPixelFormatPacket(screen, 32, 24, RfbConstants.LITTLE_ENDIAN, RfbConstants.TRUE_COLOR, 255, 255, 255, 16, 8, 0)); } } public void closeConnection() { connectionAlive = false; } public void requestFullScreenUpdate() { queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_FULL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen .getFramebufferHeight())); updateRequestSent = true; } @Override public void imagePaintedOnScreen() { if (!updateRequestSent) { queue.add(new FramebufferUpdateRequestPacket(RfbConstants.FRAMEBUFFER_INCREMENTAL_UPDATE_REQUEST, 0, 0, screen.getFramebufferWidth(), screen .getFramebufferHeight())); updateRequestSent = true; } } @Override public void frameBufferPacketReceived() { updateRequestSent = false; } @Override public void mouseDragged(MouseEvent e) { queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); } @Override public void mouseMoved(MouseEvent e) { queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); } @Override public void mouseClicked(MouseEvent e) { // Nothing to do } @Override public void mousePressed(MouseEvent e) { queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); } @Override public void mouseReleased(MouseEvent e) { queue.add(new MouseEventPacket(mapAwtModifiersToVncButtonMask(e.getModifiersEx()), e.getX(), e.getY())); } @Override public void mouseEntered(MouseEvent e) { // Nothing to do } @Override public void mouseExited(MouseEvent e) { // Nothing to do } /** * Current state of buttons 1 to 8 are represented by bits 0 to 7 of * button-mask respectively, 0 meaning up, 1 meaning down (pressed). On a * conventional mouse, buttons 1, 2 and 3 correspond to the left, middle and * right buttons on the mouse. On a wheel mouse, each step of the wheel * upwards is represented by a press and release of button 4, and each step * downwards is represented by a press and release of button 5. * * @param modifiers * extended modifiers from AWT mouse event * @return VNC mouse button mask */ public static int mapAwtModifiersToVncButtonMask(int modifiers) { int mask = (((modifiers & MouseEvent.BUTTON1_DOWN_MASK) != 0) ? 0x1 : 0) | (((modifiers & MouseEvent.BUTTON2_DOWN_MASK) != 0) ? 0x2 : 0) | (((modifiers & MouseEvent.BUTTON3_DOWN_MASK) != 0) ? 0x4 : 0); return mask; } @Override public void keyTyped(KeyEvent e) { // Do nothing } @Override public void keyPressed(KeyEvent e) { ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_DOWN, mapAwtKeyToVncKey(e.getKeyCode())); queue.add(request); } @Override public void keyReleased(KeyEvent e) { ClientPacket request = new KeyboardEventPacket(RfbConstants.KEY_UP, mapAwtKeyToVncKey(e.getKeyCode())); queue.add(request); } private int mapAwtKeyToVncKey(int key) { switch (key) { case KeyEvent.VK_BACK_SPACE: return 0xff08; case KeyEvent.VK_TAB: return 0xff09; case KeyEvent.VK_ENTER: return 0xff0d; case KeyEvent.VK_ESCAPE: return 0xff1b; case KeyEvent.VK_INSERT: return 0xff63; case KeyEvent.VK_DELETE: return 0xffff; case KeyEvent.VK_HOME: return 0xff50; case KeyEvent.VK_END: return 0xff57; case KeyEvent.VK_PAGE_UP: return 0xff55; case KeyEvent.VK_PAGE_DOWN: return 0xff56; case KeyEvent.VK_LEFT: return 0xff51; case KeyEvent.VK_UP: return 0xff52; case KeyEvent.VK_RIGHT: return 0xff53; case KeyEvent.VK_DOWN: return 0xff54; case KeyEvent.VK_F1: return 0xffbe; case KeyEvent.VK_F2: return 0xffbf; case KeyEvent.VK_F3: return 0xffc0; case KeyEvent.VK_F4: return 0xffc1; case KeyEvent.VK_F5: return 0xffc2; case KeyEvent.VK_F6: return 0xffc3; case KeyEvent.VK_F7: return 0xffc4; case KeyEvent.VK_F8: return 0xffc5; case KeyEvent.VK_F9: return 0xffc6; case KeyEvent.VK_F10: return 0xffc7; case KeyEvent.VK_F11: return 0xffc8; case KeyEvent.VK_F12: return 0xffc9; case KeyEvent.VK_SHIFT: return 0xffe1; case KeyEvent.VK_CONTROL: return 0xffe3; case KeyEvent.VK_META: return 0xffe7; case KeyEvent.VK_ALT: return 0xffe9; case KeyEvent.VK_ALT_GRAPH: return 0xffea; case KeyEvent.VK_BACK_QUOTE: return 0x0060; } return key; } }