1a4c0821e10bbc400932987770c385f4859bccdd
[guacamole.git] / src / main / webapp / client.xhtml
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE html>
3
4 <!--
5     Guacamole - Clientless Remote Desktop
6     Copyright (C) 2010  Michael Jumper
7
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.
12
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.
17
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/>.
20 -->
21
22 <html xmlns="http://www.w3.org/1999/xhtml">
23
24     <head>
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>
29     </head>
30
31     <body>
32
33         <!-- Menu -->
34         <div id="menu">
35
36             <!-- Clipboard -->
37             <button id="showClipboard">Show Clipboard</button>
38             <div id="clipboardDiv">
39                 <h2>Clipboard</h2>
40                 <p>
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.
42                 </p>
43                 <textarea rows="10" cols="40" id="clipboard"></textarea>
44             </div>
45
46             <button id="showKeyboard">Show Keyboard</button>
47             <button id="ctrlAltDelete">Ctrl-Alt-Delete</button>
48             <button id="logout">Logout</button>
49
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>
53
54         </div>
55
56
57         <!-- Display -->
58         <div id="display" class="guac-display guac-loading">
59             <!-- On-screen keyboard -->
60             <div id="keyboardContainer"></div>
61         </div>
62
63
64         <!-- Error Dialog-->
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>
70                 </div>
71             </div>
72         </div>
73
74
75         <!-- Scripts -->
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>
83
84         <!-- Init -->
85         <script type="text/javascript"> /* <![CDATA[ */
86
87             var display = document.getElementById("display");
88
89             // Instantiate client
90             var guac = new Guacamole.Client(
91                 display,
92                 new Guacamole.HTTPTunnel("tunnel")
93             );
94
95             var menu = document.getElementById("menu");
96             var logo = document.getElementById("status-logo");
97
98             var errorDialog = document.getElementById("errorDialog");
99             var errorDialogText = document.getElementById("errorText");
100
101             var state = document.getElementById("state");
102             guac.onstatechange = function(clientState) {
103
104                     switch (clientState) {
105                         case 0:
106                             state.textContent = "Idle."
107                             break;
108                         case 1:
109                             state.textContent = "Connecting...";
110                             break;
111                         case 2:
112                             state.textContent = "Connected, waiting for first update...";
113                             break;
114                         case 3:
115                             display.className = display.className.replace(/guac-loading/, '');
116                             menu.className = "connected";
117                             state.textContent = "Connected.";
118                             shadeMenu();
119                             break;
120                         case 4:
121                             state.textContent = "Disconnecting...";
122                             break;
123                         case 5:
124                             state.textContent = "Disconnected.";
125                             break;
126                         default:
127                             state.textContent = "Unknown";
128                     }
129             };
130
131             // Cache error image (might not be available when error occurs)
132             var guacErrorImage = new Image();
133             guacErrorImage.src = "images/noguacamole-logo-24.png";
134
135             guac.onname = function(name) {
136                 document.title = name;
137             };
138
139             guac.onerror = function(error) {
140
141                 guac.disconnect();
142
143                 menu.className = "error";
144                 display.className += " guac-error";
145
146                 logo.src = guacErrorImage.src;
147                 errorDialogText.textContent = error;
148                 errorDialog.style.visibility = "visible";
149
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);
154                 }
155
156                 // Filter for desaturation
157                 function desaturateFilter(data, width, height) {
158
159                     for (var i=0; i<data.length; i+=4) {
160
161                         // Get RGB values
162                         var r = data[i];
163                         var g = data[i+1];
164                         var b = data[i+2];
165
166                         // Desaturate
167                         var v = Math.max(r, g, b) / 2;
168                         data[i]   = v;
169                         data[i+1] = v;
170                         data[i+2] = v;
171
172                     }
173
174                 }
175
176             };
177
178             // Mouse
179             var mouse = new Guacamole.Mouse(display);
180             mouse.onmousedown = mouse.onmouseup = mouse.onmousemove =
181                 function(mouseState) {
182
183                     if (mouseState.y <= 5)
184                         showMenu();
185
186                     guac.sendMouseState(mouseState);
187                 };
188
189             // Keyboard
190             var keyboard = new Guacamole.Keyboard(document);
191
192             function disableKeyboard() {
193                 keyboard.onkeydown = null;
194                 keyboard.onkeyup = null;
195             }
196
197             function enableKeyboard() {
198                 keyboard.onkeydown = 
199                     function (keysym) {
200                         guac.sendKeyEvent(1, keysym);
201                     };
202
203                 keyboard.onkeyup = 
204                     function (keysym) {
205                         guac.sendKeyEvent(0, keysym);
206                     };
207             }
208
209             // Enable keyboard by default
210             enableKeyboard();
211
212             // Reconnect button
213             var reconnect = document.getElementById("reconnect");
214             reconnect.onclick = function() {
215                 window.location.reload();
216             };
217
218             // Disconnect on close
219             window.onunload = function() {
220                 guac.disconnect();
221             };
222
223             // Handle clipboard events
224             var clipboardElement = document.getElementById("clipboard");
225             clipboardElement.onchange = function() {
226
227                 var text = clipboardElement.value;
228                 guac.setClipboard(text);
229
230             };
231
232             // Ignore keypresses when clipboard is focused
233             clipboardElement.onfocus = function() {
234                 disableKeyboard();
235             };
236
237             // Capture keypresses when clipboard is not focused
238             clipboardElement.onblur = function() {
239                 enableKeyboard();
240             };
241
242             // Server copy handler
243             guac.onclipboard = function(data) {
244                 clipboardElement.value = data;
245             };
246
247
248             // Show/Hide clipboard
249             var clipboardDiv = document.getElementById("clipboardDiv");
250             var showClipboard = document.getElementById("showClipboard");
251             showClipboard.onclick = function() {
252
253                 var displayed = clipboardDiv.style.display;
254                 if (displayed != "block") {
255                     clipboardDiv.style.display = "block";
256                     showClipboard.innerHTML = "Hide Clipboard";
257                 }
258                 else {
259                     clipboardDiv.style.display = "none";
260                     showClipboard.innerHTML = "Show Clipboard";
261                     clipboardElement.onchange();
262                 }
263
264             };
265
266
267             // Show/Hide keyboard
268             var keyboardContainer = document.getElementById("keyboardContainer");
269             var showKeyboard = document.getElementById("showKeyboard");
270             showKeyboard.onclick = function() {
271
272                 var displayed = keyboardContainer.style.display;
273                 if (displayed != "block") {
274                     keyboardContainer.style.display = "block";
275                     showKeyboard.textContent = "Hide Keyboard";
276                 }
277                 else {
278                     keyboardContainer.style.display = "none";
279                     showKeyboard.textContent = "Show Keyboard";
280                 }
281
282             };
283
284             // On-screen keyboard
285             var osKeyboard = new Guacamole.OnScreenKeyboard("layouts/en-us-qwerty.xml");
286             keyboardContainer.appendChild(osKeyboard);
287
288             osKeyboard.setKeyPressedHandler(
289                     function(keysym) {
290                         guac.sendKeyEvent(1, keysym);
291                     }
292             );
293
294             osKeyboard.setKeyReleasedHandler(
295                     function(keysym) {
296                         guac.sendKeyEvent(0, keysym);
297                     }
298             );
299
300             // Send Ctrl-Alt-Delete
301             var ctrlAltDelete = document.getElementById("ctrlAltDelete");
302
303             ctrlAltDelete.onclick = function() {
304
305                 var KEYSYM_CTRL   = 0xFFE3;
306                 var KEYSYM_ALT    = 0xFFE9;
307                 var KEYSYM_DELETE = 0xFFFF;
308
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);
315             };
316
317             // Logout
318             var logout = document.getElementById("logout");
319
320             logout.onclick = function() {
321                 window.location.href = "logout";
322             };
323
324             try {
325
326                 // Get ID
327                 var id = window.location.search.substring(1);
328
329                 // Connect client
330                 guac.connect("id=" + id);
331
332             }
333             catch (e) {
334                 // TODO: Handle exception ...
335             }
336
337             display.onmouseout = function() {
338                 showMenu();
339             };
340
341             display.onmouseover = function() {
342                 shadeMenu();
343             };
344
345
346             /* ]]> */ </script>
347
348     </body>
349
350 </html>