EFL Multiple Output Support with Wayland

Supporting multiple outputs is something most of us take for granted in the world of X11 because the Xorg team has had years to implement and perfect support for multiple outputs. While we have all enjoyed the fruits of their labor, this has not been the case for Wayland. There are two primary types of multiple output configurations that are relevant: cloned and extended outputs.

Cloned Outputs

Cloned output mode is the one that most people are familiar with. This means that the contents of the primary output will be duplicated on any additional outputs that are enabled.  If you have ever hot plugged an external monitor into your Windows laptop, then you have seen this mode in action.

EFL Multiple Output Support with Wayland - mirrored-display
An example of cloned output

Extended Outputs

Extended output mode is somewhat less common, yet still very important. In this mode, the desktop is extended to span across multiple outputs, while the primary output retains sole ownership of any panels, shelves, or gadgets that reside there. This enables the secondary monitor to act as an extended desktop space where other applications can be run.

EFL Multiple Output Support with Wayland - extended-display
An example of extended outputs

Multiple Output Support in Weston and EFL

Weston, the reference Wayland compositor, has had the ability to support multiple outputs in an extended configuration for quite some time now. This extended mode configuration allows each output to run with completely separate framebuffer and repaint loops. There are patches currently being worked on to enable cloned output support, but these have not been pushed upstream yet.

While EFL has had support for multiple outputs when running under X11 for quite some years now, the support for multiple outputs under Wayland has been missing. A major hurdle to implementing this support has been that Evas, the EFL rendering library, was not accounting for multiple outputs in it’s rendering update loop. With this commit, our very own Cedric Bail has removed that hurdle and enabled work to progress further on implementing this feature.

Complete Multiple Output Support is on the Way

While the current implementation of multiple output support in EFL Wayland only supports outputs in a cloned configuration, support for extended mode is in development and is expected to be available in upstream EFL soon. When these patches get published upstream, this will bring our EFL Wayland implementation much closer in parity to our X11 support. Thankfully there are little to no changes needed to add this support to our Enlightenment Wayland compositor.

EFL: Enabling Wayland Output Rotation

The Enlightenment Foundation Libraries have long supported the ability to do output rotation when running under X11 utilizing the XRandR (resize and rotate) extension. This functionality is exposed to the user by way of the Ecore_X library which provides API function calls that can be used to rotate a given output.

Wayland

While this functionality has been inside EFL for a long time, the ability to rotate an output while running under the Enlightenment Wayland Compositor has not been possible. This is due, in part, to the fact that the Wayland protocol does not provide any form of RandR extension. Normally this would have proved a challenge when implementing output rotation inside the Enlightnement Wayland compositor, however EFL already has the ability to do this.

Software-Based Rotation

EFL’s Ecore_Evas library, which is used as the base of the Enlightenment Compositor canvas, has the ability to perform software-based rotation. This means that when a user asks for the screen to be rotated via Enlightenment’s Screen Setup dialog, the Enlightenment Compositor will draw it’s canvas rotated to the desired degree using the internal Evas engine code. While this works for any given degree of rotation, it is not incredibly efficient considering that modern video cards can do rotation themselves.

Hardware-Based Rotation

Many modern video cards support some form of hardware-based rotation. This allows the hardware to rotate the final scanout buffer before displaying to the screen and is much more efficient than software-based rotation. The main drawback is that many will only support rotating to either 0 degrees or 180 degrees. While this is a much more practical and desired approach to output rotation, the lack of other available degrees of rotation make it a less than ideal method.

Hybrid Rotation

In order to facilitate a more ideal user experience, we have decided to implement something I call hybrid rotation. Hybrid rotation simply means that when asked to rotate the compositor canvas, we will first check if the hardware is able to handle the desired rotation itself. In the event that the hardware is unable to accommodate the necessary degree of rotation, it will then fall back to the software implementation to handle it. With this patch we can now handle any degree of output rotation in the most efficient way available.

The major benefit for application developers is that they do not need to know anything about the capabilities of the underlying hardware. They can continue to use their existing application code to rotate the canvas:

