¿Cómo manejar la tecla presionada en una consola Linux en C?

4 minutos de lectura

avatar de usuario
matemático

Estoy usando la consola de Linux y me gustaría hacer un programa que genere caracteres aleatorios hasta que se presione ESC. ¿Cómo puedo hacer un controlador de teclado de este tipo?

  • similar y/o relacionado: entrada de teclado C sin bloqueo

    – jschmier

    10/01/2011 a las 16:00

avatar de usuario
jschmier

La disciplina de línea para un dispositivo terminal a menudo funciona en modo canónico de forma predeterminada. En este modo, el controlador de terminal no presenta el búfer al espacio de usuario hasta que se ve la nueva línea (Ingresar se presiona la tecla).

Puede configurar el terminal en modo sin procesar (no canónico) usando tcsetattr() para manipular el termios estructura. Borrando el ECHO y ICANON flags desactiva respectivamente el eco de los caracteres a medida que se escriben y hace que las solicitudes de lectura se satisfagan directamente desde la cola de entrada. Configuración de los valores de VTIME y VMIN a cero en el c_cc matriz provoca la solicitud de lectura (fgetc()) para volver inmediatamente en lugar de bloquear; efectivamente sondeando stdin. la llamada a fgetc() regresará EOF si un personaje no está disponible en la secuencia.

#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <time.h>

int getkey() {
    int character;
    struct termios orig_term_attr;
    struct termios new_term_attr;

    /* set the terminal to raw mode */
    tcgetattr(fileno(stdin), &orig_term_attr);
    memcpy(&new_term_attr, &orig_term_attr, sizeof(struct termios));
    new_term_attr.c_lflag &= ~(ECHO|ICANON);
    new_term_attr.c_cc[VTIME] = 0;
    new_term_attr.c_cc[VMIN] = 0;
    tcsetattr(fileno(stdin), TCSANOW, &new_term_attr);

    /* read a character from the stdin stream without blocking */
    /*   returns EOF (-1) if no character is available */
    character = fgetc(stdin);

    /* restore the original terminal attributes */
    tcsetattr(fileno(stdin), TCSANOW, &orig_term_attr);

    return character;
}

int main()
{
    int key;

    /* initialize the random number generator */
    srand(time(NULL));

    for (;;) {
        key = getkey();
        /* terminate loop on ESC (0x1B) or Ctrl-D (0x04) on STDIN */
        if (key == 0x1B || key == 0x04) {
            break;
        }
        else {
            /* print random ASCII character between 0x20 - 0x7F */
            key = (rand() % 0x7F);
            printf("%c", ((key < 0x20) ? (key + 0x20) : key));
        }
    }

    return 0;
}

Nota: este código omite la verificación de errores por simplicidad.

  • Esto parece funcionar, pero también parece proporcionar todo el búfer cada vez. Entonces, si presiono a, luego b, luego c, después de presionar c, aparece aababc

    – Jackie

    29 de junio de 2013 a las 14:03

  • @Jackie tal vez haga un while (getchar() != EOF); después character = fgetc(stdin);

    – Ciro Santilli Путлер Капут 六四事

    2 de julio de 2016 a las 16:45

  • ¡Muchas gracias por la gran respuesta concisa!

    – Brian Canard

    27 de enero de 2020 a las 0:49

cambie la configuración de tty para presionar una tecla:

int getch(void) {
      int c=0;

      struct termios org_opts, new_opts;
      int res=0;
          //-----  store old settings -----------
      res=tcgetattr(STDIN_FILENO, &org_opts);
      assert(res==0);
          //---- set new terminal parms --------
      memcpy(&new_opts, &org_opts, sizeof(new_opts));
      new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ICRNL);
      tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
      c=getchar();
          //------  restore old settings ---------
      res=tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
      assert(res==0);
      return(c);
}

  • no ICRNL entrar en el c_iflag campo, no el c_lflag ¿campo?

    – Moldeado Dan

    7 sep 2012 a las 14:17

avatar de usuario
Andrejs Cainikovs

obtener () ¿Quizás de la biblioteca Curses? Además, deberá usar notimeout() para indicarle a getch() que no espere hasta la siguiente pulsación de tecla.

  • Debe mencionar explícitamente que está hablando de la biblioteca (N)curses.

    –James Morris

    6 de junio de 2010 a las 13:56

  • nota: getch() de ncurses necesita que se inicialice la “pantalla” adecuada de ncurses, o no funcionará.

    – ShinTakezou

    6 de junio de 2010 a las 17:56

  • También debe inicializar la biblioteca, que es una variación de lo que dijo @ShinTakezou.

    –Jonathan Leffler

    6 de junio de 2010 a las 22:04


avatar de usuario
neo730

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <signal.h>

char * me = "Parent";

void sigkill(int signum)
{
    //printf("=== %s EXIT SIGNAL %d ===\n", me, signum);
    exit(0);
}

main()
{
    int pid = fork();
    signal(SIGINT, sigkill);
    signal(SIGQUIT, sigkill);
    signal(SIGTERM, sigkill);
    if(pid == 0) //IF CHILD
    {  
        int ch;
        me = "Child";
        while(1)
        {  
            ch = (rand() % 26) + 'A';   // limit range to ascii A-Z
            printf("%c",ch);
            fflush(stdout); // flush output buffer
            sleep(2);   // don't overwhelm
            if (1 == getppid())
            {  
                printf("=== CHILD EXIT SINCE PARENT DIED ===\n");
                exit(0);
            }
        }
        printf("==CHILD EXIT NORMAL==\n");
    }
    else //PARENT PROCESS
    {  
        int ch;
        if((ch = getchar())==27)
            kill(pid, SIGINT);
        //printf("==PARENT EXIT NORMAL (ch=%d)==\n", ch);
    }
    return(0);
}

En este programa solo necesitarás presionar enter después esc carbón, porque getchar()es una función de bloqueo. También puede eliminar o disminuir el tiempo de sueño para el proceso del niño según sus necesidades.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad