/**************************************************************************** ** ** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the documentation of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:FDL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Free Documentation License Usage ** Alternatively, this file may be used under the terms of the GNU Free ** Documentation License version 1.3 as published by the Free Software ** Foundation and appearing in the file included in the packaging of ** this file. Please review the following information to ensure ** the GNU Free Documentation License version 1.3 requirements ** will be met: https://www.gnu.org/licenses/fdl-1.3.html. ** $QT_END_LICENSE$ ** ****************************************************************************/ /*! * \title Qt Wayland Compositor Examples - Custom Shell * \example custom-shell * \brief Custom Shell shows how to implement a custom shell extension. * \ingroup qtwaylandcompositor-examples * * \l{Shell Extensions - Qt Wayland Compositor}{Shell extensions} to Wayland are protocols that * manage window state, position and size. Most compositors will support one or more of built-in * extensions, but in some circumstances it can be useful to be able to write a custom one which * contains the exact features your applications need. * * This requires that you implement the shell extension on both the server-side and client-side * of the Wayland connection, so it is mainly useful when you are building a platform and are in * control of both the compositor and its client applications. * * The Custom Shell example shows the implementation of a simple shell extension. It is divided into * three parts: * \list * \li A protocol description for a custom shell interface. * \li A plugin for connecting to the interface in a client application. * \li An example compositor with a server-side implementation of the interface. * \endlist * * The protocol description follows the standard XML format read by \c{wayland-scanner}. It will * not be covered in detail here, but it covers the following features: * * \list * \li An interface for creating a shell surfaces for a \c{wl_surface}. This allows the protocol * to add functionality on top of the existing \c{wl_surface} APIs. * \li A request to set a window title on the shell surface. * \li A request to minimize/de-minimize the shell surface. * \li An event informing the client of the shell surface's current minimized state. * \endlist * * \section1 The Client Plugin * * In order for the shell integration to be discovered by a Qt client, we must reimplement * the QWaylandShellIntegrationPlugin. * * \snippet custom-shell/client-plugin/main.cpp plugin * * This attaches the "example-shell" key to the shell integration and provides a way for the * \c ExampleShellIntegration class to be instantiated when a client connects to the interface. * * The APIs for creating shell extensions are available in the header \c qwaylandclientshellapi_p.h. * * \snippet custom-shell/client-plugin/main.cpp include * * This header requires including private API because unlike public Qt APIs, it does not come with * binary compatibility guarantees. The APIs are still considered stable and will remain source * compatible, and are similar in this respect to other plugin APIs in Qt. * * The \c ExampleShellIntegration is the client-side entry point for creating shell surfaces as * described above. It extends the QWaylandShellIntegrationTemplate class, using the * \l{https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern}{Curiously Recurring Template Pattern}. * * \snippet custom-shell/client-plugin/exampleshellintegration.h shell-integration * * It also inherits from the \c QtWayland::qt_example_shell class, which is generated by * \c qtwaylandscanner based on the XML description of the protocol. * * The constructor specifies the version of the protocol that we support: * * \snippet custom-shell/client-plugin/exampleshellintegration.cpp constructor * * The example_shell protocol is currently at version one, so we pass a \c{1} to the parent * class. This is used in protocol negotiation, and makes sure that older clients will continue * working if the compositor uses a newer version of the protocol. * * When the \c ExampleShellIntegration is initialized, the application is connected to the server, * and has received the broadcast of global interfaces the compositor supports. * If successful, it can issue requests for the interface. In this * case, there is only one request to support: Creating a shell surface. It uses the built-in * function \c wlSurfaceForWindow() to convert the QWaylandWindow to a \c{wl_surface}, then it issues the * request. It then extends the returned surface with a \c ExampleShellSurface object which will * handle the requests and events on the \c qt_example_shell_surface interface. * * \snippet custom-shell/client-plugin/exampleshellintegration.cpp createShellSurface * * The \c ExampleShellSurface extends two classes. * * \snippet custom-shell/client-plugin/examplesurface.h ExampleShellSurface * * The first is the \c QtWayland::qt_example_shell_surface class which is generated based on the XML * description of the protocol. This provides virtual functions for events and ordinary member * functions for the requests in the protocol. * * The \c QtWayland::qt_example_shell_surface class only has a single event. * * \snippet custom-shell/client-plugin/examplesurface.h events * * The \c ExampleShellSurface reimplements this to update its internal window state. When the * window state is change, it stores the pending state until later and calls * \c{applyConfigureWhenPossible()} in QWaylandShellSurface. State, size and position changes should * be organized like this. That way, we ensure that changes do not interfere with rendering to the * surface, and multiple related changes can easily be applied as one. * * When it is safe to reconfigure the surface, the virtual \c applyConfigure() function is called. * * \snippet custom-shell/client-plugin/examplesurface.cpp applyConfigure * * This is where we actually commit the new (minimized or de-minimized) state to the window. * * The second super class is QWaylandShellSurface. This is the interface used by Wayland's QPA * plugin and QWaylandWindow to communicate with the shell. The \c ExampleShellSurface reimplements * a few virtual functions from this interface as well. * * \snippet custom-shell/client-plugin/examplesurface.h virtuals * * For example, when the Qt applications sets the title of a window, this translates into a call to * the virtual \c setTitle() function. * * \snippet custom-shell/client-plugin/examplesurface.cpp setTitle * * In the \c ExampleShellSurface this in turn translates to a request on our custom shell surface * interface. * * \section1 The Compositor * * The final part of the example is the compositor itself. This has the same general structure as * the other compositor examples. See the * \l{Qt Wayland Compositor Examples - Minimal QML}{Minimal QML example} for more details on * the building blocks of a \l{Qt Wayland Compositor}. * * One notable difference in the Custom Shell compositor is the instantiation of the shell * extension. Where the \l{Qt Wayland Compositor Examples - Minimal QML}{the Minimal QML example} * instantiates the shell extensions \l{IviApplication}, \l{XdgShell} and \l{WlShell}, the * Custom Shell example only creates an instance of the \c ExampleShell extension. * * \snippet custom-shell/compositor/qml/main.qml ExampleShell * * We create the instance of the shell extension as a direct child of the WaylandCompositor in * order to have it registered as a global interface. This will be broadcasted to clients as they * connect, and they will be able to attach to the interface as outlined in the previous section. * * The \c ExampleShell is a subclass of the generated \c QtWaylandServer::qt_example_shell * interface, which contains the API defined in the protocol XML. It is also a subclass of * \l{QWaylandCompositorExtensionTemplate}, which ensures the objects are recognized by * QWaylandCompositor as extensions. * * \snippet custom-shell/compositor/exampleshell.h ExampleShell * * This dual inheritance is a typical pattern in Qt Wayland Compositor when building extensions. * The QWaylandCompositorExtensionTemplate class creates the connection between * QWaylandCompositorExtension and the \c qt_example_shell class generated by \c qtwaylandscanner. * * Equivalently, the \c ExampleShellSurface class extends the generated * \c QtWaylandServer::qt_example_shell_surface class as well as \l{QWaylandShellSurfaceTemplate}, * which makes it a subclass of the ShellSurface class and establishes the connection between * Qt Wayland Compositor and the generated protocol code. * * To make the type available to Qt Quick, we use the \l{Q_COMPOSITOR_DECLARE_QUICK_EXTENSION_CLASS} * preprocessor macro for the convenience. Among other things, this handles automatically * initializing the extension when it has been added to the Qt Quick graph. * * \snippet custom-shell/compositor/exampleshell.cpp initialize * * The default implementation of the \c initialize() function register the extension with the * compositor. In addition to this, we initialize the protocol extension itself. We do this by * calling the generated \c init() function in the \c QtWaylandServer::qt_example_shell_surface * class. * * We also reimplement the virtual function generated for the \c surface_create request. * * \snippet custom-shell/compositor/exampleshell.cpp surface_create * * This virtual function is called whenever a client issues the request on the connection. * * Our shell extension only supports a single QWaylandSurfaceRole, but it is still important that * we assign it to the QWaylandSurface when we create a shell surface for it. The primary reason * for this is that assigning conflicting roles to the same surface is considered a protocol error, * and it is the compositor's responsibility to issue this error if it happens. Setting a role on * the surface when we adopt it, ensures that the protocol error will be issued if the surface is * reused with a different role later. * * We use built-in functions to convert between Wayland and Qt types, and create an * \c ExampleShellSurface object. When everything is prepared, we emit the \c shellSurfaceCreated() * signal, which in turn is intercepted in the QML code and added to the list of shell surfaces. * * \snippet custom-shell/compositor/qml/main.qml ExampleShell * * In \c{ExampleShellSurface}, we equivalently enable the shell surface part of the protocol * extension. * * \section1 Running the example * * In order to have a client successfully connect to the new shell extension, there is a couple * of configuration details to be handled. * * First of all, the client has to be able to find the shell extension's plugin. One simple way * of doing this is to set the \c QT_PLUGIN_PATH to point to the plugin install directory. Since * Qt will look up plugins by category, the plugin path should point to the parent directory that * contains the directory for category \c{wayland-shell-integration}. So if the installed file is * \c{/path/to/build/plugins/wayland-shell-integration/libexampleshellplugin.so}, then you should * set \c QT_PLUGIN_PATH as follows: * * \badcode * export QT_PLUGIN_PATH=/path/to/build/plugins * \endcode * * For other ways to configure the plugin directory, see the * \l{Deploying Plugins}{plugin documentation}. * * The final step is to make sure the client actually attaches to the correct shell extension. * Qt clients will automatically try to attach to the built-in shell extensions, but this can be * overridden by setting the \c QT_WAYLAND_SHELL_INTEGRATION environment variable to the name of * the extension to load. * * \badcode * export QT_WAYLAND_SHELL_INTEGRATION=example-shell * \endcode * * And that is all there is to it. The Custom Shell example is a limited shell extension with only * a very few features, but it can be used as a starting point for building specialized extensions. */