【问题标题】:How can I capture a key stroke immediately in linux? [duplicate]如何在 linux 中立即捕获击键? [复制]
【发布时间】:2023-04-07 10:09:01
【问题描述】:

可能重复:
What is equivalent to getch() & getche() in Linux?

我是 linux 编程的新手。 :-)

我只是想知道我的程序(使用 C 语言)是否可以立即在 linux 中捕获每个键击,然后决定是否回显它以及如何显示它,就像 telnet 一样。

例如,如果用户点击“A”,我希望程序显示“B”,如果他输入“B”,则需要“C”,等等。

这听起来很有趣而且没用。我只是好奇。

【问题讨论】:

  • 你的意思是像getch()这样的东西吗?
  • @TheNewOne, getch() 会回显那个键,但我想要的是控制回显。
  • getch 不回显密钥...
  • @TheNewOne,但是,如果 getch() 是标准的 c 函数?
  • @UniMouS 有多种方法可以解决问题,具体取决于您在处理问题时将决定的细节。主要问题是,如果您需要比该方法可用的更多功能,通常一种方法不会以一种允许您轻松扩展它的方式构建。要获得更多功能,您通常必须更深入地挖掘,这意味着部分重新实现标准库中的许多内容,因为该库所做的只是比您想要的多一点。

标签: c linux scancodes


【解决方案1】:

基本上,这在很大程度上取决于您如何立即定义。

这里有两个任务。第一个是禁用大多数 C 输入库中内置的常规键回显。第二种是打印出新字符而不是旧字符。

在伪代码中。

 echo(off);
 while (capturing && charIsAvailable()) {
   c = readOneChar();
   if (c == '\n') {
     capturing = false;
   }
   printf("%c", c++);
 }
 echo(on);

有许多系统进行通信以捕获按键。

  1. 键盘
  2. (可能)USB 总线
  3. CPU 中断处理程序
  4. 操作系统
  5. X 窗口服务器进程
  6. 具有焦点的 X“窗口”。

最后一步是通过一个程序完成的,该程序运行一个连续循环,从 X 服务器捕获事件并处理它们。如果你想以某种方式扩展这个程序(获取按键被按下的时间长度),你需要告诉其他程序你想要“原始”键盘事件,这意味着你不会真正收到完全“熟” “ 人物。因此,您必须跟踪哪些键是向上和向下的,以及多长时间,并处理程序中所有奇怪的元键行为(这不是“a”而是“A”,因为 shift 已关闭,等等)。

还需要考虑其他处理模式,例如规范和非规范,它们将控制您是希望在面向行的块(行事件)还是面向字符的块(字符事件)中接收事件。同样,由于需要让上游程序了解下游客户端的需求,这有点复杂。

现在您已经对自己的环境有了一些了解,让我们重新审视抑制字符输出所需的实际代码。

// define a terminal configuration data structure
struct termios term;

// copy the stdin terminal configuration into term
tcgetattr( fileno(stdin), &term );

// turn off Canonical processing in term
term.c_lflag &= ~ICANON;

// turn off screen echo in term
term.c_lflag &= ~ECHO;

// set the terminal configuration for stdin according to term, now
tcsetattr( fileno(stdin), TCSANOW, &term);


(fetch characters here, use printf to show whatever you like)

// turn on Canonical processing in term
term.c_lflag |= ICANON;

// turn on screen echo in term
term.c_lflag |= ECHO;

// set the terminal configuration for stdin according to term, now
tcsetattr( fileno(stdin), TCSANOW, &term);

即使这也不是即时的。要立即获得,您需要更接近源,这最终意味着一个内核模块(它仍然不如键盘微控制器那么直接,它不如开关实际关闭时那么直接)。在源和目标之间有足够多的项目,最终可能会注意到差异,但是,在实践中,寻求性能和灵活性之间最佳折衷的人们已经对这段代码进行了很多工作。

【讨论】:

  • 听起来很棒,还有很多东西要学。
  • @UniMouS 总是有的,标准输入输入处理被如此漂亮的库包裹着,在你必须做一些特别的事情之前,你真的无法理解幕后发生了多少事情。
  • Edwin Buck,您可能希望将tcgetattr()tcsetattr() 调用的第一个参数更改为fileno(stdin)(在POSIX 中,使用#define _POSIX_SOURCE)。
  • @NominalAnimal 谢谢指正。我已经进行了更新。
【解决方案2】:

Edwin Buck 提供了一个很好的答案。这是另一个使用 ncurses 的替代方案,它使这些事情变得更加简单,并且几乎所有 Linux 发行版和其他 Unix 变体都支持。

#include <ncurses.h>

int main(int argc, char *argv[])
{
    int ch,c;
    initscr();
    cbreak();
    noecho();

    while( ch != 'q')
    {
        switch( c = getch() )
        {
            case 'a':
                ch = 'b';
            break;

            case 'b':
                ch = 'c';
            break;

            case 'c':
                ch = 'd';
            break;

            default:
                ch = c;                 
            break;
        }
        printw("%c",ch);
    }
    endwin();
    return(0);
}

使用 gcc code.c -o code -lncurses 编译

以下是一些支持链接:

ncurses noecho and other - man page
ncurses programming HOWTO

【讨论】:

    【解决方案3】:

    此代码可在互联网上找到,由 c-board 的 kermi3 提供

    #include <termios.h>
    #include <unistd.h>
    
    int mygetch(void)
    {
        struct termios oldt,newt;
        int ch;
        tcgetattr( STDIN_FILENO, &oldt );
        newt = oldt;
        newt.c_lflag &= ~( ICANON | ECHO );
        tcsetattr( STDIN_FILENO, TCSANOW, &newt );
        ch = getchar();
        tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
        return ch;
    }
    

    编辑:当然,如果你想获取 char 本身而不是 ascii 值更改为 char 而不是 int

    【讨论】:

    • 我的意思不是像执行一样运行,我的意思是它可以在互联网上找到。 编辑:我更改了它以澄清。
    • 哦,看起来好复杂,有没有简单的方法或者一个函数可以做到这一点?
    • 你复制这个函数,然后调用它 mygetch()。返回的 int 是 ascii 值。例如,如果键入 a,您将得到 97
    • 顺便说一句,您可以使用 并使用 getch() - char ch=getch(),ch 将包含输入的字符(这可能是最简单的方法 :) 你需要通过在 gcc 命令的后面添加 -lcurses 来编译它)。
    • 我试过 curses 的 getch(),但它似乎不像你答案中的代码块那样好用,令人困惑。
    【解决方案4】:

    这可能有点矫枉过正,但您可以使用 sdl 等游戏库来实现。

    【讨论】:

      猜你喜欢
      • 2011-05-13
      • 1970-01-01
      • 2012-12-08
      • 2018-12-08
      • 1970-01-01
      • 2013-02-22
      • 1970-01-01
      • 1970-01-01
      • 2011-05-12
      相关资源
      最近更新 更多