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

时间:2015-07-24 20:30:30

标签: c linux keyboard pthreads simultaneous

我正在尝试使用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;
}

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

2 个答案:

答案 0 :(得分:2)

由于多个原因,所提供的示例和方法不会起作用:

  1. curses库不是线程安全的
  2. 只有一个输入设备打开(使选择呼叫无意义)。
  3. 可以使用newterm在多个设备上打开curses程序,并使用超时来轮询这些设备的输入。 ncurses-examples中的ditto.c程序对此非常有用。

答案 1 :(得分:2)

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

在此模式下,您可以自行跟踪按下哪些按键以及未按下哪些按键。

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

顺便说一下,你需要特殊的权限来改变键盘模式。

此外,请确保将键盘模式恢复为更改前的状态 - 即使程序崩溃也是如此。否则,您可能会使控制台无法使用,并且您将被迫重新启动(或者如果您已启用sshd并在网络上安装了另一台计算机,则可以从网络上的其他计算机进入您的计算机。)< / p>

有关详细信息,请参阅man console_ioctl

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