我要做的是建立一个不断更新文本的聊天窗口,然后是一个输入窗口,用户可以输入一些消息添加到聊天中。
我想知道是否可以getstr()
与护士一起使用getstr()
只有在我按下回车键后才会向我返回一个字符串。
这将允许我持续监控新消息的TCP连接,然后在用户输入完整消息后向聊天添加消息。
我看过帖子提示timeout()
的用户,但这会导致getstr()
返回用户在一段时间内处于非活动状态时输入的内容。这不是我真正想要的,因为在这种情况下getstr()
在用户输入时仍会阻塞,并且如果用户停下来想一想他们正在写入消息的一半,那么该文本将由getstr()
在用户确认这是他/她想要通过按Enter键发送的消息之前。{/ p>
我对让getstr()
工作感到特别好奇,因为我不必手动处理删除/退格/光标移动操作。鉴于我的项目范围,投资有点过大。
答案 0 :(得分:1)
碰巧的是,我也在处理类似要求的事情(不过聊天应用程序)。
重申我之前说的话:如果你不需要渲染任何东西,你可以使用第二个线程。如果你做需要在等待输入时进行渲染,你需要自己进行输入处理。
为什么呢?因为ncurses只是通过stdin
/ stdout
与终端进行对话。这意味着您只需要一个光标来处理输入和输出,因此如果您移动光标以打印某些输出,则正在进行的输入将变得混乱。
但是解释并不难。自己渲染输入。这是我的首次通过解决方案的简化版本:
// Compile with -lncurses
#include <ncurses.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
struct input_line {
char *ln;
int length;
int capacity;
int cursor;
int last_rendered;
};
void make_buffer(struct input_line *buf) {
buf->ln = NULL;
buf->length = 0;
buf->capacity = 0;
buf->cursor = 0;
buf->last_rendered = 0;
}
void destroy_buffer(struct input_line *buf) {
free(buf->ln);
make_buffer(buf);
}
void render_line(struct input_line *buf) {
int i = 0;
for(; i < buf->length; i ++) {
chtype c = buf->ln[i];
if(i == buf->cursor) {
c |= A_REVERSE;
}
addch(c);
}
if(buf->cursor == buf->length) {
addch(' ' | A_REVERSE);
i ++;
}
int rendered = i;
// Erase previously rendered characters
for(; i < buf->last_rendered; i ++) {
addch(' ');
}
buf->last_rendered = rendered;
}
int retrieve_content(struct input_line *buf, char *target, int max_len) {
int len = buf->length < (max_len - 1) ? buf->length : (max_len - 1);
memcpy(target, buf->ln, len);
target[len] = '\0';
buf->cursor = 0;
buf->length = 0;
return len + 1;
}
void add_char(struct input_line *buf, char ch) {
// Ensure enough space for new character
if(buf->length == buf->capacity) {
int ncap = buf->capacity + 128;
char *nln = (char*) realloc(buf->ln, ncap);
if(!nln) {
// Out of memory!
return;
}
buf->ln = nln;
buf->capacity = ncap;
}
// Add new character
memmove(
&buf->ln[buf->cursor+1],
&buf->ln[buf->cursor],
buf->length - buf->cursor
);
buf->ln[buf->cursor] = ch;
++ buf->cursor;
++ buf->length;
}
int handle_input(struct input_line *buf, char *target, int max_len, int key) {
if(!(key & KEY_CODE_YES) && isprint(key)) {
add_char(buf, key);
return 0;
}
switch(key) {
case ERR: /* no key pressed */ break;
case KEY_LEFT: if(buf->cursor > 0) { buf->cursor --; } break;
case KEY_RIGHT: if(buf->cursor < buf->length) { buf->cursor ++; } break;
case KEY_HOME: buf->cursor = 0; break;
case KEY_END: buf->cursor = buf->length; break;
case '\t':
add_char(buf, '\t');
break;
case KEY_BACKSPACE:
case 127:
case 8:
if(buf->cursor <= 0) {
break;
}
buf->cursor --;
// Fall-through
case KEY_DC:
if(buf->cursor < buf->length) {
memmove(
&buf->ln[buf->cursor],
&buf->ln[buf->cursor+1],
buf->length - buf->cursor - 1
);
buf->length --;
}
break;
case KEY_ENTER:
case '\r':
case '\n':
return retrieve_content(buf, target, max_len);
}
return 0;
}
int get_line_non_blocking(struct input_line *buf, char *target, int max_len) {
while(1) {
int key = getch();
if(key == ERR) {
// No more input
return 0;
}
int n = handle_input(buf, target, max_len, key);
if(n) {
return n;
}
}
}
int main(void) {
initscr();
cbreak(); // Immediate key input
nonl(); // Get return key
timeout(0); // Non-blocking input
keypad(stdscr, 1); // Fix keypad
noecho(); // No automatic printing
curs_set(0); // Hide real cursor
intrflush(stdscr, 0); // Avoid potential graphical issues
leaveok(stdscr, 1); // Don't care where cursor is left
struct input_line lnbuffer;
make_buffer(&lnbuffer);
int lines_read = 0;
while(1) {
char ln[1024];
int len = get_line_non_blocking(&lnbuffer, ln, sizeof(ln));
if(len > 0) {
if(strcmp(ln, "exit") == 0) {
break;
}
mvaddstr(7 + lines_read, 5, ln);
lines_read ++;
}
move(5, 5);
render_line(&lnbuffer);
// Show that we are active
mvaddch(2, 2, '0' + (rand() % 10));
// (probably a good idea to sleep here)
}
destroy_buffer(&lnbuffer);
delwin(stdscr);
endwin();
refresh();
return 0;
}
有很多控制字符在那里没有实现(最明显的是INSERT),但是添加你认为对你的特定应用程序很重要的东西应该非常简单。另请注意,如果您需要unicode(推荐),则需要使用ncursesw及其替代功能。