PAYATHON 2026

FLTK 1.4 如何让 Cairo 自定义控件兼容 X11 和 Wayland?

数据阿明

结论

在 Fl_Dial 的 draw() 中使用 FLTK 提供的 Cairo 上下文。不要根据 fl_display、fl_window 和 fl_visual 自行创建 cairo_xlib_surface_t,因为这些是 X11 后端资源,在 Wayland 下不可用。若 FLTK 构建启用了 Cairo 支持,可以调用 Fl::cairo_make_current(window()) 获取控件所属窗口的上下文,并在绘制完成后调用 Fl::cairo_flush()。

FLTK 文档说明,可以在重写的 draw() 中通过 Fl::cairo_make_current(Fl_Window*) 获取窗口的 Cairo 上下文。要使用这项支持,构建时需启用 --enable-cairo 或相应的 CMake 选项。Cairo 支持文档 FLTK 的 Wayland 后端使用 Cairo 绘制普通 Fl_Window,但应用能否调用 Cairo 接口,仍取决于所用的 FLTK 构建是否启用了 Cairo 支持。Wayland 后端说明

在控件的 draw() 中获取上下文

Fl::cairo_make_current() 接收 Fl_Window*。对于这个继承自 Fl_Dial 的控件,传入 window() 即可获取控件所属窗口的上下文。下面的示例使用 FLTK 窗口坐标绘制;x() 和 y() 表示控件在窗口中的位置:

#include <FL/Fl.H>
#include <cairo.h>

void MyDial::draw() {
    cairo_t *cr = Fl::cairo_make_current(window());
    if (!cr) {
        // 当前配置或窗口未提供 Cairo 上下文时,不要解引用空指针。
        return;
    }

    cairo_save(cr);

    const double cx = x() + w() / 2.0;
    const double cy = y() + h() / 2.0;
    const double radius = (w() < h() ? w() : h()) / 2.0 - 2.0;

    cairo_set_source_rgb(cr, 0.2, 0.2, 0.2);
    cairo_set_line_width(cr, 2.0);
    cairo_arc(cr, cx, cy, radius, 0.0, 2.0 * M_PI);
    cairo_stroke(cr);

    cairo_restore(cr);
    Fl::cairo_flush(cr);
}

将 MyDial 替换为实际控件类名。示例演示了如何获取上下文以及使用坐标,实际的旋钮绘制逻辑可以沿用现有 Cairo 代码。若还要绘制 Fl_Dial 原有内容,请根据界面设计决定是否调用基类的 draw(),并确定调用顺序。

构建与后端注意事项

  • 确认应用链接的 FLTK 库启用了 Cairo 支持。Wayland 后端内部使用 Cairo,并不意味着当前构建一定提供 Fl::cairo_make_current() 接口。
  • 将控件所属的 FLTK 窗口传给接口。Cairo 上下文关联的是窗口,而非单独的 Fl_Dial。
  • 获取的上下文可能不可用。FLTK 文档列出了返回 NULL 的情况,因此绘制前应检查结果。接口说明
  • 旧代码使用 Fl_Window::current()->w() 和 h() 创建覆盖整个窗口的 Xlib surface。改用 FLTK 提供的上下文后,不要再按窗口尺寸自行创建 Xlib surface;绘制区域和裁剪应配合 FLTK 当前的绘制流程处理。
  • fl_wl_gc() 是 Wayland 专用接口,无法作为兼容 X11 和 Wayland 的通用入口。FLTK 的操作系统文档说明,Wayland 绘制时可以通过它获取 Cairo 上下文。需要兼容多个后端的代码,优先使用 FLTK 的 Cairo 支持接口。操作系统相关接口
  • 如果项目使用的 FLTK 构建没有启用应用可调用的 Cairo 支持,也可以考虑先离屏绘制,再交给 FLTK 处理。这种方案需要额外处理位图格式、缩放和更新同步。请先检查项目实际使用的 FLTK 构建选项及对应版本的文档。

备注:内容仅供参考。