v1.16.3 refinements continue

Keyboard handling in JavaScript is a nightmare; mapping physical keyboard events to simulated keyboard events is a nightmare within a nightmare
This commit is contained in:
Jeff Parsons 2014-12-10 15:09:32 -08:00 • committed by jeffpar
commit c0abfaa942
5 changed files with 1516 additions and 1452 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -178,8 +178,8 @@ Keyboard.KEYCODE = {
/* 0x2C */ PRTSC: 44, /* 0x2C */ PRTSC: 44,
/* 0x2D */ INS: 45, /* 0x2D */ INS: 45,
/* 0x2E */ DEL: 46, /* 0x2E */ DEL: 46,
/* 0x5B */ COMMAND: 91, // TODO: Treat this like the 'Windows' key /* 0x5B */ CMD: 91, // aka WIN
/* 0x5D */ MENU: 93, /* 0x5D */ RCMD: 93, // aka MENU
/* 0x60 */ NUM_INS: 96, // 0 /* 0x60 */ NUM_INS: 96, // 0
/* 0x61 */ NUM_END: 97, // 1 /* 0x61 */ NUM_END: 97, // 1
/* 0x62 */ NUM_DOWN: 98, // 2 /* 0x62 */ NUM_DOWN: 98, // 2
@ -209,7 +209,17 @@ Keyboard.KEYCODE = {
/* 0x7B */ F12: 123, /* 0x7B */ F12: 123,
/* 0x90 */ NUM_LOCK: 144, /* 0x90 */ NUM_LOCK: 144,
/* 0x91 */ SCROLL_LOCK: 145, /* 0x91 */ SCROLL_LOCK: 145,
/* 0xBA */ SEMI: 186,
/* 0xBB */ EQUALS: 187,
/* 0xBC */ COMMA: 188,
/* 0xBD */ DASH: 189,
/* 0xBE */ PERIOD: 190,
/* 0xBF */ SLASH: 191,
/* 0xC0 */ BQUOTE: 192, /* 0xC0 */ BQUOTE: 192,
/* 0xDB */ LBRACK: 219,
/* 0xDC */ BSLASH: 220,
/* 0xDD */ RBRACK: 221,
/* 0xDE */ QUOTE: 222,
// //
// The following biases use what I'll call "Decimal Coded Binary" or DCB (the reverse of BCD), // The following biases use what I'll call "Decimal Coded Binary" or DCB (the reverse of BCD),
// where decimal digits are used to represent binary bit values, which can be added together without // where decimal digits are used to represent binary bit values, which can be added together without
@ -229,9 +239,25 @@ Keyboard.KEYCODE = {
FAKE: 4000 FAKE: 4000
}; };
/*
* Maps "stupid" keyCodes to their "non-stupid" counterparts
*/
Keyboard.STUPID_KEYCODES = {};
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.SEMI] = Keyboard.ASCII[';']; // 186 -> 59
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.EQUALS] = Keyboard.ASCII['=']; // 187 -> 61
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.COMMA] = Keyboard.ASCII[',']; // 188 -> 44
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.DASH] = Keyboard.ASCII['-']; // 189 -> 45
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.PERIOD] = Keyboard.ASCII['.']; // 190 -> 46
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.SLASH] = Keyboard.ASCII['/']; // 191 -> 47
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.BQUOTE] = Keyboard.ASCII['`']; // 192 -> 96
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.LBRACK] = Keyboard.ASCII['[']; // 219 -> 91
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.BSLASH] = Keyboard.ASCII['\\']; // 220 -> 92
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.RBRACK] = Keyboard.ASCII[']']; // 221 -> 93
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.QUOTE] = Keyboard.ASCII["'"]; // 222 -> 39
/* /*
* Maps unshifted keyCodes to their shifted counterparts; to be used when a shift-key is down. * Maps unshifted keyCodes to their shifted counterparts; to be used when a shift-key is down.
* Alphabetic characters are handled in code, since they must also take caps-lock into consideration. * Alphabetic characters are handled in code, since they must also take CAPS_LOCK into consideration.
*/ */
Keyboard.SHIFTED_KEYCODES = {}; Keyboard.SHIFTED_KEYCODES = {};
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['1']] = Keyboard.ASCII['!']; Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['1']] = Keyboard.ASCII['!'];
@ -244,23 +270,17 @@ Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['7']] = Keyboard.ASCII['&'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['8']] = Keyboard.ASCII['*']; Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['8']] = Keyboard.ASCII['*'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['9']] = Keyboard.ASCII['(']; Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['9']] = Keyboard.ASCII['('];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['0']] = Keyboard.ASCII[')']; Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['0']] = Keyboard.ASCII[')'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['-']] = Keyboard.ASCII['_']; Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.SEMI] = Keyboard.ASCII[':'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['=']] = Keyboard.ASCII['+']; Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.EQUALS] = Keyboard.ASCII['+'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['[']] = Keyboard.ASCII['{']; Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.COMMA] = Keyboard.ASCII['<'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII[']']] = Keyboard.ASCII['}']; Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.DASH] = Keyboard.ASCII['_'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII[';']] = Keyboard.ASCII[':']; Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.PERIOD] = Keyboard.ASCII['>'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII["'"]] = Keyboard.ASCII['"']; Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.SLASH] = Keyboard.ASCII['?'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['`']] = Keyboard.ASCII['~']; Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.BQUOTE] = Keyboard.ASCII['~'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['\\']] = Keyboard.ASCII['|']; Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.LBRACK] = Keyboard.ASCII['{'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII[',']] = Keyboard.ASCII['<']; Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.BSLASH] = Keyboard.ASCII['|'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['.']] = Keyboard.ASCII['>']; Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.RBRACK] = Keyboard.ASCII['}'];
Keyboard.SHIFTED_KEYCODES[Keyboard.ASCII['/']] = Keyboard.ASCII['?']; Keyboard.SHIFTED_KEYCODES[Keyboard.KEYCODE.QUOTE] = Keyboard.ASCII['"'];
/*
* Maps "stupid" keyCodes to their "non-stupid" counterparts
*/
Keyboard.STUPID_KEYCODES = {};
Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.BQUOTE] = Keyboard.ASCII['`'];
Keyboard.SIMCODE = { Keyboard.SIMCODE = {
BS: Keyboard.KEYCODE.BS + Keyboard.KEYCODE.ONDOWN, BS: Keyboard.KEYCODE.BS + Keyboard.KEYCODE.ONDOWN,
@ -299,6 +319,8 @@ Keyboard.SIMCODE = {
PGDN: Keyboard.KEYCODE.PGDN + Keyboard.KEYCODE.ONDOWN, PGDN: Keyboard.KEYCODE.PGDN + Keyboard.KEYCODE.ONDOWN,
INS: Keyboard.KEYCODE.INS + Keyboard.KEYCODE.ONDOWN, INS: Keyboard.KEYCODE.INS + Keyboard.KEYCODE.ONDOWN,
DEL: Keyboard.KEYCODE.DEL + Keyboard.KEYCODE.ONDOWN, DEL: Keyboard.KEYCODE.DEL + Keyboard.KEYCODE.ONDOWN,
CMD: Keyboard.KEYCODE.CMD + Keyboard.KEYCODE.ONDOWN,
RCMD: Keyboard.KEYCODE.RCMD + Keyboard.KEYCODE.ONDOWN,
CTRL_C: Keyboard.ASCII.CTRL_C + Keyboard.KEYCODE.FAKE, CTRL_C: Keyboard.ASCII.CTRL_C + Keyboard.KEYCODE.FAKE,
CTRL_BREAK: Keyboard.KEYCODE.BS + Keyboard.KEYCODE.FAKE, CTRL_BREAK: Keyboard.KEYCODE.BS + Keyboard.KEYCODE.FAKE,
CTRL_ALT_DEL: Keyboard.KEYCODE.DEL + Keyboard.KEYCODE.FAKE CTRL_ALT_DEL: Keyboard.KEYCODE.DEL + Keyboard.KEYCODE.FAKE
@ -395,9 +417,9 @@ Keyboard.SCANCODE = {
/* 0x54 */ PAUSE: 84, // 101-key keyboard only /* 0x54 */ PAUSE: 84, // 101-key keyboard only
/* 0x57 */ F11: 87, /* 0x57 */ F11: 87,
/* 0x58 */ F12: 88, /* 0x58 */ F12: 88,
/* 0x5B */ WIN: 91, /* 0x5B */ WIN: 91, // aka CMD
/* 0x5C */ RWIN: 92, /* 0x5C */ RWIN: 92,
/* 0x5D */ MENU: 93, /* 0x5D */ MENU: 93, // aka CMD + ONRIGHT
/* 0x7F */ MAKE: 127, /* 0x7F */ MAKE: 127,
/* 0x80 */ BREAK: 128, /* 0x80 */ BREAK: 128,
/* 0xE0 */ EXTEND1: 224, /* 0xE0 */ EXTEND1: 224,
@ -417,8 +439,15 @@ Keyboard.LOCATION = {
}; };
/** /**
* These internal "shift key" states are used to indicate the current simulated shift/modifier key states * These internal "shift key" states are used to indicate BOTH the physical shift-key states (in bitsState)
* in shiftStates; note that right-hand shift bits match the corresponding left-hand bit shifted 1 bit right. * and the simulated shift-key states (in bitsStateSim). The LOCK keys are problematic in both cases: the
* browsers give us no way to query the LOCK key states, so we can only infer them, and because they are "soft"
* locks, the machine's notion of their state is subject change at any time as well. Granted, the IBM PC
* ROM BIOS will store its LOCK states in the ROM BIOS Data Area (@0040:0017), but that's purely a convention.
*
* TODO: Consider taking notice of the ROM BIOS Data Area state anyway, although I'm not keen on the idea.
*
* Note that right-hand state bits match the left-hand state bits shifted right 1 bit; makes sense, "right"? ;-)
* *
* @enum {number} * @enum {number}
*/ */
@ -429,13 +458,13 @@ Keyboard.STATE = {
CTRL: 0x0008, CTRL: 0x0008,
RALT: 0x0010, // 101-key keyboard only RALT: 0x0010, // 101-key keyboard only
ALT: 0x0020, ALT: 0x0020,
COMMAND: 0x0040, // 101-key keyboard only (TODO: Treat this like the 'Windows' key) RCMD: 0x0040, // 101-key keyboard only
INSERT: 0x0080, // TODO: Placeholder CMD: 0x0080, // 101-key keyboard only
CAPS_LOCK: 0x0100, INSERT: 0x0100, // TODO: Placeholder
NUM_LOCK: 0x0200, CAPS_LOCK: 0x0200,
SCROLL_LOCK: 0x0400, NUM_LOCK: 0x0400,
LOCK: 0x0700, // STATE.CAPS_LOCK | STATE.NUM_LOCK | STATE.SCROLL_LOCK SCROLL_LOCK: 0x0800,
SIMULATE: 0x003f // STATE.RSHIFT | STATE.SHIFT | STATE.RCTRL | STATE.CTRL | STATE.RALT | STATE.ALT LOCK: 0x0e00 // STATE.CAPS_LOCK | STATE.NUM_LOCK | STATE.SCROLL_LOCK
}; };
/** /**
@ -444,9 +473,12 @@ Keyboard.STATE = {
* @enum {number} * @enum {number}
*/ */
Keyboard.KEYSTATES = {}; Keyboard.KEYSTATES = {};
Keyboard.KEYSTATES[Keyboard.SIMCODE.RSHIFT] = Keyboard.STATE.RSHIFT;
Keyboard.KEYSTATES[Keyboard.SIMCODE.SHIFT] = Keyboard.STATE.SHIFT; Keyboard.KEYSTATES[Keyboard.SIMCODE.SHIFT] = Keyboard.STATE.SHIFT;
Keyboard.KEYSTATES[Keyboard.SIMCODE.CTRL] = Keyboard.STATE.CTRL; Keyboard.KEYSTATES[Keyboard.SIMCODE.CTRL] = Keyboard.STATE.CTRL;
Keyboard.KEYSTATES[Keyboard.SIMCODE.ALT] = Keyboard.STATE.ALT; Keyboard.KEYSTATES[Keyboard.SIMCODE.ALT] = Keyboard.STATE.ALT;
Keyboard.KEYSTATES[Keyboard.SIMCODE.CMD] = Keyboard.STATE.CMD;
Keyboard.KEYSTATES[Keyboard.SIMCODE.RCMD] = Keyboard.STATE.RCMD;
Keyboard.KEYSTATES[Keyboard.SIMCODE.CAPS_LOCK] = Keyboard.STATE.CAPS_LOCK; Keyboard.KEYSTATES[Keyboard.SIMCODE.CAPS_LOCK] = Keyboard.STATE.CAPS_LOCK;
Keyboard.KEYSTATES[Keyboard.SIMCODE.NUM_LOCK] = Keyboard.STATE.NUM_LOCK; Keyboard.KEYSTATES[Keyboard.SIMCODE.NUM_LOCK] = Keyboard.STATE.NUM_LOCK;
Keyboard.KEYSTATES[Keyboard.SIMCODE.SCROLL_LOCK] = Keyboard.STATE.SCROLL_LOCK; Keyboard.KEYSTATES[Keyboard.SIMCODE.SCROLL_LOCK] = Keyboard.STATE.SCROLL_LOCK;
@ -657,7 +689,7 @@ Keyboard.LEDSTATES = {
* The other problem (which is more of a problem with keyboards like the C1P than any IBM keyboards) is * The other problem (which is more of a problem with keyboards like the C1P than any IBM keyboards) is
* that the shift/modifier state for a character on the "source" keyboard may not match the shift/modifier * that the shift/modifier state for a character on the "source" keyboard may not match the shift/modifier
* state for the same character on the "target" keyboard. And since this code is inherited from C1Pjs, * state for the same character on the "target" keyboard. And since this code is inherited from C1Pjs,
* we've inherited the same solution: keySimulate() has the ability to "undo" any states in shiftStates * we've inherited the same solution: keySimulate() has the ability to "undo" any states in bitsState
* that conflict with the state(s) required for the character in question. * that conflict with the state(s) required for the character in question.
* *
* @enum {number} * @enum {number}
@ -811,6 +843,8 @@ Keyboard.SIMCODES[Keyboard.SIMCODE.DEL] = Keyboard.SCANCODE.NUM_DEL;
*/ */
Keyboard.SIMCODES[Keyboard.SIMCODE.F11] = Keyboard.SCANCODE.F11; Keyboard.SIMCODES[Keyboard.SIMCODE.F11] = Keyboard.SCANCODE.F11;
Keyboard.SIMCODES[Keyboard.SIMCODE.F12] = Keyboard.SCANCODE.F12; Keyboard.SIMCODES[Keyboard.SIMCODE.F12] = Keyboard.SCANCODE.F12;
Keyboard.SIMCODES[Keyboard.SIMCODE.CMD] = Keyboard.SCANCODE.WIN;
Keyboard.SIMCODES[Keyboard.SIMCODE.RCMD] = Keyboard.SCANCODE.MENU;
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_C] = Keyboard.SCANCODE.C | (Keyboard.SCANCODE.CTRL << 8); Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_C] = Keyboard.SCANCODE.C | (Keyboard.SCANCODE.CTRL << 8);
Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_BREAK] = Keyboard.SCANCODE.SCROLL_LOCK | (Keyboard.SCANCODE.CTRL << 8); Keyboard.SIMCODES[Keyboard.SIMCODE.CTRL_BREAK] = Keyboard.SCANCODE.SCROLL_LOCK | (Keyboard.SCANCODE.CTRL << 8);
@ -936,7 +970,7 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control)
return function onClickKeyboard(event) { return function onClickKeyboard(event) {
if (DEBUG && kbd.messageEnabled()) kbd.messageDebugger(sKey + " clicked", Debugger.MESSAGE.KEYS); if (DEBUG && kbd.messageEnabled()) kbd.messageDebugger(sKey + " clicked", Debugger.MESSAGE.KEYS);
if (kbd.cpu) kbd.cpu.setFocus(); if (kbd.cpu) kbd.cpu.setFocus();
kbd.updateShiftState(simCode); // future-proofing if/when any LOCK keys are added to CLICKCODES kbd.updateShiftState(simCode, true); // future-proofing if/when any LOCK keys are added to CLICKCODES
kbd.addActiveKey(simCode, true); kbd.addActiveKey(simCode, true);
}; };
}(this, sCode, Keyboard.CLICKCODES[sCode]); }(this, sCode, Keyboard.CLICKCODES[sCode]);
@ -1252,11 +1286,11 @@ Keyboard.prototype.reset = function()
this.initState(); this.initState();
/* /*
* The current (assumed) physical state of the various shift/lock keys. * The current (assumed) physical (and simulated) states of the various shift/lock keys.
* *
* TODO: Determine how (or whether) we can query the browser's initial shift/lock key states. * TODO: Determine how (or whether) we can query the browser's initial shift/lock key states.
*/ */
this.shiftStates = 0; this.bitsState = this.bitsStateSim = 0;
/* /*
* New scan codes are "pushed" onto abScanBuffer and then "shifted" off. * New scan codes are "pushed" onto abScanBuffer and then "shifted" off.
@ -1435,10 +1469,10 @@ Keyboard.prototype.setLED = function(control, f)
/** /**
* updateLEDs(bitState) * updateLEDs(bitState)
* *
* Updates any and all shift-related LEDs with the corresponding state in shiftStates. * Updates any and all shift-related LEDs with the corresponding state in bitsStateSim.
* *
* @this {Keyboard} * @this {Keyboard}
* @param {number} [bitState] is the bit in shiftStates that may have changed, if known; undefined if not * @param {number} [bitState] is the bit in bitsStateSim that may have changed, if known; undefined if not
*/ */
Keyboard.prototype.updateLEDs = function(bitState) Keyboard.prototype.updateLEDs = function(bitState)
{ {
@ -1447,7 +1481,7 @@ Keyboard.prototype.updateLEDs = function(bitState)
var id = "led-" + sBinding; var id = "led-" + sBinding;
var bitLED = Keyboard.LEDSTATES[sBinding]; var bitLED = Keyboard.LEDSTATES[sBinding];
if ((!bitState || bitState == bitLED) && (control = this.bindings[id])) { if ((!bitState || bitState == bitLED) && (control = this.bindings[id])) {
this.setLED(control, !!(this.shiftStates & bitLED)); this.setLED(control, !!(this.bitsStateSim & bitLED));
} }
} }
}; };
@ -1460,7 +1494,6 @@ Keyboard.prototype.updateLEDs = function(bitState)
*/ */
Keyboard.prototype.toggleCapsLock = function(event) Keyboard.prototype.toggleCapsLock = function(event)
{ {
this.updateShiftState(Keyboard.SIMCODE.CAPS_LOCK);
this.addActiveKey(Keyboard.SIMCODE.CAPS_LOCK, true); this.addActiveKey(Keyboard.SIMCODE.CAPS_LOCK, true);
}; };
@ -1472,7 +1505,6 @@ Keyboard.prototype.toggleCapsLock = function(event)
*/ */
Keyboard.prototype.toggleNumLock = function(event) Keyboard.prototype.toggleNumLock = function(event)
{ {
this.updateShiftState(Keyboard.SIMCODE.NUM_LOCK);
this.addActiveKey(Keyboard.SIMCODE.NUM_LOCK, true); this.addActiveKey(Keyboard.SIMCODE.NUM_LOCK, true);
}; };
@ -1484,31 +1516,44 @@ Keyboard.prototype.toggleNumLock = function(event)
*/ */
Keyboard.prototype.toggleScrollLock = function(event) Keyboard.prototype.toggleScrollLock = function(event)
{ {
this.updateShiftState(Keyboard.SIMCODE.SCROLL_LOCK);
this.addActiveKey(Keyboard.SIMCODE.SCROLL_LOCK, true); this.addActiveKey(Keyboard.SIMCODE.SCROLL_LOCK, true);
}; };
/** /**
* updateShiftState(simCode, fDown) * updateShiftState(simCode, fSim, fDown)
*
* For non-locking shift keys, this function is straightforward: when fDown is true, the corresponding bitState
* is set, and when fDown is false, it's cleared. However, for LOCK keys, fDown true means toggle, and fDown false
* means no change.
* *
* @this {Keyboard} * @this {Keyboard}
* @param {number} simCode (includes any ONDOWN and/or ONRIGHT modifiers) * @param {number} simCode (includes any ONDOWN and/or ONRIGHT modifiers)
* @param {boolean} [fDown] is true for down, false for up, undefined for toggle * @param {boolean} [fSim] is true to update simulated state only
* @param {boolean|null} [fDown] is true for down, false for up, undefined for toggle
* @return {boolean} true if simCode was a shift key, false if not * @return {boolean} true if simCode was a shift key, false if not
*/ */
Keyboard.prototype.updateShiftState = function(simCode, fDown) Keyboard.prototype.updateShiftState = function(simCode, fSim, fDown)
{ {
if (Keyboard.SIMCODES[simCode]) { if (Keyboard.SIMCODES[simCode]) {
var fRight = (Math.floor(simCode / 1000) & 2); var fRight = (Math.floor(simCode / 1000) & 2);
var bitState = Keyboard.KEYSTATES[simCode] || 0; var bitState = Keyboard.KEYSTATES[simCode] || 0;
if (bitState) { if (bitState) {
if (fRight) bitState >>= 1; if (fRight) bitState >>= 1;
if (fDown === undefined) { if (bitState & Keyboard.STATE.LOCK) {
fDown = !(this.shiftStates & bitState); if (fDown === false) return true;
fDown = null;
} }
this.shiftStates &= ~bitState; if (fDown == null) { // ie, null or undefined
if (fDown) this.shiftStates |= bitState; fDown = !((fSim? this.bitsStateSim : this.bitsState) & bitState);
}
if (!fSim) {
this.bitsState &= ~bitState;
if (fDown) this.bitsState |= bitState;
} else {
this.bitsStateSim &= ~bitState;
if (fDown) this.bitsStateSim |= bitState;
this.updateLEDs(bitState); this.updateLEDs(bitState);
}
return true; return true;
} }
} }
@ -1564,7 +1609,7 @@ Keyboard.prototype.addActiveKey = function(simCode, fPress)
if (i == this.aKeysActive.length) { if (i == this.aKeysActive.length) {
key = {}; key = {};
key.simCode = simCode; key.simCode = simCode;
key.shiftStates = this.shiftStates; key.bitsState = this.bitsState;
this.findBinding(simCode, "key", true); this.findBinding(simCode, "key", true);
i++; i++;
} }
@ -1591,11 +1636,11 @@ Keyboard.prototype.checkActiveKey = function()
/** /**
* checkActiveKeyShift() * checkActiveKeyShift()
* *
* @return {number|null} shiftStates for active key, null if none * @return {number|null} bitsState for active key, null if none
* *
Keyboard.prototype.checkActiveKeyShift = function() Keyboard.prototype.checkActiveKeyShift = function()
{ {
return this.aKeysActive.length? this.aKeysActive[0].shiftStates : null; return this.aKeysActive.length? this.aKeysActive[0].bitsState : null;
}; };
*/ */
@ -1737,15 +1782,19 @@ Keyboard.prototype.onKeyDown = function(event, fDown)
* receive here to a compatibly shifted simCode. * receive here to a compatibly shifted simCode.
*/ */
if (keyCode >= Keyboard.ASCII.A && keyCode <= Keyboard.ASCII.Z) { if (keyCode >= Keyboard.ASCII.A && keyCode <= Keyboard.ASCII.Z) {
if (!(this.shiftStates & (Keyboard.STATE.SHIFT | Keyboard.STATE.RSHIFT | Keyboard.STATE.CAPS_LOCK))) { if (!(this.bitsState & (Keyboard.STATE.SHIFT | Keyboard.STATE.RSHIFT | Keyboard.STATE.CAPS_LOCK))) {
simCode = keyCode + (Keyboard.ASCII.a - Keyboard.ASCII.A); simCode = keyCode + (Keyboard.ASCII.a - Keyboard.ASCII.A);
} }
} }
else if (this.shiftStates & (Keyboard.STATE.SHIFT | Keyboard.STATE.RSHIFT)) { else if (this.bitsState & (Keyboard.STATE.SHIFT | Keyboard.STATE.RSHIFT)) {
if (wCode = Keyboard.SHIFTED_KEYCODES[keyCode]) simCode = wCode; if (wCode = Keyboard.SHIFTED_KEYCODES[keyCode]) {
simCode = wCode;
}
} }
else { else {
if (wCode = Keyboard.STUPID_KEYCODES[keyCode]) simCode = wCode; if (wCode = Keyboard.STUPID_KEYCODES[keyCode]) {
simCode = wCode;
}
} }
if (Keyboard.SIMCODES[keyCode + Keyboard.KEYCODE.ONDOWN]) { if (Keyboard.SIMCODES[keyCode + Keyboard.KEYCODE.ONDOWN]) {
@ -1755,15 +1804,15 @@ Keyboard.prototype.onKeyDown = function(event, fDown)
simCode += Keyboard.KEYCODE.ONRIGHT; simCode += Keyboard.KEYCODE.ONRIGHT;
} }
if (this.updateShiftState(simCode, fDown)) { if (this.updateShiftState(simCode, false, fDown)) {
if (keyCode == Keyboard.KEYCODE.CAPS_LOCK || keyCode == Keyboard.KEYCODE.NUM_LOCK || keyCode == Keyboard.KEYCODE.SCROLL_LOCK) { if (keyCode == Keyboard.KEYCODE.CAPS_LOCK || keyCode == Keyboard.KEYCODE.NUM_LOCK || keyCode == Keyboard.KEYCODE.SCROLL_LOCK) {
/* /*
* FYI, "lock" keys generate a "down" event ONLY when getting locked, and an "up" event ONLY * FYI, "lock" keys generate a "down" event ONLY when getting locked and an "up" event ONLY
* when getting unlocked--which is a little odd, since the key did actually go UP and DOWN each time. * when getting unlocked--which is a little odd, since the key did go UP and DOWN each time.
* *
* We must treat each event like a "down" -- and as a "press", so that addActiveKey() will automatically * We must treat each event like a "down", and also as a "press", so that addActiveKey() will
* generate both the "make" and "break". * automatically generate both the "make" and "break".
*/ */
fDown = fPress = true; fDown = fPress = true;
} }
@ -1774,43 +1823,58 @@ Keyboard.prototype.onKeyDown = function(event, fDown)
/* /*
* HACK for simulating Ctrl-Break using Ctrl-Del (Mac) / Ctrl-Backspace (Windows) * HACK for simulating Ctrl-Break using Ctrl-Del (Mac) / Ctrl-Backspace (Windows)
*/ */
if (keyCode == Keyboard.KEYCODE.BS && (this.shiftStates & (Keyboard.STATE.CTRL|Keyboard.STATE.ALT)) == Keyboard.STATE.CTRL) { if (keyCode == Keyboard.KEYCODE.BS && (this.bitsState & (Keyboard.STATE.CTRL|Keyboard.STATE.ALT)) == Keyboard.STATE.CTRL) {
simCode = Keyboard.SIMCODE.CTRL_BREAK; simCode = Keyboard.SIMCODE.CTRL_BREAK;
} }
/* /*
* If I don't consume TAB on the "down" event, then that's all I'll see, because the * If I don't consume TAB on the "down" event, then that's all I'll see, because the browser will
* browser will see it and give focus to the next control. But the "down" side is that * see it and give focus to the next control. But the "down" side is that * that no "press" event will
* that no "press" event will be generated. This puts it in the same category as ESC, * be generated. This puts it in the same category as ESC, which also generates "down" and "up" events
* which also generates "down" and "up" events (LOTS of "down" events for that matter), * (LOTS of "down" events for that matter), but no "press" event.
* but no "press" event.
* *
* I don't get keyPress events for ESC (why?) and I never want the browser to act on BS * I don't get keyPress events for ESC (why?) and I never want the browser to act on BS (which does
* (which does double-duty as the "Back" button and leaves the current page). * double-duty as the "Back" button and leaves the current page).
*/ */
fPass = false; fPass = false;
} }
} }
} }
else { else {
if (keyCode == Keyboard.KEYCODE.COMMAND) { if (keyCode == Keyboard.KEYCODE.CMD) {
/* /*
* Avoid interfering with useful Browser key commands, like COMMAND-Q, COMMAND-T, etc. * Avoid interfering with useful Browser key commands, like CMD-Q, CMD-T, etc.
*/ */
this.shiftStates &= ~Keyboard.STATE.COMMAND; this.bitsState &= ~Keyboard.STATE.CMD;
if (fDown) this.shiftStates |= Keyboard.STATE.COMMAND; if (fDown) this.bitsState |= Keyboard.STATE.CMD;
fAutoClear = false; fAutoClear = false;
} }
} }
if (fAutoClear) { if (fAutoClear) {
/* /*
* When you use a command like COMMAND-T, I see the COMMAND key going down, but not going up, * When you use a command like CMD-T, I see the CMD key going down, but not going up,
* so I think the COMMAND key is still down and ignore all input; to easily get out of that state, * so I think the CMD key is still down and ignore all input; to easily get out of that state,
* I clear our internal BIT_COMMAND whenever I see ANY key go up (well, ALMOST any key; cases * I clear our internal STATE.CMD whenever I see ANY key go up (well, ALMOST any key; cases
* above that explicitly clear fAutoClear -- such as the COMMAND key itself -- are exceptions * above that explicitly clear fAutoClear -- such as the CMD key itself -- are exceptions
* to the rule). * to the rule).
*/ */
this.shiftStates &= ~Keyboard.STATE.COMMAND; this.bitsState &= ~Keyboard.STATE.CMD;
}
/*
* I used to be more "cautious" in calling preventDefault(), but there are a number of common key
* sequences that interfere with our machines; for example, the up/down arrows have a "default" behavior
* of scrolling the web page up and down, which is definitely NOT a behavior we want. Similarly, if I
* have defined system-wide ctrl-key sequences to perform common editing operations (eg, ctrl-w and ctrl-z
* to scroll pages of text), the browser allows those operations to kick in as well.
*
* So now I've taken a more aggressive approach: any simCode that exists in the SIMCODES table (not just
* those with the ONDOWN "bit" set) is assumed to be one that we should invoke preventDefault() for as well.
*
* TODO: Consider whether we should set fPass to false as well. I'm still treating that cautiously.
*/
if (Keyboard.SIMCODES[simCode]) {
event.preventDefault();
} }
if (DEBUG && this.messageEnabled(Debugger.MESSAGE.KEYS)) { if (DEBUG && this.messageEnabled(Debugger.MESSAGE.KEYS)) {
@ -1873,11 +1937,11 @@ Keyboard.prototype.onKeyPress = function(event)
fPass = false; fPass = false;
} }
else { else {
if (this.shiftStates & Keyboard.STATE.COMMAND) { if (this.bitsState & Keyboard.STATE.CMD) {
this.shiftStates &= ~Keyboard.STATE.COMMAND; this.bitsState &= ~Keyboard.STATE.CMD;
} else { } else {
// if (DEBUG && event.altKey) this.messageDebugger("ALT press: keyCode " + keyCode, Debugger.MESSAGE.KEYS); // if (DEBUG && event.altKey) this.messageDebugger("ALT press: keyCode " + keyCode, Debugger.MESSAGE.KEYS);
if (this.shiftStates & (Keyboard.STATE.CTRL | Keyboard.STATE.ALT)) { if (this.bitsState & (Keyboard.STATE.CTRL | Keyboard.STATE.ALT)) {
fPass = false; fPass = false;
} else { } else {
if (this.fEscapeDisabled && keyCode == Keyboard.ASCII['`']) keyCode = Keyboard.KEYCODE.ESC; if (this.fEscapeDisabled && keyCode == Keyboard.ASCII['`']) keyCode = Keyboard.KEYCODE.ESC;
@ -1910,7 +1974,7 @@ Keyboard.prototype.keySimulate = function(simCode, fDown)
{ {
var fSimulated = false; var fSimulated = false;
// this.updateShiftState(simCode, fDown); this.updateShiftState(simCode, true, fDown);
var wCode = Keyboard.SIMCODES[simCode] || Keyboard.SIMCODES[simCode + Keyboard.KEYCODE.ONDOWN]; var wCode = Keyboard.SIMCODES[simCode] || Keyboard.SIMCODES[simCode + Keyboard.KEYCODE.ONDOWN];
@ -1921,7 +1985,7 @@ Keyboard.prototype.keySimulate = function(simCode, fDown)
* whenever both CTRL and ALT are pressed as well, so that it's easier to simulate that old favorite: CTRL-ALT-DEL * whenever both CTRL and ALT are pressed as well, so that it's easier to simulate that old favorite: CTRL-ALT-DEL
*/ */
if (wCode == Keyboard.SCANCODE.BS) { if (wCode == Keyboard.SCANCODE.BS) {
if ((this.shiftStates & (Keyboard.STATE.CTRL | Keyboard.STATE.ALT)) == (Keyboard.STATE.CTRL | Keyboard.STATE.ALT)) { if ((this.bitsState & (Keyboard.STATE.CTRL | Keyboard.STATE.ALT)) == (Keyboard.STATE.CTRL | Keyboard.STATE.ALT)) {
wCode = Keyboard.SCANCODE.NUM_DEL; wCode = Keyboard.SCANCODE.NUM_DEL;
} }
} }
@ -1945,17 +2009,17 @@ Keyboard.prototype.keySimulate = function(simCode, fDown)
continue; continue;
} }
if (bScan == Keyboard.SCANCODE.SHIFT) { if (bScan == Keyboard.SCANCODE.SHIFT) {
if (!(this.shiftStates & (Keyboard.STATE.SHIFT | Keyboard.STATE.RSHIFT))) { if (!(this.bitsState & (Keyboard.STATE.SHIFT | Keyboard.STATE.RSHIFT))) {
if (!(this.shiftStates & Keyboard.STATE.CAPS_LOCK) || !fAlpha) { if (!(this.bitsState & Keyboard.STATE.CAPS_LOCK) || !fAlpha) {
bShift = bScan; bShift = bScan;
} }
} }
} else if (bScan == Keyboard.SCANCODE.CTRL) { } else if (bScan == Keyboard.SCANCODE.CTRL) {
if (!(this.shiftStates & (Keyboard.STATE.CTRL | Keyboard.STATE.RCTRL))) { if (!(this.bitsState & (Keyboard.STATE.CTRL | Keyboard.STATE.RCTRL))) {
bShift = bScan; bShift = bScan;
} }
} else if (bScan == Keyboard.SCANCODE.ALT) { } else if (bScan == Keyboard.SCANCODE.ALT) {
if (!(this.shiftStates & (Keyboard.STATE.ALT | Keyboard.STATE.RALT))) { if (!(this.bitsState & (Keyboard.STATE.ALT | Keyboard.STATE.RALT))) {
bShift = bScan; bShift = bScan;
} }
} else { } else {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long