最小 CPP

Minimal CPP 是一个演示如何用 C++ 编写 Wayland 合成器的示例。

Minimal CPP 是一个使用 C++ 实现完整Qt Wayland Compositor 的最小化合成器示例。QtWaylandCompositor 的 C++ API 是低级的,适用于专门的应用,如支持硬件功能,或在Qt Quick 不可用的情况下。QML API 提供了更多便利和功能。相比之下,Minimal QML 示例用 30 行 QML 实现的功能要多于本示例用 300 多行实现的功能。

本示例分为两部分。Wayland 逻辑包含在Compositor 类中,而用户界面则在Window 类中。

窗口

Window 类相当简单。为了显示 Wayland 表面,它会遍历合成器的视图,并使用QOpenGLTextureBlitter 将其渲染到屏幕上:

void Window::paintGL()
{
    m_compositor->startRender();

    QOpenGLFunctions *functions = context()->functions();
    functions->glClearColor(.4f, .7f, .1f, 0.5f);
    functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    GLenum currentTarget = GL_TEXTURE_2D;
    m_textureBlitter.bind(currentTarget);
    functions->glEnable(GL_BLEND);
    functions->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    const auto views = m_compositor->views();
    for (View *view : views) {
        auto texture = view->getTexture();
        if (!texture)
            continue;
        if (texture->target() != currentTarget) {
            currentTarget = texture->target();
            m_textureBlitter.bind(currentTarget);
        }
        GLuint textureId = texture->textureId();
        QWaylandSurface *surface = view->surface();
        if (surface && surface->hasContent()) {
            QSize s = surface->destinationSize();
            view->initPosition(size(), s);
            QPointF pos = view->globalPosition();
            QRectF surfaceGeometry(pos, s);
            QOpenGLTextureBlitter::Origin surfaceOrigin =
                    view->currentBuffer().origin() == QWaylandSurface::OriginTopLeft
                    ? QOpenGLTextureBlitter::OriginTopLeft
                    : QOpenGLTextureBlitter::OriginBottomLeft;
            QMatrix4x4 targetTransform = QOpenGLTextureBlitter::targetTransform(surfaceGeometry, QRect(QPoint(), size()));
            m_textureBlitter.blit(textureId, targetTransform, surfaceOrigin);
        }
    }
    m_textureBlitter.release();
    m_compositor->endRender();
}

所有键盘和鼠标事件都会传递给合成器。例如

void Window::mousePressEvent(QMouseEvent *event)
{
    m_compositor->handleMousePress(event->position().toPoint(), event->button());
}

合成器

Compositor 类更为复杂,因为它必须实现基于 QML 的合成器中由WaylandCompositorWaylandQuickItem 处理的大部分逻辑。

create 函数使用最基本的外壳扩展IviApplication 来设置合成器。该函数在 OpenGL 上下文初始化后调用:

void Compositor::create()
{
    QWaylandOutput *output = new QWaylandOutput(this, m_window);
    QWaylandOutputMode mode(m_window->size(), 60000);
    output->addMode(mode, true);
    QWaylandCompositor::create();
    output->setCurrentMode(mode);

    m_iviApplication = new QWaylandIviApplication(this);
    connect(m_iviApplication, &QWaylandIviApplication::iviSurfaceCreated, this, &Compositor::onIviSurfaceCreated);
}

鼠标事件和键盘焦点的所有逻辑都需要手动实现,包括隐式鼠标抓取(将所有鼠标移动发送到接收到初始鼠标按压的表面)。请注意,Wayland 协议中的鼠标按下事件并不包含鼠标位置,因此当我们收到鼠标按下时,总是必须发送鼠标移动:

void Compositor::handleMousePress(const QPoint &position, Qt::MouseButton button)
{
    if (!m_mouseView) {
        if ((m_mouseView = viewAt(position)))
            raise(m_mouseView);
    }
    auto *seat = defaultSeat();
    seat->sendMouseMoveEvent(m_mouseView, mapToView(m_mouseView, position));
    seat->sendMousePressEvent(button);
}

对于鼠标释放,我们会结束隐式抓取,并通知当前鼠标位置的曲面:

void Compositor::handleMousePress(const QPoint &position, Qt::MouseButton button)
{
    if (!m_mouseView) {
        if ((m_mouseView = viewAt(position)))
            raise(m_mouseView);
    }
    auto *seat = defaultSeat();
    seat->sendMouseMoveEvent(m_mouseView, mapToView(m_mouseView, position));
    seat->sendMousePressEvent(button);
}

当我们收到新表面的通知时,我们会创建一个View 来跟踪它,并连接信号以便处理更新。

void Compositor::onIviSurfaceCreated(QWaylandIviSurface *iviSurface)
{
    View *view = new View(iviSurface->iviId());
    view->setSurface(iviSurface->surface());
    view->setOutput(outputFor(m_window));

    m_views << view;
    connect(view, &QWaylandView::surfaceDestroyed, this, &Compositor::viewSurfaceDestroyed);
    connect(iviSurface->surface(), &QWaylandSurface::redraw, this, &Compositor::triggerRender);
}

View 类子类QWaylandView ,它代表曲面的一个特定视图。advance 函数更新视图的当前缓冲区,如果有新内容则返回 true。getTexture 函数将缓冲区内容作为 OpenGL 纹理提供给Window 类:

QOpenGLTexture *View::getTexture() {
    if (advance())
        m_texture = currentBuffer().toOpenGLTexture();
    return m_texture;
}

示例项目 @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.