/**
 * Ecore example illustrating the basics of ecore evas rotation usage.
 *
 * You'll need at least one Evas engine built for it (excluding the
 * buffer one). See stdout/stderr for output.
 *
 * @verbatim
 * gcc -o ecore_evas_basics_example ecore_evas_basics_example.c `pkg-config --libs --cflags ecore evas ecore-evas`
 * @endverbatim
 */

#include <Ecore.h>
#include <Ecore_Evas.h>
#include <unistd.h>

static Eina_Bool
_stdin_cb(void *data EINA_UNUSED, Ecore_Fd_Handler *handler EINA_UNUSED)
{
   Eina_List *l;
   Ecore_Evas *ee;
   char c;

   int ret = scanf("%c", &c);
   if (ret < 1) return ECORE_CALLBACK_RENEW;

   if (c == 'h')
     EINA_LIST_FOREACH(ecore_evas_ecore_evas_list_get(), l, ee)
       ecore_evas_hide(ee);
   else if (c == 's')
     EINA_LIST_FOREACH(ecore_evas_ecore_evas_list_get(), l, ee)
       ecore_evas_show(ee);
   else if (c == 'r')
     EINA_LIST_FOREACH(ecore_evas_ecore_evas_list_get(), l, ee)
       ecore_evas_rotation_set(ee, 90);

   return ECORE_CALLBACK_RENEW;
}

static void
_on_delete(Ecore_Evas *ee)
{
   free(ecore_evas_data_get(ee, "key"));
   ecore_main_loop_quit();
}

int
main(void)
{
   Ecore_Evas *ee;
   Evas *canvas;
   Evas_Object *bg;
   Eina_List *engines, *l;
   char *data;

   if (ecore_evas_init() <= 0)
     return 1;

   engines = ecore_evas_engines_get();
   printf("Available engines:\n");
   EINA_LIST_FOREACH(engines, l, data)
     printf("%s\n", data);
   ecore_evas_engines_free(engines);

   ee = ecore_evas_new(NULL, 0, 0, 200, 200, NULL);
   ecore_evas_title_set(ee, "Ecore Evas basics Example");
   ecore_evas_show(ee);

   data = malloc(sizeof(char) * 6);
   sprintf(data, "%s", "hello");
   ecore_evas_data_set(ee, "key", data);
   ecore_evas_callback_delete_request_set(ee, _on_delete);

   printf("Using %s engine!\n", ecore_evas_engine_name_get(ee));

   canvas = ecore_evas_get(ee);
   if (ecore_evas_ecore_evas_get(canvas) == ee)
     printf("Everything is sane!\n");

   bg = evas_object_rectangle_add(canvas);
   evas_object_color_set(bg, 0, 0, 255, 255);
   evas_object_resize(bg, 200, 200);
   evas_object_show(bg);
   ecore_evas_object_associate(ee, bg, ECORE_EVAS_OBJECT_ASSOCIATE_BASE);

   ecore_main_fd_handler_add(STDIN_FILENO, ECORE_FD_READ, _stdin_cb, NULL, NULL, NULL);

   ecore_main_loop_begin();

   ecore_evas_free(ee);
   ecore_evas_shutdown();

   return 0;
}

 

Summary

While not all hardware can support the various degrees of output rotation, EFL provides the ability to rotate an output via software. This gives the Enlightenment Wayland Compositor the ability to handle output rotation in the most efficient method available while being transparent to the user.

Ecore_Evas: Creating a Canvas for Wayland Applications

While there are some developers who are familiar with using Ecore_Evas to create a canvas for applications, we often find that new EFL users face some confusion when first trying to create an application. This article aims to provide a simple example of how to create your first EFL Wayland application.

For those not familiar with the Ecore_Evas library, it is a set of functions that make it easy to tie together Ecore’s main loop and input handling to Evas; as such, it’s a natural base for EFL applications. While this combination makes it easy to create the basic aspects all applications need, for normal applications (those that use buttons, checkboxes and layouts) one should consider using Elementary. Ecore_Evas is extremely well suited for applications that are not based on widgets. It has a main loop that delivers events, does basic window handling, and leaves all of the drawing up to the user. This works very well if used in conjunction with Edje or when doing custom drawing such as what’s done for games, for example.

