From e30fe48e4c13ed57be74eaa65749e180d03c2e0c Mon Sep 17 00:00:00 2001 From: Marcus Fritzsch Date: Wed, 13 Sep 2017 14:46:15 +0200 Subject: [PATCH] add doc/ Signed-off-by: Marcus Fritzsch --- CMakeLists.txt | 1 + doc/WindowManagerTMC.txt | 429 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 430 insertions(+) create mode 100644 doc/WindowManagerTMC.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index faeef67..464af6f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,7 @@ set(LINK_LIBCXX OFF CACHE BOOL "Link against LLVMs libc++") add_subdirectory(client-lib) add_subdirectory(src) +add_subdirectory(doc) install( FILES layers.json diff --git a/doc/WindowManagerTMC.txt b/doc/WindowManagerTMC.txt new file mode 100644 index 0000000..69182fd --- /dev/null +++ b/doc/WindowManagerTMC.txt @@ -0,0 +1,429 @@ += WindowManagerTMC +:doctype: book +:toc: +:icons: +:data-uri: +:lang: en +:encoding: utf-8 + +== Introduction +This WindowManager implements simple layout switching of applications on +multiple layers and with different layer layouts. + +=== Intended audience +This documentation is intended for developers and system integrators +that need to know how the window manager works and how it is to be used. + +=== Scope of this Document +This document covers the window manager that was implemented for TMC and +delivered to the Automotive Grade Linux (AGL) project. It includes its +implementation details, concepts of operation, configuration and usage. + +=== Known Issues +Currently there are a couple of known issues: + +* Weston seems not to redraw the screen correctly. When the window + manager makes scene changes in quick succession, Weston seems not to + redraw the screen correctly and also not send wl_surface::enter + events, which in turn leaves applications "dead" - i.e. not rendering + or showing up. We developed a simple secondary ivi-controller client + application *redraw_fixer* (See <<_redraw_fixer,redraw_fixer>> for more) + that listens for specific scene-change events and issues other commands + that should prompt a correct redraw - however, this does not work in + all instances. +* Only single-surface Qt applications are support through the AFBClient + library. This is a limitation of how Qt creates surface IDs for the + ivi-application interface. + +== Concepts +The window manager implements a couple of concepts in order to allow +efficient implementation. + +=== Layers +Layers are entities that are stacked on top of each other. Each layer +has an ID which is used for the ivi-controller interface, but this ID +also implicitly specifies its stacking order. That is, the screen render +order will be set according to the layer stacking which is determined by +the layer IDs. + +Layers are always full-screen. We do not use layer dimensions as a way +to setup the scene, rather - each layer has a layout attached to it, +which specifies an area that is used by surfaces to draw on. + +Additionally, layers will generally leave surfaces on below layers +activated, and only disable surfaces on layers the are above the +currently used layer. + +In order to deactivate surfaces on lower layer, it is possible to +deactivate these surfaces explicitly using the `deactivate_surface` API +call. + +=== Surfaces +Surfaces are _placed_ on layers according to their name. The surface +will then be resized to dimensions, according to the layer's layout +configuration. + +== Configuration +The window manager is configured with the _layers.json_ configuration +file, by default it is searched in `/etc/layers.json` but through the +use of the environment variable `LAYERS_JSON` the WM can be instructed +to use different file. Note, that the WM will not run unless this +configuration is found and valid. + +=== Configuration Items +This section describes configuration items available through +`layers.json`. It will do this, by first providing an example, and then +going into its components. + +Configuration items can contain `comment` objects, these are unused and +only for documentation purposes in the json, they can safely be removed +from the JSON file. + +==== main_surface +------ +"main_surface": { + "surface_role": "HomeScreen", +}, +------ + +The `main_surface` object describes a surface that will internally be +treated as the main surface - usually this mean _HomeScreen_. The only +special handling this surface receives, is that it is not allowed to +deactivate it. Placement of this surface on an layer is done by the +other configuration described below. + +* `surface_role` this configuration item specifies the name of the main + surface. Set this to e.g. `HomeScreen` if the main surface is called + `HomeScreen`. + +==== mappings +This configuration item is a list of surface-name to layer mappings. + +===== surface to layer mapping +------ +"mappings": [ + { + "role": "^HomeScreen$", + "name": "HomeScreen", + "layer_id": 1000, + "area": { "type": "full" }, + }, + { + "role": "^App.*", + "name": "apps", + "layer_id": 1001, + "area": { "type": "rect", + "rect": { "x": 0, + "y": 100, + "width": -1, + "height": -201 } }, + "split_layouts": [] + } +] +------ + +Each mapping defines the following items to map corresponding surfaces +to a layer. + +* `role` defines a regular expression that application drawing names are + matched against. If applications match tis regular expression, the + surface will be visible on this layer. +* `name` is just a name definition for this layer, it has no functional use + apart from identifying a layer with a name. +* `layer_id` specifies which ID this layer will use. +* `area` is an object that defines the area assigned to surfaces. +* `split_layouts` is an optional item, that - if present - defines a + number of possible split-screen layouts for this layer. + +===== Area +Areas can be either `full` or `rect`, whereas `full` means a fullscreen +layer, this is mostly useful for the main_surface or HomeScreen layer. +`rect` declares a layer drawing area specified as a rectangle with +start coordinates `x` and `y` as well as its dimensions `width` and +`height`. + +The dimensions can be specified relative to the screen dimensions. For +this negative values for width and height mus be used. + +For example, a fullscreen surface can have the following `rect` +definition: + +------ +"rect": { "x": 0, + "y": 0, + "width": -1, + "height": -1 } +------ + +So the expression for the actual surface dimensions when using +screen-size-relative values will be: + +------ +actual_width = screen_width + 1 + width +actual_height = screen_height + 1 + height +------ + +Or in other words, to leave an `N` wide border around a surface, the +actual value in the configuration needs to be `-N - 1`. + +===== split_layouts +this configuration item allows the specification of split-screen layouts +on layers for certain surfaces. A split screen layout always has a +_main_ surface and a _sub_ surface. In order to enter a split screen +layout, first the _main_ surface of the layout must be activated, and +then the _sub_ surface. In order to disable the split layout, one of the +two participating surface must be deactivate (or a surface on a layer +below the current one must be activated). + +------ +"split_layouts": [ + { + "name": "Media Player", + "main_match": "^App MPlayer Main$", + "sub_match": "^App MPlayer Sub", + } +] +------ + +A split layout object has the following attributes: + +* `name` defines its name, it has no actual function other then a way to + identify this split layout. +* `main_match` is a regular expression that matches for the _main_ + surface of this split layout. +* `sub_match` is a regular expression that matches for the _sub_ surface + of this layout. + +In the above example only the surface with drawing name +`App MPlayer Main` will be used as the _main_ surface, but all surfaces +that begin with `App MPlayer Sub` can be used as a _sub_ surface for +this layout. + +.Note +****** +The names must still match the layer's role match! +****** + +== Binding API +The binding API consists of a couple of AFB _verbs_ - that is function +calls to the Window Manager. + +=== Verbs (Functions) +* `RequestSurface(drawing_name: string): int` + Request a surface ID for the given name. This name and ID association + will live until the surface is destroyed (or e.g. the application + exits). Each surface that is managed by the window manager needs to + call this function first! +* `ActivateSurface(drawing_name: string)` + This function requests the activation of a surface. It usually is not + called by the application, but rather by the application framework or + the HomeScreen. +* `DeactivateSurface(drawing_name: string)` + Request deactivation of a surface. This function is not usually called + by applications themselves, but rather by the application framework or + the HomeScreen. +* `EndDraw(drawing_name: string)` + Signals the window manager, that the surface is finished drawing. This + is useful for consistent flicker-free layout switches, see the + Architecture document for details. + +There are a couple of non-essential (mostly for debugging and +development) API calls: + +* `list_drawing_names(): json` + List known surface _name_ to _ID_ associations. +* `ping()` + Ping the window manager. Does also dispatch pending events if any. +* `debug_status(): json` + Returns a json representation of the current layers and surfaces known + to the window manager. This represents the wayland-ivi-extension + object's properties. +* `debug_surfaces(): json` + Returns a json representation of all surfaces known to the window + manager. This represents the wayland-ivi-extension properties of the + surfaces. +* `debug_layers(): json` + Returns the current layer configuration, as configured through + _layers.json_. +* `debug_terminate()` + Terminates the afb-daemon running the window manager binding, if the + environment variable `WINMAN_DEBUG_TERMINATE` is set. + +=== Events +The window manager broadcasts certain events (to all applications) that +signal information on the state of the surface regarding the current +layout. + +* `Active(drawing_name: string)` + Signal that the surface with the name `drawing_name` is now active. +* `Inactive(drawing_name: string)` + Signal that the surface with the name `drawing_name` is now inactive. + This usually means, the layout got changed, and the surface is now + considered inactive (or sleeping). +* `Visible(drawing_name: string)` + Signal applications, that the surface with name `drawing_name` is now + visible. +* `Invisible(drawing_name: string)` + Signal applications that the surface with name `drawing_name` is now + invisible. +* `SyncDraw(drawing_name: string)` + Signal applications, that the surface with name `drawing_name` needs + to redraw its content - this usually is sent when the surface geometry + changed. +* `FlushDraw(drawing_name: string)` + Signal to applications, that the surface with name `drawing_name` can + now be swapped to its newly drawn content as the window manager is + ready to activate a new layout (i.e. a new surface geomtry). + +=== Binding API Usage +For a detailed description on how the binding API is supposed to be +used, refer to the Architecture document. + +== Building and Running + +=== Dependencies +This project is intended to be build with the 4.0 release of AGL. + +Build dependencies are as follows: + +* afb-daemon >= 1.0 +* libsystemd >= 2222 +* wayland-client >= 1.11 + +=== Build Configuration +Use cmake to configure a build tree: + +-------- +mkdir build +cd build +cmake .. +make +[sudo] make install +-------- + +== Utilities +With the actual window manager implementation, two general utilities are +provided. + +=== wm-request +A shell script, that wraps `afb-client-demo` and issues commands to the +window manager using the AFB exposed API. It will call synchronously to +the WM, and output any events that are happening in the meantime. +Replies are printed to stdout using an failed/success annotation and a +dump of the actual json reply from the AFB. + +==== Commands +* `wm-request requestsurface $NAME`: request the surface with $NAME, + this will succeed only if the surface name is not yet (or anymore) + known. The actual call return is in the "response" json object of the + reply. +* `wm-request activatesurface $NAME`: activate the surface with $NAME. + This call will only succeed of the surface is available and not + currently active. The reply just contains failure/success information, + but no actual return value from the API. +* `wm-request deactivatesurface $NAME`: deactivate the surface with + $NAME. This call will only succeed if the surface is currently active, + no return value is provided by the API. +* `wm-request ping`: Just send a *ping* to the window manager, can be + sued to check whether the WindowManager is alive or not. + +=== redraw_fixer +This utility is intended to be ran alongside the compositor, it will +listen for certain events regarding surfaces, and issue a couple of +other commands, to hopefully trigger a redraw of the surface in the +compositor. + +It will print messages for each acted-upon event, and exit when the +compositor exits. + +== Implementation Notes +The window manager is implemented as a app-framework-binder binding. That +means, the build produces one shared object that exports a binding +interface. + +=== Binding code generation +The binding API is rather simple; functions receive a json object +describing arguments and return a json object describing the result or +an error. In order to simplify development, the +`generate-binding-glue.py` script was added, that contains a description +of the API as a python dictionary. This script generates the header +`afb_binding_api.hpp` and the afb binding functions as +`afb_binding_glue.inl`. Where the latter is included in `main.cpp`. + +Each function for the AFB binding that is generated does the following: + +* Lock the binding mutex, so that we serialize all access to the + binding. +* Do some debug logging (if wanted). +* Check the binding state, i.e. the compositor might have exited + unexpectedly at which point it would not make sense to continue. +* Extract the arguments from the json object that is provided (doing + some primitive type checking). +* Call the afb_binding_api method corresponding to this binding function +* Check the afb_binding_api's function return value, log an error state + and return the result to the afb request. + +The generated functions do also check for any "loose" exception that +comes out of the afb_binding_api call (which in turn might call the +actual non-trivial implementation in `App`). However, *IF* an exception +is thrown and not handled inside the afb_binding_call, that internal +state of the window manager might be broken at this time (hence the +talkative error log). + +=== Structure +The implementation is loosely split across the following source files: + +* `main.cpp`: The program entry point as used by the afb-daemon. This + file defines the afbBindingV2 symbol tat is used by the afb-daemon in + order to load a binding. It also defines the wayland fd event + dispatcher and some globals to be used (as context for the afb calls + we receive). +* `afb_binding_api.cpp`: The implementation of the afb binding + functions. The actual functions are generated by + `generate-binding-glue.py` which generates a *.inl* file that is + included by `main.cpp`. +* `app.cpp` / `app.hpp`: This is the main application logic + implementation. +* `config.cpp` / `config.hpp`: Very simple configuration item interface. +* `controller_hooks.hpp`: hook functions called by the wayland + controller to call into the App instance. Only a very limited number + of events are passed to the Application, which allowed the usage of + such a simple interface. +* `json_helper.cpp` / `json_helper.hpp`: Smaller json related helper + functions. +* `layers.cpp` / `layers.hpp`: Actually hold all the data from + layers.json configuration, do some transformations and service the App + implementation. +* `layout.cpp` / `layout.hpp`: Very simple layout state for the + implementation of split layouts and tracking of the surfaces involved. +* `policy.hpp`: PolicyManager implementation stub. Gets passed the + current and new layout on layout switch and can decide upon it being + valid or not. +* `result.hpp`: Simple result class around `std::experimental::optional` + that additionally can hold a `char const *` to describe the error. +* `util.cpp` / `util.hpp`: general utility functions and structs - and + preprocessor definitions (e.g. `log*()` to AFB logging functions. +* `wayland.cpp` / `wayland.hpp`: A C++ object-oriented libwayland-client + wrapper. It is instanced in `main.cpp` and handles all our wayland + needs. + +=== XXX +a + +--------------------------- +Some code +--------------------------- + +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`. + +.Note +****************** +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 +https://linux.die.net/man/2/epoll_wait[epoll_wait(2)]). +****************** + +// vim:set ft=asciidoc tw=72 spell spelllang=en_US: -- 2.16.6