¿Cómo hacer que las teclas de flecha y el retroceso funcionen correctamente cuando se solicite información al usuario en el progtwig C usando termios.h?

Por lo tanto, tengo el siguiente código que básicamente solo lee los caracteres que el usuario ingresa y los imprime hasta que se ingresa ‘q’.

#include #include #include #include int main(void) { char c; static struct termios oldtio, newtio; tcgetattr(0, &oldtio); newtio = oldtio; newtio.c_lflag &= ~ICANON; newtio.c_lflag &= ~ECHO; tcsetattr(0, TCSANOW, &newtio); printf("Give text: "); fflush(stdout); while (1) { read(0, &c, 1); printf("%c", c); fflush(stdout); if (c == 'q') { break; } } printf("\n"); tcsetattr(0, TCSANOW, &oldtio); return 0; } 

Al comienzo de la función principal, desactivo el modo canónico para que el usuario pueda ver su entrada cuando la da. También apago el eco para que cosas como “^ [[A” no se activen al presionar la tecla de flecha hacia arriba, por ejemplo. Esto funciona, pero también puedo mover el cursor a las filas superiores en una ventana de terminal y eso no es bueno. ¿Hay alguna forma de solucionar esto para que el usuario solo pueda moverse dentro de la fila actual?

Otro problema es el retroceso. Cuando lo presiono, el progtwig imprime un símbolo extraño (que supongo que es 0x7f) en lugar de borrar el carácter que queda a la ubicación actual del cursor. Probablemente debería manejar la salida de la tecla de retroceso en el progtwig de alguna manera, pero no sé cómo hacerlo, ya que es este número hexadecimal raro. ¿Algún consejo para esto?

Una opción que también he estado pensando para hacer que esto funcione es usar el modo canónico para que las teclas de flecha y las funcionalidades de retroceso se utilicen automáticamente. Sin embargo, el modo canónico funciona línea por línea y, por lo tanto, el texto no aparece hasta que el usuario pulsa “Intro”. Hasta ahora, no he descubierto ninguna manera de hacer que el usuario vea su entrada mientras escribe. ¿Es esto posible?

Y por favor, no ncurses ni sugerencias de readline. Quiero hacer esto usando termios.h.

¿Has mirado las páginas del manual? (debe ser man termios o buscar en algún lugar en línea )

Allí encontré la bandera ECHOE que se dice que tiene el siguiente efecto:

Si también se configura ICANON, el carácter BORRAR borra el carácter de entrada anterior y WERASE borra la palabra anterior.

Esto debería solucionar su problema de retroceso?

También sugiero que eche un vistazo a los ejemplos en la página de manual. Por ejemplo, podrías hacer lo siguiente:

 newtio.c_lflag &= ~(ECHO | ECHOE | ICANON); 

… para establecer más de una bandera a la vez en una sola línea. Sé que las páginas de manual son difíciles de leer para los principiantes, pero usted se acostumbrará a ellas y, cuanto más las use, más eficientes serán para buscar funciones C / POSIX, etc. (por si acaso, no las usa) de todas formas).

El problema de la tecla de flecha : tal vez puedas probar la función cfmakeraw() ; Su descripción parece prometedora. No he tenido tiempo de investigar más sobre las teclas de flecha. Sin embargo, tal vez encuentre algo más útil en la página de manual.

BTW: termios parece interesante, siempre me pregunté qué funciones usan ciertos progtwigs de línea de comandos; Aprendí algo por tu pregunta, gracias!

EDITAR

He investigado un poco más este fin de semana. El símbolo “extraño” impreso al presionar la tecla de retroceso es bastante fácil de ocultar. Es el valor ASCII 0x7f . Entonces agrega un simple

 if (c == 0x7f) { continue; } 

… para simplemente ignorar la tecla de retroceso. O manéjelo de manera que elimine el último carácter (vea el ejemplo de código a continuación).

Esta solución alternativa simple no funciona con las teclas de flecha, ya que no son caracteres ASCII: / Sin embargo, estos dos temas también me ayudaron a manejar este problema: tema 1 y tema 2 . Básicamente, al presionar las teclas de flecha se envía una secuencia de un par de caracteres a stdin (consulte el segundo enlace para obtener más información).

Aquí está mi código completo que (creo) funciona de la forma que deseas:

 #include  #include  #include  #include  int main(void) { char c; static struct termios oldtio, newtio; tcgetattr(0, &oldtio); newtio = oldtio; newtio.c_lflag &= ~ICANON; newtio.c_lflag &= ~ECHO; tcsetattr(0, TCSANOW, &newtio); printf("Give text:\n"); fflush(stdout); while (1) { c = getchar(); // is this an escape sequence? if (c == 27) { // "throw away" next two characters which specify escape sequence c = getchar(); c = getchar(); continue; } // if backspace if (c == 0x7f) { // go one char left printf("\b"); // overwrite the char with whitespace printf(" "); // go back to "now removed char position" printf("\b"); continue; } if (c == 'q') { break; } printf("%c", c); } printf("\n"); tcsetattr(0, TCSANOW, &oldtio); return 0; } 

Por cierto, puedes obtener las secuencias de escape completas mediante el siguiente código:

 int main(void) { char c; while (1) { c = getchar(); printf("%d", c); } return 0; } 

Creo que no tengo que decir que esta cosa completa es un truco bastante sucio y es fácil olvidarse de manejar algunas teclas especiales. Por ejemplo, en mi código no manejo las teclas de page-up/down o de home … -> el código anterior está lejos de estar completo, pero le da un punto para comenzar. También debería echar un vistazo a terminfo que puede proporcionarle una gran cantidad de la información necesaria; También debería ayudar con una solución más portátil. Como puede ver, esta cosa “simple” puede volverse bastante compleja. Por lo tanto, podría reconsiderar su decisión en contra de ncurses 🙂

En realidad, para manejar las teclas de flecha, tendría que implementar una buena cantidad de ncurses. Existen ventajas y desventajas: el principal inconveniente de usar ncurses en una aplicación de línea de comandos puede ser que generalmente borra la pantalla. Sin embargo, (n) curses proporciona una función de filtro . Hay un progtwig de ejemplo “test / filter.c” en la fuente de ncurses que ilustra esto utilizando la tecla de flecha izquierda como un carácter de borrado, y pasa la línea resultante al sistema () para ejecutar comandos simples. La muestra tiene menos de 100 líneas de código; parece más simple y más completa que los ejemplos anteriores.