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/guacamole.css"/>
27 <link rel="stylesheet" type="text/css" href="styles/keyboard.css"/>
28 <title>Guacamole</title>
34 <div id="login-dialog-middle">
36 <div id="login-dialog">
38 <p id="login-error"></p>
40 <form id="login-form" action="#" method="post">
42 <div id="login-fields">
46 <td><input type="text" name="username" id="username" autofocus="autofocus"/></td>
50 <td><input type="password" name="password" id="password"/></td>
54 <img class="logo" src="images/guacamole-logo-64.png" alt=""/>
58 <input type="submit" name="login" id="login" value="Login"/>
64 <div id="version-dialog">
65 Guacamole ${project.version}
71 <!-- Main UI - hidden until login succeeds -->
72 <div id="main-guacamole-ui" style="display: none">
78 <button id="showClipboard">Show Clipboard</button>
79 <div id="clipboardDiv">
82 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.
84 <textarea rows="10" cols="40" id="clipboard"></textarea>
87 <button id="showKeyboard">Show Keyboard</button>
88 <button id="ctrlAltDelete">Ctrl-Alt-Delete</button>
90 <!-- Logo and status -->
91 <img id="status-logo" class="logo" src="images/guacamole-logo-24.png" alt="Guacamole" title="Guacamole ${project.version}"/>
92 <span id="state"></span>
94 <a href="agpl-3.0-standalone.html"><img id="license" src="images/agpl-logo.png" alt="AGPLv3"/></a>
99 <div id="display" class="guac-display guac-loading">
100 <!-- On-screen keyboard -->
101 <div id="keyboardContainer"></div>
106 <div id="errorDialog" class="errorDialogOuter">
107 <div class="errorDialogMiddle">
108 <div class="errorDialog">
109 <p id="errorText"></p>
110 <div class="buttons"><button id="reconnect">Reconnect</button></div>
118 <script type="text/javascript" src="guacamole-common-js/keyboard.js"></script>
119 <script type="text/javascript" src="guacamole-common-js/mouse.js"></script>
120 <script type="text/javascript" src="guacamole-common-js/layer.js"></script>
121 <script type="text/javascript" src="guacamole-common-js/tunnel.js"></script>
122 <script type="text/javascript" src="guacamole-common-js/guacamole.js"></script>
123 <script type="text/javascript" src="guacamole-common-js/oskeyboard.js"></script>
126 <script type="text/javascript"> /* <![CDATA[ */
128 var loginForm = document.getElementById("login-form");
129 var loginUI = document.getElementById("login-ui");
130 var display = document.getElementById("display");
132 loginForm.onsubmit = function() {
134 var username = document.getElementById("username");
135 var password = document.getElementById("password");
138 "username=" + encodeURIComponent(username.value)
139 + "&password=" + encodeURIComponent(password.value)
141 // Instantiate client
142 var guac = new Guacamole.Client(
144 new Guacamole.HTTPTunnel("tunnel")
155 var loginError = document.getElementById("login-error");
157 // Display error, reset and refocus password field
158 loginError.textContent = e.message;
166 // On success, display UI
167 startGuacamole(guac);
172 // Shows guacamole interface and initiates connection to guacamole
173 function startGuacamole(guac) {
175 loginUI.style.display = "none";
176 document.getElementById("main-guacamole-ui").style.display = "block";
178 var menu = document.getElementById("menu");
179 var logo = document.getElementById("status-logo");
181 var errorDialog = document.getElementById("errorDialog");
182 var errorDialogText = document.getElementById("errorText");
184 // Position display correctly
185 window.onresize = function() {
186 display.style.top = menu.offsetHeight + "px";
191 var state = document.getElementById("state");
192 guac.onstatechange = function(clientState) {
194 switch (clientState) {
196 state.textContent = "Idle."
199 state.textContent = "Connecting...";
202 state.textContent = "Connected, waiting for first update...";
205 display.className = display.className.replace(/guac-loading/, '');
206 menu.className = "connected";
207 state.textContent = "Connected.";
210 state.textContent = "Disconnecting...";
213 state.textContent = "Disconnected.";
216 state.textContent = "Unknown";
220 // Cache error image (might not be available when error occurs)
221 var guacErrorImage = new Image();
222 guacErrorImage.src = "images/noguacamole-logo-24.png";
224 guac.onname = function(name) {
225 document.title = name;
228 guac.onerror = function(error) {
232 menu.className = "error";
233 display.className += " guac-error";
235 logo.src = guacErrorImage.src;
236 errorDialogText.textContent = error;
237 errorDialog.style.visibility = "visible";
239 // Show error by desaturating display
240 var layers = guac.getLayers();
241 for (var i=0; i<layers.length; i++) {
242 layers[i].filter(desaturateFilter);
245 // Filter for desaturation
246 function desaturateFilter(data, width, height) {
248 for (var i=0; i<data.length; i+=4) {
256 var v = Math.max(r, g, b) / 2;
268 var mouse = new Guacamole.Mouse(display);
269 mouse.onmousedown = mouse.onmouseup = mouse.onmousemove =
270 function(mouseState) {
271 guac.sendMouseState(mouseState);
275 var keyboard = new Guacamole.Keyboard(document);
277 function disableKeyboard() {
278 keyboard.onkeydown = null;
279 keyboard.onkeyup = null;
282 function enableKeyboard() {
285 guac.sendKeyEvent(1, keysym);
290 guac.sendKeyEvent(0, keysym);
294 // Enable keyboard by default
298 var reconnect = document.getElementById("reconnect");
299 reconnect.onclick = function() {
300 window.location.reload();
303 // Disconnect on close
304 window.onunload = function() {
308 // Handle clipboard events
309 var clipboardElement = document.getElementById("clipboard");
310 clipboardElement.onchange = function() {
312 var text = clipboardElement.value;
313 guac.setClipboard(text);
317 // Ignore keypresses when clipboard is focused
318 clipboardElement.onfocus = function() {
322 // Capture keypresses when clipboard is not focused
323 clipboardElement.onblur = function() {
327 // Server copy handler
328 guac.onclipboard = function(data) {
329 clipboardElement.value = data;
333 // Show/Hide clipboard
334 var clipboardDiv = document.getElementById("clipboardDiv");
335 var showClipboard = document.getElementById("showClipboard");
336 showClipboard.onclick = function() {
338 var displayed = clipboardDiv.style.display;
339 if (displayed != "block") {
340 clipboardDiv.style.display = "block";
341 showClipboard.innerHTML = "Hide Clipboard";
344 clipboardDiv.style.display = "none";
345 showClipboard.innerHTML = "Show Clipboard";
346 clipboardElement.onchange();
352 // Show/Hide keyboard
353 var keyboardContainer = document.getElementById("keyboardContainer");
354 var showKeyboard = document.getElementById("showKeyboard");
355 showKeyboard.onclick = function() {
357 var displayed = keyboardContainer.style.display;
358 if (displayed != "block") {
359 keyboardContainer.style.display = "block";
360 showKeyboard.textContent = "Hide Keyboard";
363 keyboardContainer.style.display = "none";
364 showKeyboard.textContent = "Show Keyboard";
369 // On-screen keyboard
370 var osKeyboard = new Guacamole.OnScreenKeyboard("layouts/en-us-qwerty.xml");
371 keyboardContainer.appendChild(osKeyboard);
373 osKeyboard.setKeyPressedHandler(
375 guac.sendKeyEvent(1, keysym);
379 osKeyboard.setKeyReleasedHandler(
381 guac.sendKeyEvent(0, keysym);
385 // Send Ctrl-Alt-Delete
386 var ctrlAltDelete = document.getElementById("ctrlAltDelete");
388 ctrlAltDelete.onclick = function() {
390 var KEYSYM_CTRL = 0xFF03;
391 var KEYSYM_ALT = 0xFFE9;
392 var KEYSYM_DELETE = 0xFFFF;
394 guac.sendKeyEvent(1, KEYSYM_CTRL);
395 guac.sendKeyEvent(1, KEYSYM_ALT);
396 guac.sendKeyEvent(1, KEYSYM_DELETE);
397 guac.sendKeyEvent(0, KEYSYM_DELETE);
398 guac.sendKeyEvent(0, KEYSYM_ALT);
399 guac.sendKeyEvent(0, KEYSYM_CTRL);