I'm exploring with a modified program based on ncurses window tutorial to move a window without deleting and remaking it each time using mvwin
, and have run into the following questions:
erase
(comment 1 in code) or printing a newline at the start of each line. Looking at the docs, this looks like something a call to touchwin
/touchline
is supposed to solve, but don't. First question: what does touchwin
do if not that?redrawwin
, but the only thing that worked is adding another refresh right after erasing the screen (comment 2 in code). I dislike that method, it causes screen flickering, and it feels like there has to be a better method. Second question: is there a way to display a moving window after erasing the screen without another refresh?refresh
will display only the written line and not the moved window, but calling wrefresh(my_win)
will display both the moved window and the printed line. Third question: why does wrefresh
displays things outside the refreshed window?example code:
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;
}
exmaple for "leftover characters" after moving the window left twice:
moving window to position (6, 32)
┌────────┐┐┐
│ │││
└────────┘┘┘
The basic problem is that curses does not manage overlapping windows. The window which you are moving using mvwin
overlaps with stdscr
. When you move it, stdscr
is not repainted to reflect the fact that my_win
used to occupy some parts of the screen which it no longer does.
The panel
library is designed to solve this problem nicely, but as a workaround, you can ensure that stdscr
is repainted by using touchwin
and then refresh
. Repainting just a line is not effective unless you limit the program to moving the window up and down -- never left or right.
The ncurses test-programs include one which demonstrates how to use mvwin
(test/movewindow.c). The test-program keeps track of all of the windows and calls refresh_all
after calling mvwin
:
static void
repaint_one(WINDOW *win)
{
touchwin(win);
wnoutrefresh(win);
}
static void
refresh_all(WINDOW *win)
{
unsigned n;
for (n = 0; n < num_windows; ++n) {
if (all_windows[n].child != win) {
repaint_one(all_windows[n].child);
}
}
repaint_one(win);
doupdate();
}
By the way, the "documentation" referred to in the question and suggested answer is very old - from 2009. Your local machine probably has manual pages installed which are newer than that.