2010-11-09 14 views
10

¿es posible tener una forma multiplataforma para manejar las teclas de retroceso y flechas dentro de un programa C o OCaml?Reconociendo las teclas de flecha con stdin

En realidad, una solución OCaml sería apreciada, pero muchas funciones estándar de Unix se envuelven directamente a las correspondientes llamadas API, por lo que no debería haber ningún problema para portar una solución C.

Lo que voy a lograr es atrapar las teclas de flecha para anular su comportamiento dentro del caparazón (reproduciendo la última línea u operaciones como estas). Creo que esta cosa cae antes del programa real y no es manejada por el código en sí, así que no sé si es posible.

El programa se compila bien en Linux, OS X y Windows (en cygwin) así que me gustaría hacerlo para todas las plataformas ..

Respuesta

8

He hecho algo bastante similar recientemente (aunque mi código es solo Linux). Debe configurar el stdin en modo no canónico para leer las teclas de flecha. Esto debería funcionar en OS X y Linux, y probablemente funcione en Cygwin, aunque no puedo asegurarlo.

open Unix 
let terminfo = tcgetattr stdin in 
    let newterminfo = {terminfo with c_icanon = false; c_vmin = 0; c_vtime = 0} in 
    at_exit (fun _ -> tcsetattr tsdin TCSAFLUSH terminfo); (* reset stdin when you quit*) 
    tcsetattr stdin TCSAFLUSH newterminfo; 

cuando el modo canónico está apagado, no es necesario esperar a una nueva línea con el fin de leer de la entrada estándar. c_vmin representa el número mínimo de caracteres para leer antes de regresar (es probable que desee poder leer un solo carácter a la vez) y c_vtime es el tiempo máximo de espera de lectura (en unidades de 0.1s).

También puede ser que desee establecer c_echo en false para que la flecha pulsaciones de teclas se imprimen al terminal (pero entonces usted tendrá que imprimir manualmente todo lo demás.

mayoría de los terminales representan flecha pulsaciones de teclas utilizando ANSI escape sequences. Si ejecuta cat sin argumentos y empezar a golpear las teclas de flecha se puede ver las secuencias de escape utilizadas. por lo general son

up - "\027[A" 
down - "\027[B" 
left - "\027[D" 
right - "\027[C" 

Donde '\ 027' es el valor ASCII para esc

+0

Casi logré hacer que esto funcione, pero ¿cómo debo gestionar el eco manual del personaje? Pregunto esto porque el stdin se reenvía a un analizador que analiza línea por línea y lo interpreta. Debo colocar en el medio e imprimir cada char? Pero luego, cuando el usuario presione la tecla up, debería reemplazar el carácter ya impreso por algo diferente. – Jack

+0

Al volver a leer sus preguntas, creo que usar readline/ledit es probablemente su mejor opción. Pero si quiere hacer las cosas manualmente, entonces tendrá que controlar manualmente el stdin de su analizador sintáctico. Lee de stdin, comprueba si hay una secuencia de escape en la parte superior de la pantalla, directamente al stdin del analizador. Estoy haciendo algo similar aquí: https://github.com/aplusbi/reflow/blob/master/reflow.ml en la función 'main', donde estoy leyendo stdin en una cadena y luego escribiéndola en un archivo_descr . Probablemente termines haciendo lo mismo, pero primero verificando las secuencias de escape. –

+0

esos caracteres de flecha son en realidad de ECMA 48 standartd aquí http://man7.org/linux/man-pages/man4/console_codes.4.html se puede ver qué caracteres de control de ECMA 48 admite Linux: parece que mover el cursor a la izquierda 5 caracteres que puede hacer \ 027 [5D – aeroson

5

Use ncurses para extraer las secuencias de las capacidades clave de flecha y luego buscar para ellos cuando lees stdin. Probablemente sea más fácil usar algo como libedit o readline, y déjalo que lidie con el manejo de teclas.

+0

Un par de notas adicionales sobre la solución ncurses: en una sola plataforma, es un terminal cruzado, que se ocupa de las diferencias de terminales peludas para usted. Además, hay versiones disponibles para Windows para que también pueda tener las funciones de edición en las consolas de Windows. –

2

La forma estándar de admitir la entrada del teclado más allá de las líneas de caracteres imprimibles es a través de la biblioteca ncurses, que tiene un Ocaml binding. Otra posibilidad común es la biblioteca readline (la más famosa utilizada por Bash).

Si todo lo que hace es leer entrada línea por línea, pero quiere que sus usuarios tengan un editor de línea decente, no es necesario incluir ningún soporte en su programa. En su lugar, solo dígales a sus usuarios que utilicen un programa contenedora como rlwrap (que se basa en readline) o ledit. Estas envolturas proporcionan edición de línea e historial, las dos características que enumera como sus requisitos. Le recomendaría que cree un procesamiento de entrada en su programa solo si desea algo más elegante, como la finalización específica del programa cuando el usuario presiona , pestaña.

1

Retroceso es un carácter ASCII y se colocará en stdin como cualquier otro personaje. La secuencia de escape de caracteres '\b' es el carácter de retroceso.

Para las teclas del cursor, estas no están asociadas a ningún carácter de control, por lo tanto, no genere datos en la secuencia stdin. El acceso de bajo nivel es necesariamente específico de la plataforma, aunque existen bibliotecas multiplataforma que abstraen las diferencias de la plataforma. Creo que ncurses está disponible para todas las plataformas que mencionaste.

+1

Estoy bastante seguro de que cualquier terminal en el modo canónico no colocará un carácter de retroceso en stdin. El texto está almacenado en el búfer, por lo que si escribe "abcd \ b \ n", obtendrá "abc \ n" en su programa. –

+0

@Niki: Oops, paso la mayor parte de mi tiempo codificando sistemas integrados conectados al software de emulador de terminal tonta. – Clifford

Cuestiones relacionadas