A typical example of an Ecore_Evas application may look like this

/**
 * Ecore example illustrating the basics of ecore evas usage.
 *
 * You'll need at least one Evas engine built for it (excluding the
 * buffer one). See stdout/stderr for output.
 *
 * @verbatim
 * gcc -o ecore_evas_basics_example ecore_evas_basics_example.c `pkg-config --libs --cflags ecore evas ecore-evas`
 * @endverbatim
 */

#include 
#include 
#include 

static Eina_Bool
_stdin_cb(void *data EINA_UNUSED, Ecore_Fd_Handler *handler EINA_UNUSED)
{
   Eina_List *l;
   Ecore_Evas *ee;
   char c;

   int ret = scanf("%c", &c);
   if (ret < 1) return ECORE_CALLBACK_RENEW;

   if (c == 'h')
     EINA_LIST_FOREACH(ecore_evas_ecore_evas_list_get(), l, ee)
       ecore_evas_hide(ee);
   else if (c == 's')
     EINA_LIST_FOREACH(ecore_evas_ecore_evas_list_get(), l, ee)
       ecore_evas_show(ee);

   return ECORE_CALLBACK_RENEW;
}

static void
_on_delete(Ecore_Evas *ee)
{
   free(ecore_evas_data_get(ee, "key"));
   ecore_main_loop_quit();
}

int
main(void)
{
   Ecore_Evas *ee;
   Evas *canvas;
   Evas_Object *bg;
   Eina_List *engines, *l;
   char *data;

   if (ecore_evas_init() <= 0)
     return 1;

   engines = ecore_evas_engines_get();
   printf("Available engines:\n");
   EINA_LIST_FOREACH(engines, l, data)
     printf("%s\n", data);
   ecore_evas_engines_free(engines);

   ee = ecore_evas_new(NULL, 0, 0, 200, 200, NULL);
   ecore_evas_title_set(ee, "Ecore Evas basics Example");
   ecore_evas_show(ee);

   data = malloc(sizeof(char) * 6);
   sprintf(data, "%s", "hello");
   ecore_evas_data_set(ee, "key", data);
   ecore_evas_callback_delete_request_set(ee, _on_delete);

   printf("Using %s engine!\n", ecore_evas_engine_name_get(ee));

   canvas = ecore_evas_get(ee);
   if (ecore_evas_ecore_evas_get(canvas) == ee)
     printf("Everything is sane!\n");

   bg = evas_object_rectangle_add(canvas);
   evas_object_color_set(bg, 0, 0, 255, 255);
   evas_object_resize(bg, 200, 200);
   evas_object_show(bg);
   ecore_evas_object_associate(ee, bg, ECORE_EVAS_OBJECT_ASSOCIATE_BASE);

   ecore_main_fd_handler_add(STDIN_FILENO, ECORE_FD_READ, _stdin_cb, NULL, NULL, NULL);

   ecore_main_loop_begin();

   ecore_evas_free(ee);
   ecore_evas_shutdown();

   return 0;
}

This example application will use the first available Ecore_Evas engine to create a canvas that contains a blue rectangle.

Ecore_Evas Creating a Canvas for Wayland Applications - shot-2016-12-13_08-42-35

While this is not a very exciting example, it does illustrate how to create a basic Ecore_Evas application quite well.

How to Use Wayland with Ecore_Evas

With a simple modification of one line, this example application can run in a Wayland environment. The following line is changed to facilitate this.

ee = ecore_evas_new(NULL, 0, 0, 200, 200, NULL);

To run in Wayland, it should read

ee = ecore_evas_new("wayland_shm", 0, 0, 200, 200, NULL);

This essentially tells ecore_evas to utilize the Wayland_Shm engine to create a new canvas; this is an Evas engine that uses Wayland and Shared-Memory for drawing. There are other Evas Wayland engines available, such as the "wayland_egl" engine which uses Hardware Accelerated drawing (namely EGL).

