summaryrefslogtreecommitdiff
path: root/examples/wayland/custom-shell/doc/src/custom-shell.qdoc
blob: 36e84d8bc2089afc4ff38c2cfcecf7a29312ee28 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
 * \title Custom Shell
 * \example custom-shell
 * \meta category {Embedded}
 * \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{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{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.
 */