Refactor console keyboard handling for better international keyboard layout support, allow building of mapping table entry without code logic

This commit is contained in:
Kelven Yang 2012-09-28 17:33:17 -07:00
parent 11fe086ada
commit 5c60f8e480

View File

@ -63,31 +63,50 @@ function getCurrentLanguage() {
//
function KeyboardMapper() {
this.mappedInput = [];
this.jsX11KeysymMap = [];
this.jsKeyPressX11KeysymMap = [];
}
KeyboardMapper.prototype = {
inputFeed : function(eventType, code, modifiers) {
this.mappedInput.push({type: eventType, code: code, modifiers: modifiers});
},
getMappedInput : function() {
var mappedInput = this.mappedInput;
this.mappedInput = [];
return mappedInput;
},
isModifierInput : function(code) {
return $.inArray(code, [AjaxViewer.ALT_KEY_MASK, AjaxViewer.SHIFT_KEY_MASK, AjaxViewer.CTRL_KEY_MASK, AjaxViewer.META_KEY_MASK]) >= 0;
}
};
/////////////////////////////////////////////////////////////////////////////
// JsX11KeyboardMapper
//
function JsX11KeyboardMapper() {
KeyboardMapper.apply(this, arguments);
// RAW keyboard
// Primarily translates KeyDown/KeyUp event, either as is (if there is no mapping entry)
// or through mapped result.
//
// For KeyPress event, it translates it only if there exist a mapping entry
// in jsX11KeysymMap map and the entry meets the condition
//
// COOKED keyboard
// Primarily translates KeyPress event, either as is or through mapped result
// It translates KeyDown/KeyUp only there exists a mapping entry, or if there
// is no mapping entry, translate when certain modifier key is pressed (i.e.,
// CTRL or ALT key
//
// Mapping entry types
// direct : will be directly mapped into the entry value with the same event type
// boolean : only valid for jsX11KeysymMap, existence of this type, no matter true or false
// in value, corresponding KeyDown/KeyUp event will be masked
// array : contains a set of conditional mapping entry
//
// Conditional mapping entry
//
// {
// type: <event type>, code: <mapped key code>, modifiers: <modifiers>,
// shift : <shift state match condition>, -- match on shift state
// guestos : <guest os match condition>, -- match on guestos type
// browser: <browser type match condition>, -- match on browser
// browserVersion: <brower version match condition> -- match on browser version
// }
//
KeyboardMapper.KEYBOARD_TYPE_RAW = 0;
KeyboardMapper.KEYBOARD_TYPE_COOKED = 1;
this.jsX11KeysymMap = [];
KeyboardMapper.prototype = {
setKeyboardType : function(keyboardType) {
this.keyboardType = keyboardType;
if(keyboardType == KeyboardMapper.KEYBOARD_TYPE_RAW) {
// intialize keyboard mapping for RAW keyboard
this.jsX11KeysymMap[AjaxViewer.JS_KEY_CAPSLOCK] = AjaxViewer.X11_KEY_CAPSLOCK;
this.jsX11KeysymMap[AjaxViewer.JS_KEY_BACKSPACE] = AjaxViewer.X11_KEY_BACKSPACE;
this.jsX11KeysymMap[AjaxViewer.JS_KEY_TAB] = AjaxViewer.X11_KEY_TAB;
@ -165,67 +184,8 @@ function JsX11KeyboardMapper() {
{type: AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: true },
{type: AjaxViewer.KEY_UP, code: AjaxViewer.X11_KEY_ADD, modifiers: 0, shift: true }
];
}
JsX11KeyboardMapper.prototype = new KeyboardMapper();
JsX11KeyboardMapper.prototype.inputFeed = function(eventType, code, modifiers) {
if(eventType == AjaxViewer.KEY_DOWN || eventType == AjaxViewer.KEY_UP) {
// special handling for Alt + Ctrl + Ins, convert it into Alt-Ctrl-Del
if(code == AjaxViewer.JS_KEY_INSERT) {
if((modifiers & AjaxViewer.ALT_KEY_MASK) != 0 && (modifiers & AjaxViewer.CTRL_KEY_MASK) != 0) {
this.mappedInput.push({type : eventType, code: 0xffff, modifiers: modifiers});
return;
}
}
var X11Keysym = code;
if(this.jsX11KeysymMap[code] != undefined) {
X11Keysym = this.jsX11KeysymMap[code];
if(typeof this.jsX11KeysymMap[code] == "boolean") {
return;
} else if($.isArray(X11Keysym)) {
for(var i = 0; i < X11Keysym.length; i++) {
if(X11Keysym[i].type == eventType) {
this.mappedInput.push(X11Keysym[i]);
}
}
} else {
this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers});
}
} else {
this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers});
}
// special handling for ALT/CTRL key
if(eventType == AjaxViewer.KEY_UP && (code == AjaxViewer.JS_KEY_ALT || code == code == AjaxViewer.JS_KEY_CTRL))
this.mappedInput.push({type : eventType, code: this.jsX11KeysymMap[code], modifiers: modifiers});
} else if(eventType == AjaxViewer.KEY_PRESS) {
var X11Keysym = code;
X11Keysym = this.jsKeyPressX11KeysymMap[code];
if(X11Keysym) {
if($.isArray(X11Keysym)) {
for(var i = 0; i < X11Keysym.length; i++) {
var shift = ((modifiers & AjaxViewer.SHIFT_KEY_MASK) != 0 ? true : false);
if(!(X11Keysym[i].shift ^ shift))
this.mappedInput.push(X11Keysym[i]);
}
} else {
this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers});
}
}
}
}
/////////////////////////////////////////////////////////////////////////////
//JsCookedKeyboardMapper
// For Xen/KVM hypervisors, it accepts "cooked" keyborad events
//
function JsCookedKeyboardMapper() {
KeyboardMapper.apply(this, arguments);
this.jsX11KeysymMap = [];
// initialize mapping for COOKED keyboard
this.jsX11KeysymMap[AjaxViewer.JS_KEY_CAPSLOCK] = AjaxViewer.X11_KEY_CAPSLOCK;
this.jsX11KeysymMap[AjaxViewer.JS_KEY_BACKSPACE] = AjaxViewer.X11_KEY_BACKSPACE;
this.jsX11KeysymMap[AjaxViewer.JS_KEY_TAB] = AjaxViewer.X11_KEY_TAB;
@ -256,10 +216,10 @@ function JsCookedKeyboardMapper() {
this.jsX11KeysymMap[AjaxViewer.JS_KEY_SHIFT] = AjaxViewer.X11_KEY_SHIFT;
this.jsX11KeysymMap[AjaxViewer.JS_KEY_CTRL] = AjaxViewer.X11_KEY_CTRL;
this.jsX11KeysymMap[AjaxViewer.JS_KEY_ALT] = AjaxViewer.X11_KEY_ALT;
}
}
},
JsCookedKeyboardMapper.prototype = new KeyboardMapper();
JsCookedKeyboardMapper.prototype.inputFeed = function(eventType, code, modifiers) {
RawkeyboardInputHandler : function(eventType, code, modifiers, guestos, browser, browserVersion) {
if(eventType == AjaxViewer.KEY_DOWN || eventType == AjaxViewer.KEY_UP) {
// special handling for Alt + Ctrl + Ins, convert it into Alt-Ctrl-Del
@ -277,7 +237,7 @@ JsCookedKeyboardMapper.prototype.inputFeed = function(eventType, code, modifiers
return;
} else if($.isArray(X11Keysym)) {
for(var i = 0; i < X11Keysym.length; i++) {
if(X11Keysym[i].type == eventType) {
if(this.isConditionalEntryMatched(eventType, code, modifiers, X11Keysym[i], guestos, browser, browserVersion)) {
this.mappedInput.push(X11Keysym[i]);
}
}
@ -285,7 +245,57 @@ JsCookedKeyboardMapper.prototype.inputFeed = function(eventType, code, modifiers
this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers});
}
} else {
if((modifiers & AjaxViewer.CTRL_KEY_MASK) != 0) {
this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers});
}
// special handling for ALT/CTRL key
if(eventType == AjaxViewer.KEY_UP && (code == AjaxViewer.JS_KEY_ALT || code == code == AjaxViewer.JS_KEY_CTRL))
this.mappedInput.push({type : eventType, code: this.jsX11KeysymMap[code], modifiers: modifiers});
} else if(eventType == AjaxViewer.KEY_PRESS) {
var X11Keysym = code;
X11Keysym = this.jsKeyPressX11KeysymMap[code];
if(X11Keysym) {
if($.isArray(X11Keysym)) {
for(var i = 0; i < X11Keysym.length; i++) {
if(this.isConditionalEntryMatched(eventType, code, modifiers, X11Keysym[i], guestos, browser))
this.mappedInput.push(X11Keysym[i]);
}
} else {
this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: X11Keysym, modifiers: modifiers});
this.mappedInput.push({type : AjaxViewer.KEY_UP, code: X11Keysym, modifiers: modifiers});
}
}
}
},
CookedKeyboardInputHandler : function(eventType, code, modifiers, guestos, browser, browserVersion) {
if(eventType == AjaxViewer.KEY_DOWN || eventType == AjaxViewer.KEY_UP) {
// special handling for Alt + Ctrl + Ins, convert it into Alt-Ctrl-Del
if(code == AjaxViewer.JS_KEY_INSERT) {
if((modifiers & AjaxViewer.ALT_KEY_MASK) != 0 && (modifiers & AjaxViewer.CTRL_KEY_MASK) != 0) {
this.mappedInput.push({type : eventType, code: 0xffff, modifiers: modifiers});
return;
}
}
var X11Keysym = code;
if(this.jsX11KeysymMap[code] != undefined) {
X11Keysym = this.jsX11KeysymMap[code];
if(typeof this.jsX11KeysymMap[code] == "boolean") {
return;
} else if($.isArray(X11Keysym)) {
for(var i = 0; i < X11Keysym.length; i++) {
if(this.isConditionalEntryMatched(eventType, code, modifiers, X11Keysym[i], guestos, browser, browserVersion)) {
this.mappedInput.push(X11Keysym[i]);
}
}
} else {
this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers});
}
} else {
if((modifiers & (AjaxViewer.CTRL_KEY_MASK | AjaxViewer.ALT_KEY_MASK)) != 0) {
this.mappedInput.push({type : eventType, code: X11Keysym, modifiers: modifiers});
}
}
@ -295,8 +305,6 @@ JsCookedKeyboardMapper.prototype.inputFeed = function(eventType, code, modifiers
this.mappedInput.push({type : eventType, code: this.jsX11KeysymMap[code], modifiers: modifiers});
} else if(eventType == AjaxViewer.KEY_PRESS) {
var X11Keysym = code;
// special handling for * and + key on number pad
if(code == AjaxViewer.JS_NUMPAD_MULTIPLY) {
this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: AjaxViewer.X11_KEY_SHIFT, modifiers: modifiers});
@ -318,10 +326,78 @@ JsCookedKeyboardMapper.prototype.inputFeed = function(eventType, code, modifiers
if(code == AjaxViewer.JS_KEY_ENTER || code == AjaxViewer.JS_KEY_BACKSPACE)
return;
if(code > 0) {
var X11Keysym = code;
X11Keysym = this.jsKeyPressX11KeysymMap[code];
if(X11Keysym) {
if($.isArray(X11Keysym)) {
for(var i = 0; i < X11Keysym.length; i++) {
if(this.isConditionalEntryMatched(eventType, code, modifiers, X11Keysym[i], guestos, browser))
this.mappedInput.push(X11Keysym[i]);
}
} else {
this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: X11Keysym, modifiers: modifiers});
this.mappedInput.push({type : AjaxViewer.KEY_UP, code: X11Keysym, modifiers: modifiers});
}
}
} else {
// if there is no mappting entry, use the JS keypress code directly
this.mappedInput.push({type : AjaxViewer.KEY_DOWN, code: code, modifiers: modifiers});
this.mappedInput.push({type : AjaxViewer.KEY_UP, code: code, modifiers: modifiers});
}
}
}
},
inputFeed : function(eventType, code, modifiers, guestos, browser, browserVersion) {
if(this.keyboardType == KeyboardMapper.KEYBOARD_TYPE_RAW)
this.RawkeyboardInputHandler(eventType, code, modifiers, guestos, browser, browserVersion);
else
this.CookedKeyboardInputHandler(eventType, code, modifiers, guestos, browser, browserVersion);
},
getMappedInput : function() {
var mappedInput = this.mappedInput;
this.mappedInput = [];
return mappedInput;
},
isConditionalEntryMatched : function(eventType, code, modifiers, entry, guestos, browser, browserVersion) {
if(eventType == AjaxViewer.KEY_DOWN || eventType == AjaxViewer.KEY_UP) {
// for KeyDown/KeyUp events, we require that the type in entry should match with
// the real input
if(entry.type != eventType)
return false;
}
// check conditional match
if(entry.shift != undefined) {
var shift = ((modifiers & AjaxViewer.SHIFT_KEY_MASK) != 0 ? true : false);
if(entry.shift ^ shift)
return false;
}
if(entry.guestos != undefined) {
if(entry.guestos != guestos)
return false;
}
if(entry.browser != undefined) {
if(entry.browser != browser)
return false;
}
if(entry.browserVersion != undefined) {
if(entry.browserVersion != browserVersion)
return false;
}
return true;
},
isModifierInput : function(code) {
return $.inArray(code, [AjaxViewer.ALT_KEY_MASK, AjaxViewer.SHIFT_KEY_MASK, AjaxViewer.CTRL_KEY_MASK, AjaxViewer.META_KEY_MASK]) >= 0;
}
};
/////////////////////////////////////////////////////////////////////////////
// class AjaxViewer
@ -653,12 +729,14 @@ AjaxViewer.prototype = {
setupKeyboardTranslationTable : function() {
this.keyboardMappers = [];
// this.keyboardMappers[AjaxViewer.KEYBOARD_TYPE_ENGLISH] = new JsX11KeyboardMapper();
this.keyboardMappers[AjaxViewer.KEYBOARD_TYPE_ENGLISH] = new JsCookedKeyboardMapper();
// setup Japanese keyboard translation table
var mapper = new JsX11KeyboardMapper();
var mapper = new KeyboardMapper();
this.keyboardMappers[AjaxViewer.KEYBOARD_TYPE_ENGLISH] = mapper;
mapper.setKeyboardType(KeyboardMapper.KEYBOARD_TYPE_COOKED);
mapper = new KeyboardMapper();
this.keyboardMappers[AjaxViewer.KEYBOARD_TYPE_JAPANESE] = mapper;
mapper.setKeyboardType(KeyboardMapper.KEYBOARD_TYPE_RAW);
// JP keyboard plugged in a English host OS
/*
@ -1288,7 +1366,7 @@ AjaxViewer.prototype = {
dispatchKeyboardInput : function(event, code, modifiers) {
var keyboardMapper = ajaxViewer.getCurrentKeyboardMapper();
keyboardMapper.inputFeed(event, code, modifiers);
keyboardMapper.inputFeed(event, code, modifiers, this.guestos, $.browser, $.browser.version);
this.dispatchMappedKeyboardInput(keyboardMapper.getMappedInput());
},