diff options
Diffstat (limited to 'examples/wayland/custom-extension/doc/src/custom-extension.qdoc')
-rw-r--r-- | examples/wayland/custom-extension/doc/src/custom-extension.qdoc | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/examples/wayland/custom-extension/doc/src/custom-extension.qdoc b/examples/wayland/custom-extension/doc/src/custom-extension.qdoc new file mode 100644 index 000000000..7b2cb0802 --- /dev/null +++ b/examples/wayland/custom-extension/doc/src/custom-extension.qdoc @@ -0,0 +1,148 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only + +/*! + * \title Custom Extension + * \example custom-extension + * \examplecategory {Embedded} + * \brief Custom Extension shows how to implement a custom Wayland extension. + * \ingroup qtwaylandcompositor-examples + * + * It's easy to write new extensions for Wayland. They are defined using a XML-based format and + * the \c wayland-scanner tool converts this to glue code in C. Qt expands on this with the + * \c qtwaylandscanner, which generates additional glue code in Qt and C++. + * + * \image custom-extension.png + * + * The Custom Extension example shows how to use these tools to extend the Wayland protocol and + * send custom requests and events between a Wayland client and a server. + * + * The example consists of four items: + * \list + * \li The definition of the protocol itself. + * \li A compositor that supports the extension. + * \li A C++-based client that supports the extension. + * \li A QML-based client that supports the extension. + * \endlist + * + * \section1 The Protocol Definition + * + * The XML file \c custom.xml defines the protocol. It contains an interface called + * "qt_example_extension". This is the name which will be broadcast from the server and which the + * client will attach to in order to send requests and receive events. This name should be unique, + * so it is good to use a prefix that sets it apart from official interfaces. + * + * An interface typically consists of two types of remote procedure calls: + * \e requests and \e events. "Requests" are calls the client makes on the server-side, and + * "events" are calls the server makes on the client-side. + * + * The example extension contains a set of \e requests which instructs the server to apply certain + * transforms to the client window. For instance, if the client sends a "bounce" request, then the + * server should respond to this by making the window bounce on the screen. + * + * Similarly, it has a set of \e events which the server can use to provide instructions for the + * client. For instance, the "set_font_size" event is an instruction for the client to set its + * default font size to a specific size. + * + * The protocol defines the existence of requests and events, as well as the arguments they + * take. When \c qtwaylandscanner is run on it, it will generate the code needed to marshall the + * procedure call and its arguments and to transmit this over the connection. On the other end, this + * becomes a call to a virtual function which can be implemented to provide the actual response. + * + * In order to have \c qtwaylandscanner run automatically as part of the build, we use the + * CMake functions \l{qt_generate_wayland_protocol_server_sources}{qt_generate_wayland_protocol_server_sources()} and + * \l{qt_generate_wayland_protocol_client_sources}{qt_generate_wayland_protocol_client_sources()} for generating the server-side and + * client-side glue code, respectively. (When using \c qmake, the \c WAYLANDSERVERSOURCES and + * \c WAYLANDCLIENTSOURCES variables achieve the same.) + * + * \section1 The Compositor Implementation + * + * The Compositor application itself is implemented using QML and Qt Quick, but the extension is + * implemented in C++. + * + * The first step is to create a subclass of the glue code generated by \c qtwaylandscanner so + * that we can access its functionality. We add the \l QML_ELEMENT macro to the class in order to + * make it accessible from QML. + * + * \snippet custom-extension/compositor/customextension.h CustomExtension + * + * In addition to inheriting from the generated class, we also inherit the class + * \l QWaylandCompositorExtensionTemplate which provides some additional convenience when dealing + * with extensions, using the + * \l{https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern}{Curiously Recurring Template Pattern}. + * + * Note that the \l QWaylandCompositorExtensionTemplate must be first in the inheritance list, since + * it is a \l{QObject}-based class. + * + * The subclass has re-implementations of virtual functions in the generated base class, where + * we can handle requests issued by a client. + * + * \snippet custom-extension/compositor/customextension.h example_extension_bounce + * + * In these re-implementations, we simply translate the request to a \e signal emission, so that we + * can handle it in the actual QML code of the compositor. + * + * \snippet custom-extension/compositor/customextension.cpp example_extension_bounce + * + * In addition, the subclass defines \e slots for each of the events, so that these can be either + * called from QML or be connected to signals. The slots simply call the generated functions which + * send the events to the client. + * + * \snippet custom-extension/compositor/customextension.cpp setFontSize + * + * Since we added the \l QML_ELEMENT macro to the class definition (and added the corresponding + * build steps to the build system files), it can be instantiated in QML. + * + * We make it a direct child of the \l WaylandCompositor object in order for the compositor to + * register it as an extension. + * + * \snippet custom-extension/compositor/qml/main.qml CustomExtension + * + * The object has signal handlers for the requests it may get from the client and reacts to them + * accordingly. In addition, we can call its slots to send events. + * + * \snippet custom-extension/compositor/qml/main.qml setFontSize + * + * \section1 The C++ Client Implementation + * + * Both clients share the C++ implementation of the interface. Like in the compositor, we make + * a subclass of the generated code which also inherits from a template class. In this case, + * we inherit QWaylandClientExtensionTemplate. + * + * \snippet custom-extension/client-common/customextension.h CustomExtension + * + * The approach is very similar to that of the compositor, except inverted: Requests are implemented + * as slots which call the generated functions, and events virtual functions which we re-implement + * to emit signals. + * + * \snippet custom-extension/client-common/customextension.cpp sendBounce + * + * The client code itself is very simple and only intended to show how to trigger the behavior. In + * a custom paint event, it draws a set of rectangles and labels. When any of these are clicked, it + * issues requests to the server. + * + * \snippet custom-extension/cpp-client/main.cpp mousePressEvent + * + * To update the font size when the \c set_font_size event is received, the signal in our extension + * class is connected to a slot. + * + * \snippet custom-extension/cpp-client/main.cpp connection + * + * The slot will update the font size and repaint the window. + * + * \section1 The QML Client Implementation + * + * The QML client is similar to the C++ client. It relies on the same implementation of the custom + * extension as the C++ client, and instantiates this in QML to enable it. + * + * \snippet custom-extension/qml-client/main.qml CustomExtension + * + * The UI consists of some clickable rectangles, and uses \l TapHandler to send the corresponding + * requests when a rectangle is clicked. + * + * \snippet custom-extension/qml-client/main.qml sendBounce + * + * For simplicity, the example has been limited to only demonstrate the \c bounce and \c spin + * requests as well as the \c set_font_size event. Adding support for the additional features is + * left as an exercise for the reader. + */ |