summaryrefslogtreecommitdiffstats
path: root/examples/wayland/custom-extension/doc/src/custom-extension.qdoc
blob: 7b2cb0802a52beed7166fb1054c8886ab399b3de (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
// 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.
 */