最小CPP

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

Minimal CPPは、C++を使って完全なQt Waylandコンポジターを実装する最小限のコンポジター例です。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

©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。