ncurses,面板,光标位置和轮询STDIN

时间:2018-12-25 12:19:35

标签: c ncurses

我正在尝试使用ncurses编写一个程序,该程序轮询主循环中的各种文件描述符。我设法用一个文件描述符将其缩减为一个示例,并且达到了预期的行为:

  • 有两个窗口,由面板库管理。
  • 按空格键可将光标向右移动,到达第一个窗口的末尾时会四处滚动。
  • 按'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,则程序将按预期运行。如果已定义,则程序的工作原理几乎相同,但是光标显示在窗口B的右下角,并且不会移动。按下t后,光标会暂时移至预期位置。

运行预处理程序后,我查看了该程序,发现没有任何意外,只有mvaddch被扩展了。

我只是认为等待中的版本有点不雅观,并且还想知道为什么在看似无关的更改之后光标显示在错误的位置。

编辑:

我发现调用getch是使光标显示的原因。在两个窗口上都使用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;
}

我不确定这是否是显示光标的最佳方法,但是如果没有其他人发布它,我将其发布为答案。

EDIT2:

在@Thomas Dickey评论后编辑的后代代码。

1 个答案:

答案 0 :(得分:0)

我认为添加wrefresh是显示光标的正确方法,由this answer判断(编辑是在@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
}