NetBSD curses erase functions fixed

15 Mar 2020 • 3 min read

NetBSD PR lib/23910 was filed over 16 years ago. It describes how NetBSD curses fails to work with Vifm, a Vim interface for a curses GUI file manager. It's quite a nice idea after playing around with it some as Vim is my favourite text editor.

Since I was the main protagonist in bringing terminfo to NetBSD, I had a reasonable grasp on how our curses worked and I had looked at this bug before, but left scratching my head over it.

Since I looked at it, Valery Ushakov added a simple test case to demonstrate the issue.

#include <curses.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main()
{
	const char *errmsg = NULL;
	int err;

#define CHECKERR(_msg)					\
	do {						\
		if (err != OK) {			\
			errmsg = _msg " failed";	\
			goto out;			\
		}                                       \
	} while (0)


	WINDOW *scrw = initscr();
	if (scrw == NULL)
		errx(EXIT_FAILURE, "initscr failed\n");

	if (!has_colors()) {
		errmsg = "no colors";
		goto out;
	}

	err = start_color();
	CHECKERR("start_color");

	err = init_pair(2, COLOR_WHITE, COLOR_RED);
	CHECKERR("init_pair");

	int maxy, maxx;
	getmaxyx(stdscr, maxy, maxx);

	WINDOW *lborder = newwin(maxy - 1, 1, 0, 0);
	if (lborder == NULL) {
		errmsg = "newwin failed";
		goto out;
	}

	wbkgdset(lborder, COLOR_PAIR(2));

	werase(lborder);

	mvwaddstr(lborder, 0, 0, "A");
	mvwaddstr(lborder, maxy - 2, 0, "Z");

	wnoutrefresh(stdscr);
	wnoutrefresh(lborder);
	doupdate();

	sleep(5);
out:
	endwin();

	if (err != OK) {
		if (errmsg == NULL)
			errmsg = "unknown error";
		fprintf(stderr, "%s\n", errmsg);
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}

He then noted that using wbkgd instead of wbkgdset makes it work.

Now, the difference between the two is that wbkgd forces new attributes on the whole window background, whereas wbkgdset sets new attributes for new actions on the window. Keen observers will notice that the following function called is werase, which does what it says on the tin and erases the window but also resetting the attributes of the contents. So, I looked at this function and noticed the attribute checking code was faulty and a simple fix was enough to solve it!

Valery then pointed out that wclrtoeol probably needed fixing to and as it turned out, wclrtobot likewise. As it transpired, all 3 had different logic in working out when to erase something! We went through a few iterations of patches to harmonise the code and have settled on a macro that all 3 now share, so hopefully no more erasing issues in our curses.

As a follow on task, I should go through the NetBSD bug backlog and see if this fixes any other reported issues.