某些应用程序如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.只是尝试
不是一个简单的解决方案:由于这些是不同的进程,尝试检索游标位置不会影响其他进程执行期间发生的更改。
例如,如果父接受输入,那么孩子不知道光标位置如何变化,并且在控制台的另一部分执行输出行后无法恢复光标位置。
实施这一过程将涉及一些进程间通信,如果有其他解决方案,则可能更为可取。
答案 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)
这个答案表明了一种略有不同的方法,但它避免了使用当前方法引入的大量复杂情况。我希望能够实现这些简化。
示例代码(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。