3 * Guacamole - Clientless Remote Desktop
4 * Copyright (C) 2010 Michael Jumper
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Affero General Public License for more details.
16 * You should have received a copy of the GNU Affero General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 function GuacamoleKeyboard(element) {
24 var unshiftedKeySym = new Array();
25 unshiftedKeySym[8] = 0xFF08; // backspace
26 unshiftedKeySym[9] = 0xFF09; // tab
27 unshiftedKeySym[13] = 0xFF0D; // enter
28 unshiftedKeySym[16] = 0xFFE1; // shift
29 unshiftedKeySym[17] = 0xFFE3; // ctrl
30 unshiftedKeySym[18] = 0xFFE9; // alt
31 unshiftedKeySym[19] = 0xFF13; // pause/break
32 unshiftedKeySym[20] = 0xFFE5; // caps lock
33 unshiftedKeySym[27] = 0xFF1B; // escape
34 unshiftedKeySym[33] = 0xFF55; // page up
35 unshiftedKeySym[34] = 0xFF56; // page down
36 unshiftedKeySym[35] = 0xFF57; // end
37 unshiftedKeySym[36] = 0xFF50; // home
38 unshiftedKeySym[37] = 0xFF51; // left arrow
39 unshiftedKeySym[38] = 0xFF52; // up arrow
40 unshiftedKeySym[39] = 0xFF53; // right arrow
41 unshiftedKeySym[40] = 0xFF54; // down arrow
42 unshiftedKeySym[45] = 0xFF63; // insert
43 unshiftedKeySym[46] = 0xFFFF; // delete
44 unshiftedKeySym[91] = 0xFFEB; // left window key (super_l)
45 unshiftedKeySym[92] = 0xFF67; // right window key (menu key?)
46 unshiftedKeySym[93] = null; // select key
47 unshiftedKeySym[112] = 0xFFBE; // f1
48 unshiftedKeySym[113] = 0xFFBF; // f2
49 unshiftedKeySym[114] = 0xFFC0; // f3
50 unshiftedKeySym[115] = 0xFFC1; // f4
51 unshiftedKeySym[116] = 0xFFC2; // f5
52 unshiftedKeySym[117] = 0xFFC3; // f6
53 unshiftedKeySym[118] = 0xFFC4; // f7
54 unshiftedKeySym[119] = 0xFFC5; // f8
55 unshiftedKeySym[120] = 0xFFC6; // f9
56 unshiftedKeySym[121] = 0xFFC7; // f10
57 unshiftedKeySym[122] = 0xFFC8; // f11
58 unshiftedKeySym[123] = 0xFFC9; // f12
59 unshiftedKeySym[144] = 0xFF7F; // num lock
60 unshiftedKeySym[145] = 0xFF14; // scroll lock
62 // Shifted versions, IF DIFFERENT FROM UNSHIFTED!
63 // If any of these are null, the unshifted one will be used.
64 var shiftedKeySym = new Array();
65 shiftedKeySym[18] = 0xFFE7; // alt
68 /*****************************************/
69 /*** Keyboard Handler ***/
70 /*****************************************/
72 // Single key state/modifier buffer
77 var keydownChar = new Array();
80 // ID of routine repeating keystrokes. -1 = not repeating.
81 var repeatKeyTimeoutId = -1;
82 var repeatKeyIntervalId = -1;
84 // Starts repeating keystrokes
85 function startRepeat(keySym) {
86 repeatKeyIntervalId = setInterval(function() {
87 sendKeyReleased(keySym);
88 sendKeyPressed(keySym);
92 // Stops repeating keystrokes
93 function stopRepeat() {
94 if (repeatKeyTimeoutId != -1) clearInterval(repeatKeyTimeoutId);
95 if (repeatKeyIntervalId != -1) clearInterval(repeatKeyIntervalId);
99 function getKeySymFromKeyIdentifier(shifted, keyIdentifier) {
101 var unicodePrefixLocation = keyIdentifier.indexOf("U+");
102 if (unicodePrefixLocation >= 0) {
104 var hex = keyIdentifier.substring(unicodePrefixLocation+2);
105 var codepoint = parseInt(hex, 16);
108 // Convert case if shifted
110 typedCharacter = String.fromCharCode(codepoint).toLowerCase();
112 typedCharacter = String.fromCharCode(codepoint).toUpperCase();
115 codepoint = typedCharacter.charCodeAt(0);
117 return getKeySymFromCharCode(codepoint);
125 function getKeySymFromCharCode(keyCode) {
127 if (keyCode >= 0x0000 && keyCode <= 0x00FF)
130 if (keyCode >= 0x0100 && keyCode <= 0x10FFFF)
131 return 0x01000000 | keyCode;
137 function getKeySymFromKeyCode(keyCode) {
140 if (modShift == 0) keysym = unshiftedKeySym[keyCode];
142 keysym = shiftedKeySym[keyCode];
143 if (keysym == null) keysym = unshiftedKeySym[keyCode];
151 // Sends a single keystroke over the network
152 function sendKeyPressed(keysym) {
153 if (keysym != null && keyPressedHandler)
154 keyPressedHandler(keysym);
157 // Sends a single keystroke over the network
158 function sendKeyReleased(keysym) {
160 keyReleasedHandler(keysym);
167 var keySymSource = null;
170 var keydownCode = null;
171 element.onkeydown = function(e) {
173 // Only intercept if handler set
174 if (!keyPressedHandler) return true;
177 if (window.event) keynum = window.event.keyCode;
178 else if (e.which) keynum = e.which;
183 else if (keynum == 17)
185 else if (keynum == 18)
188 var keysym = getKeySymFromKeyCode(keynum);
190 // Get keysyms and events from KEYDOWN
191 keySymSource = KEYDOWN;
194 // If modifier keys are held down, and we have keyIdentifier
195 else if ((modCtrl == 1 || modAlt == 1) && e.keyIdentifier) {
197 // Get keysym from keyIdentifier
198 keysym = getKeySymFromKeyIdentifier(modShift, e.keyIdentifier);
200 // Get keysyms and events from KEYDOWN
201 keySymSource = KEYDOWN;
206 // Get keysyms and events from KEYPRESS
207 keySymSource = KEYPRESS;
209 keydownCode = keynum;
211 // Ignore key if we don't need to use KEYPRESS.
212 // Send key event here
213 if (keySymSource == KEYDOWN) {
215 if (keydownChar[keynum] != keysym) {
218 keydownChar[keynum] = keysym;
219 sendKeyPressed(keysym);
221 // Clear old key repeat, if any.
224 // Start repeating (if not a modifier key) after a short delay
225 if (keynum != 16 && keynum != 17 && keynum != 18)
226 repeatKeyTimeoutId = setTimeout(function() { startRepeat(keysym); }, 500);
237 element.onkeypress = function(e) {
239 // Only intercept if handler set
240 if (!keyPressedHandler) return true;
242 if (keySymSource != KEYPRESS) return false;
245 if (window.event) keynum = window.event.keyCode;
246 else if (e.which) keynum = e.which;
248 var keysym = getKeySymFromCharCode(keynum);
249 if (keysym && keydownChar[keynum] != keysym) {
251 // If this button already pressed, release first
252 var lastKeyDownChar = keydownChar[keydownCode];
254 sendKeyReleased(lastKeyDownChar);
256 keydownChar[keydownCode] = keysym;
258 // Clear old key repeat, if any.
262 sendKeyPressed(keysym);
264 // Start repeating (if not a modifier key) after a short delay
265 repeatKeyTimeoutId = setTimeout(function() { startRepeat(keysym); }, 500);
272 element.onkeyup = function(e) {
274 // Only intercept if handler set
275 if (!keyReleasedHandler) return true;
278 if (window.event) keynum = window.event.keyCode;
279 else if (e.which) keynum = e.which;
284 else if (keynum == 17)
286 else if (keynum == 18)
291 // Get corresponding character
292 var lastKeyDownChar = keydownChar[keynum];
294 // Clear character record
295 keydownChar[keynum] = null;
297 // Send release event
298 sendKeyReleased(lastKeyDownChar);
303 // When focus is lost, clear modifiers.
304 var docOnblur = element.onblur;
305 element.onblur = function() {
309 if (docOnblur != null) docOnblur();
312 var keyPressedHandler = null;
313 var keyReleasedHandler = null;
315 this.setKeyPressedHandler = function(kh) { keyPressedHandler = kh; };
316 this.setKeyReleasedHandler = function(kh) { keyReleasedHandler = kh; };