What many new EFL developers do not realize is that there is an even easier way to make this application run in a Wayland environment, without changing a single line of code! While running in a Wayland environment (Weston, Enlightenment-Wayland, Gnome-Wayland, etc) the following command should be run from the terminal

export ECORE_EVAS_ENGINE=wayland_shm

Then, the application can be run, and when ecore_evas_new executes it will check for the ECORE_EVAS_ENGINE environment variable and attempt to use the engine specified there.

This means application developers who utilize Ecore_Evas can easily make any application run in a Wayland environment. It's possible to choose to make it Wayland specific by modifying the function call that creates the canvas, or it's possible to simply export an environment variable to change the engine that's in use. Personally, I have always found it easier to set the environment variable because this makes it possible to test the application under various windowing systems (such as X11, Wayland, Framebuffer) without modifying and recompiling the code.

For more information on how to utilize Ecore_Evas to create a basic EFL application, please see the Ecore_Evas documentation.

Ecore_Wl2: An EFL Library for Wayland Applications

Throughout the years of developing Wayland support for EFL, few EFL libraries have had as much impact on EFL Wayland applications as the Ecore_Wayland library has. This library was one of the first to make it possible to truly run EFL applications in a Wayland environment. As the years progressed, it became apparent that Ecore_Wayland had some shortcomings; this blog post will introduce you to the replacement for Ecore_Wayland, called Ecore_Wl2.

Ecore_Wayland’s Shortcomings

While testing our first Wayland implementation, it became apparent that the initial implementation of the Ecore_Wayland library had some drawbacks. Publicly exposed structures could not be changed easily without breaking existing applications, and any changes to existing Wayland protocols would require significant changes to our Ecore_Wayland library. It was also discovered that when an EFL Wayland application creates a new window, the backend library also creates an entirely new display and connection to the Wayland server. This resulted in high memory usage in EFL applications.

Due to these shortcomings, the community decided that EFL should have a better library for integrating with Wayland, and thus Ecore_Wl2 was born. Determined not to repeat the mistakes of the past, I began the long and arduous task of giving birth to a new library.

Building a More Capable Library

One of the first tasks with the new library was to move structures fields out of the public API so they could later be changed without breaking existing applications. As a result, in Ecore_Wl2 there are some publicly exposed structures (via Ecore_Wl2.h header file), however, the individual fields of these structures are not directly accessible. There are now public API functions to retrieve structure members to allow some access to these fields; this makes it possible to make necessary changes in the future while also maintaining existing applications.

The next area of focus for the new library was to reduce memory usage. In the older library, every window of every application would create it’s own Ecore_Wayland_Display: a huge waste of resources. I decided to implement display caching; this means the library code now maintains a cache of existing displays. When an application requests a display it calls ecore_wl2_display_connect():

EAPI Ecore_Wl2_Display *
ecore_wl2_display_connect(const char *name)

However, behind the scenes the library checks for any existing displays in the cache that match the given display name and return it. If no display is found in the internal cache, a new one is created and cached. This allows for display reuse without added memory overhead, especially for an application that intends to create more than one window. Since we use this new library in the server implementation, caching server displays has also been implemented.

While this blog post has briefly touched on the new Ecore_Wl2 library, I invite everyone to keep an eye open for another upcoming post concerning the Ecore_Wl2 library. It will go into more detail related to the improvements the library provides and how to make use of it.

Ecore_Drm2: How to Use Atomic Modesetting

In a previous article, I briefly discussed how the Ecore_Drm2 library came into being. This article will expand on that article and provide a brief introduction to the Atomic Modesetting and Nuclear Pageflip features inside the new Ecore_Drm2 library.

What Makes Atomic Modesetting and Nuclear Pageflip so Great?

For those that are unaware of what “modesetting” is, you may read more about it here. Atomic Modesetting is a feature that allows for output modes (resolutions, refresh rate, etc) to be tested in advance on a single screen or on multiple outputs. A benefit of this feature is that the given mode may be tested prior to being applied. If the test of a given output mode fails, the screen image doesn’t need to be changed to confirm a given mode works or not, thus reducing screen flickering.

