如何在 c 中同时识别来自多个键的输入 (linux)
how can one recognize inputs from multiple keys simultaneously in c (linux)
我正在尝试使用 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 语言中同时识别多个按键?任何关于如何做到这一点的例子将不胜感激:)
由于不止一个原因,给出的示例和方法不起作用:
- curses 库将不是线程安全的
- 只打开了一个输入设备(使 select 调用毫无意义)。
使用 newterm, and use timeouts to poll for input from those devices. The ditto.c program in ncurses-examples 可以在多台设备上打开 curses 程序,阅读本文很有用。
在 Linux 中,您可以使用 ioctl(fd, KDSKBMODE, mode)
更改键盘模式,其中 mode
是 K_RAW
、K_XLATE
、K_MEDIUMRAW
之一或 K_UNICODE
。如果将其设置为 K_RAW
,那么您将收到原始扫描码;大多数键在按下时发送一个扫描码,在松开时发送另一个扫描码。
在此模式下,由您来跟踪哪些键被按下,哪些键未被按下。
各个键发送的精确扫描码可能因键盘而异。您可以使用 showkeys -s
进行试验(但我建议在控制台模式下进行,而不是通过 X 图形界面)。
顺便说一句,您需要特殊权限才能更改键盘模式。
此外,请确保将键盘模式恢复到更改之前的状态——即使您的程序崩溃了。否则,您可能会导致您的控制台无法使用,并且您将被迫重新启动(或者从您网络上的另一台机器通过 ssh 连接到您的机器,如果您启用了 sshd
并且您的网络上有另一台机器。)
有关详细信息,请参阅 man console_ioctl
。
在其他操作系统上也会有类似的功能。查看 ioctl
文档。
我正在尝试使用 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 语言中同时识别多个按键?任何关于如何做到这一点的例子将不胜感激:)
由于不止一个原因,给出的示例和方法不起作用:
- curses 库将不是线程安全的
- 只打开了一个输入设备(使 select 调用毫无意义)。
使用 newterm, and use timeouts to poll for input from those devices. The ditto.c program in ncurses-examples 可以在多台设备上打开 curses 程序,阅读本文很有用。
在 Linux 中,您可以使用 ioctl(fd, KDSKBMODE, mode)
更改键盘模式,其中 mode
是 K_RAW
、K_XLATE
、K_MEDIUMRAW
之一或 K_UNICODE
。如果将其设置为 K_RAW
,那么您将收到原始扫描码;大多数键在按下时发送一个扫描码,在松开时发送另一个扫描码。
在此模式下,由您来跟踪哪些键被按下,哪些键未被按下。
各个键发送的精确扫描码可能因键盘而异。您可以使用 showkeys -s
进行试验(但我建议在控制台模式下进行,而不是通过 X 图形界面)。
顺便说一句,您需要特殊权限才能更改键盘模式。
此外,请确保将键盘模式恢复到更改之前的状态——即使您的程序崩溃了。否则,您可能会导致您的控制台无法使用,并且您将被迫重新启动(或者从您网络上的另一台机器通过 ssh 连接到您的机器,如果您启用了 sshd
并且您的网络上有另一台机器。)
有关详细信息,请参阅 man console_ioctl
。
在其他操作系统上也会有类似的功能。查看 ioctl
文档。