カスタムエクステンション

Custom ExtensionはカスタムWayland拡張機能の実装方法を示します。

Wayland用の新しい拡張機能を書くのは簡単です。これらはXMLベースのフォーマットで定義され、wayland-scanner ツールがこれをCのグルーコードに変換します。Qtはこれをqtwaylandscanner で拡張し、QtとC++で追加のグルーコードを生成します。

Custom Extensionの例では、これらのツールを使ってWaylandプロトコルを拡張し、Waylandクライアントとサーバー間でカスタムリクエストやイベントを送信する方法を示します。

この例は4つの項目から構成されています:

  • プロトコルの定義
  • 拡張機能をサポートするコンポジター
  • 拡張機能をサポートする C++ ベースのクライアント。
  • 拡張機能をサポートするQMLベースのクライアント。

プロトコルの定義

XML ファイルcustom.xml はプロトコルを定義しています。このファイルには "qt_example_extension" というインターフェースが含まれています。これはサーバからブロードキャストされる名前で、クライアントがリクエストを送ったりイベントを受け取ったりするときにアタッチされます。この名前は一意でなければならないので、公式のインターフェイスとは異なるプレフィックスを使用するのが良いでしょう。

インターフェースは通常、リクエストと イベントの2種類のリモートプロシージャ 呼び出しから構成される。「リクエスト」はクライアントがサーバー側で行う呼び出しで、「イベント」はサーバーがクライアント側で行う呼び出しです。

エクステンションの例では、クライアントウィンドウに特定のトランスフォームを適用するようサーバーに指示するリクエストのセットを含んでいます。例えば、クライアントが "bounce "リクエストを送信した場合、サーバーはウィンドウを画面上で跳ねさせることでこれに応答する必要があります。

同様に、サーバーがクライアントに指示を与えるために使用できる一連のイベントがある。例えば、"set_font_size "イベントは、クライアントにデフォルトのフォントサイズを特定のサイズに設定するよう指示するものです。

プロトコルはリクエストとイベントの存在と、それらが取る引数を定義する。qtwaylandscanner が実行されると、プロシージャコールとその引数をマーシャリングし、これをコネクション上で送信するために必要なコードが生成されます。もう一方の側では、これは実際の応答を提供するために実装できる仮想関数への呼び出しとなる。

ビルドの一部としてqtwaylandscanner を自動的に実行させるために、CMake 関数のqt_generate_wayland_protocol_server_sources()qt_generate_wayland_protocol_client_sources()を使用して、それぞれサーバー側とクライアント側のグルー・コードを生成します。(qmake を使用する場合は、WAYLANDSERVERSOURCESWAYLANDCLIENTSOURCES 変数で同じことができます)。

コンポジターの実装

Compositorアプリケーション自体はQMLとQt Quick を使用して実装されていますが、拡張機能はC++で実装されています。

まず、qtwaylandscanner によって生成されたグルーコードのサブクラスを作成し、その機能にアクセスできるようにします。QMLからアクセスできるように、QML_ELEMENT マクロをクラスに追加します。

