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>
84 <script type="text/javascript"> /* <![CDATA[ */
86 var display = document.getElementById("display");
89 var guac = new Guacamole.Client(
91 new Guacamole.HTTPTunnel("tunnel")
94 var menu = document.getElementById("menu");
95 var logo = document.getElementById("status-logo");
97 var errorDialog = document.getElementById("errorDialog");
98 var errorDialogText = document.getElementById("errorText");
100 // Position display correctly
101 window.onresize = function() {
102 display.style.top = menu.offsetHeight + "px";
107 var state = document.getElementById("state");
108 guac.onstatechange = function(clientState) {
110 switch (clientState) {
112 state.textContent = "Idle."
115 state.textContent = "Connecting...";
118 state.textContent = "Connected, waiting for first update...";
121 display.className = display.className.replace(/guac-loading/, '');
122 menu.className = "connected";
123 state.textContent = "Connected.";
126 state.textContent = "Disconnecting...";
129 state.textContent = "Disconnected.";
132 state.textContent = "Unknown";
136 // Cache error image (might not be available when error occurs)
137 var guacErrorImage = new Image();
138 guacErrorImage.src = "images/noguacamole-logo-24.png";
140 guac.onname = function(name) {
141 document.title = name;
144 guac.onerror = function(error) {
148 menu.className = "error";
149 display.className += " guac-error";
151 logo.src = guacErrorImage.src;
152 errorDialogText.textContent = error;
153 errorDialog.style.visibility = "visible";
155 // Show error by desaturating display
156 var layers = guac.getLayers();
157 for (var i=0; i<layers.length; i++) {
158 layers[i].filter(desaturateFilter);
161 // Filter for desaturation
162 function desaturateFilter(data, width, height) {
164 for (var i=0; i<data.length; i+=4) {
172 var v = Math.max(r, g, b) / 2;
184 var mouse = new Guacamole.Mouse(display);
185 mouse.onmousedown = mouse.onmouseup = mouse.onmousemove =
186 function(mouseState) {
187 guac.sendMouseState(mouseState);
191 var keyboard = new Guacamole.Keyboard(document);
193 function disableKeyboard() {
194 keyboard.onkeydown = null;
195 keyboard.onkeyup = null;
198 function enableKeyboard() {
201 guac.sendKeyEvent(1, keysym);
206 guac.sendKeyEvent(0, keysym);
210 // Enable keyboard by default
214 var reconnect = document.getElementById("reconnect");
215 reconnect.onclick = function() {
216 window.location.reload();
219 // Disconnect on close
220 window.onunload = function() {
224 // Handle clipboard events
225 var clipboardElement = document.getElementById("clipboard");
226 clipboardElement.onchange = function() {
228 var text = clipboardElement.value;
229 guac.setClipboard(text);
233 // Ignore keypresses when clipboard is focused
234 clipboardElement.onfocus = function() {
238 // Capture keypresses when clipboard is not focused
239 clipboardElement.onblur = function() {
243 // Server copy handler
244 guac.onclipboard = function(data) {
245 clipboardElement.value = data;
249 // Show/Hide clipboard
250 var clipboardDiv = document.getElementById("clipboardDiv");
251 var showClipboard = document.getElementById("showClipboard");
252 showClipboard.onclick = function() {
254 var displayed = clipboardDiv.style.display;
255 if (displayed != "block") {
256 clipboardDiv.style.display = "block";
257 showClipboard.innerHTML = "Hide Clipboard";
260 clipboardDiv.style.display = "none";
261 showClipboard.innerHTML = "Show Clipboard";
262 clipboardElement.onchange();
268 // Show/Hide keyboard
269 var keyboardContainer = document.getElementById("keyboardContainer");
270 var showKeyboard = document.getElementById("showKeyboard");
271 showKeyboard.onclick = function() {
273 var displayed = keyboardContainer.style.display;
274 if (displayed != "block") {
275 keyboardContainer.style.display = "block";
276 showKeyboard.textContent = "Hide Keyboard";
279 keyboardContainer.style.display = "none";
280 showKeyboard.textContent = "Show Keyboard";
285 // On-screen keyboard
286 var osKeyboard = new Guacamole.OnScreenKeyboard("layouts/en-us-qwerty.xml");
287 keyboardContainer.appendChild(osKeyboard);
289 osKeyboard.setKeyPressedHandler(
291 guac.sendKeyEvent(1, keysym);
295 osKeyboard.setKeyReleasedHandler(
297 guac.sendKeyEvent(0, keysym);
301 // Send Ctrl-Alt-Delete
302 var ctrlAltDelete = document.getElementById("ctrlAltDelete");
304 ctrlAltDelete.onclick = function() {
306 var KEYSYM_CTRL = 0xFF03;
307 var KEYSYM_ALT = 0xFFE9;
308 var KEYSYM_DELETE = 0xFFFF;
310 guac.sendKeyEvent(1, KEYSYM_CTRL);
311 guac.sendKeyEvent(1, KEYSYM_ALT);
312 guac.sendKeyEvent(1, KEYSYM_DELETE);
313 guac.sendKeyEvent(0, KEYSYM_DELETE);
314 guac.sendKeyEvent(0, KEYSYM_ALT);
315 guac.sendKeyEvent(0, KEYSYM_CTRL);
319 var logout = document.getElementById("logout");
321 logout.onclick = function() {
322 window.location.href = "logout";
328 var url = window.location.href;
329 var query = url.indexOf("?");
330 var id = url.substring(query+1);
333 guac.connect("id=" + id);
337 // TODO: Handle exception ...