最小化 CPP

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

最小化 CPP 是一个实现完整的 Qt Wayland 合成器的极简合成器示例。QtWaylandCompositor API 是低级别的,旨在用于特殊应用程序,例如支持硬件特性或者当 Qt Quick 不可用时。QML API 提供了更多便利和功能。为了比较,最小 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());
}

合成器

由于需要实现 WaylandCompositorWaylandQuickItem 在基于 QML 的合成器中处理的许多逻辑,因此 Compositor 类更为复杂。

创建函数使用 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。《code translate="no">getTexture 函数使缓冲区内容可作为 OpenGL 纹理提供给《code translate="no">Window 类

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

示例项目 @ code.qt.io

© 2024 Qt公司有限公司。本文件中包含的文档贡献作品均为各自所有者的版权。本文件提供的文档受GNU自由文档许可证第1.3版许可,该许可证由自由软件基金会发布。Qt及其相关标志为芬兰的Qt公司及其在世界其他国家的注册商标。所有其他商标均为各自所有者的财产。