1 <?xml version="1.0" encoding="UTF-8"?>
5 Guacamole - Clientless Remote Desktop
6 Copyright (C) 2010 Michael Jumper
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU Affero General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Affero General Public License for more details.
18 You should have received a copy of the GNU Affero General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 <html xmlns="http://www.w3.org/1999/xhtml">
25 <link rel="icon" type="image/png" href="images/guacamole-logo-64.png"/>
26 <link rel="stylesheet" type="text/css" href="styles/client.css"/>
27 <link rel="stylesheet" type="text/css" href="styles/keyboard.css"/>
28 <title>Guacamole ${project.version}</title>
37 <button id="showClipboard">Show Clipboard</button>
38 <div id="clipboardDiv">
41 Text copied/cut within Guacamole will appear here. Changes to the text will affect the remote clipboard, and will be pastable within the remote desktop. Use the textbox below as an interface between the client and server clipboards.
43 <textarea rows="10" cols="40" id="clipboard"></textarea>
46 <button id="showKeyboard">Show Keyboard</button>
47 <button id="ctrlAltDelete">Ctrl-Alt-Delete</button>
48 <button id="logout">Logout</button>
50 <!-- Logo and status -->
51 <img id="status-logo" class="logo" src="images/guacamole-logo-24.png" alt="Guacamole" title="Guacamole ${project.version}"/>
52 <span id="state"></span>
58 <div id="display" class="guac-display guac-loading">
59 <!-- On-screen keyboard -->
60 <div id="keyboardContainer"></div>
65 <div id="errorDialog" class="errorDialogOuter">
66 <div class="errorDialogMiddle">
67 <div class="errorDialog">
68 <p id="errorText"></p>
69 <div class="buttons"><button id="reconnect">Reconnect</button></div>
76 <script type="text/javascript" src="guacamole-common-js/keyboard.js"></script>
77 <script type="text/javascript" src="guacamole-common-js/mouse.js"></script>
78 <script type="text/javascript" src="guacamole-common-js/layer.js"></script>
79 <script type="text/javascript" src="guacamole-common-js/tunnel.js"></script>
80 <script type="text/javascript" src="guacamole-common-js/guacamole.js"></script>
81 <script type="text/javascript" src="guacamole-common-js/oskeyboard.js"></script>
82 <script type="text/javascript" src="scripts/interface.js"></script>
85 <script type="text/javascript"> /* <![CDATA[ */
87 var display = document.getElementById("display");
90 var guac = new Guacamole.Client(
92 new Guacamole.HTTPTunnel("tunnel")
95 var menu = document.getElementById("menu");
96 var logo = document.getElementById("status-logo");
98 var errorDialog = document.getElementById("errorDialog");
99 var errorDialogText = document.getElementById("errorText");
101 var state = document.getElementById("state");
102 guac.onstatechange = function(clientState) {
104 switch (clientState) {
106 state.textContent = "Idle."
109 state.textContent = "Connecting...";
112 state.textContent = "Connected, waiting for first update...";
115 display.className = display.className.replace(/guac-loading/, '');
116 menu.className = "connected";
117 state.textContent = "Connected.";
121 state.textContent = "Disconnecting...";
124 state.textContent = "Disconnected.";
127 state.textContent = "Unknown";
131 // Cache error image (might not be available when error occurs)
132 var guacErrorImage = new Image();
133 guacErrorImage.src = "images/noguacamole-logo-24.png";
135 guac.onname = function(name) {
136 document.title = name;
139 guac.onerror = function(error) {
143 menu.className = "error";
144 display.className += " guac-error";
146 logo.src = guacErrorImage.src;
147 errorDialogText.textContent = error;
148 errorDialog.style.visibility = "visible";
150 // Show error by desaturating display
151 var layers = guac.getLayers();
152 for (var i=0; i<layers.length; i++) {
153 layers[i].filter(desaturateFilter);
156 // Filter for desaturation
157 function desaturateFilter(data, width, height) {
159 for (var i=0; i<data.length; i+=4) {
167 var v = Math.max(r, g, b) / 2;
179 var mouse = new Guacamole.Mouse(display);
180 mouse.onmousedown = mouse.onmouseup = mouse.onmousemove =
181 function(mouseState) {
183 if (mouseState.y <= 5)
186 guac.sendMouseState(mouseState);
190 var keyboard = new Guacamole.Keyboard(document);
192 function disableKeyboard() {
193 keyboard.onkeydown = null;
194 keyboard.onkeyup = null;
197 function enableKeyboard() {
200 guac.sendKeyEvent(1, keysym);
205 guac.sendKeyEvent(0, keysym);
209 // Enable keyboard by default
213 var reconnect = document.getElementById("reconnect");
214 reconnect.onclick = function() {
215 window.location.reload();
218 // Disconnect on close
219 window.onunload = function() {
223 // Handle clipboard events
224 var clipboardElement = document.getElementById("clipboard");
225 clipboardElement.onchange = function() {
227 var text = clipboardElement.value;
228 guac.setClipboard(text);
232 // Ignore keypresses when clipboard is focused
233 clipboardElement.onfocus = function() {
237 // Capture keypresses when clipboard is not focused
238 clipboardElement.onblur = function() {
242 // Server copy handler
243 guac.onclipboard = function(data) {
244 clipboardElement.value = data;
248 // Show/Hide clipboard
249 var clipboardDiv = document.getElementById("clipboardDiv");
250 var showClipboard = document.getElementById("showClipboard");
251 showClipboard.onclick = function() {
253 var displayed = clipboardDiv.style.display;
254 if (displayed != "block") {
255 clipboardDiv.style.display = "block";
256 showClipboard.innerHTML = "Hide Clipboard";
259 clipboardDiv.style.display = "none";
260 showClipboard.innerHTML = "Show Clipboard";
261 clipboardElement.onchange();
267 // Show/Hide keyboard
268 var keyboardContainer = document.getElementById("keyboardContainer");
269 var showKeyboard = document.getElementById("showKeyboard");
270 showKeyboard.onclick = function() {
272 var displayed = keyboardContainer.style.display;
273 if (displayed != "block") {
274 keyboardContainer.style.display = "block";
275 showKeyboard.textContent = "Hide Keyboard";
278 keyboardContainer.style.display = "none";
279 showKeyboard.textContent = "Show Keyboard";
284 // On-screen keyboard
285 var osKeyboard = new Guacamole.OnScreenKeyboard("layouts/en-us-qwerty.xml");
286 keyboardContainer.appendChild(osKeyboard);
288 osKeyboard.setKeyPressedHandler(
290 guac.sendKeyEvent(1, keysym);
294 osKeyboard.setKeyReleasedHandler(
296 guac.sendKeyEvent(0, keysym);
300 // Send Ctrl-Alt-Delete
301 var ctrlAltDelete = document.getElementById("ctrlAltDelete");
303 ctrlAltDelete.onclick = function() {
305 var KEYSYM_CTRL = 0xFFE3;
306 var KEYSYM_ALT = 0xFFE9;
307 var KEYSYM_DELETE = 0xFFFF;
309 guac.sendKeyEvent(1, KEYSYM_CTRL);
310 guac.sendKeyEvent(1, KEYSYM_ALT);
311 guac.sendKeyEvent(1, KEYSYM_DELETE);
312 guac.sendKeyEvent(0, KEYSYM_DELETE);
313 guac.sendKeyEvent(0, KEYSYM_ALT);
314 guac.sendKeyEvent(0, KEYSYM_CTRL);
318 var logout = document.getElementById("logout");
320 logout.onclick = function() {
321 window.location.href = "logout";
327 var id = window.location.search.substring(1);
330 guac.connect("id=" + id);
334 // TODO: Handle exception ...
337 display.onmouseout = function() {
341 display.onmouseover = function() {