如何在Linux中获取键盘状态?

15

我想检查在程序启动前用户是否按下了Shift键。(也就是说,用户在程序启动之前按下了Shift键)这是一个简单的控制台程序,与X无关。

这可能类似于Win32 GetKeyboardState()函数。

我想知道我能否做到这一点以及如何做到,但不需要讨论直接访问终端的利弊。


你使用的是哪种编程语言? - Delan Azabani
4个回答

34

我认为有一种方法可以做到这一点。问题在于你必须直接从键盘设备读取,而不是从终端获取输入。我也有同样的问题。我有一个在后台运行的程序,我想知道用户是否按住了Shift键。

我相信这是可能的,一个开始的地方可能是/dev/input/by-path/*-kbd

这个文件每次按下键或重复按下键时都会提供输入,因此值得一看。(尝试使用cat /dev/input/by-path/*-kbd

如果你确实找到了解决方法,我很乐意听听你是如何做到的。

编辑:我已经找到了解决方法

我已经找到了解决方法。我的程序如下:

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

#include <linux/input.h>

void usage ( int argc, char *argv[] )
{
    printf("Usage:\n\t%s key\n\nvalid keys are:\n\tlshift\t- Left Shift key\n" , argv[0]);

    exit(EXIT_FAILURE);
}

int main ( int argc, char *argv[], char *env[] )
{
    if ( argc != 2 )    usage(argc, argv);

    int key;

    if ( strcmp(argv[1], "lshift") == 0 )       key = KEY_LEFTSHIFT;
    else if ( strcmp(argv[1], "rshift") == 0 )  key = KEY_RIGHTSHIFT;
    else if ( strcmp(argv[1], "lalt") == 0 )    key = KEY_LEFTALT;
    else if ( strcmp(argv[1], "ralt") == 0 )    key = KEY_RIGHTALT;
    else if ( strcmp(argv[1], "lctrl") == 0 )   key = KEY_LEFTCTRL;
    else if ( strcmp(argv[1], "rctrl") == 0 )   key = KEY_RIGHTCTRL;


    FILE *kbd = fopen("/dev/input/by-path/platform-i8042-serio-0-event-kbd", "r");

    char key_map[KEY_MAX/8 + 1];    //  Create a byte array the size of the number of keys

    memset(key_map, 0, sizeof(key_map));    //  Initate the array to zero's
    ioctl(fileno(kbd), EVIOCGKEY(sizeof(key_map)), key_map);    //  Fill the keymap with the current keyboard state

    int keyb = key_map[key/8];  //  The key we want (and the seven others arround it)
    int mask = 1 << (key % 8);  //  Put a one in the same column as out key state will be in;

    return !(keyb & mask);  //  Returns true if pressed otherwise false

}

信息提示不够充分(我太懒了)。但是,第一个参数将与键列表进行比较,并使用相应的键标识符。如果按下该键,则返回true;否则返回false。

请注意

您需要更改键盘设备的名称。我不知道如何找到默认的键盘设备。(如果您知道,我会很高兴听到的 ;))

这个方法非常好用:我用它来启动Xorg的自启动,只需按住Shift键即可。


3
这个程序需要ROOT权限,否则在我的机器上会出现“段错误”。这是由于键盘设备文件的权限导致的(顺便说一句,这里使用的是相同的platform-i8042-serio-0-event-kbd,所以可能每台PC都有它?)。通过运行sudo xxd /dev/input/by-path/platform-i8042-serio-0-event-kbd,我发现每当键盘状态改变时,该文件就会“增长”另一个sizeof(keymap) - Tomasz Gandor
@Youarefunny,这太棒了。你应该把它打包起来! - Julien__
我在我的系统上需要更多的键盘控制,并在此基础上开发了键盘监控程序。它通过特殊文件夹提供按键信息的访问,并具备定义全局快捷键的能力。请随意使用:https://github.com/virtimus/kstate - undefined
顺便说一下,我通过创建一个链接来解决“文件名问题”:sudo ln -s /dev/input/by-path/*-kbd /dev/input/kbd。 - undefined

7
据我所知,如果没有根级别的权限,这不能在没有Xlib(又名X)的情况下完成。使用XQueryKeymap()将实现您想要的功能。但是,您指出无法使用X。无论如何,也需要打开显示连接。
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <stdbool.h>
#include <stdio.h>

int main()
{
    Display* dpy = XOpenDisplay(NULL);
    char keys_return[32];
    XQueryKeymap( dpy, keys_return );
    KeyCode kc2 = XKeysymToKeycode( dpy, XK_Shift_L );
    bool bShiftPressed = !!( keys_return[ kc2>>3 ] & ( 1<<(kc2&7) ) );
    printf("Shift is %spressed\n", bShiftPressed ? "" : "not ");
    XCloseDisplay(dpy);
}

对于那些上面无法工作的人,请查看https://gist.github.com/javiercantero/7753445。 - Dhruv

5
我通过gtk/gdk找到了一种非常简单的方法。
int main ( int argc, char *argv[], char *env[] )
{
    gtk_init(&argc, &argv);

    GdkModifierType button_state;
    gdk_window_get_pointer(NULL, NULL, NULL, &button_state);
    if(button_state & GDK_CONTROL_MASK) {
        printf("ctrl key is pressed");
    }
}

你是怎么编译的?在 VSCode+vcpkg+cmake 中,我编译 gtkmm-4.0 时遇到了错误。 - Jonathan

-12

不行。

Shift键不被视为字符键,因此即使您直接访问终端,也无法检测到此键。

也许您不应该这样做。例如,想象一下您正在使用一个美国键盘,在该键盘上,数字可以在顶部行中无需修改键即可访问,并且还检查了Shift键。使用其他键盘布局的人可能必须使用Shift修改键才能访问数字。如果您的程序对此Shift按键做出反应,则您的程序基本上无法使用。同样的事情也适用于其他修饰键:您只能在按下普通字符键后才能检测到其中一些。或者更糟糕的是,他们可能需要使用Shift键来使用“enter”运行您的程序。

另外,您要监视哪个Shift键?本地机器上的键还是用户所在位置的键?请记住,SSH存在并且常用于远程访问伪终端。

如果您是root用户并想要监视本地计算机上的Shift键,您可以读取evdev设备以获取有关Shift键的事件。但这仅在自动键重复的情况下才可能发生,因此您不会检测到在运行程序之前按下的Shift键,而只能在几秒钟之前检测到。

当然,您不能在远程计算机上执行此操作,那将是一个安全漏洞。

而且,无论如何,为什么要这样做呢?在您的情况下,X应用程序不是正确的选择吗?


1
这是一个通用的命令调用器,当按住_SHIFT_键时,调用器将进入配置模式。虽然还有其他方法可以实现这一点,例如使用命令行--conf选项,但是知道这是不可能的就足够了,我会尝试其他方法。 - Lenik
10
通常开头用“你不能”回答问题是很不礼貌的,因为通常这是回答者无效地否定提问者的问题的迹象。此外,假设您有权给任何人许可做某事是非常不公平的。更糟糕的是,如果其他人回来明显地展示了您“可以”,那么您就会看起来很糟糕。无论如何,我以前写过这样的评论,这并不是一个新概念。而且,我没有写答案,因为我不知道,一点点谦卑可以走得很远 ;) - osirisgothra
2
也许你不应该这样做。如果你的程序对这种按键反应迟钝,那么你的程序基本上是无法使用的。但是,这仅适用于GUI处理。对于其他所有内容,“如果您的程序不对此按键反应,则基本上无法使用。”即相机控制、图像编辑应用程序等,更不用说处理ctrl、alt或esc等键了。简而言之:没有人要求你根据你的有限经验发表意见,例如“为什么要这样做?”和“你不能”。 - Pablo Ariel
为什么要检查键盘状态?在打字的时候问这个问题很奇怪。 - Jonathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接