viewer-side keymap

it's useful when dealing with broken vnc server implementations,
namely qemu/kvm.
This commit is contained in:
Hirokazu Takahashi 2012-12-11 20:05:06 +09:00
parent 77c250c442
commit e37086c1e1
6 changed files with 6487 additions and 4 deletions

View File

@ -17,6 +17,7 @@ is not limited to):
include/vnc.js include/vnc.js
include/websock.js include/websock.js
include/webutil.js include/webutil.js
include/keymap.js
The HTML, CSS, font and images files that included with the noVNC The HTML, CSS, font and images files that included with the noVNC
source distibution (or repository) are not considered part of the source distibution (or repository) are not considered part of the

6407
include/keymap.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -126,7 +126,9 @@ var that = {}, // Public API methods
mouse_buttonMask = 0, mouse_buttonMask = 0,
mouse_arr = [], mouse_arr = [],
viewportDragging = false, viewportDragging = false,
viewportDragPos = {}; viewportDragPos = {},
keymap = null;
// Configuration attributes // Configuration attributes
Util.conf_defaults(conf, that, defaults, [ Util.conf_defaults(conf, that, defaults, [
@ -604,6 +606,11 @@ keyPress = function(keysym, down, km) {
if (conf.view_only) { return; } // View only, skip keyboard events if (conf.view_only) { return; } // View only, skip keyboard events
// Remap the modifiers with apppropriate ones depending on the
// keyboard type of the vnc server if this is a presskey event.
if (!!keymap && down === 2)
km = remapModifiers(km, keysym);
// Generate all modifier keys' events on demand. // Generate all modifier keys' events on demand.
// Send key events for modifiers to the vnc server when: // Send key events for modifiers to the vnc server when:
// 1. the status of modifers have been changed // 1. the status of modifers have been changed
@ -639,10 +646,44 @@ keyPress = function(keysym, down, km) {
// This is a keydown or keyup event. // This is a keydown or keyup event.
arr = arr.concat(keyEvent(keysym, down)); arr = arr.concat(keyEvent(keysym, down));
} }
if (!!keymap && down === 2) {
// Turn off SHIFT and ALTGR every time when emulating modifiers.
if (remote_status.shift) {
arr = arr.concat(keyEvent(0xFFE1, 0)); // SHIFT up
remote_status.shift = false;
}
if (remote_status.altgr) {
arr = arr.concat(keyEvent(0xFE03, 0)); // ALTGR up
remote_status.altgr = false;
}
}
arr = arr.concat(fbUpdateRequests()); arr = arr.concat(fbUpdateRequests());
ws.send(arr); ws.send(arr);
}; };
// Remap the modifiers depending on keyboard types. It depends on keyboards
// whether a certain keycode should be issued with modifiers. For example,
// the US keyboard issues '@' with SHIFT, the Japanese keyboard issues '@'
// without any modifers, and the German keyboard issues '@' with ALTGR.
// When the keyboard types are different between the viewer and the vnc
// server, the server is expected to generate fake modifer key events
// if needed. But unfortunately there are a lot of VNC servers that can't
// handle this. Then there is no choice but to make noVNC take care of it
// on behalf of them.
function remapModifiers(km, keysym)
{
var remap = {'altKey': km.altKey, 'ctrlKey': km.ctrlKey,
'shiftKey': km.shiftKey, 'altgrKey': km.altgrKey};
var key = keymap[keysym];
if (typeof key !== "undefined") {
remap.altgrKey = key.altgr;
remap.shiftKey = key.shift;
}
return remap;
}
mouseButton = function(x, y, down, bmask) { mouseButton = function(x, y, down, bmask) {
if (down) { if (down) {
mouse_buttonMask |= bmask; mouse_buttonMask |= bmask;
@ -1897,6 +1938,14 @@ that.testMode = function(override_send, data_mode) {
}; };
}; };
that.setKeymap = function(kb) {
if (!kb || kb === 'default') {
keymap = null;
} else {
keymap = Kmap.getKeymap(kb);
}
};
return constructor(); // Return the public API interface return constructor(); // Return the public API interface

View File

@ -13,7 +13,8 @@
// Load supporting scripts // Load supporting scripts
window.onscriptsload = function () { UI.load(); }; window.onscriptsload = function () { UI.load(); };
Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js", Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
"input.js", "display.js", "jsunzip.js", "rfb.js"]); "input.js", "display.js", "jsunzip.js", "rfb.js",
"keymap.js"]);
var UI = { var UI = {
@ -31,7 +32,7 @@ load: function (callback) {
// Render default UI and initialize settings menu // Render default UI and initialize settings menu
start: function(callback) { start: function(callback) {
var html = '', i, sheet, sheets, llevels; var html = '', i, sheet, sheets, llevels, kbtypes;
// Stylesheet selection dropdown // Stylesheet selection dropdown
sheet = WebUtil.selectStylesheet(); sheet = WebUtil.selectStylesheet();
@ -46,6 +47,19 @@ start: function(callback) {
UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]); UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]);
} }
// Keyboard type selection dropdown
kbtypes = ['default', 'ar', 'bepo', 'da', 'de', 'de-ch', 'en-gb',
'en-us', 'es', 'et', 'fi', 'fo', 'fr', 'fr-be', 'fr-ca',
'fr-ch', 'hr', 'hu', 'is', 'it', 'ja', 'lt', 'lv', 'mk',
'nl', 'nl-be', 'no', 'pl', 'pt', 'pt-br', 'ru', 'sl',
'sv', 'th', 'tr'];
for (i = 0; i < kbtypes.length; i += 1) {
UI.addOption($D('noVNC_keymap'), kbtypes[i], kbtypes[i]);
}
UI.initSetting('keymap', 'default');
// Settings with immediate effects // Settings with immediate effects
UI.initSetting('logging', 'warn'); UI.initSetting('logging', 'warn');
WebUtil.init_logging(UI.getSetting('logging')); WebUtil.init_logging(UI.getSetting('logging'));
@ -71,6 +85,7 @@ start: function(callback) {
UI.rfb = RFB({'target': $D('noVNC_canvas'), UI.rfb = RFB({'target': $D('noVNC_canvas'),
'onUpdateState': UI.updateState, 'onUpdateState': UI.updateState,
'onClipboard': UI.clipReceive}); 'onClipboard': UI.clipReceive});
UI.rfb.setKeymap(UI.getSetting('keymap'));
UI.updateVisualState(); UI.updateVisualState();
// Unfocus clipboard when over the VNC area // Unfocus clipboard when over the VNC area
@ -326,6 +341,7 @@ toggleSettingsPanel: function() {
UI.updateSetting('repeaterID'); UI.updateSetting('repeaterID');
UI.updateSetting('stylesheet'); UI.updateSetting('stylesheet');
UI.updateSetting('logging'); UI.updateSetting('logging');
UI.updateSetting('keymap');
UI.openSettingsMenu(); UI.openSettingsMenu();
} }
@ -370,8 +386,10 @@ settingsApply: function() {
UI.saveSetting('repeaterID'); UI.saveSetting('repeaterID');
UI.saveSetting('stylesheet'); UI.saveSetting('stylesheet');
UI.saveSetting('logging'); UI.saveSetting('logging');
UI.saveSetting('keymap');
// Settings with immediate (non-connected related) effect // Settings with immediate (non-connected related) effect
UI.rfb.setKeymap(UI.getSetting('keymap'));
WebUtil.selectStylesheet(UI.getSetting('stylesheet')); WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
WebUtil.init_logging(UI.getSetting('logging')); WebUtil.init_logging(UI.getSetting('logging'));
UI.setViewClip(); UI.setViewClip();

View File

@ -138,6 +138,12 @@
<select id="noVNC_logging" name="vncLogging"> <select id="noVNC_logging" name="vncLogging">
</select></label> </select></label>
</li> </li>
<!-- keyboard selection dropdown -->
<li><label><strong>Keyboard: </strong>
<select id="noVNC_keymap" name="vncKeymap">
</select></label>
</li>
<hr> <hr>
<li><input type="button" id="noVNC_apply" value="Apply"></li> <li><input type="button" id="noVNC_apply" value="Apply"></li>
</ul> </ul>

View File

@ -43,7 +43,8 @@
// Load supporting scripts // Load supporting scripts
Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js", Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
"input.js", "display.js", "jsunzip.js", "rfb.js"]); "input.js", "display.js", "jsunzip.js", "rfb.js",
"keymap.js"]);
var rfb; var rfb;
@ -126,6 +127,7 @@
'view_only': WebUtil.getQueryVar('view_only', false), 'view_only': WebUtil.getQueryVar('view_only', false),
'updateState': updateState, 'updateState': updateState,
'onPasswordRequired': passwordRequired}); 'onPasswordRequired': passwordRequired});
rfb.setKeymap(WebUtil.getQueryVar('keymap', 'default'));
rfb.connect(host, port, password, path); rfb.connect(host, port, password, path);
}; };
</script> </script>