在c(linux)中,如何同时识别来自多个键的输入

在c(linux)中,如何同时识别来自多个键的输入,c,linux,keyboard,pthreads,simultaneous,C,Linux,Keyboard,Pthreads,Simultaneous,我正在尝试使用ncurses创建C语言的乒乓球,现在我遇到了一个巨大的挫折,因为我不知道如何允许两个玩家同时移动垫子。我尝试的是创建一个单独的线程,然后使用select检测任何缓冲按键,然后将其放入包含我的控件的数组中。但是,它只读取第一个键,不识别另一个键 #include <stdlib.h> #include <stdio.h> #include <sys/select.h> #include <sys/time.h> #include &l

我正在尝试使用ncurses创建C语言的乒乓球,现在我遇到了一个巨大的挫折,因为我不知道如何允许两个玩家同时移动垫子。我尝试的是创建一个单独的线程,然后使用select检测任何缓冲按键,然后将其放入包含我的控件的数组中。但是,它只读取第一个键,不识别另一个键

#include <stdlib.h>
#include <stdio.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <ncurses.h>
#include <pthread.h>
#include <errno.h>

#define DELAY 30000

#define P1_UP 0
#define P1_DOWN 1
#define P2_UP 2
#define P2_DOWN 3

struct player {
    int x;
    int y;
    int score;
};

void *_thread_func(void *);

int main(int argc, char **argv) {
  struct player p[2];
  int x, y, max_y, max_x;
  int *keys;
  pthread_t _thread;

  keys = calloc(4, sizeof(int));

  if(pthread_create(&_thread, NULL, _thread_func, &keys) != 0) {
    perror("pthread_create");
    exit(EXIT_FAILURE);
  }

  initscr();
  noecho();
  curs_set(FALSE);

  getmaxyx(stdscr, max_y, max_x);

  p[0].score = p[1].score = 0;
  p[0].y = p[1].y = max_y/2-3; // length of pad is 6
  p[0].x = 0; // width of pad is 1
  p[1].x = max_x-1;
  while(1) {
    getmaxyx(stdscr, max_y, max_x);

    clear();

    mvprintw(p[0].y  , p[0].x  , "|");
    mvprintw(p[0].y+1, p[0].x  , "|");
    mvprintw(p[0].y+2, p[0].x  , "|");
    mvprintw(p[0].y+3, p[0].x  , "|");
    mvprintw(p[0].y+4, p[0].x  , "|");
    mvprintw(p[0].y+5, p[0].x  , "|");

    mvprintw(p[1].y  , p[1].x  , "|");
    mvprintw(p[1].y+1, p[1].x  , "|");
    mvprintw(p[1].y+2, p[1].x  , "|");
    mvprintw(p[1].y+3, p[1].x  , "|");
    mvprintw(p[1].y+4, p[1].x  , "|");
    mvprintw(p[1].y+5, p[1].x  , "|");

    refresh();
    usleep(DELAY);

    if(keys[P2_UP]) {
      keys[P2_UP] = 0;
      if(--p[1].y < 0) p[1].y++; 
    }

    if(keys[P2_DOWN]) {
      keys[P2_DOWN] = 0;
      if(++p[1].y >= max_y-5) p[1].y--; 
    }

    if(keys[P1_UP]) {
      keys[P1_UP] = 0;
      if(--p[0].y < 0) p[0].y++; 
    }

    if(keys[P1_DOWN]) {
      keys[P1_DOWN] = 0;
      if(++p[0].y >= max_y-5) p[0].y--; 
    }
  }

  free(keys);

  endwin();
}


