clockwerk-guacamole – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 1  
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is guacamole-common-js.
16 *
17 * The Initial Developer of the Original Code is
18 * Michael Jumper.
19 * Portions created by the Initial Developer are Copyright (C) 2010
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 *
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
35 *
36 * ***** END LICENSE BLOCK ***** */
37  
38 // Guacamole namespace
39 var Guacamole = Guacamole || {};
40  
41 /**
42 * Provides cross-browser and cross-keyboard keyboard for a specific element.
43 * Browser and keyboard layout variation is abstracted away, providing events
44 * which represent keys as their corresponding X11 keysym.
45 *
46 * @constructor
47 * @param {Element} element The Element to use to provide keyboard events.
48 */
49 Guacamole.Keyboard = function(element) {
50  
51 /**
52 * Reference to this Guacamole.Keyboard.
53 * @private
54 */
55 var guac_keyboard = this;
56  
57 /**
58 * Fired whenever the user presses a key with the element associated
59 * with this Guacamole.Keyboard in focus.
60 *
61 * @event
62 * @param {Number} keysym The keysym of the key being pressed.
63 * @returns {Boolean} true if the originating event of this keypress should
64 * be allowed through to the browser, false or undefined
65 * otherwise.
66 */
67 this.onkeydown = null;
68  
69 /**
70 * Fired whenever the user releases a key with the element associated
71 * with this Guacamole.Keyboard in focus.
72 *
73 * @event
74 * @param {Number} keysym The keysym of the key being released.
75 * @returns {Boolean} true if the originating event of this key release
76 * should be allowed through to the browser, false or
77 * undefined otherwise.
78 */
79 this.onkeyup = null;
80  
81 /**
82 * Map of known JavaScript keycodes which do not map to typable characters
83 * to their unshifted X11 keysym equivalents.
84 * @private
85 */
86 var unshiftedKeySym = {
87 8: 0xFF08, // backspace
88 9: 0xFF09, // tab
89 13: 0xFF0D, // enter
90 16: 0xFFE1, // shift
91 17: 0xFFE3, // ctrl
92 18: 0xFFE9, // alt
93 19: 0xFF13, // pause/break
94 20: 0xFFE5, // caps lock
95 27: 0xFF1B, // escape
96 33: 0xFF55, // page up
97 34: 0xFF56, // page down
98 35: 0xFF57, // end
99 36: 0xFF50, // home
100 37: 0xFF51, // left arrow
101 38: 0xFF52, // up arrow
102 39: 0xFF53, // right arrow
103 40: 0xFF54, // down arrow
104 45: 0xFF63, // insert
105 46: 0xFFFF, // delete
106 91: 0xFFEB, // left window key (super_l)
107 92: 0xFF67, // right window key (menu key?)
108 93: null, // select key
109 112: 0xFFBE, // f1
110 113: 0xFFBF, // f2
111 114: 0xFFC0, // f3
112 115: 0xFFC1, // f4
113 116: 0xFFC2, // f5
114 117: 0xFFC3, // f6
115 118: 0xFFC4, // f7
116 119: 0xFFC5, // f8
117 120: 0xFFC6, // f9
118 121: 0xFFC7, // f10
119 122: 0xFFC8, // f11
120 123: 0xFFC9, // f12
121 144: 0xFF7F, // num lock
122 145: 0xFF14 // scroll lock
123 };
124  
125 /**
126 * Map of known JavaScript keycodes which do not map to typable characters
127 * to their shifted X11 keysym equivalents. Keycodes must only be listed
128 * here if their shifted X11 keysym equivalents differ from their unshifted
129 * equivalents.
130 * @private
131 */
132 var shiftedKeySym = {
133 18: 0xFFE7 // alt
134 };
135  
136 /**
137 * All modifiers and their states.
138 */
139 this.modifiers = {
140  
141 /**
142 * Whether shift is currently pressed.
143 */
144 "shift": false,
145  
146 /**
147 * Whether ctrl is currently pressed.
148 */
149 "ctrl" : false,
150  
151 /**
152 * Whether alt is currently pressed.
153 */
154 "alt" : false
155  
156 };
157  
158 /**
159 * The state of every key, indexed by keysym. If a particular key is
160 * pressed, the value of pressed for that keysym will be true. If a key
161 * is not currently pressed, the value for that keysym may be false or
162 * undefined.
163 */
164 this.pressed = [];
165  
166 var keydownChar = new Array();
167  
168 // ID of routine repeating keystrokes. -1 = not repeating.
169 var repeatKeyTimeoutId = -1;
170 var repeatKeyIntervalId = -1;
171  
172 // Starts repeating keystrokes
173 function startRepeat(keySym) {
174 repeatKeyIntervalId = setInterval(function() {
175 sendKeyReleased(keySym);
176 sendKeyPressed(keySym);
177 }, 50);
178 }
179  
180 // Stops repeating keystrokes
181 function stopRepeat() {
182 if (repeatKeyTimeoutId != -1) clearInterval(repeatKeyTimeoutId);
183 if (repeatKeyIntervalId != -1) clearInterval(repeatKeyIntervalId);
184 }
185  
186  
187 function getKeySymFromKeyIdentifier(shifted, keyIdentifier) {
188  
189 var unicodePrefixLocation = keyIdentifier.indexOf("U+");
190 if (unicodePrefixLocation >= 0) {
191  
192 var hex = keyIdentifier.substring(unicodePrefixLocation+2);
193 var codepoint = parseInt(hex, 16);
194 var typedCharacter;
195  
196 // Convert case if shifted
197 if (shifted == 0)
198 typedCharacter = String.fromCharCode(codepoint).toLowerCase();
199 else
200 typedCharacter = String.fromCharCode(codepoint).toUpperCase();
201  
202 // Get codepoint
203 codepoint = typedCharacter.charCodeAt(0);
204  
205 return getKeySymFromCharCode(codepoint);
206  
207 }
208  
209 return null;
210  
211 }
212  
213 function getKeySymFromCharCode(keyCode) {
214  
215 if (keyCode >= 0x0000 && keyCode <= 0x00FF)
216 return keyCode;
217  
218 if (keyCode >= 0x0100 && keyCode <= 0x10FFFF)
219 return 0x01000000 | keyCode;
220  
221 return null;
222  
223 }
224  
225 function getKeySymFromKeyCode(keyCode) {
226  
227 var keysym = null;
228 if (!guac_keyboard.modifiers.shift) keysym = unshiftedKeySym[keyCode];
229 else {
230 keysym = shiftedKeySym[keyCode];
231 if (keysym == null) keysym = unshiftedKeySym[keyCode];
232 }
233  
234 return keysym;
235  
236 }
237  
238  
239 // Sends a single keystroke over the network
240 function sendKeyPressed(keysym) {
241  
242 // Mark key as pressed
243 guac_keyboard.pressed[keysym] = true;
244  
245 // Send key event
246 if (keysym != null && guac_keyboard.onkeydown)
247 return guac_keyboard.onkeydown(keysym) != false;
248  
249 return true;
250  
251 }
252  
253 // Sends a single keystroke over the network
254 function sendKeyReleased(keysym) {
255  
256 // Mark key as released
257 guac_keyboard.pressed[keysym] = false;
258  
259 // Send key event
260 if (keysym != null && guac_keyboard.onkeyup)
261 return guac_keyboard.onkeyup(keysym) != false;
262  
263 return true;
264  
265 }
266  
267  
268 var KEYDOWN = 1;
269 var KEYPRESS = 2;
270  
271 var keySymSource = null;
272  
273 // When key pressed
274 var keydownCode = null;
275 element.onkeydown = function(e) {
276  
277 // Only intercept if handler set
278 if (!guac_keyboard.onkeydown) return true;
279  
280 var keynum;
281 if (window.event) keynum = window.event.keyCode;
282 else if (e.which) keynum = e.which;
283  
284 // Ctrl/Alt/Shift
285 if (keynum == 16) guac_keyboard.modifiers.shift = true;
286 else if (keynum == 17) guac_keyboard.modifiers.ctrl = true;
287 else if (keynum == 18) guac_keyboard.modifiers.alt = true;
288  
289 // If keysym is defined for given key code, key events can come from
290 // KEYDOWN.
291 var keysym = getKeySymFromKeyCode(keynum);
292 if (keysym)
293 keySymSource = KEYDOWN;
294  
295 // Otherwise, if modifier keys are held down, try to get from keyIdentifier
296 else if ((guac_keyboard.modifiers.ctrl || guac_keyboard.modifiers.alt) && e.keyIdentifier) {
297  
298 // Get keysym from keyIdentifier
299 keysym = getKeySymFromKeyIdentifier(guac_keyboard.modifiers.shift, e.keyIdentifier);
300  
301 // Get keysyms and events from KEYDOWN
302 keySymSource = KEYDOWN;
303  
304 }
305  
306 // Otherwise, resort to KEYPRESS
307 else
308 keySymSource = KEYPRESS;
309  
310 keydownCode = keynum;
311  
312 // Ignore key if we don't need to use KEYPRESS.
313 // Send key event here
314 if (keySymSource == KEYDOWN) {
315  
316 if (keydownChar[keynum] != keysym) {
317  
318 // Send event
319 keydownChar[keynum] = keysym;
320 var returnValue = sendKeyPressed(keysym);
321  
322 // Clear old key repeat, if any.
323 stopRepeat();
324  
325 // Start repeating (if not a modifier key) after a short delay
326 if (keynum != 16 && keynum != 17 && keynum != 18)
327 repeatKeyTimeoutId = setTimeout(function() { startRepeat(keysym); }, 500);
328  
329 // Use return code provided by handler
330 return returnValue;
331  
332 }
333  
334 // Default to canceling event if no keypress is being sent, but
335 // source of events is keydown.
336 return false;
337  
338 }
339  
340 return true;
341  
342 };
343  
344 // When key pressed
345 element.onkeypress = function(e) {
346  
347 // Only intercept if handler set
348 if (!guac_keyboard.onkeydown) return true;
349  
350 if (keySymSource != KEYPRESS) return false;
351  
352 var keynum;
353 if (window.event) keynum = window.event.keyCode;
354 else if (e.which) keynum = e.which;
355  
356 var keysym = getKeySymFromCharCode(keynum);
357 if (keysym && keydownChar[keynum] != keysym) {
358  
359 // If this button already pressed, release first
360 var lastKeyDownChar = keydownChar[keydownCode];
361 if (lastKeyDownChar)
362 sendKeyReleased(lastKeyDownChar);
363  
364 keydownChar[keydownCode] = keysym;
365  
366 // Clear old key repeat, if any.
367 stopRepeat();
368  
369 // Send key event
370 var returnValue = sendKeyPressed(keysym);
371  
372 // Start repeating (if not a modifier key) after a short delay
373 repeatKeyTimeoutId = setTimeout(function() { startRepeat(keysym); }, 500);
374  
375 return returnValue;
376 }
377  
378 // Default to canceling event if no keypress is being sent, but
379 // source of events is keypress.
380 return false;
381  
382 };
383  
384 // When key released
385 element.onkeyup = function(e) {
386  
387 // Only intercept if handler set
388 if (!guac_keyboard.onkeyup) return true;
389  
390 var keynum;
391 if (window.event) keynum = window.event.keyCode;
392 else if (e.which) keynum = e.which;
393  
394 // Ctrl/Alt/Shift
395 if (keynum == 16) guac_keyboard.modifiers.shift = false;
396 else if (keynum == 17) guac_keyboard.modifiers.ctrl = false;
397 else if (keynum == 18) guac_keyboard.modifiers.alt = false;
398 else
399 stopRepeat();
400  
401 // Get corresponding character
402 var lastKeyDownChar = keydownChar[keynum];
403  
404 // Clear character record
405 keydownChar[keynum] = null;
406  
407 // Send release event
408 return sendKeyReleased(lastKeyDownChar);
409  
410 };
411  
412 // When focus is lost, clear modifiers.
413 element.onblur = function() {
414 guac_keyboard.modifiers.alt = false;
415 guac_keyboard.modifiers.ctrl = false;
416 guac_keyboard.modifiers.shift = false;
417 };
418  
419 };