Как я могу подавить строку чтения, чтобы что-то напечатать, а затем восстановить это?

Я пытаюсь написать клиент консольного чата, используя только escape-коды readline и ANSI.

Моя цель - просто позволить терминалу обрабатывать прокрутку и прокрутку истории чата, при этом всегда предоставляя подсказку readline после сообщений для нового ввода.

Я пробовал следующее с моими двумя потоками. Мой поток ввода консоли:

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

И моя ветка получения сообщений:

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

Вышеупомянутое работает до тех пор, пока ввод readline не переносится. Когда он оборачивается, восстановление сохраненной позиции курсора не работает должным образом, кажется, что восстанавливается только горизонтальное положение, а не вертикальное.

Как я могу адаптировать приведенный выше код для работы, даже если строка ввода переносится?


person Nicholas H.    schedule 13.03.2013    source источник
comment
См. этот вопрос для лучшего описания желаемого поведения. Однако вопрос не охватывает и не решает мою конкретную проблему, когда пользовательский ввод переносится на несколько строк.   -  person Nicholas H.    schedule 14.03.2013


Ответы (2)


Этот вопрос, оказывается, включает гораздо лучшее решение. Я воспроизвел код из процитированного решения, имеющий отношение к ответу на этот вопрос здесь:

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);

Для полноты картины входной поток очищается от сохранения курсора и становится просто:

message = readline("Prompt > ");

Я не знаю, как я не видел перед тем, как опубликовать свой вопрос.

person Nicholas H.    schedule 21.03.2013

Меня вдохновило то, как получить ширину терминала в C, чтобы вручную выяснить, действительно ли Я завернул.

Теперь, когда мне нужно что-то напечатать перед приглашением readline, я делаю следующее:

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

Для полноты картины входной поток очищается от сохранения курсора и становится просто:

message = readline("Prompt > ");
person Nicholas H.    schedule 14.03.2013