class CustomExtension  : public QWaylandCompositorExtensionTemplate<CustomExtension>
        , public QtWaylandServer::qt_example_extension
{
    Q_OBJECT
    QML_ELEMENT

生成されたクラスを継承するだけでなく、Curiously Recurring Templateパターンを使って、拡張機能を扱うときに便利なQWaylandCompositorExtensionTemplate

QWaylandCompositorExtensionTemplateQObject ベースのクラスであるため、継承リストの最初になければならないことに注意してください。

サブクラスは生成された基底クラスの仮想関数の再実装を持っており、クライアントによって発行されたリクエストを処理できます。

protected:
    void example_extension_bounce(Resource *resource, wl_resource *surface, uint32_t duration) override;

これらの再実装では、コンポジターの実際のQMLコードで扱えるように、単にリクエストをシグナル発信に変換します。

voidCustomExtension::example_extension_bounce(QtWaylandServer::qt_example_extension::Resource*resource,wl_resource*wl_surface,uint32_t duration) { Q_UNUSED(resource);autosurface=QWaylandSurface::fromResource(wl_surface);    qDebug() << "server received bounce" << surface << duration;
    bounce(surface,duration); }

さらに、このサブクラスでは各イベントのスロットを定義し、QMLから呼び出したり、シグナルに接続できるようにしています。スロットは単にイベントをクライアントに送る生成関数を呼び出すだけです。

voidCustomExtension::setFontSize(QWaylandSurface*surfaceuintpixelSize) {if(surface) { Resource*target =resourceMap().value(surface->waylandClient());if(target) { if (surface->waylandClient());if(target) {if(surface->waylandClient())            qDebug() << "Server-side extension sending setFontSize:" << pixelSize;
            send_set_font_size(target->handle,  surface->resource(),pixelSize); } }

クラス定義にQML_ELEMENT マクロを追加したので(対応するビルドステップをビルドシステムファイルに追加したので)、QML でインスタンス化できます。

コンポジターが拡張機能として登録するために、WaylandCompositor オブジェクトの直接の子オブジェクトにしています。

    CustomExtension {
        id: custom

        onSurfaceAdded: (surface) => {
            var item = itemForSurface(surface)
            item.isCustom = true
        }

        onBounce: (surface, ms) => {
            var item = itemForSurface(surface)
            item.doBounce(ms)
        }

        onSpin: (surface, ms) => {
            var item = itemForSurface(surface)
            item.doSpin(ms)
        }

        onCustomObjectCreated: (obj) => {
            var item = customObjectComponent.createObject(defaultOutput.surfaceArea,
                                                          { "color": obj.color,
                                                            "text": obj.text,
                                                            "obj": obj } )
        }
    }

    function setDecorations(shown) {
        var n = itemList.length
        for (var i = 0; i < n; i++) {
            if (itemList[i].isCustom)
                custom.showDecorations(itemList[i].surface.client, shown)
        }
    }

このオブジェクトは、クライアントからのリクエストに対応するシグナルハンドラを持ち、それに応じて反応します。さらに、スロットを呼び出してイベントを送信することもできます。

            onFontSizeChanged: {
                custom.setFontSize(surface, fontSize)
            }

C++クライアントの実装

どちらのクライアントもインターフェースのC++実装を共有している。コンポジターと同様に、生成されたコードもテンプレート・クラスを継承したサブクラスを作成します。この場合、QWaylandClientExtensionTemplateを継承します。

class CustomExtension : public QWaylandClientExtensionTemplate<CustomExtension>
        , public QtWayland::qt_example_extension

アプローチはコンポジターと非常に似ていますが、逆になっています:リクエストは生成された関数を呼び出すスロットとして実装され、イベントはシグナルを発するために再実装する仮想関数として実装されます。

void CustomExtension::sendBounce(QWindow *window, uint ms)
{
    QtWayland::qt_example_extension::bounce(getWlSurface(window), ms);
}

クライアントのコード自体は非常にシンプルで、動作をトリガーする方法を示すことだけを目的としている。カスタムペイントイベントでは、矩形とラベルのセットを描画する。これらのいずれかがクリックされると、サーバーにリクエストを発行します。

    void mousePressEvent(QMouseEvent *ev) override
    {
        if (rect1.contains(ev->position()))
            doSpin();
        else if (rect2.contains(ev->position()))
            doBounce();
        else if (rect3.contains(ev->position()))
            newWindow();
        else if (rect4.contains(ev->position()))
            newObject();
    }

set_font_size イベントを受信したときにフォントサイズを更新するために、拡張クラスのシグナルはスロットに接続されています。

        connect(m_extension, &CustomExtension::fontSize, this, &TestWindow::handleSetFontSize);

スロットはフォントサイズを更新し、ウィンドウを再描画します。

QMLクライアントの実装

QMLクライアントはC++クライアントと似ています。C++クライアントと同じカスタム拡張機能の実装に依存し、QMLでこれをインスタンス化することで有効にしています。

    CustomExtension {
        id: customExtension
        onActiveChanged: {
            registerWindow(topLevelWindow)
        }
        onFontSize: (window, pixelSize) => {
            topLevelWindow.fontSize = pixelSize
        }
    }

UIはクリック可能な矩形で構成され、矩形がクリックされたときにTapHandler を使って対応するリクエストを送信します。

            TapHandler {
                onTapped: {
                    if (customExtension.active)
                        customExtension.sendBounce(topLevelWindow, 1000)
                }
            }

簡単にするために、この例ではbouncespin のリクエストとset_font_size イベントのデモンストレーションだけに限定しています。追加機能のサポートを追加することは、読者のための練習として残されています。

サンプルプロジェクト @ 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.