[packaging] 写了一个针对wayland客户端的c++代码生成器。
Tofloor
poster avatar
枫晓岳
deepin
2026-05-28 22:32
Author

项目地址

该项目AI含量80%,做这个的原因是因为,我在用wayland的CAPI写玩具窗口的时候,看到了令人震惊的繁琐程度,于是决定包装一下。

这个是针对客户端cAPI的薄封装。尽可能保留c的功能,并用c++方式增强

采用多阶段生成,一次性解析全部xml,按功能聚合成声明,枚举,方法实现,等。

使用了c++23标准,暂时不支持更早版本编译器。需要安装wayland-scannerwayland-protocolslibwayland-dev。需要在wayland环境下才能够完整生成。

命名风格,把协议的名称的 _第一个 _前的前缀作为命名空间wl::display。尽可能不重新额外命名除非冲突。

生成了两种监听器,一个是通过lambda设置回调方法,一个是通过模板设置回调方法

第一次做这种项目,欢迎提意见。

不多说,下方是ai写的简单窗口示例。

#include "mayland/mayland-all.hpp"
#include 
#include 
#include 
#include 
#include 
#include 

constexpr int WIN_WIDTH = 800;
constexpr int WIN_HEIGHT = 600;

struct pixel { uint8_t b, g, r, a; };

struct shm_buffer
{
    int fd = -1;
    uint8_t* data = nullptr;
    size_t size = 0;
    size_t stride = 0;
    int width = 0, height = 0;
    wl::buffer buf;

    void init(wl::shm& shm, int w, int h)
    {
        width = w; height = h;
        stride = w * 4;
        size = stride * h;

        fd = memfd_create("wayland-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING);
        ftruncate(fd, size);

        data = (uint8_t*)mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

        fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_SEAL);

        auto pool = shm.create_pool(fd, size);
        buf = pool.create_buffer(0, width, height, stride, wl::shm::format::argb8888);
        pool.destroy();
    }

    ~shm_buffer()
    {
        buf.destroy();
        if (data) munmap(data, size);
        if (fd >= 0) close(fd);
    }

    shm_buffer() = default;
    shm_buffer(const shm_buffer&) = delete;
    shm_buffer& operator=(const shm_buffer&) = delete;
    shm_buffer(shm_buffer&& o) noexcept
        : fd(std::exchange(o.fd, -1))
        , data(std::exchange(o.data, nullptr))
        , size(std::exchange(o.size, 0))
        , stride(std::exchange(o.stride, 0))
        , width(std::exchange(o.width, 0))
        , height(std::exchange(o.height, 0))
        , buf(std::move(o.buf))
    {}
    shm_buffer& operator=(shm_buffer&& o) noexcept
    {
        if (this != &o)
        {
            this->~shm_buffer();
            fd = std::exchange(o.fd, -1);
            data = std::exchange(o.data, nullptr);
            size = std::exchange(o.size, 0);
            stride = std::exchange(o.stride, 0);
            width = std::exchange(o.width, 0);
            height = std::exchange(o.height, 0);
            buf = std::move(o.buf);
        }
        return *this;
    }
};

class triangle_renderer
{
    float cx = 400, cy = 250, radius = 200;
    float angle = 0;
    uint64_t frame_count = 0;

public:
    void set_size(int w, int h)
    {
        cx = w / 2.0f;
        cy = h / 2.0f - 20;
        radius = std::min(w, h) * 0.35f;
    }

    void draw(uint8_t* data, int width, int height, size_t stride, wl::time)
    {
        angle += 0.01f;
        frame_count++;

        float ax = cx + radius * std::cos(angle);
        float ay = cy + radius * std::sin(angle);
        float bx = cx + radius * std::cos(angle + 2.0943951f);
        float by = cy + radius * std::sin(angle + 2.0943951f);
        float cx2 = cx + radius * std::cos(angle + 4.1887902f);
        float cy2 = cy + radius * std::sin(angle + 4.1887902f);

        float v0x = bx - ax, v0y = by - ay;
        float v1x = cx2 - ax, v1y = cy2 - ay;
        float denom = v0x * v1y - v1x * v0y;

        int min_x = std::max(0, (int)(std::min({ax, bx, cx2}) - 1));
        int min_y = std::max(0, (int)(std::min({ay, by, cy2}) - 1));
        int max_x = std::min(width - 1, (int)(std::max({ax, bx, cx2}) + 1));
        int max_y = std::min(height - 1, (int)(std::max({ay, by, cy2}) + 1));

        float hue_base = std::fmod(frame_count * 0.002f, 1.0f);

        for (int y = min_y; y <= max_y; y++)
        {
            auto* row = (pixel*)(data + y * stride);
            for (int x = min_x; x <= max_x; x++)
            {
                float px = (float)x - ax, py = (float)y - ay;
                float u = (px * v1y - py * v1x) / denom;
                float v = (v0x * py - v0y * px) / denom;
                float w = 1.0f - u - v;

                if (u >= 0 && v >= 0 && w >= 0)
                {
                    float hue_a = std::fmod(hue_base + 0.0f, 1.0f);
                    float hue_b = std::fmod(hue_base + 0.33f, 1.0f);
                    float hue_c = std::fmod(hue_base + 0.66f, 1.0f);

                    float rh = hue_a * u + hue_b * v + hue_c * w;
                    rh = std::fmod(rh, 1.0f);

                    float c = 1.0f, xv = c * (1 - std::abs(std::fmod(rh * 6, 2) - 1));
                    float rr, gg, bb;
                    if (rh < 1.0f/6)      { rr = c; gg = xv; bb = 0; }
                    else if (rh < 2.0f/6) { rr = xv; gg = c; bb = 0; }
                    else if (rh < 3.0f/6) { rr = 0; gg = c; bb = xv; }
                    else if (rh < 4.0f/6) { rr = 0; gg = xv; bb = c; }
                    else if (rh < 5.0f/6) { rr = xv; gg = 0; bb = c; }
                    else                  { rr = c; gg = 0; bb = xv; }

                    row[x].r = (uint8_t)(rr * 255);
                    row[x].g = (uint8_t)(gg * 255);
                    row[x].b = (uint8_t)(bb * 255);
                    row[x].a = 255;
                }
            }
        }
    }

