ncurses in C prints more than it should and prints colour codes

202 views Asked by At

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.

Screenshot

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;
}
1

There are 1 answers

2
Thomas Dickey On BEST ANSWER

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:

This implementation can be configured to provide rudimentary support for multi-threaded applications. See curs_threads(3x) for details.

When it is compiled like this (using the --with-pthread option), a library with a different name is built, e.g., libncursest.so or libncurseswt.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 as LINES which are (where possible) implemented as macros to help with porting between the different types of library.