Atomic/Nuclear Pageflipping allows for a given scanout framebuffer object and/or one or more hardware plane framebuffers to be updated in a single atomic operation. This means that updates to the screen output, whether they be updates of a given hardware plane, updates to a CRTC framebuffer, or even updates to rotation can happen in a single vblank interrupt. For the end user, this provides a much more friendly screen update by reducing redraws and flicker.

One of the things we strive to do with EFL is to keep our API simple to use. With that in mind, you will not notice any new API functions related to using Atomic Modesetting or Nuclear Pageflipping inside the Ecore_Drm2 library. You may be asking yourself right now: “Well, how do I make use of this then?” I have great news, you may already be using it :-). Rather than add new API functions, which need to be explicitly called for these features, I decided early that these benefits should come without any cost so that existing applications can make use of them without having to be modified.

The Technical Details

Inside the Ecore_Drm2 library there are various API functions that allow for setting output modes, or even sending a framebuffer object to scanout. These existing API functions have been modified to make use of Atomic Modesetting/Nuclear Pageflipping automatically where applicable. This means applications that are already using the various APIs of the Ecore_Drm2 library do not need to be changed.

Behind the scenes, the usage of Atomic Modesetting/Pageflipping is determined at runtime. This means that when the Ecore_Drm2 library gets initialized, it will make some checks to see if atomic usage is supported. Currently, these features are only supported on an Intel (i915) graphics card with a linux kernel version >= 4.8.0. If the Ecore_Drm2 initialization finds the proper driver and kernel version, it will then attempt to enable DRM_CLIENT_CAP_ATOMIC to support Atomic Modesetting/Pageflipping. If the client capability is successfully set, it will then attempt to enable DRM_CLIENT_CAP_UNIVERSAL_PLANES in order to enable hardware plane support.

Assuming all of the internal checks pass and the client capabilities are set successfully, the application will gain the added benefits of these features without any added work :-). There is one more thing that May be needed on some systems to get this working: it has been my experience that some boxes still need kernel boot-time options enabled for atomic to function. The necessary options (if required) are: drm.atomic=1, and i915.nuclear_pageflip=1.

While these features may not yet be available for all end-users to enjoy, the benefits they provide should be something that everyone can look forward to. With the continued hard work of linux kernel developers and graphics driver developers, we may all be able to enjoy these redraws soon :-)

Introducing the New & Improved Ecore_Drm2 Library

In the early days of developing Wayland support in EFL/Enlightenment, it was quickly apparent that EFL would need an abstraction library to interface with libdrm. We wanted users of EFL to be able to call simple functions without having to know about the underlying internals of libdrm, thus the original Ecore_Drm library was born. First, efforts to develop this library were undertaken with much enthusiasm and little fan-fare. After the birth of Ecore_Drm, we then proceeded to integrate it’s usage into some new Evas and Ecore_Evas engines so that the Enlightenment Desktop Shell could make use of it and render our first standalone Wayland desktop implementation.

After kicking the tires of our Wayland desktop for a while, we came to realize some shortcomings of the existing Ecore_Drm implementation. For starters, it would create it’s own Ecore_Drm_Device structure when launching the Enlightenment Wayland desktop (this structure was a representation of the physical connection to the /dev/dri card used in rendering). Along with this structure came lists of outputs, inputs, seats, planes, etc. In short, this structure was not very light and memory was being used up needlessly. Other shortcomings of the Ecore_Drm library included things such as structures being declared in public headers that couldn’t be easily changed, input device handling, systemd/logind support being coded directly into the library, and several other issues.

Redesigning From the Ground up

The community came to the easy decision that a better implementation was needed, thus we started efforts to create a new implementation and the Ecore_Drm2 library was born. This new library was developed slowly, while taking into account the shortcomings of the first iteration because we didn’t want to repeat the mistakes of the past. To help avoid these mistakes, the Ecore_Drm2 library API is currently marked as a ‘beta’ API which means it’s susceptible to changes at any time until the beta API marking is removed.

After several months of careful development and several months of testing, Ecore_Drm2 was deemed stable and complete enough to be pushed upstream. Efforts then ensued to modify the existing Evas and Ecore_Evas engines to use this shiny new library.

