将输入事件从网络摄像头重新映射到键盘上的按键

将输入事件从网络摄像头重新映射到键盘上的按键

我正在开发一个应用程序,其中我需要读取网络摄像头上触发器的输入,以告诉我的应用程序执行某些操作。

该项目涉及使用 QT 支持构建的 OpenCV,当我应用此触发器时,我会随机崩溃(ASSERT:文件 qasciikey.cpp 中的“false”,第 495 行)。

因此,我认为一种更简单、更可靠的方法是将外部触发器映射到按键(特别是空格键)。我不想覆盖空格键,只是为了让触发器看起来像按下了空格键。

到目前为止,我使用 evtest 选择了我的设备:

/dev/input/event13: See3CAM_CU51

并且可以看到当我触发相机时,它显示以下内容:

Event: time 1507757166.472300, type 1 (EV_KEY), code 212 (KEY_CAMERA), value 1
Event: time 1507757166.472300, -------------- SYN_REPORT ------------
Event: time 1507757167.147649, type 1 (EV_KEY), code 212 (KEY_CAMERA), value 0
Event: time 1507757167.147649, -------------- SYN_REPORT ------------

“值1”是当引脚被拉高(触发)时,“值0”是当引脚分离时。所以我想我只需要将 KEY_CAMERA 重新映射到空格键,但我不确定使用 Ubuntu 16.04 执行此操作的最干净的方法。

当我选择键盘设备时:

/dev/input/event4: AT Translated Set 2 keyboard

我按空格键,得到以下输出:

Event: time 1507757327.011812, -------------- SYN_REPORT ------------
Event: time 1507757328.818177, type 4 (EV_MSC), code 4 (MSC_SCAN), value 39
Event: time 1507757328.818177, type 1 (EV_KEY), code 57 (KEY_SPACE), value 1
Event: time 1507757328.818177, -------------- SYN_REPORT ------------
Event: time 1507757328.896970, type 4 (EV_MSC), code 4 (MSC_SCAN), value 39
Event: time 1507757328.896970, type 1 (EV_KEY), code 57 (KEY_SPACE), value 0

我一直在阅读有关编写 hwdb udev 文件的内容,但我还没有找到一种将输入从一个设备一直映射到另一个设备的方法。这可能吗?完成这件事最简单的方法是什么?

提前致谢。

答案1

正确的答案可能是编写一个 [hwdb] 文件以将键码的解释覆盖KEY_CAMERAKEY_SPACE. 貌似是个例子,但是细节上有点欠缺。

文件的格式主要在文件头部的注释中描述/usr/lib/udev/hwdb.d/60-keyboard.hwdb(在我的系统上),你可以看到一些架构Linux对此的讨论。请注意,此格式取决于 systemd 的版本,因此请检查您自己的文件。您可以将您的输入设备与以下 3 种模式之一相匹配:

  • 通用输入设备匹配:

      evdev:输入:bZZZZvYYYYpXXXXeWWWW-VVVV

    这与输入设备的内核模态别名相匹配,主要是:ZZZZ 是总线 id(参见 /usr/include/linux/input.h BUS_*),YYYY、XXXX 和 WWW 是 4 位十六进制大写供应商,产品和版本 ID 和 VVVV 是描述设备功能的任意长度的输入模态。

  • AT键盘DMI数据匹配:

      evdev:atkbd:dmi:bvn*:bvr*:bd*:svn小贩:pn产品:个人录像*

    供应商和产品是由内核DMI模态导出的固件提供的字符串。

  • 输入驱动程序设备名称和 DMI 数据匹配:

      evdev:名称:输入设备名称:dmi:bvn*:bvr*:bd*:svn小贩:pn*

    输入设备名称是驱动程序指定的名称设备,供应商是内核 DMI modalias 导出的固件提供的字符串。

另一种可能性是编写一个小程序来读取网络摄像头事件流,就像 evtest 所做的那样,当看到密钥时,将 a 注入KEY_SPACE到另一个事件流中。似乎有更多关于这方面的文档,还有很多像这样的Python示例教程用于注入事件的片段。

答案2

添加到 @meuh 关于使用 hwdb 的答案,以下是我了解到的有关 hwdb 如何工作的一些内容。这主要是关于 hwdb 的键盘扫描码映射,但其中很多对于其他 hwdb 信息也很有用。

资料来源:

有关我如何处理此处收集的信息的实际示例,请参阅这个帖子关于重新利用“Ecobutton”,这是一种单按钮“键盘”,按下时会发送按键序列。

hwdb 中使用哪些标识符?

要实际找出正在使用哪些 hwdb 标识符,您可以使用udevadm

