You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ubuntu 22.6下ncurses程序鼠标点击触发printw重复打印问题求助

ncurses鼠标左键点击触发重复打印问题求助

我正在学习ncurses库,在Ubuntu 22.04系统的Gnome终端中运行以下C语言代码时遇到问题:点击指定区域的"Exit"按钮时,MouseLeftClicked函数中的mvprintw会被触发两次,而非预期的一次。我已经在网上搜索类似问题但没找到解决方案,推测是遗漏了ncurses的某些基础知识点,恳请各位帮忙解决。

#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));       
}

结果截图:
鼠标点击重复触发的界面截图


问题原因与解决方案

问题核心在于两点:

  1. 鼠标掩码捕获了重复事件:使用ALL_MOUSE_EVENTS会同时捕获鼠标按下、释放等多个事件,而BUTTON1_CLICKED的判断会在这些事件中重复触发,导致函数被调用两次。
  2. 重复初始化颜色对:在MouseLeftClicked函数中反复调用init_pair(1)会干扰颜色对的状态,可能引发界面更新异常。

具体修复步骤:

  • 精简鼠标掩码:将mousemask(ALL_MOUSE_EVENTS, NULL);改为只捕获需要的点击事件,比如:

    mousemask(BUTTON1_CLICKED, NULL);
    

    若只想响应鼠标按下动作,可改用BUTTON1_PRESSED作为掩码,同时将判断条件改为event.bstate & BUTTON1_PRESSED。

  • 统一颜色对初始化:将颜色对初始化移到main函数中,避免重复调用init_pair。比如在start_color()后新增绿色背景的颜色对:

    start_color();          
    init_pair(1, COLOR_WHITE, COLOR_BLUE);
    init_pair(2, COLOR_WHITE, COLOR_GREEN);
    

    然后MouseLeftClicked函数中直接使用COLOR_PAIR(2):

    void MouseLeftClicked(int x, int y, char *caption)
    {
        attron(COLOR_PAIR(2));
        mvprintw(y, x, "%s", caption);
        attroff(COLOR_PAIR(2)); 
        refresh();
    }
    
  • 优化刷新逻辑:减少不必要的refresh()调用,可考虑在主循环末尾统一刷新,避免界面更新冲突。

修改后,鼠标左键点击按钮只会触发一次mvprintw操作,符合预期效果。


内容的提问来源于stack exchange,提问作者Chief Cook

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 03:04:50