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

如何清除Cairo填充图形?实时鼠标选框绘制叠加问题求助

解决Cairo绘制选框时图形叠加的问题

你的问题核心是每次鼠标移动都直接在根窗口表面绘制新矩形,且没有清除之前的绘制内容,同时重复创建surface和上下文导致资源浪费,最终造成图形叠加。以下是修复方案:

问题根源

  • 每次MotionNotify事件都重新创建cairo_surface_t和cairo_t,旧的绘制内容会保留在根窗口上
  • 没有处理选框坐标的正负值(比如鼠标向左/向上拖动时,宽度/高度会为负)
  • 缺少背景恢复机制,无法清除上一帧的选框

修复后的代码实现

首先在全局或初始化区域声明必要变量:

#include <X11/Xlib.h>
#include <cairo.h>
#include <cairo-xlib.h>
#include <stdlib.h>

#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))

Display *xdisplay;
Window xroot;
cairo_surface_t *surface = NULL;
cairo_t *cr = NULL;
Pixmap bg_pixmap = None;
int old_x1, old_y1, old_x2, old_y2;
bool isPressed = false;
int init_x, init_y;

初始化时创建一次surface和上下文(不要在事件循环里重复创建):

xdisplay = XOpenDisplay(NULL);
xroot = DefaultRootWindow(xdisplay);
int scr = DefaultScreen(xdisplay);
surface = cairo_xlib_surface_create(xdisplay, xroot, DefaultVisual(xdisplay, scr), 
                                    DisplayWidth(xdisplay, scr), DisplayHeight(xdisplay, scr));
cr = cairo_create(surface);

修改鼠标事件处理逻辑:

// 处理鼠标按下事件(记录初始坐标)
if (xevent.type == ButtonPress && xevent.xbutton.button == Button1) {
    isPressed = true;
    init_x = xevent.xbutton.x_root;
    init_y = xevent.xbutton.y_root;
    old_x1 = old_x2 = init_x;
    old_y1 = old_y2 = init_y;
}

// 处理鼠标移动事件(绘制选框)
if (xevent.type == MotionNotify && isPressed == true)
{
    int tmp_x = xevent.xmotion.x_root;
    int tmp_y = xevent.xmotion.y_root;

    // 计算选框的有效坐标(确保宽高为正)
    int x1 = MIN(init_x, tmp_x);
    int y1 = MIN(init_y, tmp_y);
    int x2 = MAX(init_x, tmp_x);
    int y2 = MAX(init_y, tmp_y);

    // 清除上一次的选框:恢复保存的背景
    if (bg_pixmap != None) {
        XCopyArea(xdisplay, bg_pixmap, xroot, DefaultGC(xdisplay, scr), 
                  0, 0, old_x2 - old_x1, old_y2 - old_y1, old_x1, old_y1);
        XFreePixmap(xdisplay, bg_pixmap);
        bg_pixmap = None;
    }

    // 保存当前选框区域的背景到Pixmap
    bg_pixmap = XCreatePixmap(xdisplay, xroot, x2 - x1, y2 - y1, DefaultDepth(xdisplay, scr));
    XCopyArea(xdisplay, xroot, bg_pixmap, DefaultGC(xdisplay, scr), 
              x1, y1, x2 - x1, y2 - y1, 0, 0);

    // 绘制新选框
    cairo_rectangle(cr, x1, y1, x2 - x1, y2 - y1);
    cairo_set_source_rgba(cr, 0.3, 0.4, 0.6, 0.1);
    cairo_fill(cr);
    cairo_surface_flush(surface); // 强制刷新到屏幕

    // 保存当前选框坐标,用于下次清除
    old_x1 = x1;
    old_y1 = y1;
    old_x2 = x2;
    old_y2 = y2;
}

// 处理鼠标释放事件(清理资源)
if (xevent.type == ButtonRelease && xevent.xbutton.button == Button1) {
    isPressed = false;
    if (bg_pixmap != None) {
        XCopyArea(xdisplay, bg_pixmap, xroot, DefaultGC(xdisplay, scr), 
                  0, 0, old_x2 - old_x1, old_y2 - old_y1, old_x1, old_y1);
        XFreePixmap(xdisplay, bg_pixmap);
        bg_pixmap = None;
    }
    // 这里可以添加选框完成后的逻辑,比如获取最终选框坐标
}

程序退出时清理资源:

cairo_destroy(cr);
cairo_surface_destroy(surface);
XCloseDisplay(xdisplay);

关键改进点

  1. 复用上下文和surface:只初始化一次,避免重复创建导致的资源泄漏和绘制叠加
  2. 背景恢复机制:用XCopyArea保存选框区域的原始背景,绘制新选框前先恢复旧背景,彻底清除上一帧的选框
  3. 坐标合法性处理:通过MIN/MAX确保选框的宽高为正,避免绘制异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 05:54:09