2015-07-24 1 views
0

J'essaie de créer ping pong en C en utilisant ncurses, et j'ai maintenant un énorme revers parce que je n'arrive pas à comprendre comment je peux autoriser deux joueurs pour déplacer les pads simultanément. Ce que j'ai essayé est de créer un thread séparé qui utiliserait ensuite select pour détecter toutes les presses tamponnées, puis mettre cela dans un tableau contenant mes contrôles. Cependant, il ne lit que la première clé et ne reconnaît pas l'autre.comment reconnaître les entrées de plusieurs clés simultanément dans c (linux)

#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; 
} 

Comment est-ce que je peux reconnaître plus d'une touche en même temps? Tout exemple sur la façon dont cela peut être fait serait grandement appréciée :)

+1

TL; DR! Veuillez essayer de créer un [exemple minimal, complet et vérifiable] (http://stackoverflow.com/help/mcve) et nous montrer. Quoi qu'il en soit, il n'est pas supporté par la norme C, vous devez utiliser des fonctions spécifiques à la plate-forme pour lire l'état du clavier. –

Répondre

1

Dans Linux, vous pouvez modifier le mode de clavier à l'aide ioctl(fd, KDSKBMODE, mode)mode est l'un des K_RAW, K_XLATE, K_MEDIUMRAW ou K_UNICODE. Si vous le définissez sur K_RAW, vous recevrez des codes d'échecs bruts; la plupart des touches envoient un code de balayage lorsqu'elles sont pressées et un autre scancode lorsqu'il est relâché.

Dans ce mode, il vous appartient de garder la trace des touches enfoncées et de celles qui ne sont pas enfoncées.

Les codes de correction précis envoyés par les touches individuelles peuvent varier légèrement d'un clavier à l'autre. Vous pouvez utiliser showkeys -s pour expérimenter (mais je recommande de le faire à partir du mode console, plutôt que via l'interface graphique X).

En passant, vous aurez besoin de privilèges spéciaux pour changer de mode de clavier. De plus, assurez-vous de restaurer le mode du clavier tel qu'il était avant de le changer, même si votre programme plante. Sinon, vous risquez de rendre votre console inutilisable et vous devrez redémarrer (ou ssh dans votre machine à partir d'une autre machine de votre réseau, si vous avez activé sshd et que vous avez une autre machine sur votre réseau.)

Voir man console_ioctl pour plus d'informations.

Il y aura des fonctionnalités similaires sur d'autres systèmes d'exploitation. Regardez autour de vous dans la documentation ioctl.

+0

Ok, cela semble utile, seriez-vous si gentil de fournir un petit exemple en C :) – Linus

+0

Eh bien, voici la source de 'showkey': https://kernel.googlesource.com/pub/scm/linux/kernel/ git/legion/kbd/+/1.10/src/showkey.c C'est un joli petit exemple. Il ne fait pas le suivi des clés, mais c'est la partie facile.(Par exemple, vous pourriez utiliser un petit vecteur de booléens.) Au fait, l'autorepeat n'est pas affecté par K_RAW, donc si vous appuyez sur la touche, vous continuerez à avoir des scancodes de keydown périodiques. Cela peut être utile si vous ne voulez pas garder trace de l'état des clés. – rici

+0

Puis-je simplement le compiler en utilisant le makefile fourni dans le code source ou y a-t-il un moyen de le compiler directement? #include "getfd.h" ne fonctionnera évidemment pas si je le compile individuellement :) – Linus

1

L'exemple et l'approche donnée ne fonctionnera pas pour plus d'une raison:

  1. la bibliothèque curses ne sera pas thread-safe
  2. Il n'y a qu'un seul périphérique d'entrée ouvert (rendant un appel sélectif inutile).

Un programme curses peut être ouvert sur plusieurs périphériques en utilisant newterm, et utiliser des délais d'attente pour interroger les entrées de ces périphériques. Le programme ditto.c dans ncurses-examples serait une lecture utile pour cela.

+0

il n'était pas destiné à être thread-safe, c'était juste moi folling autour avec des idées pour créer un programme acceptant plusieurs entrées en C. Merci pour les liens en passant. – Linus