カスタム拡張機能
「カスタム拡張機能」では、カスタム Wayland 拡張機能を実装する方法について説明しています。
Wayland用の新しい拡張機能の作成は簡単です。拡張機能はXMLベースの形式で定義され、wayland-scanner ツールがこれをC言語のグルーコードに変換します。Qtでは、qtwaylandscanner を用いてこれをさらに拡張し、QtおよびC++の追加のグルーコードを生成します。

「カスタム拡張機能」の例では、これらのツールを使用して Wayland プロトコルを拡張し、Wayland クライアントとサーバー間でカスタムリクエストやイベントを送信する方法を示しています。
この例は、以下の4つの要素で構成されています:
- プロトコル自体の定義。
- この拡張機能をサポートするコンポジター。
- この拡張機能をサポートするC++ベースのクライアント。
- この拡張機能をサポートするQMLベースのクライアント。
プロトコルの定義
XMLファイル `custom.xml ` がプロトコルを定義します。このファイルのベース名は、ルート要素( `protocol` )の `name ` 属性と一致している必要があります。このファイルには、「qt_example_extension」という名前のインターフェースが含まれています。これは、サーバーからブロードキャストされる名前であり、クライアントがリクエストを送信したりイベントを受信したりするために接続する対象となります。 この名前は一意である必要があるため、公式のインターフェースと区別できるプレフィックスを使用することをお勧めします。
インターフェースは通常、「リクエスト」と「イベント」という2種類のリモートプロシージャコールで構成されます。「リクエスト」はクライアントがサーバー側に対して行う呼び出しであり、「イベント」はサーバーがクライアント側に対して行う呼び出しです。
この拡張機能の例には、クライアントのウィンドウに特定の変換を適用するようサーバーに指示する一連のリクエストが含まれています。例えば、クライアントが「bounce」リクエストを送信した場合、サーバーはこれに応答して、ウィンドウを画面上でバウンドさせる必要があります。
同様に、この拡張機能には、サーバーがクライアントに指示を与えるために使用できる一連のイベントも含まれています。例えば、「set_font_size」イベントは、クライアントに対してデフォルトのフォントサイズを特定のサイズに設定するよう指示するものです。
このプロトコルでは、リクエストやイベントの存在、およびそれらが受け取る引数が定義されています。qtwaylandscanner をこれに対して実行すると、プロシージャ呼び出しとその引数をマーシャリングし、接続を介して送信するために必要なコードが生成されます。接続の反対側では、これは仮想関数への呼び出しとなり、この仮想関数を実装することで実際の応答を提供することができます。
ビルドの一環としてqtwaylandscanner を自動的に実行させるため、CMake関数qt_generate_wayland_protocol_server_sources() とqt_generate_wayland_protocol_client_sources()を使用して、それぞれサーバー側およびクライアント側のグルーコードを生成します。 (qmake を使用する場合、WAYLANDSERVERSOURCES およびWAYLANDCLIENTSOURCES 変数でも同様の処理が可能です。)
コンポジターの実装
Compositor アプリケーション自体は QML およびQt Quick を使用して実装されていますが、拡張機能は C++ で実装されています。
まず、qtwaylandscanner によって生成されたグルーコードのサブクラスを作成し、その機能にアクセスできるようにします。QMLからアクセスできるようにするため、クラスにQML_ELEMENT マクロを追加します。
class CustomExtension : public QWaylandCompositorExtensionTemplate<CustomExtension>
, public QtWaylandServer::qt_example_extension
{
Q_OBJECT
QML_ELEMENT生成されたクラスを継承するだけでなく、QWaylandCompositorExtensionTemplate クラスも継承します。これにより、Curiously Recurring Template Pattern(CRTP)を使用して、拡張機能を扱う際にさらなる利便性が得られます。
QWaylandCompositorExtensionTemplate はQObject を基にしたクラスであるため、継承リストの最初に配置する必要がある点に注意してください。
サブクラスでは、生成された基底クラス内の仮想関数を再実装しており、そこでクライアントからのリクエストを処理することができます。
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;
emitbounce(surface,duration);
}さらに、このサブクラスでは各イベントに対応するスロットが定義されており、これらはQMLから呼び出されるか、シグナルに接続されるかのいずれかになります。スロットは、生成された関数を呼び出すだけであり、その関数がイベントをクライアントに送信します。
voidCustomExtension::setFontSize(QWaylandSurface*surface,uint pixelSize)
{
if(surface) {
Resource*target =resourceMap().value(surface->waylandClient());
if(target) {
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) => {
const item = comp.itemForSurface(surface)
item.isCustom = true
}
onBounce: (surface, ms) => {
const item = comp.itemForSurface(surface)
item.doBounce(ms)
}
onSpin: (surface, ms) => {
const item = comp.itemForSurface(surface)
item.doSpin(ms)
}
onCustomObjectCreated: (obj) => {
const item = customObjectComponent.createObject(output.surfaceArea, { "obj": obj } )
}
}
function setDecorations(shown) {
for (const elem of itemList) {
if (elem.isCustom)
custom.showDecorations(elem.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)
}
}簡潔にするため、この例ではbounce およびspin リクエスト、ならびにset_font_size イベントの実演に限定しています。その他の機能のサポート追加については、読者の課題として残しておきます。
© 2026 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.