向外部 USB 键盘发送特定信号?或者,如何修复 fdopen()?

向外部 USB 键盘发送特定信号?或者,如何修复 fdopen()?

一些背景:当我从命令行运行命令时,我试图使外部键盘上的大写锁定灯按命令亮起。理想情况下,它是可编写脚本的,这样我就可以将它用于警报和其他事情(我拆解了键盘)。

我得到了这个命令,看起来应该可以工作:

fd=fdopen("/dev/console"); ioctl(fd, 0x4B32, 0x04);

当我尝试在 OS X 或我现有的 Ubuntu 服务器上运行此命令时,会发生这种情况:

me@server1:~$ fd=fdopen("/dev/console"); ioctl(fd, 0x4B32, 0x04);
-bash: syntax error near unexpected token `('
me@server1:~$ fd=fdopen(/dev/console);
-bash: syntax error near unexpected token `('

所以看起来问题出在第一部分。我需要安装一套软件/实用程序吗?如何手动控制外接键盘上的大写锁定灯?

答案1

您的问题是您尝试在 shell 提示符中输入 C 代码,但由于明显的原因,这不起作用。您可以将其放入正确的 C 文件中,编译它并获得可以执行的有效二进制文件:

#include <linux/kd.h>

#include <sys/ioctl.h>

#include <fcntl.h>
#include <unistd.h>

#include <err.h>
#include <stdio.h>
#include <stdlib.h>

static void
usage(char *argv0)
{
    fprintf(stderr, "Usage: %s <on|off>\n", argv0);
    exit(EXIT_FAILURE);
}

int
main(int argc, char *argv[])
{
    int fd;
    int on;
    unsigned char state;

    if (argc != 2)
        usage(argv[0]);

    if (strcmp(argv[1], "on") == 0)
        on = 1;
    else if(strcmp(argv[1], "off") == 0)
        on = 0;
    else
        usage(argv[0]);


    fd = open("/dev/console", O_RDWR);
    if (fd == -1)
        err(EXIT_FAILURE, "open /dev/console");

    if (ioctl(fd, KDGETLED, &state) == -1)
        err(EXIT_FAILURE, "KDGETLED");

    if (on)
        state |= LED_CAP;
    else
        state &= ~LED_CAP;

    if (ioctl(fd, KDSETLED, state) == -1)
        err(EXIT_FAILURE, "KDSETLED");

    close(fd);

    return 0;
}

将其放入名为eg的文件中caps.c并编译它:

$ gcc -o caps caps.c

然后你可以运行它

$ ./caps on

打开 LED 或

$ ./caps off

将其关闭(切换留给读者作为练习)。

笔记:要打开/dev/console您需要超级用户权限。

还有一个注意事项:这也不会阻止您的终端或 X 服务器偶尔更改 CapsLock LED(例如,Caps按下 时)。这也不适用于 OS X,而仅适用于 Linux,因为没有标准化的方法来执行此操作。最后,你不能分别更改多个键盘上的 LED。

答案2

该 C 代码无论如何都无法工作——fdopen()需要多个参数并返回一个文件流,而不是文件描述符。

我记得几年前通过 Xlib 这样做过,但我找不到我的旧代码(有时集合会被清除),但我认为有人必须在 github 或其他地方有一个简单的应用程序。瞧,我发现的第一页是这个虽然我不记得发布过它,但它是我四年前写的(“akashiraffee”是我当时在网上经常使用的一个名字)。

它实际上并不使用 Xlib —— 它使用的是ioctl(),就像你试图做的那样 —— 所以应该可以在没有 GUI 的情况下工作。我整理了一下,在这里用 5 LED 键盘试了一下:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/kd.h>
#include <sys/ioctl.h>

int main (void) {
    int tty = open("/dev/console", 0), led;
    unsigned long int arg;

    if (tty < 3) {
        perror("open: ");
        return -1;
    }

    if (ioctl(tty,KDGKBTYPE, &arg) > 0) perror("ioctl: ");
    if (arg == KB_101) puts("You have a 101 key keyboard.");

    for (led = 1; led < 9; led++) {
        if (ioctl(tty,KDSETLED, led) > 0) perror("ioctl led on: ");
        printf("LED %d on...hit enter", led);
        getchar();
        if (ioctl(tty,KDSETLED, led+0xff) > 0) perror("ioctl led off: ");
        printf("off (hit enter)\n");
        getchar();
    }

    close(tty);

    return 0;
} 

gcc whatever.c -o testleds然后编译./testleds(注意你需要超级用户权限)。前 7 个对我有用;其中有些是单独的 LED,有些是组合。

我把键盘拆了

当然,您需要按 ENTER 才能进行测试。如果没有,请发表评论,我将更改为使用延迟自动测试。如果它最终确实有效,我很乐意将其变成可以用来按数字切换灯光的东西(例如toggleLED 3)。

答案3

Linux

您发布的只是一小段 C 代码。(甚至都不能运行。)它在 shell 提示符下无法运行。

没有 shell 接口ioctl,但您可以使用 Perl。

#!/usr/bin/perl
require "sys/ioctl.ph";
if (@ARGV) {
    $ioctl = 0x4b32; # KDSETLED
    $ARGV[0] =~ /^[0-8]+$/ or die "$0: $ARGV[0]: invalid argument";
    $arg = int($ARGV[0]);
} else {
    $ioctl = 0x4b31; # KDGETLED
    $arg = "?";
}
sysopen CONSOLE, "/dev/console", "r" or die "$0: /dev/console: $!\n";
ioctl CONSOLE, $ioctl, $arg or die "$0: ioctl: $!\n";
print ord($arg), "\n" unless @ARGV;

或者Python:

#!/usr/bin/env python
import array, fcntl, os, sys
console = open("/dev/console", "r")
if len(sys.argv) > 1:
    fcntl.ioctl(console, 0x4b32, int(sys.argv[1])) # KDSETLED
else:
    arg = array.array('B', [0xff]) # KDGETLED
    fcntl.ioctl(console, 0x4b31, arg, True)
    print arg[0]

如果不带参数执行,任一程序都会打印当前 LED 设置;如果传递参数,则更改 LED。 LED 设置表示为 1(Scroll Lock)、2(Num Lock)和 4(Caps Lock)之和。您可以传递参数 8 来重置 LED 以对应操作系统的锁定设置。

除了调用您自己的程序之外,您还可以调用setleds标准的效用控制台工具

您需要登录到文本模式控制台(不在 X 下且不是远程),或者以 root 身份运行。

Linux,特定键盘

您还可以通过访问特定输入设备而不是控制台设备来打开和关闭该设备的 LED。 ioctl 不同:您需要使用evdev接口并且寄出EV_LED事件

使用此界面的最简单方法是安装evdevPython 包 ( pip install evdev)。

</dev/input/by-id/usb-_USB_Keyboard-event-kbd \
python -c 'import evdev; dev = evdev.InputDevice("/dev/stdin"); print dev.leds()'

请参阅《获取和设置 LED 状态》教程了解更多示例。

操作系统X

在 Mac OS X 上,您可以通过以下方式访问键盘 LED:HID接口。您可以找到示例和解释Mac OS X 内部结构或上通灵折纸

相关内容