ncurses、面板、光标位置和轮询 STDIN
ncurses, panel, cursor position, and polling STDIN
我正在尝试使用 ncurses 编写一个程序,在主循环中轮询各种文件描述符。我设法 trim 将其归结为一个带有单个文件描述符的示例,以及预期的行为:
- 有两个 windows,由面板库管理。
- 按 space 将光标向右移动,当它到达第一个 window 的末尾时环绕。
- 按 't' 更改左上角的文本。
- 按 'q' 退出程序。
代码:
#include <stdio.h>
#include <stdlib.h>
#include <panel.h>
#include <unistd.h>
#include <poll.h>
#include <sys/eventfd.h>
#define POLL_STDIN
int main() {
int event_fd, cursor_x, cursor_y, ch, n_fds;
bool trigd;
uint64_t event;
WINDOW *win_a, *win_b;
PANEL *panel_a, *panel_b;
event_fd = eventfd(0, 0);
if (event_fd < 0) {
perror("eventfd");
exit(1);
}
struct pollfd poll_fds[2];
poll_fds[0].fd = STDIN_FILENO;
#ifdef POLL_STDIN
poll_fds[0].events = POLLIN;
#else
poll_fds[0].events = 0;
#endif
poll_fds[1].fd = event_fd;
poll_fds[1].events = POLLIN;
initscr();
halfdelay(1);
noecho();
win_a = newwin(10, 10, 0, 0);
win_b = newwin(10, 10, 0, 10);
panel_a = new_panel(win_a);
panel_b = new_panel(win_b);
cursor_x = 0;
cursor_y = 0;
wmove(win_a, cursor_y, cursor_x);
do {
for (int i=0; i<10; ++i) {
for (int j=0; j<10; ++j) {
mvwaddch(win_a, i, j, '.');
mvwaddch(win_b, i, j, '_');
}
}
mvwprintw(win_a, 0, 0, trigd ? "foo" : "bar");
update_panels();
doupdate();
wmove(win_a, cursor_y, cursor_x);
#ifdef POLL_STDIN
n_fds = poll(poll_fds, 2, -1);
#else
n_fds = poll(poll_fds, 2, 0);
#endif
if (n_fds < 0) {
perror("poll");
break;
}
ch = wgetch(win_a);
if (poll_fds[1].revents & POLLIN) {
if (read(event_fd, &event, 8) != 8) {
perror("read");
break;
}
trigd = !trigd;
}
if (' ' == ch) {
cursor_x = (cursor_x + 1) % 10;
} else if ('t' == ch) {
event = 1;
if (write(event_fd, &event, 8) != 8) {
perror("write");
break;
}
}
} while ('q' != ch);
endwin();
return 0;
}
如果未定义 POLL_STDIN
,程序将按预期运行。如果定义了,程序几乎一样,只是光标显示在windowB的右下角,并没有移动。按 t
后光标暂时移动到预期位置。
我查看了 运行 预处理器之后的程序,没有发现任何意外,只有 mvaddch
得到了扩展。
只是觉得忙等版本有点不雅,也想知道为什么一个看似无关的改动后,光标显示在错误的地方。
编辑:
我发现调用 getch
是使光标显示的原因。在 windows 上使用 nodelay
并调用 getch
两次后,该程序现在可以在两个版本中运行,并且第二个 getch
可以成为有条件的。这是更新后的代码:
#include <stdio.h>
#include <stdlib.h>
#include <panel.h>
#include <unistd.h>
#include <poll.h>
#include <sys/eventfd.h>
int main() {
int event_fd, cursor_x, cursor_y, ch, n_fds;
bool trigd;
uint64_t event;
WINDOW *win_a, *win_b;
PANEL *panel_a, *panel_b;
event_fd = eventfd(0, 0);
if (event_fd < 0) {
perror("eventfd");
exit(1);
}
struct pollfd poll_fds[2];
poll_fds[0].fd = STDIN_FILENO;
poll_fds[0].events = POLLIN;
poll_fds[1].fd = event_fd;
poll_fds[1].events = POLLIN;
initscr();
cbreak();
noecho();
win_a = newwin(10, 10, 0, 0);
win_b = newwin(10, 10, 0, 10);
panel_a = new_panel(win_a);
panel_b = new_panel(win_b);
cursor_x = 0;
cursor_y = 0;
wmove(win_a, cursor_y, cursor_x);
do {
for (int i=0; i<10; ++i) {
for (int j=0; j<10; ++j) {
mvwaddch(win_a, i, j, '.');
mvwaddch(win_b, i, j, '_');
}
}
mvwprintw(win_a, 0, 0, trigd ? "foo" : "bar");
update_panels();
doupdate();
wmove(win_a, cursor_y, cursor_x);
wrefresh(win_a);
n_fds = poll(poll_fds, 2, -1);
if (n_fds < 0) {
perror("poll");
break;
}
if (poll_fds[0].revents & POLLIN) {
ch = wgetch(win_a);
}
if (poll_fds[1].revents & POLLIN) {
if (read(event_fd, &event, 8) != 8) {
perror("read");
break;
}
trigd = !trigd;
}
if (' ' == ch) {
cursor_x = (cursor_x + 1) % 10;
} else if ('t' == ch) {
event = 1;
if (write(event_fd, &event, 8) != 8) {
perror("write");
break;
}
}
} while ('q' != ch);
endwin();
return 0;
}
我不确定这是否是显示光标的最佳方式,但如果没有其他人post,我会post将其作为答案。
编辑 2:
在@Thomas Dickey 的评论后编辑了 posterity 的代码。
我认为添加 wrefresh
是显示光标的正确方法,根据 this answer 判断(EDIT 在@Thomas Dickey 的评论后更新:wgetch
是没有必要的)。所以它归结为:
nodelay(win_a); // to make wgetch non-blocking
/* ... */
wrefresh(win_a); // to show the cursor
poll(poll_fds, 2, -1);
if (poll_fds[0].revents & POLLIN) {
ch = wgetch(win_a); // to get the actual character
}
我正在尝试使用 ncurses 编写一个程序,在主循环中轮询各种文件描述符。我设法 trim 将其归结为一个带有单个文件描述符的示例,以及预期的行为:
- 有两个 windows,由面板库管理。
- 按 space 将光标向右移动,当它到达第一个 window 的末尾时环绕。
- 按 't' 更改左上角的文本。
- 按 'q' 退出程序。
代码:
#include <stdio.h>
#include <stdlib.h>
#include <panel.h>
#include <unistd.h>
#include <poll.h>
#include <sys/eventfd.h>
#define POLL_STDIN
int main() {
int event_fd, cursor_x, cursor_y, ch, n_fds;
bool trigd;
uint64_t event;
WINDOW *win_a, *win_b;
PANEL *panel_a, *panel_b;
event_fd = eventfd(0, 0);
if (event_fd < 0) {
perror("eventfd");
exit(1);
}
struct pollfd poll_fds[2];
poll_fds[0].fd = STDIN_FILENO;
#ifdef POLL_STDIN
poll_fds[0].events = POLLIN;
#else
poll_fds[0].events = 0;
#endif
poll_fds[1].fd = event_fd;
poll_fds[1].events = POLLIN;
initscr();
halfdelay(1);
noecho();
win_a = newwin(10, 10, 0, 0);
win_b = newwin(10, 10, 0, 10);
panel_a = new_panel(win_a);
panel_b = new_panel(win_b);
cursor_x = 0;
cursor_y = 0;
wmove(win_a, cursor_y, cursor_x);
do {
for (int i=0; i<10; ++i) {
for (int j=0; j<10; ++j) {
mvwaddch(win_a, i, j, '.');
mvwaddch(win_b, i, j, '_');
}
}
mvwprintw(win_a, 0, 0, trigd ? "foo" : "bar");
update_panels();
doupdate();
wmove(win_a, cursor_y, cursor_x);
#ifdef POLL_STDIN
n_fds = poll(poll_fds, 2, -1);
#else
n_fds = poll(poll_fds, 2, 0);
#endif
if (n_fds < 0) {
perror("poll");
break;
}
ch = wgetch(win_a);
if (poll_fds[1].revents & POLLIN) {
if (read(event_fd, &event, 8) != 8) {
perror("read");
break;
}
trigd = !trigd;
}
if (' ' == ch) {
cursor_x = (cursor_x + 1) % 10;
} else if ('t' == ch) {
event = 1;
if (write(event_fd, &event, 8) != 8) {
perror("write");
break;
}
}
} while ('q' != ch);
endwin();
return 0;
}
如果未定义 POLL_STDIN
,程序将按预期运行。如果定义了,程序几乎一样,只是光标显示在windowB的右下角,并没有移动。按 t
后光标暂时移动到预期位置。
我查看了 运行 预处理器之后的程序,没有发现任何意外,只有 mvaddch
得到了扩展。
只是觉得忙等版本有点不雅,也想知道为什么一个看似无关的改动后,光标显示在错误的地方。
编辑:
我发现调用 getch
是使光标显示的原因。在 windows 上使用 nodelay
并调用 getch
两次后,该程序现在可以在两个版本中运行,并且第二个 getch
可以成为有条件的。这是更新后的代码:
#include <stdio.h>
#include <stdlib.h>
#include <panel.h>
#include <unistd.h>
#include <poll.h>
#include <sys/eventfd.h>
int main() {
int event_fd, cursor_x, cursor_y, ch, n_fds;
bool trigd;
uint64_t event;
WINDOW *win_a, *win_b;
PANEL *panel_a, *panel_b;
event_fd = eventfd(0, 0);
if (event_fd < 0) {
perror("eventfd");
exit(1);
}
struct pollfd poll_fds[2];
poll_fds[0].fd = STDIN_FILENO;
poll_fds[0].events = POLLIN;
poll_fds[1].fd = event_fd;
poll_fds[1].events = POLLIN;
initscr();
cbreak();
noecho();
win_a = newwin(10, 10, 0, 0);
win_b = newwin(10, 10, 0, 10);
panel_a = new_panel(win_a);
panel_b = new_panel(win_b);
cursor_x = 0;
cursor_y = 0;
wmove(win_a, cursor_y, cursor_x);
do {
for (int i=0; i<10; ++i) {
for (int j=0; j<10; ++j) {
mvwaddch(win_a, i, j, '.');
mvwaddch(win_b, i, j, '_');
}
}
mvwprintw(win_a, 0, 0, trigd ? "foo" : "bar");
update_panels();
doupdate();
wmove(win_a, cursor_y, cursor_x);
wrefresh(win_a);
n_fds = poll(poll_fds, 2, -1);
if (n_fds < 0) {
perror("poll");
break;
}
if (poll_fds[0].revents & POLLIN) {
ch = wgetch(win_a);
}
if (poll_fds[1].revents & POLLIN) {
if (read(event_fd, &event, 8) != 8) {
perror("read");
break;
}
trigd = !trigd;
}
if (' ' == ch) {
cursor_x = (cursor_x + 1) % 10;
} else if ('t' == ch) {
event = 1;
if (write(event_fd, &event, 8) != 8) {
perror("write");
break;
}
}
} while ('q' != ch);
endwin();
return 0;
}
我不确定这是否是显示光标的最佳方式,但如果没有其他人post,我会post将其作为答案。
编辑 2:
在@Thomas Dickey 的评论后编辑了 posterity 的代码。
我认为添加 wrefresh
是显示光标的正确方法,根据 this answer 判断(EDIT 在@Thomas Dickey 的评论后更新:wgetch
是没有必要的)。所以它归结为:
nodelay(win_a); // to make wgetch non-blocking
/* ... */
wrefresh(win_a); // to show the cursor
poll(poll_fds, 2, -1);
if (poll_fds[0].revents & POLLIN) {
ch = wgetch(win_a); // to get the actual character
}