最小CPP

Minimal CPPはC++でWaylandコンポジターを書く方法を示すサンプルです。

Minimal CPPは、C++を使って完全なQt Wayland Compositor を実装する最小限のコンポジターの例です。QtWaylandCompositor のC++ APIは低レベルで、ハードウェア機能のサポートやQt Quick が利用できない場合など、特殊なアプリケーション向けです。QMLのAPIはより便利で機能的です。比較のため、Minimal QMLの例では、30行のQMLで、この例が300行以上で実装しているよりも多くの機能を実装しています。

この例は2つの部分に分かれています。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 関数は、Window クラスのために、バッファの内容を OpenGL テクスチャとして利用できるようにします:

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.