首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

在基于 Web 的 VNC 应用程序中支持多种键盘布局-3

在基于 Web 的 VNC 应用程序中支持多种键盘布局-3

NumLock 的独特情况:键符发挥作用的时刻我提到过键符基本上被忽略。在至少一种情况下,QEMU VNC                服务器考虑键符:当使用数字键盘 (Numpad) 中的键时。
在我的解决方案的第一个实现中(忽略 QEMU KeyEvent 消息的键符字段),出现了一种奇怪的行为:当按下任何多用途数字键盘键时,比如                0、1、2、3、4、6、7、8、9 或小数点(en_US 布局中的句点),即使虚拟机和客户端上的 NumLock 状态为                ON,QEMU VNC 服务器也会:
  • 将虚拟机的 NumLock 状态更改为 OFF(如果它为 ON)
  • 按键
例如,在客户端和虚拟机上的 NumLock 状态为 ON 时按数字键盘键 8,会将虚拟机中的 NumLock 状态更改为                OFF,然后执行向上箭头键的操作。在 NumLock 状态为 OFF 时按数字键盘键 8                的行为才是符合预期的。
此问题可通过可靠方式利用客户端和虚拟机的 NumLock 状态来解决。但远程 QEMU VNC 服务器不可能知道客户端键盘的 NumLock                状态。服务器可以看到何时按下/释放 NumLock 键,但无从了解当前的 NumLock 状态,因为 QEMU VNC                KeyEvent 消息未传递该信息。
经过在桌面 VNC 客户端上广泛测试后,我认识到在这些环境中发送了键符。尽管键码不会基于 NumLock                状态而发生更改,但键符会受到影响。结论是,QEMU VNC 服务器使用键符字段来猜测客户端的 NumLock                状态,并采取相应行动来尝试同步虚拟机状态。在实现中,发送的键符为 0 时,服务器将此解释为 “客户端的 NumLock 状态为                OFF”,强制将客户端 NumLock 状态更改为 OFF,然后发送按下的键码。
因为如果不发送键符,会默认为 NumLock 状态为 OFF,所以解决方案是仅在 NumLock 状态为                ON 时发送键符。
发送数字键盘的键符生成键符的键盘事件是 keypressed 事件,我的解决方案忽略了该事件。那么如何将键符应用于 QEMU                KeyEvent 消息?
幸运的是,确定键符不是一定需要 keypressed                事件。数字键盘在所有布局中都是标准的(否则,如果没有键盘布局图,QEMU VNC 服务器就无法猜测 NumLock                状态)。所以,数字键盘键的键符值可预先确定。
这就留下了一个问题,如果不使用 keypress 事件,如何区分数字键 7 用作 Home 键的情况和用作数字 7                的情况。我的实现使用了 KeyboardEvent.keyCode 属性(在 keydown                事件上设置)来进行区分,如下面的代码片段所示。
下面的函数接收一个键盘事件 evt,并将 KeyboardEvent.code                值与属于数字键盘的值相比较:
function isNumPadMultiKey(evt) {
    var numPadCodes = ["Numpad0", "Numpad1", "Numpad2",
        "Numpad3", "Numpad4", "Numpad5", "Numpad6",
        "Numpad7", "Numpad8", "Numpad9", "NumpadDecimal"];
    return (numPadCodes.indexOf(evt.code) !== -1);
}