The new Ecore_Drm2 library fixes all of the shortcomings of the previous library. The community decided at inception that input handling should reside in it’s own library (thus the elput library was created). This removed a lot of internal overhead inside the Ecore_Drm2 library while also removing the need for the library itself to interface with systemd/logind. Public structures exposed via the Ecore_Drm2 library now have their actual implementations kept private so that changes could be made in the future if necessary. Internally, the size of the Ecore_Drm2_Device structure has been greatly reduced, saving memory.

While many improvements have been made to this new library (compared to the old one), one of the greatest overall improvements came as a result of it’s existence. With the new version of this library, we realized that the Evas and Ecore_Evas drm engines were also ripe for improvement. These rendering improvements did not happen by accident, but rather are the result of some great efforts, led by Derek Foreman, that provided tangible benefits by reducing tearing, decreasing time to first frame, improving rendering speed, and reducing memory usage. We believe we will see many more improvements just like this as a result of Ecore_Drm2.

Elput: A Libinput Abstraction for EFL

Input is something generally taken for granted, but it’s not without issues. While working on a new EFL library for Direct Rendering, the community decided that having the same libinput code duplicated across multiple internal subsystems like Ecore_Fb, Ecore_Drm, etc. would be a great effort to maintain in the future. To reduce this effort, Elput was created.

Introducing Elput

Elput is a library designed to abstract all the gory details of using libinput, and it provides a central API that can be used to initialize, iterate, and manipulate various input devices found on a system. These can include keyboards, pointers, touch screens, and any other input device that libinput supports.

Elput is also multi-seat aware, meaning that when a new input device gets attached to the system and belongs to a different seat, Elput will automatically create a new seat internally and do any setup required for that new input device to function. This library will also handle udev event processing. This means that when the mouse is moved or a button or keyboard key is pressed, Elput will automatically handle parsing that information from udev and sending out events to let other subsystems know that an input event has occurred. This makes application development easier since it’s possible to setup listeners for these events and to catch keypress events, etc.

Elput is designed to support various system interfaces such as systemd-logind, direct manipulation, or any other type of interface that may be needed in the future. This library provides a general “Manager” that is responsible for getting session and seat information from the system along with any other setup that a given interface may require. Currently, Elput provides an interface with systemd-logind and dbus integration out of the box; other interfaces can be easily added if needed.

The Components of Elput

A typical Elput interface would need to supply pointers to various functions which are required in order to support inputs on a given system. The Elput_Interface structure contains these possible function pointers:

typedef struct _Elput_Interface
 {
 Eina_Bool (*connect)(Elput_Manager **manager, const char *seat, unsigned int tty);
 void (*disconnect)(Elput_Manager *manager);
 int (*open)(Elput_Manager *manager, const char *path, int flags);
 void (*open_async)(Elput_Manager *manager, const char *path, int flags);
 void (*close)(Elput_Manager *manager, int fd);
 Eina_Bool (*vt_set)(Elput_Manager *manager, int vt);
 } Elput_Interface;

When designing a new Elput_Interface, function pointers should be provided for connect, disconnect, open, and close. Without these functions, an Elput_Interface would be pretty useless since it wouldn’t be possible to connect to the system backend nor open/close any input devices. Function pointers to open_async and vt_set are optional. These 2 functions provide the ability to open an input device asynchronously and switch virtual terminals from code (possibly in reaction to a keybinding).

Elput also provides various API functions to manipulate or retrieve information about an input device, as well as support for manually setting pointer position, left-handed pointer mode, keyboard key remapping, and input device calibration.

With this new library in place, we can now greatly reduce duplicated input code across multiple subsystems. This makes dealing with input much easier and provides one central place where input devices can be handled. There are still some existing subsystems inside EFL that have not been ported to use Elput yet, so we may still need to add some API functions to the Elput library. For this reason, the Elput API is marked as a beta api and may change in the future; as a result, there’s no documentation for this quite yet, but this will be generated once EFL 1.18 is released. For now, if you are interested in trying this out, take a look at the EFL documentation.