Pregunta

Yo uso GNU Readline a la moda "seleccionar", mediante el registro de una función de devolución de llamada de esta manera:

rl_callback_handler_install("", on_readline_input);

Y a continuación, conectar rl_callback_read_char como la devolución de llamada para mi lazo para select() STDIN_FILENO. Eso es todo cosas bastante estándar, y funciona bien.

Ahora, mi programa imprime de forma asíncrona mensajes a la pantalla, a veces intercaladas con la entrada del usuario. Una sesión de "limpia" se vería así:

user input
SERVER OUTPUT
SERVER OUTPUT
user input
SERVER OUTPUT

Pero lo que si el usuario está a medio camino a través de una línea cuando llega la respuesta del servidor? A continuación, se pone feo:

user input
SERVER OUTPUT
user inSERVER OUTPUT
put
SERVER OUTPUT

He arreglado esto simplemente mediante la impresión de un salto de línea antes de la salida del servidor si el usuario hubiera tecleado nada (esto es fácil de decir comprobando rl_line_buffer), y luego hacer rl_forced_update_display() después de imprimir la salida del servidor. Ahora parece que esto:

user input
SERVER OUTPUT
user in
SERVER OUTPUT
user input
SERVER OUTPUT

Esto es mejor, pero todavía no es perfecto. El problema viene cuando el usuario ha introducido toda una línea, pero aún no se presione Enter - a continuación, se ve así:

user input
SERVER OUTPUT
user input
SERVER OUTPUT
user input
SERVER OUTPUT

Esto es malo, ya que se muestra al usuario que escriben tres comandos (tres respuestas de tres entradas es tan posible como tres respuestas para dos entradas, que es lo que realmente ocurrió).

Un truco desagradable (que funciona) es hacer esto:

user input
SERVER OUTPUT
user input - INCOMPLETE
SERVER OUTPUT
user input
SERVER OUTPUT

pensé que podría mejorar esta imprimiendo caracteres en lugar de la tecla de retroceso " - INCOMPLETE" ( '\ b'), pero eso no parece hacer nada en absoluto en mi terminal (gnome-terminal en Ubuntu Hardy). printf("ABC\b"); sólo imprime ABC, por la razón que sea.

Entonces, ¿cómo puedo borrar la línea de entrada incompleta? Ya sea mediante la impresión de alguna manera retrocesos (puedo averiguar cuántas para imprimir - es strlen(rl_line_buffer))?, O utilizando alguna facilidad Readline Todavía no sé

¿Fue útil?

Solución

Con espacios? Intente imprimir "\b \b" para cada carácter que desea "eliminar" en lugar de una sola '\b'.


Editar

¿Cómo funciona?
Supongamos que usted ha escrito "Hola, mundo!" al dispositivo de visualización y desea reemplazar "mundo!" con "Jim".

Hello, world!
             ^ /* active position */ /* now write "\b \b" */
               /* '\b' moves the active position back;
               // ' ' writes a space (erases the '!')
               // and another '\b' to go back again */
Hello, world
            ^ /* active position */ /* now write "\b \b" again */
Hello, worl
           ^ /* active position */ /* now write "\b \b" 4 times ... */
Hello, 
       ^ /* active position */ /* now write "Jim." */
Hello, Jim.
           ^ /* active position */

Portabilidad
No estoy seguro, pero la norma describe específicamente el comportamiento de '\ r' '\ b' y como se ha descrito en las respuestas a su pregunta.

Sección 5.2.2 Visualizador de caracteres semántica

> 1   The active position is that location on a display device where the next character output by
>     the fputc function would appear. The intent of writing a printing character (as defined
>     by the isprint function) to a display device is to display a graphic representation of
>     that character at the active position and then advance the active position to the next
>     position on the current line. The direction of writing is locale-specific. If the active
>     position is at the final position of a line (if there is one), the behavior of the display devic e
>     is unspecified.
>  
> 2   Alphabetic escape sequences representing nongraphic characters in the execution
>     character set are intended to produce actions on display devices as follows:
>     \a (alert) Produces an audible or visible alert without changing the active position.
>     \b (backspace) Moves the active position to the previous position on the current line. If
>        the active position is at the initial position of a line, the behavior of the display
>        device is unspecified.
>     \f ( form feed) Moves the active position to the initial position at the start of the next
>        logical page.
>     \n (new line) Moves the active position to the initial position of the next line.
>     \r (carriage return) Moves the active position to the initial position of the current line.
>     \t (horizontal tab) Moves the active position to the next horizontal tabulation position
>        on the current line. If the active position is at or past the last defined horizontal
>        tabulation position, the behavior of the display device is unspecified.
>     \v (vertical tab) Moves the active position to the initial position of the next vertical
>         tabulation position. If the active position is at or past the last defined vertical
>         tabulation position, the behavior of the display device is unspecified.
>  
> 3   Each of these escape sequences shall produce a unique implementation-defined value
>     which can be stored in a single char object. The external representations in a text file
>     need not be identical to the internal representations, and are outside the scope of this
>     International Standard.