$ sudo udevadm test /sys/class/input/event256 |& grep builtin.command..hwdb
event256: /usr/lib/udev/rules.d/60-evdev.rules:8 Importing properties from results of builtin command 'hwdb --subsystem=input --lookup-prefix=evdev:'
event256: /usr/lib/udev/rules.d/60-evdev.rules:18 Importing properties from results of builtin command 'hwdb 'evdev:name:HID 3412:7856:phys:usb-0000:00:14.0-1.1.2.4/input0:ev:120013:dmi:bvnINSYDECorp.:bvr03.17:bd10/27/2022:br3.17:svnFramework:pnLaptop:pvrAB:rvnFramework:rnFRANBMCP0B:rvrAB:cvnFramework:ct10:cvrAB:skuFRANBMCP0B:''
event256: /usr/lib/udev/rules.d/60-evdev.rules:23 Importing properties from results of builtin command 'hwdb 'evdev:name:HID 3412:7856:dmi:bvnINSYDECorp.:bvr03.17:bd10/27/2022:br3.17:svnFramework:pnLaptop:pvrAB:rvnFramework:rnFRANBMCP0B:rvrAB:cvnFramework:ct10:cvrAB:skuFRANBMCP0B:''
event256: /usr/lib/udev/rules.d/60-input-id.rules:6 Importing properties from results of builtin command 'hwdb --subsystem=input --lookup-prefix=id-input:modalias:'
event256: /usr/lib/udev/rules.d/65-libwacom.rules:19 Importing properties from results of builtin command 'hwdb --subsystem=input '--lookup-prefix=libwacom:name:HID 3412:7856:''

请注意,这会尝试上面 udev 规则中显示的四个 hwdb 命令中的三个,其中 evdev:atkbd 命令仅适用于此DRIVERS=="atkbd"USB 键盘,并不适用于该 USB 键盘。

例如,上面的输出包含:

event256: /usr/lib/udev/rules.d/60-evdev.rules:18 Importing properties from results of builtin command 'hwdb 'evdev:name:HID 3412:7856:phys:usb-0000:00:14.0-1.1.2.4/input0:ev:120013:dmi:bvnINSYDECorp.:bvr03.17:bd10/27/2022:br3.17:svnFramework:pnLaptop:pvrAB:rvnFramework:rnFRANBMCP0B:rvrAB:cvnFramework:ct10:cvrAB:skuFRANBMCP0B:''

这似乎包含很多特定于我的笔记本电脑的标识符,而不是我连接的 USB 键盘,但 USB vidpid (3412:7856) 包含在开头,可以像这样匹配:

evdev:name:HID 3412:7856:*
  KEYBOARD_KEY_70028=backspace

或者,evdev 输出还包含(由于 grep 的原因,上面没有显示第二行,但它存在于完整输出中):

event256: /usr/lib/udev/rules.d/60-evdev.rules:8 Importing properties from results of builtin command 'hwdb --subsystem=input --lookup-prefix=evdev:'
event256: hwdb modalias key: "input:b0003v3412p7856e0100-e0,1,4,11,14,k74,75,77,7D,7E,7F,B7,ram4,l0,1,2,3,4,sfw"

看来这个 hwdb 调用没有像后面的调用那样获得指定的完整匹配键,但它是从前缀和(由 hwdb)自动生成的 modalias 键构建的。所以上面的结果是完整的键:

evdev:input:b0003v3412p7856e0100-e0,1,4,11,14,k74,75,77,7D,7E,7F,B7,ram4,l0,1,2,3,4,sfw

这与 USB vid/pid 相匹配,您可以使用如下内容:

evdev:input:b????v3412p7856e*
  KEYBOARD_KEY_70028=backspace

请注意,这使用通配符匹配总线编号?,以确保当您插入另一个 USB 总线/端口时它仍然匹配。它还使用*通配符作为版本号和其后的其他模态内容,这可能与这里无关。

我应该使用什么扫描码?

获得正确扫描码的最可靠方法似乎是使用 evtest。运行它,然后按一个按钮,给出类似以下内容:

sudo evtest --grab /dev/input/event256 
[ snip debug output]
Event: time 1675505630.859393, -------------- SYN_REPORT ------------
Event: time 1675505630.867333, type 4 (EV_MSC), code 4 (MSC_SCAN), value 70028
Event: time 1675505630.867333, type 1 (EV_KEY), code 28 (KEY_ENTER), value 0

