Removed keymap (merged into keyboard), fixed JS semicolons, etc.
[guacamole-common-js.git] / src / main / resources / keyboard.js
1
2 /*
3  *  Guacamole - Clientless Remote Desktop
4  *  Copyright (C) 2010  Michael Jumper
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 function GuacamoleKeyboard(element) {
21
22     // Keymap
23
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
61
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
66
67
68         /*****************************************/
69         /*** Keyboard Handler                  ***/
70         /*****************************************/
71
72         // Single key state/modifier buffer
73         var modShift = 0;
74         var modCtrl = 0;
75         var modAlt = 0;
76
77     var keydownChar = new Array();
78
79
80     // ID of routine repeating keystrokes. -1 = not repeating.
81     var repeatKeyTimeoutId = -1;
82     var repeatKeyIntervalId = -1;
83
84         // Starts repeating keystrokes
85         function startRepeat(keySym) {
86                 repeatKeyIntervalId = setInterval(function() {
87             sendKeyReleased(keySym);
88             sendKeyPressed(keySym);
89         }, 50);
90         }
91
92         // Stops repeating keystrokes
93         function stopRepeat() {
94                 if (repeatKeyTimeoutId != -1) clearInterval(repeatKeyTimeoutId);
95                 if (repeatKeyIntervalId != -1) clearInterval(repeatKeyIntervalId);
96         }
97
98
99     function getKeySymFromKeyIdentifier(shifted, keyIdentifier) {
100
101         var unicodePrefixLocation = keyIdentifier.indexOf("U+");
102         if (unicodePrefixLocation >= 0) {
103
104             var hex = keyIdentifier.substring(unicodePrefixLocation+2);
105             var codepoint = parseInt(hex, 16);
106             var typedCharacter;
107
108             // Convert case if shifted
109             if (shifted == 0)
110                 typedCharacter = String.fromCharCode(codepoint).toLowerCase();
111             else
112                 typedCharacter = String.fromCharCode(codepoint).toUpperCase();
113
114             // Get codepoint
115             codepoint = typedCharacter.charCodeAt(0);
116
117             return getKeySymFromCharCode(codepoint);
118
119         }
120
121         return null;
122
123     }
124
125     function getKeySymFromCharCode(keyCode) {
126
127         if (keyCode >= 0x0000 && keyCode <= 0x00FF)
128             return keyCode;
129
130         if (keyCode >= 0x0100 && keyCode <= 0x10FFFF)
131             return 0x01000000 | keyCode;
132
133         return null;
134
135     }
136
137     function getKeySymFromKeyCode(keyCode) {
138
139         var keysym = null;
140                 if (modShift == 0) keysym = unshiftedKeySym[keyCode];
141                 else {
142             keysym = shiftedKeySym[keyCode];
143             if (keysym == null) keysym = unshiftedKeySym[keyCode];
144         }
145
146         return keysym;
147
148     }
149
150
151         // Sends a single keystroke over the network
152         function sendKeyPressed(keysym) {
153                 if (keysym != null && keyPressedHandler)
154                         keyPressedHandler(keysym);
155         }
156
157         // Sends a single keystroke over the network
158         function sendKeyReleased(keysym) {
159                 if (keysym != null)
160                         keyReleasedHandler(keysym);
161         }
162
163
164     var KEYDOWN = 1;
165     var KEYPRESS = 2;
166
167     var keySymSource = null;
168
169         // When key pressed
170     var keydownCode = null;
171         element.onkeydown = function(e) {
172
173         // Only intercept if handler set
174         if (!keyPressedHandler) return true;
175
176                 var keynum;
177                 if (window.event) keynum = window.event.keyCode;
178                 else if (e.which) keynum = e.which;
179
180                 // Ctrl/Alt/Shift
181                 if (keynum == 16)
182                         modShift = 1;
183                 else if (keynum == 17)
184                         modCtrl = 1;
185                 else if (keynum == 18)
186                         modAlt = 1;
187
188         var keysym = getKeySymFromKeyCode(keynum);
189         if (keysym) {
190             // Get keysyms and events from KEYDOWN
191             keySymSource = KEYDOWN;
192         }
193
194         // If modifier keys are held down, and we have keyIdentifier
195         else if ((modCtrl == 1 || modAlt == 1) && e.keyIdentifier) {
196
197             // Get keysym from keyIdentifier
198             keysym = getKeySymFromKeyIdentifier(modShift, e.keyIdentifier);
199
200             // Get keysyms and events from KEYDOWN
201             keySymSource = KEYDOWN;
202
203         }
204
205         else
206             // Get keysyms and events from KEYPRESS
207             keySymSource = KEYPRESS;
208
209         keydownCode = keynum;
210
211         // Ignore key if we don't need to use KEYPRESS.
212         // Send key event here
213         if (keySymSource == KEYDOWN) {
214
215             if (keydownChar[keynum] != keysym) {
216
217                 // Send event
218                 keydownChar[keynum] = keysym;
219                 sendKeyPressed(keysym);
220
221                 // Clear old key repeat, if any.
222                 stopRepeat();
223
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);
227             }
228
229             return false;
230         }
231
232         return true;
233
234         };
235
236         // When key pressed
237     element.onkeypress = function(e) {
238
239         // Only intercept if handler set
240         if (!keyPressedHandler) return true;
241
242         if (keySymSource != KEYPRESS) return false;
243
244                 var keynum;
245                 if (window.event) keynum = window.event.keyCode;
246                 else if (e.which) keynum = e.which;
247
248         var keysym = getKeySymFromCharCode(keynum);
249         if (keysym && keydownChar[keynum] != keysym) {
250
251             // If this button already pressed, release first
252             var lastKeyDownChar = keydownChar[keydownCode];
253             if (lastKeyDownChar)
254                 sendKeyReleased(lastKeyDownChar);
255
256             keydownChar[keydownCode] = keysym;
257
258             // Clear old key repeat, if any.
259             stopRepeat();
260
261             // Send key event
262             sendKeyPressed(keysym);
263
264             // Start repeating (if not a modifier key) after a short delay
265             repeatKeyTimeoutId = setTimeout(function() { startRepeat(keysym); }, 500);
266         }
267
268         return false;
269         };
270
271         // When key released
272         element.onkeyup = function(e) {
273
274         // Only intercept if handler set
275         if (!keyReleasedHandler) return true;
276
277                 var keynum;
278                 if (window.event) keynum = window.event.keyCode;
279                 else if (e.which) keynum = e.which;
280                 
281                 // Ctrl/Alt/Shift
282                 if (keynum == 16)
283                         modShift = 0;
284                 else if (keynum == 17)
285                         modCtrl = 0;
286                 else if (keynum == 18)
287                         modAlt = 0;
288         else
289             stopRepeat();
290
291         // Get corresponding character
292         var lastKeyDownChar = keydownChar[keynum];
293
294         // Clear character record
295         keydownChar[keynum] = null;
296
297         // Send release event
298         sendKeyReleased(lastKeyDownChar);
299
300                 return false;
301         };
302
303         // When focus is lost, clear modifiers.
304         var docOnblur = element.onblur;
305         element.onblur = function() {
306                 modAlt = 0;
307                 modCtrl = 0;
308                 modShift = 0;
309                 if (docOnblur != null) docOnblur();
310         };
311
312         var keyPressedHandler = null;
313         var keyReleasedHandler = null;
314
315         this.setKeyPressedHandler = function(kh) { keyPressedHandler = kh; };
316         this.setKeyReleasedHandler = function(kh) { keyReleasedHandler = kh; };
317
318 }