void *_thread_func(void *arg) {
  fd_set readfds;
  int    num_readable;
  int    num_bytes;
  struct timeval tv;
  int    **keys;
  char   buf[1];
  int    fd_stdin;

  keys = (int**) arg;
  fd_stdin = fileno(stdin);

  while(1) {
    FD_ZERO(&readfds);
    FD_SET(fileno(stdin), &readfds);

    tv.tv_sec = 0;
    tv.tv_usec = 0;

    fflush(stdout);
    num_readable = select(fd_stdin + 1, &readfds, NULL, NULL, &tv);
    if (num_readable == -1) {
      fprintf(stderr, "\nError in select : %s\n", strerror(errno));
      exit(1);
    }
    if (num_readable > 0) {
      num_bytes = read(fd_stdin, buf, 1);
      if (num_bytes < 0) {
        fprintf(stderr, "\nError on read : %s\n", strerror(errno));
        exit(1);
      }

      switch(buf[0]) {
        case 105: /* i -> ascii 105*/
          (*keys)[P2_UP] = 1;
          break;
        case 107: /* k -> ascii 107*/
          (*keys)[P2_DOWN] = 1;
          break;
        case 119: /* w -> ascii 119*/
          (*keys)[P1_UP] = 1;
          break;
        case 115: /* s -> ascii 115*/
          (*keys)[P1_DOWN] = 1;
          break;
      }
    }
  }

  return NULL;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义延迟30000
#定义P1_UP 0
#定义P1_向下1
#定义P2_UP 2
#定义P2_向下3
结构播放器{
int x;
int-y;
智力得分;
};
void*_thread_func(void*);
int main(int argc,字符**argv){
结构游戏者p[2];
int x,y,max_y,max_x;
int*键;
pthread_t_线程;
keys=calloc(4,sizeof(int));
if(pthread_create(&_thread,NULL,_thread_func,&keys)!=0){
perror(“pthread_create”);
退出(退出失败);
}
initscr();
noecho();
curs_set(假);
getmaxyx(stdscr,max_y,max_x);
p[0]。得分=p[1]。得分=0;
p[0].y=p[1].y=max_y/2-3;//焊盘长度为6
p[0].x=0;//焊盘宽度为1
p[1].x=max_x-1;
而(1){
getmaxyx(stdscr,max_y,max_x);
清除();
mvprintw(p[0].y,p[0].x,“|”);
mvprintw(p[0].y+1,p[0].x,“|”);
mvprintw(p[0].y+2,p[0].x,“|”);
mvprintw(p[0].y+3,p[0].x,“|”);
mvprintw(p[0].y+4,p[0].x,“|”);
mvprintw(p[0].y+5,p[0].x,“|”);
mvprintw(p[1].y,p[1].x,“|”);
mvprintw(p[1].y+1,p[1].x,“|”);
mvprintw(p[1].y+2,p[1].x,“|”);
mvprintw(p[1].y+3,p[1].x,“|”);
mvprintw(p[1].y+4,p[1].x,“|”);
mvprintw(p[1].y+5,p[1].x,“|”);
刷新();
usleep(延迟);
如果(键[P2\U UP]){
键[P2_UP]=0;
if(-p[1].y<0)p[1].y++;
}
如果(键[P2\U向下]){
键[P2_向下]=0;
如果(++p[1].y>=max_y-5)p[1].y--;
}
如果(键[P1\U UP]){
键[P1_UP]=0;
if(-p[0].y<0)p[0].y++;
}
如果(键[P1\U向下]){
键[P1_向下]=0;
如果(++p[0].y>=max_y-5)p[0].y--;
}
}
免费(钥匙);
endwin();
}
void*\u thread\u func(void*arg){
fd_设置读取FDS;
int num_可读;
int num_字节;
结构时间值电视;
整数**键;
char-buf[1];
int fd_stdin;
键=(int**)arg;
fd_stdin=文件号(stdin);
而(1){
FD_零(&readfds);
FD_集(文件号(标准输入)和读取FDS);
tv.tv_sec=0;
tv.tv_usec=0;
fflush(stdout);
num_readable=select(fd_stdin+1和readfds、NULL、NULL和tv);
如果(num_readable==-1){
fprintf(stderr,“\n选择中的错误:%s\n”,strerror(errno));
出口(1);
}
如果(可读数量>0){
num_bytes=读取(fd_stdin,buf,1);
if(num_字节<0){
fprintf(stderr,“\n读取时出错:%s\n”,strerror(errno));
出口(1);
}
开关(buf[0]){
案例105:/*i->ascii 105*/
(*键)[P2_UP]=1;
打破
案例107:/*k->ascii 107*/
(*键)[P2_向下]=1;
打破
案例119:/*w->ascii 119*/
(*键)[P1_UP]=1;
打破
案例115:/*s->ascii 115*/
(*键)[P1_向下]=1;
打破
}
}
}
返回NULL;
}

如何在C中同时识别多个键?任何关于如何做到这一点的例子都将不胜感激:)

给出的例子和方法由于一个以上的原因不起作用:

  • 诅咒库不会是线程安全的
  • 只打开了一个输入设备(使select调用毫无意义)

  • 可以使用在多个设备上打开curses程序,并使用超时轮询这些设备的输入。中的ditto.c程序对此很有帮助。

    给出的示例和方法由于以下原因无效:

  • 诅咒库不会是线程安全的
  • 只打开了一个输入设备(使select调用毫无意义)

  • 可以使用在多个设备上打开curses程序,并使用超时轮询这些设备的输入。在Linux中,您可以使用
    ioctl(fd,KDSKBMODE,mode)
    更改键盘模式,其中
    模式是
    K_-RAW
    K_-XLATE
    K_-MEDIUMRAW
    K_-UNICODE
    中的一种。如果将其设置为
    K_RAW
    ,则会收到原始扫描码;大多数按键按下时发送一个扫描码,松开时发送另一个扫描码

    在这种模式下,由您跟踪哪些键被按下,哪些键未被按下

    各个键发送的精确扫描码可能因键盘而异。您可以使用
    showkeys-s
    进行试验(但我建议您在控制台模式下进行试验,而不是通过X图形界面)

    顺便说一下,您需要特殊权限才能更改键盘模式

    此外,确保将键盘模式恢复到更改前的状态,即使程序崩溃。否则,您可能会使控制台无法使用,并且您将被迫重新启动(或者,如果您启用了
    sshd
    并且网络上有另一台机器,则从网络上的另一台机器使用ssh连接到您的机器。)

    有关更多信息,请参见
    man console\u ioctl


    其他操作系统上也会有类似的功能。查看
    ioctl
    文档。

    在Linux中,您可以使用
    ioctl(fd,KDSKBMODE,mode)
    更改键盘模式,其中
    模式是
    K_RAW
    K_XLATE
    K_MEDIUMRAW
    K_UNICODE
    之一。如果将其设置为
    K_RAW
    ,则会收到原始扫描码;大多数按键按下时发送一个扫描码,松开时发送另一个扫描码

    在这种模式下,它是