    void draw_background(uint8_t* data, int width, int height, size_t stride)
    {
        for (int y = 0; y < height; y++)
        {
            auto* row = (pixel*)(data + y * stride);
            uint8_t g = (uint8_t)(30 + (y * 30 / height));
            for (int x = 0; x < width; x++)
            {
                row[x].r = 10;
                row[x].g = g;
                row[x].b = 20 + (uint8_t)(x * 20 / width);
                row[x].a = 255;
            }
        }
    }
};

class window
{
    wl::display display;
    raii compositor;
    raii wm_base;
    raii xdg_surface;
    raii toplevel;
    raii callback;
    raii registry;
    raii shm;

    xdg::surface::fn_listener surface_listener{this};
    wl::registry::listener registry_listener;
    wl::callback::fn_listener callback_listener{this};
    xdg::toplevel::listener toplevel_listener;
    xdg::wm_base::fn_listener wm_base_listener{wm_base.data()};

    int win_w = WIN_WIDTH, win_h = WIN_HEIGHT;
    bool close_requested = false;
    wl::surface surface;
    triangle_renderer renderer;
    shm_buffer buffer;

public:
    window()
    {
        callback_listener.done<&window::frame_callback_done>();
        registry_listener
            .global([this](uint32_t name, const char* iface, uint32_t) {
                if (iface == wl::compositor::interface::name)
                    compositor = registry->bind(name);
                else if (iface == xdg::wm_base::interface::name)
                {
                    wm_base = registry->bind(name);
                    wm_base->add_listener(wm_base_listener);
                }
                else if (iface == wl::shm::interface::name)
                    shm = registry->bind(name);
            });
        toplevel_listener
            .configure([this](int32_t w, int32_t h, wl::array) {
                if (w > 0 && h > 0) {
                    win_w = w; win_h = h;
                    renderer.set_size(w, h);
                    xdg_surface->set_window_geometry(0, 0, w, h);
                }
            })
            .close([this] { close_requested = true; });
        wm_base_listener.ping<&xdg::wm_base::pong>();
        surface_listener.configure<&window::xdg_surface_configure>();
    }

    void xdg_surface_configure(uint32_t serial)
    {
        xdg_surface->ack_configure(serial);
        if (!callback)
        {
            buffer.init(*shm.data(), win_w, win_h);
            renderer.set_size(win_w, win_h);
            renderer.draw_background(buffer.data, win_w, win_h, buffer.stride);
            renderer.draw(buffer.data, win_w, win_h, buffer.stride, wl::time{});
            surface.attach(buffer.buf, 0, 0);
            surface.damage(0, 0, win_w, win_h);
            surface.commit();
            callback = surface.frame();
            callback->add_listener(callback_listener);
        }
    }

    void frame_callback_done(wl::time t)
    {
        if (win_w != buffer.width || win_h != buffer.height)
        {
            buffer = shm_buffer{};
            buffer.init(*shm.data(), win_w, win_h);
            renderer.set_size(win_w, win_h);
        }
        renderer.draw_background(buffer.data, win_w, win_h, buffer.stride);
        renderer.draw(buffer.data, win_w, win_h, buffer.stride, t);
        surface.attach(buffer.buf, 0, 0);
        surface.damage(0, 0, win_w, win_h);
        surface.commit();
        callback = surface.frame();
        callback->add_listener(callback_listener);
    }

    void run()
    {
        display = display.connect(nullptr);
        registry = display.get_registry();
        registry->add_listener(registry_listener);
        display.roundtrip();
        if (!compositor || !wm_base || !shm) return;

        renderer.set_size(win_w, win_h);
        surface = compositor->create_surface();
        xdg_surface = wm_base->get_xdg_surface(surface);
        xdg_surface->add_listener(surface_listener);
        toplevel = xdg_surface->get_toplevel();
        toplevel->add_listener(toplevel_listener);
        toplevel->set_title("shm-triangle");
        toplevel->set_min_size(0, 0);
        toplevel->set_max_size(2048, 2048);

        surface.commit();

        while (!close_requested && display.dispatch() != -1) {}
    }
};

int main() { window w; w.run(); }
Reply Favorite View the author
All Replies

No replies yet