Ответ 1
Используя стандартные библиотеки, нет способа сделать это; используя ncurses, как вы уже сказали, это легко возможно; Я думаю, этот учебник объясняет это довольно хорошо.
Некоторые приложения, такие как vim, mutt, aptitude, содержат
(Предположим, что есть один дочерний процесс для вывода, а другой - для ввода пользователя. Цель состоит в том, чтобы разрешить обновленный вывод одновременно с вводом ввода или просмотром состояния.)
Actions Undo Package Resolver Search Options Views Help
C-T: Menu ?: Help q: Quit u: Update g: Download/Install/Remove Pkgs
|
|
|
|
|
┌─────────────┐ |
│Loading cache│ |
└─────────────┘ |
|
|
|
|
|
|
|
--------------------------------------------------------------------------- |
Initialising package states 100% |
+-------------------------------------------------------+
| some output here |
| |
| |
| |
| |
| |
|-------------------------------------------------------+
|:input here |
+-------------------------------------------------------+
Учебник Ncurses не упоминает, что это очевидно.
Запрос на "c print to {window, screen, terminal, console} bottom" в StackOverflow или в поисковой системе не помогает.
Можно ли это сделать на C программно?
В то время как некоторые из приведенных ниже решений могут перемещать символ в заданную позицию, существует проблема, что может потребоваться отменить ввод пользователя, а не оставить его на экране. Как и в случае vim
, набрав ":w
" и нажав Enter, не оставит на экране ":w
".
Update. Это можно найти здесь: Как удалить текст после getstr() С++ ncurses
Пока вы вводите ввод в нижней части окна, а текст в верхней части изменяется, мы видим проблему перемещения фокуса назад. Это отсутствует в решениях по состоянию на 29 декабря.
Обновление 1. Просто попробуйте
не простое решение: поскольку это разные процессы, попытки получить позицию курсора не влияют на изменения, произошедшие во время выполнения другого процесса.
Например, если родитель принимает вход, тогда ребенок не знает, как изменилось положение курсора и не может восстановить позицию курсора после выполнения строки вывода в другой части консоли.
Реализация этого будет связана с некоторой межпроцессной связью, и если есть другие решения, они могут быть предпочтительнее.
Используя стандартные библиотеки, нет способа сделать это; используя ncurses, как вы уже сказали, это легко возможно; Я думаю, этот учебник объясняет это довольно хорошо.
Используя escape-последовательность ANSI, можно управлять положением курсора:
void gotoxy(int x, int y) {
printf("\033[%d;%dH",x,y);
}
Итак, как только вы определяете высоту и ширину терминала тогда вы можете использовать эту функцию для размещения курсора везде, где хотите, и печатать материал.
Этот ответ предлагает несколько иной подход, но он избегает много осложнений, которые вы бы ввели с помощью текущего метода. Я надеюсь, что эти упрощения можно реализовать.
Пример кода (здесь) покажет вам, как настроить такой входной цикл. Этот пример "опросы и сны", который может означать задержку, если вы не уменьшите сон. Также я думаю, что вы можете использовать termio для установки тайм-аута (подождите секунду и не возвращайте "нет ввода" или дайте мне вход СЕЙЧАС, если он поступит раньше). Но если вы действительно собираетесь контролировать другой процесс, опрос может быть более гибким вариантом. Вы можете действительно опробовать 30 раз в секунду и жить с процессором .00001%, который он вызовет, но вам понравится простота и ошибка. предотвращение.
Вам не нужно использовать 2 процесса/нити, если единственная проблема, которую вы пытаетесь решить, - это тот факт, что getch() блокирует. Это потребовалось бы, если бы было невозможно предотвратить блокировку входных функций. "Canonical" (основанный на правилах) означает, что всевозможные полезные "правила" действуют, например: "не вводите ввод в программу до тех пор, пока не будет нажата ENTER". Для полноэкранного консольного приложения вы хотите отключить все правила и сделать все сами.
... Затем вы можете просто использовать ansi escape csi codes, чтобы поместить курсор обратно туда, где вы хотите. Предостережение: вы не можете писать в правом нижнем углу экрана. Все будет прокручиваться.
В программировании MS-окон есть раздражающая вещь, где только поток, создающий окно, может безопасно обновлять его. На самом деле есть причина для этого. Если вы говорите о консоли или системе окон. рано или поздно, если у вас есть несколько потоков/обработки, попавших на одно устройство вывода, вы прервите escape-последовательность (или вам придется сделать дополнительный код для управления этим, что плохо), боритесь за выходной порт и т.д. вам нужен один поток для управления выходом.
, если, вам действительно интересно, что делает какой-то другой поток или процесс, просто проверьте его в своем главном контуре управления контуром. Например, у вас есть другой процесс, который вы просто хотите сообщить о его прогрессе, запустите его из своей программы и запишите его stdouput и посмотрите на это; другой поток, просто заблокируйте что-то общее, вы можете проверить свою процедуру опроса. Если это просто байт, и он используется только для статусов, даже не блокируйте эту чертову вещь. Вы можете бросить пару GOTO, просто чтобы показать свою индивидуальность: -)
caveat Я не знаю, что ncurses будет хорошо играть с вами, если вы вручную возитесь с termio? Я предполагаю, что он хочет сделать это сам. Никогда не пробовал смешивать. Если ваше приложение просто, вы можете сделать это самостоятельно без помощи, особенно если вы можете бороться с ncurses в том, что хотите. Я не эксперт в тех приложениях, которые вы упоминаете, но я бы поспорил, что они микроменеджмент все.
У меня была аналогичная проблема несколько недель назад при написании IRC-клиента, который работает в терминале. Я написал его, используя библиотеку Windows conio, но я уверен, что это должно быть применимо к проклятиям. Идея заключается в том, что вывод консоли обрабатывается одним потоком, а вход в консоль обрабатывается в отдельном потоке. В принципе, все, что вам нужно, это цикл, который подталкивает возврат getch() к FIFO с мьютированием, который работает в течение всего времени программы. В потоке отображения вы можете вызывать нажатия клавиш FIFO и обрабатывать их, как вам нравится. Вы не можете использовать стандартную функцию, например fgets(), но это очень надежное решение вашей проблемы. Я могу предоставить полный (беспорядочный) источник по запросу.
Изменить: хорошо, ну вот соответствующий код из FIFO:
bool keyqueuewriting = false;
std::deque<int> keyqueue;
void grabkey( void* in )
{
int t;
while( true ){
t = getch();
#ifdef _WIN32
if( t == 224 || t == 0 )
{
t += getch() << 8;
}
#else
int off = 8;
if( t == 27 ){
int e = getch();
t += e << off;
off += 8;
while( e ==91 || (e >= '0' && e <= '9') || e == ';' )
{
e = getch();
t += e << off;
off += 8;
}
}
#endif
while( keyqueuewriting ){}
keyqueuewriting = true;
keyqueue.push_back( t );
keyqueuewriting = false;
}
}
И обработка:
while( keyqueuewriting ){}
keyqueuewriting = true;
while( keyqueue.size() > 0 )
{
shouldsleep = false;
int t = keyqueue.front();
keyqueue.pop_front();
switch( t )
{
case K_BACKSPACE:
if( pos > 0 ){
for( int i = pos-1; input[i] != 0; i++ ){input[i] = input[i+1];}
movecursorback( 1 );
pos -= 1;
} break;
case K_LEFT: if( pos > 0 ){ movecursorback( 1 ); pos -= 1; } break;
case K_RIGHT: if( input[pos] != 0 ) {movecursorforward( 1 ); pos += 1;} break;
case K_HOME: { gotoxy(0,SCREENHIG-1); pos = 0; } break;
case K_END: { int a = strlen( input ); /*gotoxy( 79,39 );*/ pos = a;} break;
case 3: exit(3); break;
default: if( t >= 0x20 && t < 0x80 ){
int a = strlen( input );
if( a > 998 )
a = 998;
int deadcode = 1;
input[999] = 0;
for( int i = a+1; i > pos; i-- ){input[i] = input[i-1];}
input[ pos ] = t;
movecursorforward( 1 );
pos++;
} break;
}
change = bufpos[curroom] - bufprev;
if( pos > 998 ) pos = 998;
if( pos - mescroll < 1 ) {mescroll += (pos-mescroll-1); gotoxy( pos-mescroll, SCREENHIG-1 );}
if( pos - mescroll > 78 ) {mescroll += (pos-mescroll-78); gotoxy( pos-mescroll, SCREENHIG-1 );}
if( mescroll < 0 ) {mescroll = 0; gotoxy( 0, SCREENHIG-1 ); }
savexy();
gotoxy( 0, SCREENHIG-1 );
char y = (input+mescroll)[79];
(input+mescroll)[79] = 0;
printf( "%s ", input+mescroll );
(input+mescroll)[79] = y;
returntosaved();
change2 = change;
bufprev = bufpos[curroom];
}
keyqueuewriting = false;
Да, он использует std:: deque. Это должно быть единственной спецификой С++. Просто замените его на C-совместимый FIFO.
Весь клиент может найти здесь. Да, он компилируется в Linux, но он не работает. Я никогда не беспокоился о том, как использовать ncurses, прежде чем я начну работать над ним.