我使用前面的函数来查看是否需要对某个指定的键盘事件进行任何特殊处理。
下面的函数接收一个键盘事件 evt,并将它的 keyboardevent.keyCode                属性与一个名为 numLockOnKeyCodes 的预定义值集相比较:
function getNumPadKeySym(evt) {
    var numLockOnKeySyms = {
        "Numpad0": 0xffb0, "Numpad1": 0xffb1, "Numpad2": 0xffb2,
        "Numpad3": 0xffb3, "Numpad4": 0xffb4, "Numpad5": 0xffb5,
        "Numpad6": 0xffb6, "Numpad7": 0xffb7, "Numpad8": 0xffb8,
        "Numpad9": 0xffb9, "NumpadDecimal": 0xffac
    };
    var numLockOnKeyCodes = [96, 97, 98, 99, 100, 101, 102,
        103, 104, 105, 108, 110];

    if (numLockOnKeyCodes.indexOf(evt.keyCode) !== -1) {
        return numLockOnKeySyms[evt.code];
    }
    return 0;




在 NumLock ON 状态下,numLockOnKeyCodes 值对应于数字键盘键 0 到                9 和小数点。如果 evt.keyCode 是这些值之一,那么该函数会返回                numLockOnKeySyms 提供的等效键符;否则,它会返回 0。
以下是在代码内调用这些函数的方式:
result.code = evt.code;
result.keysym = 0;

if (isNumPadMultiKey(evt)) {
    result.keysym = getNumPadKeySym(evt);
}




在此代码中,result 是在处理过程中传递的对象。这样,解决方案就可以确保正确处理 NumLock 键。
AltGR 和 Windows我在 Windows 10 上运行的所有支持的浏览器(Chrome、Firefox 和 Opera)中测试 noVNC                解决方案时出现了另一个异常:AltGR 修饰键在 Linux 虚拟机上未按预期工作。
通过调试代码,我发现, AltGR 键通过两条 KeyEvent 消息发送到 QEMU VNC                服务器,而不是一条消息。第一条消息是一个左 Ctrl 键;第二条消息是一个右 Alt 键 — 与您期望某人按下左 Ctrl 后立即按右                Alt 的效果相同。当客户端在 Linux PC 中运行时,发送 AltGR 键作为右 Alt。
是有历史原因的。长话短说:旧的美国键盘没有 AltGR 键,Windows 最初使用左 Ctrl + 右 Alt 来模拟它。此解决方案适合没有                AltGR 键的键盘,但在使用有 AltGR 的键盘时可能带来误导。
一个解决方案是记录此行为,并强制用户删除此默认映射。另一个是我选择的解决方案 — 用于处理 noVNC 中的行为。我的代码包含对按左                Ctrl 后按右 Alt 的组合的特殊处理:
if (state.length > 0 && state[state.length-1].code == 'ControlLeft') {
     if (evt.code !== 'AltRight') {
         next({code: 'ControlLeft', type: 'keydown', keysym: 0});
     } else {
         state.pop();
     }
}
             (...)
            if (evt.code !== 'ControlLeft') {
next(evt);
            }




此代码告诉 noVNC:在 keydown 事件中,如果 KeyboardEvent.code                等于 ControlLeft,则不要立即转发该事件。等待第二个 keydown                事件,并验证它的代码是否等于 AltRight,这意味着浏览器收到了一个左 Ctrl + 右 Alt                的组合,这可能意味着在 Windows 浏览器中按下了 AltGR 键。在这种情况下,丢弃左 Ctrl,仅转发右 Alt,这是 Linux                中的默认行为。这种处理使 AltGR 键能按预期工作,甚至在 Windows 浏览器中也是如此。
此方法的缺点是,即使用户合理地按下了左 Ctrl + 右 Alt 的组合,也不会转发该组合。我将此视为可接受的缺点,因为左 Ctrl + 右 Alt                不是一种常用的组合键(左 Ctrl + 左 Alt 和右 Ctrl + 右 Alt 容易键入得多)。适用性影响极小,而且用户不需要在 Windows                重新配置键盘布局图。
弃用的属性我的实现的另一个已知缺陷,是一个用于处理 NumLock 问题的属性:
if (numLockOnKeyCodes.indexOf(evt.keyCode) !== -1) {




KeyboardEvent.keyCode(连同 which 和                charCode,可在 “” 部分看到)自 2015                年以来已被 。但是,当时在大部分浏览器中没有实现应在它们的位置使用的属性                KeyboardEvent.key(而且在编写本文时,所有 Safari 版本和 Chrome                移动版本仍不支持它)。所有这些弃用的属性被广泛用在 noVNC                和其他任何需要键盘控制的应用程序中。我不希望浏览器很快丢弃这些属性,但依靠一个弃用的属性不是推荐做法。我强烈建议受影响应用程序的开发人员将                keyCode、which 和 charCode 重构为新的                KeyboardEvent.key API。
结束语桌面与 Web VNC 客户端尽管更方便使用,但基于 Web 的 VNC 客户端比桌面客户端更慢。Kimchi 的一个社区要求是,使用名为 Virt Viewer 的                    Linux VNC 客户端让桌面 VNC 客户端更容易连接到 Kimchi 管理的虚拟机。作为 Kimchi 项目的活跃贡献者,我开发了一个                    Kimchi 特性,让用户可以选择使用 Virt Viewer 还是 noVNC 来连接到 Kimchi 虚拟机的 VNC 服务器。

调查 VNC Web 应用程序中的键盘布局问题,在 noVNC 项目中实现解决方案并处理未预见的问题,是一个虽艰苦但有益的过程。
Web 开发自早期的噩梦时代以来已有了很大改善。浏览器兼容性的提高使得大部分 Web                应用程序都只需编码一次,即可在所有主要浏览器中按预期运行。但当应用程序需要更高级的 API                时,比如键盘处理或者甚至移动设备加速计,问题就出现了。在这些 API 中,浏览器支持缓冲区,这直接影响了应使用相同代码库在多个设备上运行(借助响应式                Web 设计和 HTML5)的应用程序的开发。
在面临键盘布局问题时,VNC Web 客户端就会受到这类跨浏览器差异的影响。未实现 QEMU VNC KeyEvent                扩展的项目无法摆脱一些问题,比如如何在不知道使用的键盘布局图的情况下解释非美国键盘中的给定键符。除非                KeyboardEvent.code 属性可用于所有浏览器,否则实现该扩展的项目(正如我为 noVNC                所做的一样)需要支持两种不同的键盘处理模式。
返回列表