Панель ввода внизу консоли в C - PullRequest
19 голосов
/ 29 декабря 2011

Дно окна

Некоторые приложения, такие как 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 печать в {окно, экран, терминал, консоль} внизу " в StackOverflow или в поисковой системе в Интернете не помогает.

Можно ли это сделать программно в C?

Отбрасывание ввода

Хотя некоторые из приведенных ниже решений могут перемещать символ в заданную позицию, существует проблема в том, что может потребоваться отменить ввод пользователя, а не оставлять его на экране. Как и в случае vim, при наборе «:w» и нажатии Enter не остается «:w» на экране.

Обновление. Это можно найти здесь: Как удалить текст после getstr () c ++ ncurses

Фокус окна - Неразрешенная часть проблемы

Пока вы печатаете ввод в нижней части окна, а текст вверху меняется, мы видим проблему смещения фокуса обратно вниз. Это отсутствует в решениях по состоянию на 29 декабря.


Обновление 1. Просто пытаюсь

  • запомнить предыдущую позицию курсора, затем
  • отобразить вывод, а затем
  • восстановить позицию

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

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

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

Относящиеся

Ответы [ 4 ]

12 голосов
/ 29 декабря 2011

Используя стандартные библиотеки, нет способа сделать это;используя ncurses, как вы уже предлагаете, это легко возможно;Я думаю этот урок объясняет это довольно хорошо.

7 голосов
/ 29 декабря 2011

Используя escape-последовательность ANSI, можно управлять положением курсора:

void gotoxy(int x, int y) {
    printf("\033[%d;%dH",x,y);
}

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

4 голосов
/ 10 января 2012

В этом ответе предлагается несколько иной подход, но он позволяет избежать многих сложностей, которые вы бы представили, используя текущий метод. Я надеюсь, что возможно реализовать эти упрощения.

Используйте termio для отключения канонического режима.

Пример кода (здесь) точно покажет, как настроить такой входной цикл. Этот пример «опрашивает и спит», что может означать задержку, если вы не уменьшите сон. Также я думаю , что вы можете использовать termio для установки таймаута (подождите секунду и верните 'no input' или дайте мне ввод NOW, если он наступит раньше). Но если вы действительно собираетесь следить за другим процессом, опрос может быть более гибким вариантом. Вы действительно можете опрашивать 30 раз в секунду и жить с ударом процессора .00001%, который он вызовет, но вам понравится простота и ошибка профилактика это дает.

Избегайте множественных потоков и процессов, таких как чума

Вам не нужно использовать 2 процесса / потока, если единственная проблема, которую вы пытаетесь решить, это то, что getch () блокирует. Это потребовалось бы, если бы было невозможно предотвратить блокировку входных функций. «Канонический» (основанный на правилах) означает, что действуют всевозможные «правила», например, «не вводите данные в программу, пока не нажмете ENTER». Для полноэкранного консольного приложения вы хотите, чтобы отключил все правила и делал все сам.

Поставь главный поток, отвечающий за окно ...

... Тогда вы можете просто использовать ansi escape csi коды , чтобы переместить курсор туда, где вы хотите. Предостережение: вы не можете писать в нижнем правом окне на экране. Все будет прокручиваться.

В программировании MS Windows есть раздражающая вещь, где только поток, который создает окно, может безопасно обновить его. На самом деле есть причина для этого. Будь то консоль или оконная система. рано или поздно, если у вас есть несколько потоков / обработок, попадающих на одно устройство вывода, вы прервете escape-последовательность (или вам придется создать дополнительный код для управления этим, что плохо), будете бороться за выходной «порт» и т. д. . вам нужен один поток для управления выводом.

если вы действительно заботитесь о том, что делает какой-то другой поток или процесс, просто отметьте это в цикле управления главной консоли. Например, у вас есть другой процесс, который вы просто хотите сообщить о своем прогрессе, запустите его из вашей программы, запишите его стандартный вывод и посмотрите на него; другой поток, просто заблокируйте что-то общее, что вы можете проверить в своей процедуре опроса. Черт возьми, если это просто байт, и он используется только для статусов, даже не блокируйте эту чертову штуку. Вы также можете добавить пару GOTO, просто чтобы показать свою индивидуальность: -)

предостережение Я не знаю, что ncurses будет хорошо с вами играть, если вы будете вручную связываться с termio? Я предполагаю, что он хочет сделать это сам. Никогда не пробовал смешивать. Если ваше приложение простое, вы можете использовать его самостоятельно без помощи, особенно если вы можете заставить ncurses делать то, что вы хотите. Я не эксперт по тем приложениям, о которых вы упомянули, но держу пари, что они все управляют микро.

1 голос
/ 06 января 2012

У меня была похожая проблема несколько недель назад при написании 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 ++.Просто замените его на C-совместимый FIFO.

Весь клиент можно найти здесь . Да, он компилируется в Linux, но он не работает.Я никогда не задумывался над тем, как использовать ncurses, прежде чем начать работу над ним.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...