Domanda

I am trying to write a console chat client using only readline and ANSI escape codes.

My goal is to just let the terminal handle the scrollback and scrolling of the chat history while always providing a readline prompt after the messages for new input.

I have tried the following with my two threads. My console input thread does:

printf("\x1B[s"); // Save cursor position
message = readline("Prompt > ");

And my message receiving thread does:

message = receive_message(); // Blocks for next message
printf("\x1B[u"); // Restore cursor to before the prompt
printf("\x1B[J"); // Erase readline prompt and input (down to bottom of screen)
printf("%s\n", message); // Print the message (where readline was)
printf("\x1B[s"); // Save new cursor position
rl_forced_update_display(); // Restore readline

The above works as long as the readline input doesn't wrap. When it wraps the restoring of the saved cursor position doesn't work as expected, it only seems to restore the horizontal position, not the vertical position.

How can I adapt the above code to work even if the input line wraps?

È stato utile?

Soluzione

This question turns out to include a far better solution. I have reproduced the code from the cited solution relevant to answering this question here:

message = receive_message();

// Solution
int need_hack = (rl_readline_state & RL_STATE_READCMD) > 0;

int saved_point = rl_point;
char *saved_line = rl_copy_text(0, rl_end);
rl_save_prompt();
rl_replace_line("", 0);
rl_redisplay();

printf(message);

rl_restore_prompt();
rl_replace_line(saved_line, 0);
rl_point = saved_point;
rl_redisplay();
free(saved_line);

For completeness' sake, the input thread is cleared of cursor saving and becomes simply:

message = readline("Prompt > ");

I don't know how I didn't see before posting my question.

Altri suggerimenti

I was inspired by how to get terminal width in C to figure out manually whether or not I have wrapped.

I now do the following when I need to print something before the readline prompt:

message = receive_message(); // Blocks for next message

/* Solution */
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); // Get terminal dimensions
printf("\r"); // Move cursor to the begining of the line
// Calculate the length of the prompt and cursor position
int readline_length = rl_point + strlen(rl_prompt);
// Integer divide cursor position by the terminal width
int wrapped_lines = readline_length/w.ws_col;
// If there are wraped lines
if (wrapped_lines > 0)
    // move the cursor up by that number of lines
    printf("\x1B[%dA", wrapped_lines);
printf("\r"); // Move cursor to the beginning of the line
printf("\x1B[J"); // Erase readline prompt and input (down to bottom of screen)

printf("%s\n", message); // Print the message (where readline was)
rl_forced_update_display(); // Restore readline

For completeness' sake, the input thread is cleared of cursor saving and becomes simply:

message = readline("Prompt > ");
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top