/* * Guacamole - Clientless Remote Desktop * Copyright (C) 2010 Michael Jumper * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License */ // Guacamole namespace var Guacamole = Guacamole || {}; /** * Guacamole protocol client. Given a display element and {@link Guacamole.Tunnel}, * automatically handles incoming and outgoing Guacamole instructions via the * provided tunnel, updating the display using one or more canvas elements. * * @constructor * @param {Element} display The display element to add canvas elements to. * @param {Guacamole.Tunnel} tunnel The tunnel to use to send and receive * Guacamole instructions. */ Guacamole.Client = function(display, tunnel) { var guac_client = this; var STATE_IDLE = 0; var STATE_CONNECTING = 1; var STATE_WAITING = 2; var STATE_CONNECTED = 3; var STATE_DISCONNECTING = 4; var STATE_DISCONNECTED = 5; var currentState = STATE_IDLE; tunnel.oninstruction = doInstruction; // Display must be relatively positioned for mouse to be handled properly display.style.position = "relative"; function setState(state) { if (state != currentState) { currentState = state; if (guac_client.onstatechange) guac_client.onstatechange(currentState); } } function isConnected() { return currentState == STATE_CONNECTED || currentState == STATE_WAITING; } var cursorImage = null; var cursorHotspotX = 0; var cursorHotspotY = 0; var cursorRectX = 0; var cursorRectY = 0; var cursorRectW = 0; var cursorRectH = 0; var cursorHidden = 0; function redrawCursor(x, y) { // Hide hardware cursor if (cursorHidden == 0) { display.className += " guac-hide-cursor"; cursorHidden = 1; } // Erase old cursor cursor.clearRect(cursorRectX, cursorRectY, cursorRectW, cursorRectH); // Update rect cursorRectX = x - cursorHotspotX; cursorRectY = y - cursorHotspotY; cursorRectW = cursorImage.width; cursorRectH = cursorImage.height; // Draw new cursor cursor.drawImage(cursorRectX, cursorRectY, cursorImage); } guac_client.sendKeyEvent = function(pressed, keysym) { // Do not send requests if not connected if (!isConnected()) return; tunnel.sendMessage("key:" + keysym + "," + pressed + ";"); }; guac_client.sendMouseState = function(mouseState) { // Do not send requests if not connected if (!isConnected()) return; // Draw client-side cursor if (cursorImage != null) { redrawCursor( mouseState.x, mouseState.y ); } // Build mask var buttonMask = 0; if (mouseState.left) buttonMask |= 1; if (mouseState.middle) buttonMask |= 2; if (mouseState.right) buttonMask |= 4; if (mouseState.up) buttonMask |= 8; if (mouseState.down) buttonMask |= 16; // Send message tunnel.sendMessage("mouse:" + mouseState.x + "," + mouseState.y + "," + buttonMask + ";"); }; guac_client.setClipboard = function(data) { // Do not send requests if not connected if (!isConnected()) return; tunnel.sendMessage("clipboard:" + escapeGuacamoleString(data) + ";"); }; // Handlers guac_client.onstatechange = null; guac_client.onname = null; guac_client.onerror = null; guac_client.onclipboard = null; // Layers var displayWidth = 0; var displayHeight = 0; var layers = new Array(); var buffers = new Array(); var cursor = null; guac_client.getLayers = function() { return layers; }; function getLayer(index) { // If negative index, use buffer if (index < 0) { index = -1 - index; var buffer = buffers[index]; // Create buffer if necessary if (buffer == null) { buffer = new Guacamole.Layer(0, 0); buffer.autosize = 1; buffers[index] = buffer; } return buffer; } // If non-negative, use visible layer else { var layer = layers[index]; if (layer == null) { // Add new layer layer = new Guacamole.Layer(displayWidth, displayHeight); // Set layer position var canvas = layer.getCanvas(); canvas.style.position = "absolute"; canvas.style.left = "0px"; canvas.style.top = "0px"; layers[index] = layer; // (Re)-add existing layers in order for (var i=0; i