C中控制台底部的输入栏

时间:2011-12-29 11:17:01

标签: c unix input console command-line-interface

窗口底部

某些应用程序如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 tutorial没有提到这显然是可能的。

在StackOverflow或网络搜索引擎上查询“ c打印到{window,screen,terminal,console} bottom ”是没有用的。

可以通过编程方式在C中完成吗?

丢弃输入

虽然下面的一些解决方案可以将字符移动到给定位置,但是存在可能需要丢弃用户输入而不是将其留在屏幕上的问题。与vim的情况一样,输入“:w”并按Enter键不会在屏幕上显示“:w”。

更新。这可以在这里找到:How to delete text after getstr() c++ ncurses

窗口焦点 - 问题的未解决部分

当您在窗口底部键入输入并且顶部的文本发生更改时,我们会看到将焦点移回底部的问题。截至12月29日的解决方案中没有这一点。


更新1.只是尝试

  • 记住上一个光标位置,然后是
  • 显示输出,然后
  • 恢复位置

不是一个简单的解决方案:由于这些是不同的进程,尝试检索游标位置不会影响其他进程执行期间发生的更改。

例如,如果父接受输入,那么孩子不知道光标位置如何变化,并且在控制台的另一部分执行输出行后无法恢复光标位置。

实施这一过程将涉及一些进程间通信,如果有其他解决方案,则可能更为可取。

相关

4 个答案:

答案 0 :(得分:12)

使用标准库,没有办法做到这一点;使用ncurses,正如您已经建议的那样,它很容易实现;我认为this tutorial很好地解释了它。

答案 1 :(得分:7)

使用ANSI转义序列可以控制光标的位置:

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

因此,一旦你找到terminal height and width,你就可以使用该功能将光标定位在你喜欢的任何地方并打印东西。

答案 2 :(得分:4)

这个答案表明了一种略有不同的方法,但它避免了使用当前方法引入的大量复杂情况。我希望能够实现这些简化。

使用termio关闭规范模式。

示例代码(here)将向您展示如何设置此类输入循环。这个样本'民意调查 - 睡眠'可能意味着延迟,除非你减少睡眠。此外,我认为你可以使用termio来设置超时(等待一秒钟然后返回'无输入'或者如果它更早,则给我输入NOW)。但是如果你真的要监视另一个进程,轮询可能是更灵活的选择..你真的可以每秒轮询30x并使用它会导致的.00001%处理器命中率,但你会喜欢简单和bug它给予预防。

避免多个线程和进程,如瘟疫

如果您尝试解决的唯一问题是getch()阻塞,则不需要使用2个进程/线程。如果不可能阻止输入功能阻塞,则需要这样做。 'Canonical'(基于规则)意味着各种方便的'规则'都有效,比如'在命中ENTER之前不要给程序输入'。对于全屏控制台应用,您希望关闭所有规则并自行完成所有操作。

让主线程负责窗口......

...然后你可以使用ansi escape csi codes将光标定位回你想要的位置。警告:你不能写入屏幕右下方的框。一切都会滚动。

在MS Windows编程中有一件烦人的事情,只有创建窗口的线程才能安全地更新它。实际上有一个原因。无论您是在谈论控制台还是窗口系统。迟早,如果你有多个线程/处理命中一个输出设备,你将中断一个转义序列(或必须制作额外的代码来管理这是一件坏事),争取输出'端口'等。你需要一个线程来管理输出。

如果你真的关心其他线程或进程正在做什么,只需在主控制台管理循环中检查它。例如,您有另一个流程,您只想报告其进度,从您的程序启动它并捕获其stdouput并查看它;另一个线程,只需锁定共享的东西,你可以检查你的轮询例程。如果它只是一个字节并且它仅用于状态,那么甚至不要锁定该死的东西。你也可以投入几张GOTO,只是为了展示你的个性: - )

警告我不知道如果你手动搞乱termio,ncurses会很适合你吗?我猜它想要自己做。没试过混合。如果您的应用程序很简单,您可以在没有帮助的情况下单独行动,特别是如果您可以摔跤ncurses做您想做的事情。我不是你提到的那些应用程序的专家,但我敢打赌他们会对所有内容进行微观管理。

答案 3 :(得分:1)

几周前,我在编写终端中运行的IRC客户端时遇到了类似的问题。我使用Windows conio库编写它,但我很确定这应该适用于curses。这个想法是控制台输出由单个线程处理,控制台输入在单独的线程中处理。基本上,您只需要一个循环,将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。

整个客户端可以是found here. 是的,它可以在Linux中编译,但它不起作用。在开始研究之前,我从不打算弄清楚应该如何使用ncurses。