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 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.