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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
|
Introduction
============
The AFBClient library provides a simple interface to manipulate and
query the s of the window manager application framework binding. It is
composed of one singleton class that needs to be integrated and called
from the client application.
Intended audience
-----------------
This document is intended to be useful to application developers.
Scope of this Document
----------------------
This document describes the singleton class interface to the *Window
Manager* binding service.
class AFBClient
===============
This is the public interface of the class `AFBClient`. Private members
and methods are not reproduced as they will not affect usage of the
class by client applications.
class AFBClient
{
public:
static AFBClient &instance();
int init(int port, char const *token);
int dispatch();
// WM API
int requestSurface(const char *label);
int activateSurface(const char *label);
int deactivateSurface(const char *label);
int endDraw(const char *label);
enum EventType {
Event_Active,
Event_Inactive,
Event_Visible,
Event_Invisible,
Event_SyncDraw,
Event_FlushDraw,
};
void set_event_handler(enum EventType et,
std::function<void(char const *label)> f);
};
Errors
------
Methods returning an `int` signal successful operation when returning
`0`. In case of an error, an error value is returned as a negative errno
value. E.g. `-EINVAL` to signal that some input value was invalid.
Additionally, logging of error messages is done on the standard error
file descriptor to help debugging the issue.
Labels
------
Surface labels are any valid strings. For `requestSurface()` these
strings must match the *Window Manager* configuration in order to be
allowed to be displayed on one layer or the other. For all other calls
the label must match the exact name of a requested surface.
Caveats
-------
Any of the API calls to the *Window Manager* will be synchronous (and
thus block until a reply from the *Window Manager* service is received).
These are the Methods `requestSurface()`, `activateSurface()`,
`deactivateSurface()` and `endDraw()`. However, `requestSurface()` is
only ever called once to request a surface so this should not be a
concern for this Method.
Methods
-------
### AFBClient::init(port, token)
Initialize the Binding communication.
The `token` parameter is a string consisting of only alphanumeric
characters, and with a maximum length of 20 characters. If these
conditions are not met, the AFBClient instance will not initialize, i.e.
this call will return `-EINVAL`.
The `port` parameter is the port the afb daemon is listening on, an
invalid port will lead to a failure of the call and return `-EINVAL`.
### AFBClient::requestSurface(label)
This method requests a surface with the label given from the *Window
Manager*. It will return `0` for a successful surface request, and
`-errno` on failure. Additionally, on the standard error, messages are
logged to help debgging the issue.
### AFBClient::activateSurface(label)
This method is mainly intended for *manager* applications that control
other applications (think an application manager or the *HomeScreen*).
It instructs the window manager to activate the surface with the given
*label*.
This method only is effective after the actual window or surface was
created by the application.
### AFBClient::deactivateSurface(label)
This method is mainly intended for *manager* applications that control
other applications. It instructs the window manager to deactivate the
surface associated with the given label. Note, that deactivating a
surface also means to implicitly activate another (the last active or if
not available *main surface* or *HomeScreen*.)
This method only is effective after the actual window or surface was
created by the application.
### AFBClient::endDraw(label)
This function is called from a client application when it is done
drawing its surface content.
It is not crucial to make this call at every time a drawing is finished
- it is mainly intended to allow the window manager to synchronize
drawing in case of layout switch. The exact semantics are explained in
the next [Events](#_events) Section.
### AFBClient::dispatch()
This function needs to be called periodically from the application main
loop in order to dispatch binder events and requests. This function will
block at most 1ms if no events are ready. For more information, see the
[Usage](#_usage) and [Example Use Case](#_example_use_case) sections
below.
### AFBClient::set\_event\_handler(et, func)
This method needs to be used to register event handlers for the WM
events described in the EventType enum. Only one hendler for each
EventType is possible, i.e. if it is called multiple times with the same
EventType the previous handler will be replaced.
The `func` handler functions will receive the label of the surface this
event is targeted at.
See Section [Events](#_events) for mor detailed information about event
delivery to client applications.
Usage
-----
### Initialization of AFBClient
Before usage of the AFBClient singleton, the method `init()` must be
called once, it will return `-errno` in case of en error and log
diagnostic messages to stderr.
### Request a surface
When creating a surface with *Qt* - it is necessary to request a surface
from the WM, internally this will communicate with the window manager
binding. Only after `requestSurface()` was successful, a surface should
be created.
This is also true for *QML* aplications, where only after the
`requestSurface()` should the load of the resource be done. The method
returns `0` after the surface was requested successfully.
#### Workings of requestSurface()
`AFBClient::requestSurface()` calls the AFB binding verb
`requestsurface` of the `winman` API. This API call will return a
numeric ID to be used when creating the surface. This ID is never
explicitly returned to the client application, instead, it is set in the
application environment in order for *Qt* to then use it when creating
the surface.
With the current *Qt* implementation this means, that only one surface
will be available to client applications, as subsequent windows will
increment this numeric ID internally - which then will lead to IDs that
cannot be known by the window manager as there is no direct
communication from *Qt* to the WM.
### Integration into the application main loop
Calls directed at the window manager are synchronoous, i.e. they wil
ensure communication to the WM happens at the time of the call and
finishes before returning to the client application. However, in order
for events to be received by the application, the `dispatch()` method
needs to be called periodically with a small timeout from the
application mainloop. In *Qt* this can be achieved by a code fragment
analogous to the following:
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, &app,
[] {AFBClient::instance().dispatch();});
timer.setInterval(16);
timer.start();
This creates a contineously firing timer that calls the AFBClient’s
`dispatch()` method. Note that calls to event handlers will be done in
this context from the thread that called `dispatch()`.
The timeout should be small in order to not block too long, but also a 0
timeout will not dispatch anything and return immediately (see
[epoll\_wait(2)](https://linux.die.net/man/2/epoll_wait)).
Events
------
Events are a way for the *Window Manager* to propagate information to
client applications. It was vital for the project to implement a number
of events, that mirror functionality that is already present in the
wayland protocol.
All events have the surface `label` as argument - a way to enable future
multi-surface applications.
As already stated above, this is currently not possible with the way
*Qt* implements its surface ID setting.
### Active and Inactive Events
These events signal an application that it was activated or deactivated
respectively. Usually this means it was switched visible - which means
the surface will now be on the screen and therefor continue to render.
### Visible and Invisible
These events signal an application that it was switched to be visible or
invisible respectively. These events too are handled implicitly through
the wayland protocol by means of `wl_surface::enter` and
`wl_surface::leave` events to the client.
### SyncDraw and FlushDraw
These events instruct applications that they should redraw their surface
contents - again, this is handled implicitly by the wayland protocol.
`SyncDraw` is sent to the application when it has to redraw its surface.
`FlushDraw` is sent to the application when it should swap its buffers,
that is *signal* the compositor that its surface contains new content.
Example Use Case
----------------
In order to enable application to use the `WM` surface registration
function the above described steps need to be implemented.
As a minimal example the usage and initialization can look like the
following.
// Assume a program argc and argv.
QGuiApplication app(argc, argv);
auto &wm = AFBClient::instance();
// initialize the AFBClient binding.
if(wm.init(1234, "wmtest") != 0) {
exit(EXIT_FAILURE);
}
// Request a surface label from the WM.
char const *surface_label = "AppMediaPlayer";
if (wm.requestSurface(surface_label) != 0) {
exit(EXIT_FAILURE);
}
// Register an Active event handler.
wm.set_event_handler(Event_Active,
[](char const *label) {
qDebug() << "Surface" << label << "got activated";
});
// Initialize application window
// ...
// request to activate the surface, this should usually
// not be done by the client application.
if (wm.activateSurface(surface_label) != 0) {
fprintf(stderr, "Could not activate the surface\n");
exit(EXIT_FAILURE);
}
// enable main loop integration.
QTimer timer;
QObject::connect(&timer, &QTimer::timeout, &app,
[&wm] {wm.dispatch();});
timer.setInterval(16);
timer.start();
// e.g. exec the qt application
app.exec();
Alternatively to the `QTimer` mainloop integration a thread could be
started up which does the periodic polling of the binding. However,
using a timer event is a much cleaner approach.
|