When I try to print some values in an ncurses
window with mvwprintf
, it will sometimes print extra characters after what it was expected to print, as if it were reading old memory. This should not happen however, as the value to be printed is strdup
-ed beforehand and should only contain a character and a NUL
to terminate the string.
The error also happens when using colours with start_color()
followed by init_pair
and attron(COLOR_PAIR(n))
calls, where the colour code is printed, when clearly it shouldn't be.
I could not find anything related to this on Google so I hope that somebody can find the error in there.
This is inside a two-level for
loop, where print_tile
returns a single char
.
// Convert the tile to a character to print
char str[2] = { print_tile(map.tiles[y][j]), 0 };
int colour = tile_colour(map.tiles[y][j]);
// Queue the character up for displaying
Vector2 pos = { x, y };
add_to_queue(display_queue, window, str, pos, colour);
This adds an item to the display queue:
void add_to_queue(Queue *queue, WINDOW *window, char *value, Vector2 coords, int colour)
{
DisplayItem *item = malloc(sizeof(DisplayItem));
item->window = window;
item->value = strdup(value);
item->coords = coords;
item->colour = colour;
pthread_mutex_lock(&mutex);
push(queue, item);
pthread_mutex_unlock(&mutex);
}
And this actually prints the current item in the queue:
void* display(void *arg)
{
Queue *queue = arg;
while (1) {
// Wait to receive an item to display
while (queue->size == 0);
pthread_mutex_lock(&mutex);
DisplayItem *item = queue_item(take(queue), DisplayItem);
// Exit when receiving the exit message
if (strcmp(item->value, DISP_QUIT) == 0)
break;
// Process the current message in the queue
wattron(item->window, COLOR_PAIR(item->colour));
mvwprintw(item->window, item->coords.y, item->coords.x, item->value);
wrefresh(item->window);
wattroff(item->window, COLOR_PAIR(item->colour));
free(item);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
The problem lies in the use of threads. curses/ncurses maintain state in static variables, and are not as a rule thread-safe. ncurses can be compiled to reduce the use of static variables, as noted in the manual page section on PORTABILITY:
When it is compiled like this (using the
--with-pthread
option), a library with a different name is built, e.g.,libncursest.so
orlibncurseswt.so
, which has a different binary interface than ncurses/ncursesw. "Most" programs can be compiled and run without source changes; there are "getter" functions for the global variables such asLINES
which are (where possible) implemented as macros to help with porting between the different types of library.