关于ncurses中的窗口移动和刷新

问题描述 投票:0回答:1

我正在探索基于 ncurses 窗口教程的修改程序来移动窗口,而无需每次使用

mvwin
删除和重新制作它,并且遇到了以下问题:

  1. 移动窗口会留下旧窗口的痕迹(请参见下面的代码示例)。我已经习惯了,需要用
    erase
    (代码中的注释 1)清除旧字符,或者在每行开头打印换行符。查看 docs,这看起来像是调用
    touchwin
    /
    touchline
    应该可以解决的问题,但事实并非如此。 第一个问题:如果不是这样,
    touchwin
    会做什么?
  2. 擦除整个屏幕,然后重新打印第一行并移动窗口将显示第一行,但不会显示刷新后的窗口。我尝试用
    redrawwin
    强制显示移动的窗口,但唯一有效的是在擦除屏幕后立即添加另一个刷新(代码中的注释 2)。我不喜欢这种方法,它会导致屏幕闪烁,感觉必须有更好的方法。 第二个问题:有没有办法在擦除屏幕后显示移动窗口而无需再次刷新?
  3. 在代码中的注释3中,移动窗口并重新打印第一行后,调用
    refresh
    将仅显示写入的行,而不显示移动的窗口,但调用
    wrefresh(my_win)
    将显示移动的窗口和打印的行。 第三个问题:为什么
    wrefresh
    显示刷新窗口之外的东西?

示例代码:

int main()
{   WINDOW *my_win;
    int startx, starty, width, height;
    int ch, err;

    initscr();
    cbreak();
    keypad(stdscr, TRUE);

    height = 3;
    width = 10;
    starty = (LINES - height) / 2;
    startx = (COLS - width) / 2;
    
    refresh();
    my_win = create_newwin(height, width, starty, startx);

    while((ch = getch()) != KEY_F(4))
    {
        erase();  // <--(1)
        refresh();  // <--(2)

        switch(ch)
        {   
            case KEY_LEFT:
                mvprintw(0, 0, "moving window to position (%d, %d)", starty, --startx);
                err = mvwin(my_win, starty, startx);
                break;
            case KEY_RIGHT:
                mvprintw(0, 0, "moving window to position (%d, %d)", starty, ++startx);
                err = mvwin(my_win, starty, startx);
                break;
            case KEY_UP:
                mvprintw(0, 0, "moving window to position (%d, %d)", --starty, startx);
                err = mvwin(my_win, starty, startx);
                break;
            case KEY_DOWN:
                mvprintw(0, 0, "moving window to position (%d, %d)", ++starty, startx);
                err = mvwin(my_win, starty, startx);
                break;
            default:
                mvprintw(0, 0, "UNKNOWN INPUT!");
        }

        if (err != NCURSES_OK)
        {
            mvprintw(0, 0, "Error %d while moving window", err);
        }

        wrefresh(my_win);  // <--(3)
    }
        
    endwin();
    return 0;
}

例如将窗口向左移动两次后的“剩余字符”:

moving window to position (6, 32)





                                ┌────────┐┐┐
                                │        │││
                                └────────┘┘┘


c ncurses
1个回答
0
投票

在 (n)curses 中,刷新意味着将与给定窗口关联的数据绘制到物理屏幕1。作为优化,已经标记为绘制的数据将不会再次绘制。

使用新数据

更新窗口中的位置会删除该位置上的任何标记。

erase
(
werase
) 用空白更新窗口中的每个位置(删除所有标记)。

touchwin
(
touchline
) 强制删除关联窗口内的每个标记,而不更改任何数据,以便下次 刷新 时重新绘制所有内容。

mvwin(my_win, ...)
wrefresh(my_win);
更改与
my_win
相关的数据,并将其绘制到物理屏幕上。
stdscr
不知道发生了这种情况,并且不知道重新绘制先前已标记为已绘制的另一个窗口“后面”的位置。这就是为什么您必须以某种方式删除这些标记。

wrefresh(my_win);
不会刷新
my_win
stdscr
,而是因为
getch
更新
)而执行
refresh
来自

mvprintw

如果窗口不是 pad,并且自上次调用
wrefresh

以来已移动或修改过,则将在读取另一个字符之前调用 wrefresh

请注意,所有这些交互在很大程度上取决于调用函数的
顺序

。典型的顺序是:

(可选)擦除/清除/触摸窗口
  1. 用新数据更新窗口
  2. 刷新(绘制)窗口
  3. 2
  4. 在您的程序中,您遵循以下顺序:

刷新
    getch(3)
  1. (通过
    stdscr
    ,第一次迭代后)
    擦除
  2. getch
  3. 刷新
  4. stdscr
  5. 更新
  6. stdscr
  7. 更新
  8. stdcr
  9. 刷新
  10. my_win
  11. 
    
  12. 如上所述,
my_win

的阻塞性质以及它刷新标准窗口的事实,使您能够实际

看到
getch生成的行(否则您有一个更新 - >擦除对于
mvprintw
)。

这是一个重构的示例:

stdscr

(80x10端子)

#include <ncurses.h> #define clearline(y) do { move(y, 0); clrtoeol(); } while (0) int main(void) { initscr(); cbreak(); keypad(stdscr, TRUE); curs_set(0); const int height = 3; const int width = 10; int y = (LINES - height) / 2; int x = (COLS - width) / 2; WINDOW *my_win = newwin(height, width, y, x); box(my_win, 0, 0); mvwaddstr(my_win, 1, 2, "Hello!"); while (1) { /* draw our background, then our boxed window */ refresh(); wrefresh(my_win); int ch = getch(); /* touch the 'background' lines behind our boxed window, * AFTER `getch` would have redrawn, but BEFORE updating `y` */ touchline(stdscr, y, height); switch (ch) { case KEY_UP: --y; break; case KEY_DOWN: ++y; break; case KEY_LEFT: --x; break; case KEY_RIGHT: ++x; break; default: } if ('q' == ch || ERR == ch) break; /* `touchline` above, and this `clearline` are a small optimization over * just calling `erase()` right here */ clearline(0); mvprintw(0, 0, "moving window to position (%d, %d)", y, x); if (OK != mvwin(my_win, y, x)) mvprintw(1, 0, "Error moving window <%p>", (void *) my_win); else clearline(1); } delwin(my_win); endwin(); }


1。这是一个小的简化:在窗口->物理屏幕管道之间实际上有一个虚拟屏幕。详情请参阅:

moving window to position (3, 34) ┌────────┐ │ Hello! │ └────────┘

2。

稍后刷新将覆盖由早期刷新绘制的任何数据,这会在物理屏幕上发生冲突。

© www.soinside.com 2019 - 2024. All rights reserved.