这里,70028是扫描码(请注意,它实际上是十六进制,但这也是所KEYBOARD_KEY_xx期望的(例如KEYBOARD_KEY_70028=backspace)。请注意,USB 键盘似乎使用 700xx 扫描码,而老式 AT 键盘似乎使用较低的扫描码,所以最好只需用键盘进行测试,而不是在线查找代码。

另请注意,这些扫描代码似乎是以十六进制编写的(到处都省略了 0x 前缀),而键码是以十进制编写和解析的,但我不能 100% 确定。

也可以看看这一页

我应该使用什么键码?

这些键码的规范来源是输入事件代码在内核中,所有的KEY_xxx常量。

似乎数字值、KEY_xxx 和 xxx 值都被接受。例如,以下内容都相当于映射回车键以产生退格键:

 KEYBOARD_KEY_70028=14
 KEYBOARD_KEY_70028=key_backspace
 KEYBOARD_KEY_70028=backspace

evtest启动时(作为支持的 EV_KEY 事件代码)、evtest按下按键时的输出(作为EV_KEY事件代码)似乎显示相同的键代码

也可以看看这一页

如何查看输入设备的当前扫描码映射?

我假设这应该可以使用输入设备和一些 ioctl 进行查询,但我不确定现有的命令行工具可以为您执行此操作。如果您发现了,请发表评论。

启动时打印的受支持的 EV_KEY 列表似乎确实evtest提供了一个提示:该列表似乎只包含映射了一些扫描码的键码。

如何将更改应用到 hwdb 文件?

您需要使用以下命令将 hwdb 文件重新编译到 /etc/udev/hwdb.bin 中:

$ sudo systemd-hwdb update

重新插入 USB 设备,或使用以下命令强制 udev 重新运行其规则:

$ sudo udevadm trigger /sys/class/input/event256

hwdb是从哪里调用的?

hwdb 中的键和值不是硬编码在 udev 中,而是由规则文件确定。例如, /usr/lib/udev/rules.d/60-evdev.rules 处理输入/键盘规则:

IMPORT{builtin}="hwdb --subsystem=input --lookup-prefix=evdev:", \
  IMPORT{builtin}="keyboard", GOTO="evdev_end"

# AT keyboard matching by the machine's DMI data
DRIVERS=="atkbd", \
  IMPORT{builtin}="hwdb 'evdev:atkbd:$attr{[dmi/id]modalias}'", \
  IMPORT{builtin}="keyboard", GOTO="evdev_end"

# device matching the input device name + properties + the machine's DMI data
KERNELS=="input*", \
  IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:phys:$attr{phys}:ev:$attr{capabilities/ev}:$attr{[dmi/id]modalias}'", \
  IMPORT{builtin}="keyboard", GOTO="evdev_end"

# device matching the input device name and the machine's DMI data
KERNELS=="input*", \
  IMPORT{builtin}="hwdb 'evdev:name:$attr{name}:$attr{[dmi/id]modalias}'", \
  IMPORT{builtin}="keyboard", GOTO="evdev_end"

所以上面的规则准确地展示了 hwdb 标识符是如何构造的。此外,这个想法是 60-evdev.rules 和 60-keyboard.hwdb 是相关的,但这只是约定 - 所有 hwdb 条目都放入单个数据库中,并且可以与提供正确标识符的任何 udev 规则进行匹配。

hwdb 属性是如何处理的?

这些行的作用IMPORT{builtin}="hwdb ..."是查阅 hwdb,并将其中列出的任何属性公开为常规 udev 设备属性(即您可以使用 查询它们udevadm info)。

此外,上面的规则还包含IMPORT{builtin}="keyboard",它调用一个 udev 内置实用程序来处理KEYBOARD_*属性并使用它们在内核中配置扫描码到键码映射(使用EVIOCSKEYCODE输入设备上的 ioctl,请参阅来源)。

同样,udevadmin info如上所示使用在这里很有帮助,因为它应该显示内置决定应用的任何键盘映射。

$ sudo udevadm test /sys/class/input/event256 |& grep mapping
event256: keyboard: mapping scan code 18 (0x12) to key code 0 (0x0)
event256: keyboard: mapping scan code 458770 (0x70012) to key code 0 (0x0)
event256: keyboard: mapping scan code 36 (0x24) to key code 0 (0x0)

(请注意,udevadm test实际上像这样运行直接应用这些键映射,即使文档表明它应该只进行一次演练。这可能是一个错误,因为我怀疑“内置”机制最初旨在仅收集信息而不应用更改)

从 udev 规则设置 hwdb / KEYBOARD 属性

Hwdb 条目本质上只是设置设备属性,就像 udev 规则可以直接执行的操作一样(但我猜 hwdb 的发明是为了更容易紧凑地设置多个属性,而不需要非常详细的 udev 规则)。然而,这意味着应该可以仅使用 udev 规则而不是更复杂的 hwdb 来完成此键重新映射,例如:

SUBSYSTEM=="input", KERNEL=="event3", ENV{KEYBOARD_KEY_01}="capslock", ENV{KEYBOARD_KEY_3a}="esc"
SUBSYSTEM=="input", KERNEL=="event3", IMPORT{builtin}="keyboard"

请注意,运行“键盘”插件的 udev 规则60-evdev.rules与内置的 hwdb 规则相同。我相信,这意味着仅当 hwdb 内置函数找到任何信息时才会运行“键盘”规则。

如果您的 hwdb 规则与此特定设备不匹配,则“键盘”内置可能无法运行,因此我在上面添加了一条显式规则来运行它(这必须是一个单独的规则,因为当它们位于同一规则中时,似乎ENV条目仅在内置函数运行之后应用,而不是之前)。

如果你的 hwdb 规则匹配这个特定的设备,你可以省略上面的第二条规则并依赖于60-evdev.rules运行内置的键盘(但是上面的 udev 规则应该在运行的规则文件中 60-evdev.rules,不是之后。似乎旧版本的 udev/eudev 使用RUN而不是调用内置键盘IMPORT,并且没有此顺序约束)。

此信息部分来源是这个问题

这些 udev 内置函数记录在哪里?

似乎无处可去。看这个答案一些指向这些内置函数的源代码的指针。

相关内容