Removed native components (now in own repositories)
[guacamole.git] / web / guacamole-default-webapp / src / main / webapp / guac-web-lib / javascript / 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         /*****************************************/
23         /*** Keyboard Handler                  ***/
24         /*****************************************/
25
26         // Single key state/modifier buffer
27         var modShift = 0;
28         var modCtrl = 0;
29         var modAlt = 0;
30
31     var keydownChar = new Array();
32
33
34     // ID of routine repeating keystrokes. -1 = not repeating.
35     var repeatKeyTimeoutId = -1;
36     var repeatKeyIntervalId = -1;
37
38         // Starts repeating keystrokes
39         function startRepeat(keySym) {
40                 repeatKeyIntervalId = setInterval(function() {
41             sendKeyReleased(keySym);
42             sendKeyPressed(keySym);
43         }, 50);
44         }
45
46         // Stops repeating keystrokes
47         function stopRepeat() {
48                 if (repeatKeyTimeoutId != -1) clearInterval(repeatKeyTimeoutId);
49                 if (repeatKeyIntervalId != -1) clearInterval(repeatKeyIntervalId);
50         }
51
52
53     function getKeySymFromKeyIdentifier(shifted, keyIdentifier) {
54
55             var unicodePrefixLocation = keyIdentifier.indexOf("U+");
56             if (unicodePrefixLocation >= 0) {
57
58                 var hex = keyIdentifier.substring(unicodePrefixLocation+2);
59                 var codepoint = parseInt(hex, 16);
60                 var typedCharacter;
61
62                 // Convert case if shifted
63                 if (shifted == 0)
64                     typedCharacter = String.fromCharCode(codepoint).toLowerCase();
65                 else
66                     typedCharacter = String.fromCharCode(codepoint).toUpperCase();
67
68                 // Get codepoint
69                 codepoint = typedCharacter.charCodeAt(0);
70
71                 return getKeySymFromCharCode(codepoint);
72
73             }
74
75             return null;
76
77     }
78
79     function getKeySymFromCharCode(keyCode) {
80
81         if (keyCode >= 0x0000 && keyCode <= 0x00FF)
82             return keyCode;
83
84         if (keyCode >= 0x0100 && keyCode <= 0x10FFFF)
85             return 0x01000000 | keyCode;
86
87         return null;
88
89     }
90
91     function getKeySymFromKeyCode(keyCode) {
92
93         var keysym = null;
94                 if (modShift == 0) keysym = unshiftedKeySym[keyCode];
95                 else {
96             keysym = shiftedKeySym[keyCode];
97             if (keysym == null) keysym = unshiftedKeySym[keyCode];
98         }
99
100         return keysym;
101
102     }
103
104
105         // Sends a single keystroke over the network
106         function sendKeyPressed(keysym) {
107                 if (keysym != null && keyPressedHandler)
108                         keyPressedHandler(keysym);
109         }
110
111         // Sends a single keystroke over the network
112         function sendKeyReleased(keysym) {
113                 if (keysym != null)
114                         keyReleasedHandler(keysym);
115         }
116
117
118     var KEYDOWN = 1;
119     var KEYPRESS = 2;
120
121     var keySymSource = null;
122
123         // When key pressed
124     var keydownCode = null;
125         element.onkeydown = function(e) {
126
127         // Only intercept if handler set
128         if (!keyPressedHandler) return true;
129
130                 var keynum;
131                 if (window.event) keynum = window.event.keyCode;
132                 else if (e.which) keynum = e.which;
133
134                 // Ctrl/Alt/Shift
135                 if (keynum == 16)
136                         modShift = 1;
137                 else if (keynum == 17)
138                         modCtrl = 1;
139                 else if (keynum == 18)
140                         modAlt = 1;
141
142         var keysym = getKeySymFromKeyCode(keynum);
143         if (keysym) {
144             // Get keysyms and events from KEYDOWN
145             keySymSource = KEYDOWN;
146         }
147
148         // If modifier keys are held down, and we have keyIdentifier
149         else if ((modCtrl == 1 || modAlt == 1) && e.keyIdentifier) {
150
151             // Get keysym from keyIdentifier
152             keysym = getKeySymFromKeyIdentifier(modShift, e.keyIdentifier);
153
154             // Get keysyms and events from KEYDOWN
155             keySymSource = KEYDOWN;
156
157         }
158
159         else
160             // Get keysyms and events from KEYPRESS
161             keySymSource = KEYPRESS;
162
163         keydownCode = keynum;
164
165         // Ignore key if we don't need to use KEYPRESS.
166         // Send key event here
167         if (keySymSource == KEYDOWN) {
168
169             if (keydownChar[keynum] != keysym) {
170
171                 // Send event
172                 keydownChar[keynum] = keysym;
173                 sendKeyPressed(keysym);
174
175                 // Clear old key repeat, if any.
176                 stopRepeat();
177
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);
181             }
182
183             return false;
184         }
185
186         };
187
188         // When key pressed
189     element.onkeypress = function(e) {
190
191         // Only intercept if handler set
192         if (!keyPressedHandler) return true;
193
194         if (keySymSource != KEYPRESS) return false;
195
196                 var keynum;
197                 if (window.event) keynum = window.event.keyCode;
198                 else if (e.which) keynum = e.which;
199
200         var keysym = getKeySymFromCharCode(keynum);
201         if (keysym && keydownChar[keynum] != keysym) {
202
203             // If this button already pressed, release first
204             var lastKeyDownChar = keydownChar[keydownCode];
205             if (lastKeyDownChar)
206                 sendKeyReleased(lastKeyDownChar);
207
208             keydownChar[keydownCode] = keysym;
209
210             // Clear old key repeat, if any.
211             stopRepeat();
212
213             // Send key event
214             sendKeyPressed(keysym);
215
216             // Start repeating (if not a modifier key) after a short delay
217             repeatKeyTimeoutId = setTimeout(function() { startRepeat(keysym); }, 500);
218         }
219
220         return false;
221         };
222
223         // When key released
224         element.onkeyup = function(e) {
225
226         // Only intercept if handler set
227         if (!keyReleasedHandler) return true;
228
229                 var keynum;
230                 if (window.event) keynum = window.event.keyCode;
231                 else if (e.which) keynum = e.which;
232                 
233                 // Ctrl/Alt/Shift
234                 if (keynum == 16)
235                         modShift = 0;
236                 else if (keynum == 17)
237                         modCtrl = 0;
238                 else if (keynum == 18)
239                         modAlt = 0;
240         else
241             stopRepeat();
242
243         // Get corresponding character
244         var lastKeyDownChar = keydownChar[keynum];
245
246         // Clear character record
247         keydownChar[keynum] = null;
248
249         // Send release event
250         sendKeyReleased(lastKeyDownChar);
251
252                 return false;
253         };
254
255         // When focus is lost, clear modifiers.
256         var docOnblur = element.onblur;
257         element.onblur = function() {
258                 modAlt = 0;
259                 modCtrl = 0;
260                 modShift = 0;
261                 if (docOnblur != null) docOnblur();
262         };
263
264         var keyPressedHandler = null;
265         var keyReleasedHandler = null;
266
267         this.setKeyPressedHandler = function(kh) { keyPressedHandler = kh; };
268         this.setKeyReleasedHandler = function(kh) { keyReleasedHandler = kh; };
269
270 }