Otros consejos

Después de un buen montón de piratería que era capaz de conseguir este mecanismo. Espero que otras personas lo encuentren útil. Ni siquiera se utilice seleccionar (), pero espero que obtendrá el punto.

#include <readline/readline.h>
    #include <readline/history.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>

    const char const* prompt = "PROMPT> ";

    void printlog(int c) {
        char* saved_line;
        int saved_point;
        saved_point = rl_point;
        saved_line = rl_copy_text(0, rl_end);
        rl_set_prompt("");
        rl_replace_line("", 0);
        rl_redisplay();
        printf("Message: %d\n", c);
        rl_set_prompt(prompt);
        rl_replace_line(saved_line, 0);
        rl_point = saved_point;
        rl_redisplay();
        free(saved_line);
    }


    void handle_line(char* ch) {
        printf("%s\n", ch);
        add_history(ch);
    }

    int main() {
        int c = 1;

        printf("Start.\n");
        rl_callback_handler_install(prompt, handle_line);

        while (1) {
            if (((++c) % 5) == 0) {
                printlog(c);
            }

            usleep(10);
            rl_callback_read_char();
        }
        rl_callback_handler_remove();
    }

Una cosa que puede hacer es utilizar \r para saltar al principio de la línea para la salida del servidor. A continuación, puede utilizar especificadores ancho campo para la pastilla derecha y la salida al resto de la línea. Esto, en efecto, sobrescribir lo que el usuario ya había entrado.

fprintf (stdout, "\r%-20s\n", "SERVER OUTPUT");

Es posible que desee fflush(stdout) para asegurar que los amortiguadores están en un estado coherente antes de hacerlo.

He intentado separar salida del servidor y la entrada del usuario con las ventanas de ncurses. salida del servidor se simula con un hilo. El programa hasta que haya introducido una línea que comienza con 'q'.

#include <unistd.h> 
#include <curses.h> 
#include <pthread.h> 

WINDOW *top, *bottom;

int win_update( WINDOW *win, void *data ){
  wprintw(win,"%s", (char*)data ); wrefresh(win);
  return 0;
}

void *top_thread( void *data ){
  char buff[1024];
  int i=0;
  while(1){
    snprintf(buff, 1024, "SERVER OUTPUT: %i\n", i++ );
    use_window( top, win_update, (void*)buff );
    sleep(1);
  }
  return NULL;
}

int main(){
  initscr();
  int maxy, maxx;
  getmaxyx( stdscr, maxy, maxx );

  top = newwin(maxy-1,maxx,0,0);
  wsetscrreg(top,0,maxy-1); idlok(top,1); scrollok(top,1);
  pthread_t top_tid;
  pthread_create(&top_tid, NULL, top_thread, NULL);

  bottom = newwin(1,maxx,maxy-1,0);
  char buff[1024], input[maxx];
  do{
    werase(bottom); wmove(bottom,0,0);
    wprintw(bottom,"input> " ); wrefresh(bottom);
    wgetnstr(bottom,input,sizeof(input));
    snprintf(buff, 1024, "user input: '%s'\n", input );
    use_window( top, win_update, (void*)buff );
  }while( input[0] != 'q' );

  endwin();
}

¿Alguna de estas funciones de ayuda?

  • rl_reset_line_state()
  • rl_clear_message()
  • rl_delete_text()
  • rl_kill_text()

Además, se puede mediar en la salida del servidor - han controlado la salida del servidor para que sólo aparece cuando y donde lo desee, en lugar de simplemente extiende sobre lo que el usuario está escribiendo? Por ejemplo, si la aplicación se ejecuta en modo maldiciones, ¿podría tener una ventana dividida con una o dos líneas en la parte inferior en una sub-ventana reservado para la entrada del usuario y el resto de la salida (salida del servidor y la entrada del usuario aceptado) en un segundo sub-ventana por encima de ella?

Esto también parece funcionar:

rl_clear_visible_line();
printf(...);
rl_reset_line_state();
rl_redisplay();
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top