dankamongmen / notcurses

blingful character graphics/TUI library. definitely not curses.
https://nick-black.com/dankwiki/index.php/Notcurses
Other
3.4k stars 113 forks source link

Resizing window causes ghost characters to remain #2751

Closed zhiayang closed 5 months ago

zhiayang commented 5 months ago

When I shrink the window, "ghost" characters remain when they should be cleared. I think this is best seen through a video:

https://github.com/dankamongmen/notcurses/assets/500236/8395768d-5394-4544-b898-0a5390955853

the % on the right should definitely not remain there.

I've tried calling both notcurses_refresh and ncplane_erase, to no avail. I'll try to come with a MWE asap, but for now, would you have any idea what's causing this?

Environment:


COLORTERM truecolor
LANG en_US.UTF-8
TERM xterm-256color
TERMINFO_DIRS :/opt/pacman/usr/share/terminfo
TERM_PROGRAM WezTerm
TERM_PROGRAM_VERSION 20240128-161958-c75c15b9
WEZTERM_CONFIG_DIR /Users/zhiayang/.config/wezterm
WEZTERM_CONFIG_FILE /Users/zhiayang/.config/wezterm/wezterm.lua
WEZTERM_EXECUTABLE /Applications/WezTerm.app/Contents/MacOS/wezterm-gui
WEZTERM_EXECUTABLE_DIR /Applications/WezTerm.app/Contents/MacOS
WEZTERM_PANE 83
WEZTERM_UNIX_SOCKET /Users/zhiayang/.local/share/wezterm/gui-sock-14024

NotCurses 3.0.9, Wezterm 20240128-161958-c75c15b9

EDIT: reproducer:


#include <unistd.h>
#include <notcurses/notcurses.h>

int main(int argc, char** argv)
{
    auto opts = notcurses_options {
        .loglevel = NCLOGLEVEL_ERROR,
        .flags = NCOPTION_NO_FONT_CHANGES | NCOPTION_SUPPRESS_BANNERS,
    };

    auto nc = notcurses_init(&opts, stdout);
    auto np = notcurses_stdplane(nc);

    while(true)
    {
        ncplane_cursor_move_yx(np, 0, 0);

        auto width = ncplane_dim_x(np) - 10;
        for(unsigned i = 0; i < width; i++)
            ncplane_putstr(np, "■");

        ncplane_putstr(np, " 69%");
        notcurses_render(nc);
        usleep(100 * 1000);
    }

    notcurses_stop(nc);
}
dankamongmen commented 5 months ago

huh! ok, that's definitely not desirable behavior. are you absolutely certain you're not redrawing the characters? your reproducer seems fine from a quick glance. a notcurses_refresh() doesn't resolve this (i see you claim as much)? hrmmm. that seems very surprising, and suggests the bad output is present in the ncplane. if you insert an ncplane_erase() at the top of the loop, does that resolve the problem?

dankamongmen commented 5 months ago

btw these have been really solid writeups, thanks for the quality bug submissions!

zhiayang commented 5 months ago

yep, if I add an ncplane_erase at the top, it works correctly

edit: ok, i tried the ncplane_erase trick in my actual program; it seems to work, no idea why it didn't work when i tried it before (maybe i forgot to recompile or something).

anyway, it causes the entire window to be blank while resizing (obviously), which isn't great, but if it's unfixable it's not the end of the world

dankamongmen commented 5 months ago

thanks for performing the test. i'm not suggesting it as a workaround; i just wanted to make sure i understood what was going on.

so the ncplane is keeping the character across the resize, which is unsurprising, as the resize is not cutting off the area where the "ghost character" lives. let me look at your reproducer more carefully..

dankamongmen commented 5 months ago

ok wait this seems to be working as expected.

each loop through, you're placing a ■ character from 0 to auto width = ncplane_dim_x(np) - 10. btw, you can get slightly better performance here using ncplane_putegc(), though you're unlikely to ever see it matter.

then you do a putstr() at the end of that.

so if you start at dimx = 60, width is 50. you put 50 squares and then your string. let's call your string just "%". so you have 50 squares and '%' on offsets 0 through 50.

you shrink the window by 1. width is 49. you write 49 squares and the '%'. but since you were at dimx - 10, and you only reduced by 1, the visible characters aren't removed. they will be once you shrink the window enough. i'm almost certain that starting with large enough dimx, you'll build up 10 '%'s, at which point you won't get any more.

so what you can do is either clear out everything after the '%' on the line, or run ncplane_erase(), a very heavy-handed (but simple) way of doing it. you shouldn't get blank screens while resizing so long as you rewrite all your output and call notcurses_render() between each processing of NCKEY_RESIZE.

soooooo i'm closing this up as works-as-intended. reopen it if you have any questions/difficulties or can't get it to work without blanking.

zhiayang commented 5 months ago

aaaaaa, now that you explain it it makes much more sense. I was under the (mistaken, i guess) impression that the underlying machinery would handle this for me. thank you!

sorry for taking up your time ><

zhiayang commented 5 months ago

okay, yep, I managed to get it to resize without blanking. i did it the "hammer" way, by ncplane_erase-ing in the resize callback, then immediately re-drawing the contents. thanks again 🙏🏻

dankamongmen commented 5 months ago

no problem, always happy to help out a user =] very glad we got this to work! hack on!

dankamongmen commented 5 months ago

aaaaaa, now that you explain it it makes much more sense. I was under the (mistaken, i guess) impression that the underlying machinery would handle this for me. thank you!

i'd love to, but i can't really =. i have no idea what you want to do in response to a resize. you want to move 10 away from the right boundary, but someone else might want to keep using everything available. etc.