19: 0xFF13, // pause/break
20: 0xFFE5, // caps lock
27: 0xFF1B, // escape
+ 32: 0x0020, // space
33: 0xFF55, // page up
34: 0xFF56, // page down
35: 0xFF57, // end
// Stops repeating keystrokes
function stopRepeat() {
- if (repeatKeyTimeoutId != -1) clearInterval(repeatKeyTimeoutId);
+ if (repeatKeyTimeoutId != -1) clearTimeout(repeatKeyTimeoutId);
if (repeatKeyIntervalId != -1) clearInterval(repeatKeyIntervalId);
}
}
- function getKeySymFromCharCode(keyCode) {
+ function isControlCharacter(codepoint) {
+ return codepoint <= 0x1F || (codepoint >= 0x7F && codepoint <= 0x9F);
+ }
+
+ function getKeySymFromCharCode(codepoint) {
+
+ // Keysyms for control characters
+ if (isControlCharacter(codepoint)) return 0xFF00 | codepoint;
- if (keyCode >= 0x0000 && keyCode <= 0x00FF)
- return keyCode;
+ // Keysyms for ASCII chars
+ if (codepoint >= 0x0000 && codepoint <= 0x00FF)
+ return codepoint;
- if (keyCode >= 0x0100 && keyCode <= 0x10FFFF)
- return 0x01000000 | keyCode;
+ // Keysyms for Unicode
+ if (codepoint >= 0x0100 && codepoint <= 0x10FFFF)
+ return 0x01000000 | codepoint;
return null;
// Done with deferred key event
deferred_keypress = null;
+ keypress_keysym = null;
+ keydown_keysym = null;
+ keydown_code = null;
+
+ }
+
+ function isTypable(keyIdentifier) {
+
+ // Find unicode prefix
+ var unicodePrefixLocation = keyIdentifier.indexOf("U+");
+ if (unicodePrefixLocation == -1)
+ return false;
+
+ // Parse codepoint value
+ var hex = keyIdentifier.substring(unicodePrefixLocation+2);
+ var codepoint = parseInt(hex, 16);
+
+ // If control character, not typable
+ if (isControlCharacter(codepoint)) return false;
+
+ // Otherwise, typable
+ return true;
}
element.onkeydown = function(e) {
// Only intercept if handler set
- if (!guac_keyboard.onkeydown) return true;
+ if (!guac_keyboard.onkeydown) return;
var keynum;
if (window.event) keynum = window.event.keyCode;
else if (e.which) keynum = e.which;
+ // Ignore any unknown key events
+ if (keynum == 0) {
+ e.preventDefault();
+ return;
+ }
+
// Ctrl/Alt/Shift
if (keynum == 16) guac_keyboard.modifiers.shift = true;
else if (keynum == 17) guac_keyboard.modifiers.ctrl = true;
// Try to get keysym from keycode
keydown_keysym = getKeySymFromKeyCode(keynum);
+ // If key is known from keycode, prevent default
+ if (keydown_keysym)
+ e.preventDefault();
+
+
// Also try to get get keysym from keyIdentifier
- if (e.keyIdentifier)
- keydown_keysym = getKeySymFromKeyIdentifier(guac_keyboard.modifiers.shift, e.keyIdentifier);
+ if (e.keyIdentifier) {
+
+ keydown_keysym = keydown_keysym ||
+ getKeySymFromKeyIdentifier(guac_keyboard.modifiers.shift, e.keyIdentifier);
+
+ // Prevent default if non-typable character or if modifier combination
+ // likely to be eaten by browser otherwise (NOTE: We must not prevent
+ // default for Ctrl+Alt, as that combination is commonly used for
+ // AltGr. If we receive AltGr, we need to handle keypress, which
+ // means we cannot cancel keydown).
+ if (!isTypable(e.keyIdentifier)
+ || ( guac_keyboard.modifiers.ctrl && !guac_keyboard.modifiers.alt)
+ || (!guac_keyboard.modifiers.ctrl && guac_keyboard.modifiers.alt))
+ e.preventDefault();
+
+ }
// Set keycode which will be associated with any future keypress
keydown_code = keynum;
if (!deferred_keypress)
deferred_keypress = window.setTimeout(fireKeyPress, 0);
- return false;
-
};
// When key pressed
element.onkeypress = function(e) {
// Only intercept if handler set
- if (!guac_keyboard.onkeydown) return true;
-
- if (keySymSource != KEYPRESS) return false;
+ if (!guac_keyboard.onkeydown) return;
var keynum;
if (window.event) keynum = window.event.keyCode;
keypress_keysym = getKeySymFromCharCode(keynum);
+ // If event identified as a typable character, and we're holding Ctrl+Alt,
+ // assume Ctrl+Alt is actually AltGr, and release both.
+ if (!isControlCharacter(keynum) && guac_keyboard.modifiers.ctrl && guac_keyboard.modifiers.alt) {
+ sendKeyReleased(0xFFE3);
+ sendKeyReleased(0xFFE9);
+ }
+
// Defer handling of event until after any other pending
// key events.
if (!deferred_keypress)
deferred_keypress = window.setTimeout(fireKeyPress, 0);
- return false;
-
};
// When key released
element.onkeyup = function(e) {
// Only intercept if handler set
- if (!guac_keyboard.onkeyup) return true;
+ if (!guac_keyboard.onkeyup) return;
+
+ e.preventDefault();
var keynum;
if (window.event) keynum = window.event.keyCode;
keydownChar[keynum] = null;
// Send release event
- return sendKeyReleased(lastKeyDownChar);
+ sendKeyReleased(lastKeyDownChar);
};