4 Guacamole - Clientless Remote Desktop
5 Copyright (C) 2010 Michael Jumper
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Affero General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Affero General Public License for more details.
17 You should have received a copy of the GNU Affero General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 <link rel="icon" type="image/png" href="images/guacamole-logo-64.png"/>
25 <link rel="stylesheet" type="text/css" href="styles/guacamole.css"/>
26 <link rel="stylesheet" type="text/css" href="styles/keyboard.css"/>
27 <title>Guacamole</title>
33 <div id="login-dialog-middle">
35 <div id="login-dialog">
37 <p id="login-error"></p>
39 <form id="login-form" action="#" method="post">
41 <div id="login-fields">
45 <td><input type="text" name="username" id="username"/></td>
49 <td><input type="password" name="password" id="password"/></td>
53 <img class="logo" src="images/guacamole-logo-64.png" alt=""/>
57 <input type="submit" name="login" id="login" value="Login"/>
63 <div id="version-dialog">
64 Guacamole ${project.version}
70 <!-- Main UI - hidden until login succeeds -->
71 <div id="main-guacamole-ui" style="display: none">
77 <button id="showClipboard">Show Clipboard</button>
78 <div id="clipboardDiv">
81 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.
83 <textarea rows="10" cols="40" id="clipboard"></textarea>
86 <button id="showKeyboard">Show Keyboard</button>
87 <button id="CtrlAltDelete">Ctrl-Alt-Delete</button>
89 <!-- Logo and status -->
90 <img id="status-logo" class="logo" src="images/guacamole-logo-24.png" alt="Guacamole" title="Guacamole ${project.version}"/>
91 <span id="state"></span>
93 <a href="agpl-3.0-standalone.html"><img id="license" src="images/agpl-logo.png" alt="AGPLv3"/></a>
98 <div id="display" class="guac-display guac-loading">
99 <!-- On-screen keyboard -->
100 <div id="keyboardContainer"></div>
105 <div id="errorDialog" class="errorDialogOuter">
106 <div class="errorDialogMiddle">
107 <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/keymap.js"></script>
119 <script type="text/javascript" src="guacamole-common-js/keyboard.js"></script>
120 <script type="text/javascript" src="guacamole-common-js/mouse.js"></script>
121 <script type="text/javascript" src="guacamole-common-js/layer.js"></script>
122 <script type="text/javascript" src="guacamole-common-js/tunnel.js"></script>
123 <script type="text/javascript" src="guacamole-common-js/guacamole.js"></script>
124 <script type="text/javascript" src="guacamole-common-js/oskeyboard.js"></script>
127 <script type="text/javascript"> /* <![CDATA[ */
129 var loginForm = document.getElementById("login-form");
130 var loginUI = document.getElementById("login-ui");
131 var display = document.getElementById("display");
133 loginForm.onsubmit = function() {
135 var username = document.getElementById("username");
136 var password = document.getElementById("password");
139 "username=" + encodeURIComponent(username.value)
140 + "&password=" + encodeURIComponent(password.value)
142 // Instantiate client
143 var guac = new GuacamoleClient(
145 new GuacamoleHTTPTunnel("tunnel")
156 var loginError = document.getElementById("login-error");
158 // Display error, reset and refocus password field
159 loginError.textContent = e.message;
167 // On success, display UI
168 startGuacamole(guac);
173 // Shows guacamole interface and initiates connection to guacamole
174 function startGuacamole(guac) {
176 loginUI.style.display = "none";
177 document.getElementById("main-guacamole-ui").style.display = "block";
179 var menu = document.getElementById("menu");
180 var logo = document.getElementById("status-logo");
182 var errorDialog = document.getElementById("errorDialog");
183 var errorDialogText = document.getElementById("errorText");
185 // Position display correctly
186 window.onresize = function() {
187 display.style.top = menu.offsetHeight + "px";
192 var state = document.getElementById("state");
193 guac.setOnStateChangeHandler(function(clientState) {
195 switch (clientState) {
197 state.textContent = "Idle."
200 state.textContent = "Connecting...";
203 state.textContent = "Connected, waiting for first update...";
206 display.className = display.className.replace(/guac-loading/, '');
207 menu.className = "connected";
208 state.textContent = "Connected.";
211 state.textContent = "Disconnecting...";
214 state.textContent = "Disconnected.";
217 state.textContent = "Unknown";
221 // Cache error image (might not be available when error occurs)
222 var guacErrorImage = new Image();
223 guacErrorImage.src = "images/noguacamole-logo-24.png";
225 guac.setNameHandler(function(name) {
226 document.title = name;
229 guac.setErrorHandler(function(error) {
233 menu.className = "error";
234 display.className += " guac-error";
236 logo.src = guacErrorImage.src;
237 errorDialogText.textContent = error;
238 errorDialog.style.visibility = "visible";
240 // Show error by desaturating display
241 var layers = guac.getLayers();
242 for (var i=0; i<layers.length; i++) {
243 layers[i].filter(desaturateFilter);
246 // Filter for desaturation
247 function desaturateFilter(data, width, height) {
249 for (var i=0; i<data.length; i+=4) {
257 var v = Math.max(r, g, b) / 2;
269 var mouse = new GuacamoleMouse(display);
270 mouse.setButtonPressedHandler(
271 function(mouseState) {
272 guac.sendMouseState(mouseState);
276 mouse.setButtonReleasedHandler(
277 function(mouseState) {
278 guac.sendMouseState(mouseState);
282 mouse.setMovementHandler(
283 function(mouseState) {
284 guac.sendMouseState(mouseState);
289 var keyboard = new GuacamoleKeyboard(document);
291 function disableKeyboard() {
292 keyboard.setKeyPressedHandler(null);
293 keyboard.setKeyReleasedHandler(null);
296 function enableKeyboard() {
297 keyboard.setKeyPressedHandler(
299 guac.sendKeyEvent(1, keysym);
303 keyboard.setKeyReleasedHandler(
305 guac.sendKeyEvent(0, keysym);
310 // Enable keyboard by default
314 var reconnect = document.getElementById("reconnect");
315 reconnect.onclick = function() {
316 window.location.reload();
319 // Disconnect on close
320 window.onunload = function() {
324 // Handle clipboard events
325 var clipboardElement = document.getElementById("clipboard");
326 clipboardElement.onchange = function() {
328 var text = clipboardElement.value;
329 guac.setClipboard(text);
333 // Ignore keypresses when clipboard is focused
334 clipboardElement.onfocus = function() {
338 // Capture keypresses when clipboard is not focused
339 clipboardElement.onblur = function() {
343 // Server copy handler
344 guac.setClipboardHandler(
346 clipboardElement.value = data;
351 // Show/Hide clipboard
352 var clipboardDiv = document.getElementById("clipboardDiv");
353 var showClipboard = document.getElementById("showClipboard");
354 showClipboard.onclick = function() {
356 var displayed = clipboardDiv.style.display;
357 if (displayed != "block") {
358 clipboardDiv.style.display = "block";
359 showClipboard.innerHTML = "Hide Clipboard";
362 clipboardDiv.style.display = "none";
363 showClipboard.innerHTML = "Show Clipboard";
364 clipboardElement.onchange();
370 // Show/Hide keyboard
371 var keyboardContainer = document.getElementById("keyboardContainer");
372 var showKeyboard = document.getElementById("showKeyboard");
373 showKeyboard.onclick = function() {
375 var displayed = keyboardContainer.style.display;
376 if (displayed != "block") {
377 keyboardContainer.style.display = "block";
378 showKeyboard.textContent = "Hide Keyboard";
381 keyboardContainer.style.display = "none";
382 showKeyboard.textContent = "Show Keyboard";
387 // On-screen keyboard
388 var osKeyboard = new GuacamoleOnScreenKeyboard("layouts/en-us-qwerty.xml");
389 keyboardContainer.appendChild(osKeyboard);
391 osKeyboard.setKeyPressedHandler(
393 guac.sendKeyEvent(1, keysym);
397 osKeyboard.setKeyReleasedHandler(
399 guac.sendKeyEvent(0, keysym);
403 // Send Ctrl-Alt-Delete
404 var CtrlAltDelete = document.getElementById("CtrlAltDelete");
406 CtrlAltDelete.onclick = function() {
407 guac.sendKeyEvent(1, KEYSYM_CTRL);
408 guac.sendKeyEvent(1, KEYSYM_ALT);
409 guac.sendKeyEvent(1, KEYSYM_DELETE);
410 guac.sendKeyEvent(0, KEYSYM_DELETE);
411 guac.sendKeyEvent(0, KEYSYM_ALT);
412 guac.sendKeyEvent(0, KEYSYM_CTRL);