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) {
22 /*****************************************/
23 /*** Keyboard Handler ***/
24 /*****************************************/
26 // Single key state/modifier buffer
31 var keydownChar = new Array();
34 // ID of routine repeating keystrokes. -1 = not repeating.
35 var repeatKeyTimeoutId = -1;
36 var repeatKeyIntervalId = -1;
38 // Starts repeating keystrokes
39 function startRepeat(keySym) {
40 repeatKeyIntervalId = setInterval(function() {
41 sendKeyReleased(keySym);
42 sendKeyPressed(keySym);
46 // Stops repeating keystrokes
47 function stopRepeat() {
48 if (repeatKeyTimeoutId != -1) clearInterval(repeatKeyTimeoutId);
49 if (repeatKeyIntervalId != -1) clearInterval(repeatKeyIntervalId);
53 function getKeySymFromKeyIdentifier(shifted, keyIdentifier) {
55 var unicodePrefixLocation = keyIdentifier.indexOf("U+");
56 if (unicodePrefixLocation >= 0) {
58 var hex = keyIdentifier.substring(unicodePrefixLocation+2);
59 var codepoint = parseInt(hex, 16);
62 // Convert case if shifted
64 typedCharacter = String.fromCharCode(codepoint).toLowerCase();
66 typedCharacter = String.fromCharCode(codepoint).toUpperCase();
69 codepoint = typedCharacter.charCodeAt(0);
71 return getKeySymFromCharCode(codepoint);
79 function getKeySymFromCharCode(keyCode) {
81 if (keyCode >= 0x0000 && keyCode <= 0x00FF)
84 if (keyCode >= 0x0100 && keyCode <= 0x10FFFF)
85 return 0x01000000 | keyCode;
91 function getKeySymFromKeyCode(keyCode) {
94 if (modShift == 0) keysym = unshiftedKeySym[keyCode];
96 keysym = shiftedKeySym[keyCode];
97 if (keysym == null) keysym = unshiftedKeySym[keyCode];
105 // Sends a single keystroke over the network
106 function sendKeyPressed(keysym) {
107 if (keysym != null && keyPressedHandler)
108 keyPressedHandler(keysym);
111 // Sends a single keystroke over the network
112 function sendKeyReleased(keysym) {
114 keyReleasedHandler(keysym);
121 var keySymSource = null;
124 var keydownCode = null;
125 element.onkeydown = function(e) {
127 // Only intercept if handler set
128 if (!keyPressedHandler) return true;
131 if (window.event) keynum = window.event.keyCode;
132 else if (e.which) keynum = e.which;
137 else if (keynum == 17)
139 else if (keynum == 18)
142 var keysym = getKeySymFromKeyCode(keynum);
144 // Get keysyms and events from KEYDOWN
145 keySymSource = KEYDOWN;
148 // If modifier keys are held down, and we have keyIdentifier
149 else if ((modCtrl == 1 || modAlt == 1) && e.keyIdentifier) {
151 // Get keysym from keyIdentifier
152 keysym = getKeySymFromKeyIdentifier(modShift, e.keyIdentifier);
154 // Get keysyms and events from KEYDOWN
155 keySymSource = KEYDOWN;
160 // Get keysyms and events from KEYPRESS
161 keySymSource = KEYPRESS;
163 keydownCode = keynum;
165 // Ignore key if we don't need to use KEYPRESS.
166 // Send key event here
167 if (keySymSource == KEYDOWN) {
169 if (keydownChar[keynum] != keysym) {
172 keydownChar[keynum] = keysym;
173 sendKeyPressed(keysym);
175 // Clear old key repeat, if any.
178 // Start repeating (if not a modifier key) after a short delay
179 if (keynum != 16 && keynum != 17 && keynum != 18)
180 repeatKeyTimeoutId = setTimeout(function() { startRepeat(keysym); }, 500);
189 element.onkeypress = function(e) {
191 // Only intercept if handler set
192 if (!keyPressedHandler) return true;
194 if (keySymSource != KEYPRESS) return false;
197 if (window.event) keynum = window.event.keyCode;
198 else if (e.which) keynum = e.which;
200 var keysym = getKeySymFromCharCode(keynum);
201 if (keysym && keydownChar[keynum] != keysym) {
203 // If this button already pressed, release first
204 var lastKeyDownChar = keydownChar[keydownCode];
206 sendKeyReleased(lastKeyDownChar);
208 keydownChar[keydownCode] = keysym;
210 // Clear old key repeat, if any.
214 sendKeyPressed(keysym);
216 // Start repeating (if not a modifier key) after a short delay
217 repeatKeyTimeoutId = setTimeout(function() { startRepeat(keysym); }, 500);
224 element.onkeyup = function(e) {
226 // Only intercept if handler set
227 if (!keyReleasedHandler) return true;
230 if (window.event) keynum = window.event.keyCode;
231 else if (e.which) keynum = e.which;
236 else if (keynum == 17)
238 else if (keynum == 18)
243 // Get corresponding character
244 var lastKeyDownChar = keydownChar[keynum];
246 // Clear character record
247 keydownChar[keynum] = null;
249 // Send release event
250 sendKeyReleased(lastKeyDownChar);
255 // When focus is lost, clear modifiers.
256 var docOnblur = element.onblur;
257 element.onblur = function() {
261 if (docOnblur != null) docOnblur();
264 var keyPressedHandler = null;
265 var keyReleasedHandler = null;
267 this.setKeyPressedHandler = function(kh) { keyPressedHandler = kh; };
268 this.setKeyReleasedHandler = function(kh) { keyReleasedHandler = kh; };