Search code examples
pythonncursescursespython-curses

Python curses' newwin not being displayed if stdscr hasn't been refreshed first


I'm currently playing around with curses and trying to understand how everything reacts. I was thinking I was making progress until I stumbled on this simple piece of code:

import curses

def main(stdscr: curses.window) -> None:
    stdscr.addstr(0, 0, "A")
    # stdscr.refresh()

    win = curses.newwin(2, 2, 1, 0)
    win.addstr(0, 0, "B")
    win.refresh()
    stdscr.getch()
    
curses.wrapper(main)

What I don't understand here is why the result is a screen with only an A, and no B. What I also find intriguing is that if I uncomment the commented line, I get both an A and a B.

Could someone please explain what's happening, or at least point me to some document that explains it.

Thanks in advance!


Solution

  • The "B" is actually written to the terminal, but immediately overwritten in the repainting done for the refresh done as a side-effect of stdscr.getch(). The manual page says that getch does that:

    If the window is not a pad, and it has been moved or modified since the last call to wrefresh, wrefresh will be called before another character is read.

    Initializing curses makes stdscr cleared on the first time it is painted. Again, in the manual page (for initscr):

    initscr also causes the first call to refresh(3x) to clear the screen.

    I generated this listing using a utility (unmap) which makes everything readable (actually spaces aren't converted), taking the output to the terminal using script (and limiting the display to a 5x5 screen):

    Script started on 2021-04-09 19:08:24-04:00 [TERM="screen.xterm-new" TTY="/dev/pts/0" COLUMNS="80" LINES="40"]
    \n
    \E[?1049h
    \E[22;0;0t
    \E[1;5r
    \E(B
    \E[m
    \E[4l
    \E[?7h
    \E[?1h
    \E=
    \E[39;49m
    \E[39;49m
    \E[37m
    \E[40m
    \E[1;1H     
    \E[2;1H     
    \E[3;1H     
    \E[4;1H     
    \E[5;1H    
    \E[?7l 
    \E[?7h
    \E[H
    \E[2dB
    \E[39;49m
    \E[37m
    \E[40m
    \E[H     
    \E[2;1H     
    \E[3;1H     
    \E[4;1H     
    \E[5;1H    
    \E[?7l 
    \E[?7h
    \E[HA
    \E[?1l
    \E>
    \E[39;49m\r
    \E[5d
    \E[K
    \E[5;1H
    \E[?1049l
    \E[23;0;0t\r
    \E[?1l
    \E>
    \nScript done on 2021-04-09 19:08:24-04:00 [COMMAND_EXIT_CODE="0"]
    \n
    

    The "B" comes out on this line:

    \E[2dB
    

    and the "A" on this:

    \E[HA
    

    (the other characters are part of escape sequences).

    Uncommenting that line finishes the repainting needed by initscr, and there's no remaining work for stdscr needed in the stdscr.getch() call (so none of the new window is overwritten).