在C中实现一个KeyPress事件

vhmi4jdf  于 2023-04-29  发布在  其他
关注(0)|答案(4)|浏览(137)

我有一个像下面这样的无限循环,在这个循环中,我想不断地检查键盘,看看是否按下了escape键(ESC)。如果按下它,则循环应断开。如何在C中做到这一点?(我使用gcc,并访问pthreads,以防必须通过线程完成)

while(1){
        //do something
        //check for the ESC key
 }
9rbhqvlz

9rbhqvlz1#

这严重依赖于系统。在Unix/Linux系统中,默认的终端处理程序收集行,并且只在完整的行可用时通知程序(在按下Enter之后)。)如果您想立即按键,则需要将终端置于非规范模式:

#include <termios.h>

struct termios info;
tcgetattr(0, &info);          /* get current terminal attirbutes; 0 is the file descriptor for stdin */
info.c_lflag &= ~ICANON;      /* disable canonical mode */
info.c_cc[VMIN] = 1;          /* wait until at least one keystroke available */
info.c_cc[VTIME] = 0;         /* no timeout */
tcsetattr(0, TCSANOW, &info); /* set immediately */

一旦你完成了这一步,你就可以使用任何从stdin读取的调用,它们将返回键,而不需要等待行尾。您还可以设置c_cc[VMIN] = 0,使其在从stdin读取时根本不等待击键。
然而,如果你正在阅读stdin与stdio FILE相关的调用(getchar等),设置VMIN = 0将使它认为你已经达到EOF,每当没有可用的键时,所以你必须在这之后调用clearerr来尝试读取更多的字符。你可以像这样使用循环:

int ch;
while((ch = getchar()) != 27 /* ascii ESC */) {
    if (ch < 0) {
        if (ferror(stdin)) { /* there was an error... */ }
        clearerr(stdin);
        /* do other stuff */
    } else {
        /* some key OTHER than ESC was hit, do something about it? */
    }
}

完成后,您可能希望确保将终端设置回规范模式,以免其他程序(例如您的shell)感到困惑:

tcgetattr(0, &info);
info.c_lflag |= ICANON;
tcsetattr(0, TCSANOW, &info);

你还可以用tcsetattr做其他事情--详细信息请参见手册页。有一件事可以满足您的目的,那就是设置一个可选的EOL字符。

nue99wik

nue99wik2#

如果你正在做的主要工作可以放在这个主循环中,你可以在非阻塞模式下使用STDIN。你仍然有一个问题的终端,行缓冲正常。您还应将终端设置为原始模式。
如何使用Ctrl-C(中断)?
非阻塞意味着read()系统调用总是立即返回,即使文件中没有新字节。在Linux/Unix上,可以通过以下方式使STDIN非阻塞:

#include <unistd.h>
#include <fcntl.h>
fcntl(0, F_SETFL, O_NONBLOCK); /* 0 is the stdin file decriptor */
2exbekwf

2exbekwf3#

这就是你想要的:

#include <stdio.h>
#include <conio.h>

void main() {

   int c;

   while((c = getch()) != EOF )
      if(c == 27)   break;
/* 27 is the ASCII code for Esc */

}
xmjla07d

xmjla07d4#

要在执行其他命令的同时持续监视键盘,需要使用termios.h更改终端模式,并使用pthread.h进行多线程处理。
终端以规范模式启动,这意味着标准输入仅在按下Enter后发送到程序。对于按键后的即时响应,我们需要原始模式。
当调用C输入函数getchar()时,线程停止并等待,直到接收到输入。因此,在监听键盘输入的同时连续执行动作需要多线程。
全局变量帮助不同的线程进行通信。
代码如下:

#include <pthread.h> // Multithreading
#include <stdio.h>
#include <stdlib.h>  // for atexit()
#include <termios.h> // For changing terminal mode
#include <unistd.h>  // For changing terminal mode

struct termios original; // A struct to save the original state of terminal
int ESCPressed = 0;      // For thread communication

void disableRAWMode();
void enableRAWMode();
void *asciRead();
void *print();

int main() {
  // Start Multithreading
  pthread_t id_print, id_read;

  pthread_create(&id_print, NULL, print, NULL);
  pthread_create(&id_read, NULL, asciRead, NULL);

  pthread_join(id_print, NULL);
  pthread_join(id_read, NULL);

  return 0;
}

/// Reads keyboard input
void *asciRead() {
  enableRAWMode(); // local function: Enable Raw Mode
  char ch;
  while ((ch = getchar()) != 27)
    ; // ASCI code for ESC is 27
  ESCPressed = 1;
  printf("ESC Pressed!\n");
}

/// Doing Stuff while listening to keyboard
void *print() {
  while (!ESCPressed) { // When ESC is not pressed
    sleep(1);
    printf("I am Printing!\n");
  }
  printf("Printing Thread Finished!\n");
}

/// This function enables RAW mode for terminal.
void enableRAWMode() {
  struct termios raw;
  tcgetattr(STDIN_FILENO, &raw); // Save the state of the terminal to struct raw
                                 // STDIN_FILENO is from <stdlib.h>
                                 // tcgetattr() from <termios.h>
  tcgetattr(STDIN_FILENO, &original);
  atexit(&disableRAWMode); // Revert to canonical mode when exiting the program
                           // atext() from <stdlib.h>
  raw.c_lflag &=
      ~(ECHO | ICANON); // Turn off canonical mode
                        // Turn off ECHO mode so that keyboard is not
                        // printing to terminal
                        // ICANON and ECHO is bitflag. ~ is binary NOT operator

  tcsetattr(STDIN_FILENO, TCSAFLUSH,
            &raw); // Set the terminal to be in raw mode
                   // tcsetattr() from <termios.h>
}

void disableRAWMode() {
  tcsetattr(STDIN_FILENO, TCSAFLUSH,
            &original); // Set terminal to original state
}

有用的资源:
Terminal Raw Mode
Multithreading

相关问题