C 中控制台底部的输入栏
窗口底部
一些应用程序(如 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 教程 没有提到这显然是可能的。
在 StackOverflow 或网络搜索引擎上查询“c print to {window,screen,terminal,console}bottom”没有帮助。
这可以用 C 语言编程完成吗?
丢弃输入
虽然下面的一些解决方案可以将字符移动到给定位置,但存在的问题是可能需要丢弃用户输入而不是将其保留在屏幕上。就像 vim
的情况一样,输入“:w
”并按 Enter 键不会在屏幕上留下“:w
”。
更新。可以在这里找到:如何在 getstr() c++ ncurses 之后删除文本
窗口焦点 - 问题的未解决部分
当您键入时窗口底部的输入和顶部的文本发生变化,我们看到将焦点移回底部的问题。截至 12 月 29 日的解决方案中不存在这一点。
更新 1。仅尝试
- 记住之前的光标位置,然后
- 显示输出,然后
- 恢复该位置
不是一个简单的解决方案:因为这些是不同的进程中,尝试检索光标位置不会影响其他进程执行期间发生的更改。
例如,如果父级接受输入,则子级不知道光标位置如何变化,并且在控制台的另一部分执行一行输出后无法恢复光标位置。
实现这一点将涉及一些进程间通信,如果有其他解决方案,它们可能会更好。
相关
- 从键盘获取输入而不等待输入相关,但不够具体。
- 如何使用 Python Urwid 进行类似 UI 的聊天? Urwid for Python 可以完成这项工作(根据JF Sebastian 在下面的评论中)。不幸的是不在C 中。
Window bottom
Some applications like vim, mutt, aptitude contain
- a top window section for output and
- a bottom section for the user to type in or for status display.
(Suppose there is one child process to output and another one to take user input. The purpose is to allow for updated output at the same time as you are typing the input or viewing the status.)
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 does not mention this to be obviously possible.
A query on "c print to {window,screen,terminal,console} bottom" at StackOverflow or at a web search engine isn't helpful.
Can this be done in C programmatically?
Discarding input
While some of the solutions below can move character to a given position, there is the problem that it may be needed to discard user input rather than leaving it on screen. Like in vim
's case, typing ":w
" and pressing Enter does not leave a ":w
" on the screen.
Update. This is found here: How to delete text after getstr() c++ ncurses
Window focus - the UNSOLVED PART OF THE PROBLEM
While you are typing the input at window bottom and the text at the top changes, we see the problem of moving the focus back to the bottom. This is absent in the solutions as of December 29.
Update 1. Just attempting to
- remember the previous cursor position, then
- display the output, and then
- restore the position
is not an easy solution: as these are different processes, attempts to retrieve cursor position don't affect the changes that happened during the other process execution.
Eg if parent takes input then the child doesn't know how the cursor position changed and can't restore the cursor position after performing a line of output at another part of the console.
Implementing this would involve some inter process communication and if there are other solutions they could be preferable.
Related
- Get Input from keyboard without waiting for input Related, but not specific enough.
- How to make a chat like UI using Python Urwid? Urwid for Python which does the job (per J.F. Sebastian in the comment below). Unfortunately not in C.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
使用标准库,没有办法做到这一点;正如您已经建议的那样,使用 ncurses 很容易实现;我认为 本教程 很好地解释了这一点。
Using the standard libraries, there's no way to do that; using ncurses, as you already suggest, it is easily possible; I think this tutorial explains it quite nicely.
使用 ANSI 转义序列可以控制光标的位置:
因此,一旦您计算出 终端高度和宽度 然后您可以使用该功能将光标定位在您喜欢的任何位置并打印内容。
Using ANSI escape sequence it's possible to control the position of the cursor:
So once you figure out the terminal height and width then you can use that function to position the cursor wherever you like and print stuff.
这个答案提出了一种稍微不同的方法,但它避免了使用当前方法带来的许多复杂性。我希望能够实施这些简化。
使用 termio 关闭规范模式。
示例代码(此处)将向您展示如何设置此类输入循环。此示例“轮询和睡眠”可能意味着延迟,除非您减少睡眠。另外,我认为您可以使用 termio 设置超时(稍等一下并返回“无输入”,或者如果输入较早,请立即给我输入)。但是,如果您确实要监视另一个进程,轮询可能是更灵活的选择。您实际上可以每秒轮询 30 次,并承受它所造成的 0.00001% 处理器命中,但您会喜欢这种简单性和错误它提供预防。
避免像瘟疫一样的多线程和进程
如果您要解决的唯一问题是 getch() 阻塞,则不需要使用 2 个进程/线程。如果无法防止输入函数阻塞,则需要这样做。 “规范”(基于规则)意味着各种方便的“规则”都有效,例如“在按下 ENTER 之前不要向程序提供输入”。对于全屏控制台应用程序,您想要关闭所有规则并自己完成所有操作。
让主线程负责窗口......
然后你就可以使用 ansi转义 CSI 代码 将光标定位回您想要的位置。注意:您无法写入屏幕右下的框。一切都会滚动。
MS Windows 编程中有一件烦人的事情,只有创建窗口的线程才能安全地更新它。这其实是有原因的。无论您是在谈论控制台还是窗口系统。迟早,如果您有多个线程/处理命中一个输出设备,您将中断转义序列(或者必须编写额外的代码来管理这是一件坏事),争夺输出“端口”等您需要一个线程来管理输出。
如果您确实关心其他线程或进程正在做什么,只需在主控制台管理循环中检查它即可。例如,您有另一个进程,您只想报告其进度,从您的程序启动它并捕获其标准输出并查看它;另一个线程,只需锁定共享的内容,您就可以在轮询例程中检查。哎呀,如果它只是一个字节并且仅用于状态,那么甚至不要锁定该死的东西。您也可以添加几个 GOTO,只是为了显示您的个性:-)
警告 如果您手动弄乱 termio,我不知道 ncurses 会很好地配合您吗?我猜它自己也想这样做。从未尝试过混合。如果您的应用程序很简单,您可以在没有帮助的情况下单独使用,特别是如果您可以努力让 ncurses 做您想做的事情。我不是你提到的那些应用程序的专家,但我敢打赌他们正在对一切进行微观管理。
This answer suggests a slightly different approach, but it avoids a lot of complication you'd introduce by using the current method. I hope it is possible to implement these simplifications.
Use termio to turn off canonical mode.
Sample code (here) will show you exactly how to set up such an input loop. This sample 'polls-and-sleeps' which could mean a delay unless you reduce the sleep. Also I think you can use termio to set a timeout (wait a second and return 'no input' or give me the input NOW if it comes earlier). But if you are really going to monitor another process, polling might be the more flexible option.. You can really poll 30x a second and live with the .00001% processor hit it is going to cause, but you will love the simplicity and bug prevention it gives.
Avoid multiple threads and processes like the plague
You don't need to use 2 processes/threads if the only problem you are trying to solve is the fact that getch() blocks. That would be required if it were impossible to prevent the input functions from blocking. 'Canonical' (rules based) means all sorts of handy 'rules' are in effect, like, 'don't give the input to the program until ENTER is hit'. For a full screen console app you want to turn off all the rules and do everything yourself.
Put the main thread in charge of the window...
... Then you can just use the ansi escape csi codes to position the cursor back to where you want it. Caveat: you can't write to the lower-right box in the screen. Everything will scroll.
There is an annoying thing in MS windows programming where only the thread that creates a window can safely update it. There is actually a reason for this. Whether you're talking console, or windowing system. sooner or later, if you have multiple threads/processing hitting one output device, you'll interrupt an escape sequence (or have to make extra code to manage this which is a bad thing), be fighting for the output 'port', etc. you need one thread to manage the output.
if you really care about what some other thread or process is doing, just check it in your main console management loop. For example of you have another process that you just want to report its progress, start it from your program and capture its stdouput and look at that; another thread, just lock something shared you can check in your polling routine. Heck if it's just a byte and it's only used for statuses, don't even lock the damn thing. You can throw in a couple of GOTO's too, just to show your individuality :-)
caveat I don't know that ncurses would play well with you if you are manually messing with termio? I would guess it wants to do that itself. Never tried mixing. If your app is simple you could go it alone without the help, especially if you can wrestle ncurses into doing what you want. I'm no expert on those apps you mention but I'd bet they are micromanaging everything.
几周前,我在编写在终端中运行的 IRC 客户端时遇到了类似的问题。我使用 Windows conio 库编写了它,但我相当确定这应该适用于诅咒。这个想法是控制台输出由单个线程处理,控制台输入由单独的线程处理。基本上,您所需要的只是一个循环,将 getch() 的返回值推送到在程序运行期间运行的互斥 FIFO 上。在显示线程中,您可以将按键从 FIFO 中弹出并按照您喜欢的方式处理它们。您不能使用像 fgets() 这样的标准函数,但它是解决您问题的非常可靠的解决方案。我可以根据要求提供完整(混乱)的源代码。
编辑:好的,这是 FIFO 推送的相关代码:
处理:
是的,它使用 std::deque。但这应该是唯一 C++ 特定的东西。只需将其替换为 C 兼容的 FIFO 即可。
整个客户端可以在这里找到。
是的,它确实可以在 Linux 中编译,但不起作用。在开始研究 ncurses 之前,我从来没有费心去弄清楚应该如何使用它。
I had a similar issue a few weeks ago while writing an IRC client that runs in the terminal. I wrote it using the Windows conio library, but I'm fairly sure this should be applicable to curses. The idea is that console output is handled by a single thread, and console input is handled in a separate thread. Basically, all you need is a loop that pushes the return of getch() onto a mutexed FIFO that runs for the duration of the program. In the display thread, you can pop the keypresses off the FIFO and handle them however you like. You can't use a standard function like fgets(), but it's a very solid solution to your problem. I can provide the full (messy) source on request.
Edit: alright, well here's the relevant code from the FIFO pushing:
And Handling:
Yes, it uses std::deque. That should be the only C++ specific thing though. Just replace it with a C compatible FIFO.
The entire client can be found here.
Yes, it DOES compile in Linux, but it doesn't work. I never bothered to figure out how ncurses should be used before I started work on it.