Linux, C, ncurses. Gnome terminal Ubuntu 22.6: printw printing twice in one instance

40 Views Asked by At

In the code example below, the ncurses printw located in the MouseLeftClicked function is printing twice instead of once as intended. I've scoured the Internet looking for a similar issue but have been unable to find anything. I'm sure I'm missing something fundamental to ncurses, which I'm in the beginning stages of learning. Any help is greatly appreciated. TIA

#include <stdbool.h>
#include <time.h>
#include <ncurses.h>

int main(void);
void MouseLeftClicked(int x, int y, char *caption);

int main(void)
{
    int ch;
    bool exit = false;
    
    MEVENT event;
    
    initscr();
    noecho();
    cbreak();   

    keypad(stdscr, TRUE);
    mousemask(ALL_MOUSE_EVENTS, NULL);
    
    clear();
    curs_set(0);
    
    mvprintw(0, 0, "F1 exits");
    
    start_color();          
    init_pair(1, COLOR_WHITE, COLOR_BLUE);

    attron(COLOR_PAIR(1));
    mvprintw(10, 1, " Exit ");
    attroff(COLOR_PAIR(1)); 
    refresh();
    
    do
    {
        ch = getch();

        if(ch == KEY_MOUSE)
        {
            mvprintw(2, 0, "Got KEY_MOUSE");
            if(getmouse(&event) == OK)
            {
                mvprintw(3, 0, "Got mouse event");
                if(event.bstate & BUTTON1_CLICKED)
                {   
                    mvprintw(4, 0, "Got click on button #1");
                    mvprintw(5, 0, "Mouse position: x=%d   y=%d   ", event.x, event.y);
                    refresh();
                    
                    if(event.x > 0 && event.x < 7 && event.y == 10)
                        MouseLeftClicked(event.x+1, event.y+1, " Exit ");
                    
                    ch = 0;
                }
            }
        }
        
        if(ch == KEY_F(1))
           exit = true;

    } while(! exit);
    
    curs_set(1);
    endwin();
    
    return 0;
}

void MouseLeftClicked(int x, int y, char *caption)
{
    struct timespec tim, tim2;
    tim.tv_sec  = 0;
    tim.tv_nsec = 999999998L;
    
    init_pair(1, COLOR_WHITE, COLOR_GREEN);
    attron(COLOR_PAIR(1));
    mvprintw(y, x, "%s", caption);
    //printw("%s", caption);
    attroff(COLOR_PAIR(1)); 
    refresh();
    
    //nanosleep(&tim, &tim2);
    
    //init_pair(1, COLOR_WHITE, COLOR_BLUE);
    //attron(COLOR_PAIR(1));
    //mvprintw(y, x, " Exit ");
    //attroff(COLOR_PAIR(1));       
}

Result screenshot

1

There are 1 best solutions below

1
paddy On

The mvprintw is not printing twice in that function. It is in fact printing text exactly where you told it to. That happens to not be in the same location as the original Exit text because you provided the co-ordinate event.x+1, event.y+1.

The reason why both are drawn in green is because you replaced color pair 1 with a different color and then called refresh(). The original Exit text uses that attribute, so the result is that it's re-colored.

If all you want to do is change the button color, you do not need to draw the text again with mvprintw. Just change the color pair and refresh:

init_pair(1, COLOR_WHITE, COLOR_GREEN);
refresh();

Further reading: init_pair documentation

I would highly recommend you use enum values or similar to name your color pairs, instead of hard-coding magic numbers. That will make the code more readable and less error-prone.

Example:

enum ColorPairs {
    kColorPairDefault = 0,  // special default color pair
    kColorPairButton,       // color of our button
};

Then, the initial setup looks like this:

    start_color();          
    init_pair(kColorPairButton, COLOR_WHITE, COLOR_BLUE);

    attron(COLOR_PAIR(kColorPairButton));
    mvprintw(10, 1, " Exit ");
    attroff(COLOR_PAIR(kColorPairButton)); 
    refresh();

And the update looks like this:

    init_pair(kColorPairButton, COLOR_WHITE, COLOR_GREEN);
    refresh();