# Wayland (/integration/embedded_linux/drivers/wayland)



Overview [#overview]

The [Wayland](https://en.wikipedia.org/wiki/Wayland_\(protocol\)) driver renders LVGL into a Wayland surface and reads keyboard, pointer and touch input from the compositor.
It suits production deployments, kiosk interfaces, control panels, and other embedded GUIs on systems that already run a
Wayland compositor. Additionally it can be useful for development on a Linux desktop, as an alternative to the
[X11](/integration/embedded_linux/drivers/X11) and [SDL](/integration/pc/sdl) drivers.

The driver offers four rendering backends: **SHM** (shared memory, the default), **DMA-BUF**, **EGL**
(OpenGL ES) and **G2D** (NXP i.MX 2D accelerator). Which one produces the frames determines how they reach
the compositor and whether that work lands on the CPU or on hardware; everything else about the driver
stays the same.

You do not have to commit to one at build time. Compile in as many as make sense for your target and the
driver probes them when it opens a window, taking the first one that works.

Configuration [#configuration]

Enable <ApiLink name="LV_USE_WAYLAND" />, and set <ApiLink name="LV_COLOR_FORMAT_DEFAULT" /> to
<ApiLink name="LV_COLOR_FORMAT_XRGB8888" /> or <ApiLink name="LV_COLOR_FORMAT_RGB565" />.

Enabling the driver is also what pulls the Wayland client libraries into the build, LVGL's CMake
integration resolves `wayland-client`, `wayland-cursor` and `xkbcommon` for you, so there is nothing to link
by hand. None of them can be built from source by LVGL, so they must already exist on the system or in your
sysroot. See [Dependency Management](/integration/building/cmake) for how resolution works and how to steer
it.

The driver also needs Wayland protocol bindings generated by `wayland-scanner`, which LVGL's CMake
integration runs for you: `xdg-shell` always, and `linux-dmabuf-v1` when a backend needs it. The protocol
XML is looked up under `$SDKTARGETSYSROOT`/`$SYSROOT`/`/usr/share/wayland-protocols`.

Enabling rendering backends [#enabling-rendering-backends]

Turn on every one you want compiled into the binary:

| Symbol                  | Backend                                      | Default                         |
| ----------------------- | -------------------------------------------- | ------------------------------- |
| `LV_WAYLAND_USE_SHM`    | Shared memory via `wl_shm`                   | on, except with a GPU draw unit |
| `LV_WAYLAND_USE_DMABUF` | Software frames presented as linear DMA-BUFs | off                             |
| `LV_WAYLAND_USE_EGL`    | OpenGL ES via EGL                            | off; on with a GPU draw unit    |
| `LV_WAYLAND_USE_G2D`    | NXP i.MX G2D accelerator                     | off                             |

At least one has to be enabled; the build fails otherwise. Enabling several is the interesting case, see
[Runtime backend selection](#runtime-backend-selection) for how the driver chooses between them.

Some backends require some extra libraries:

| Backend                 | Extra libraries    |
| ----------------------- | ------------------ |
| `LV_WAYLAND_USE_EGL`    | `wayland-egl`      |
| `LV_WAYLAND_USE_G2D`    | `g2d`, `libdrm`    |
| `LV_WAYLAND_USE_DMABUF` | `libdrm`, `libgbm` |

<Callout type="warning" title="A GPU draw unit leaves EGL as the only option">
  SHM and G2D hand LVGL a CPU buffer, which the <ApiLink name="LV_USE_DRAW_OPENGLES" /> and
  <ApiLink name="LV_USE_DRAW_NANOVG" /> draw units cannot render into.
  There is no fallback in this configuration, so if EGL cannot start, window creation fails.
</Callout>

Runtime backend selection [#runtime-backend-selection]

Backend selection happens in two steps.

**At driver init**, which the first <ApiLink name="lv_wayland_window_create" /> call performs, every
compiled-in backend is initialized and gets to bind the compositor globals it needs. A backend that fails
here is dropped for the rest of the process and is not considered again.

**At window creation** the surviving backends are tried in a fixed order of preference:

1. G2D
2. DMA-BUF
3. EGL
4. SHM

The first one that can set up a display for that window owns it. If none can,
<ApiLink name="lv_wayland_window_create" /> returns `NULL`.

The probe runs per window, meaning that you can have a window driven by EGL and another one by SHM, for instance.

<Callout type="info" title="EGL only supports one window">
  At this time, the EGL backend can only drive one window. The work to support multiple windows is in progress.
</Callout>

Combinations that make sense [#combinations-that-make-sense]

| Enable        | Result                                                                                                                                                                           |
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SHM           | The default. Works on any compositor, no hardware requirements.                                                                                                                  |
| SHM + DMA-BUF | Software rendering either way, but the compositor scans the frames out directly where DMA-BUF works and copies them out of shared memory where it does not. One binary for both. |
| SHM + G2D     | Hardware blits and rotation on i.MX parts, shared memory elsewhere or when DMA-BUF is unavailable.                                                                               |
| SHM + EGL     | GPU rendering where the GPU stack is usable, shared memory where it is not. Under a software renderer this also pulls in DMA-BUF, which is tried ahead of EGL.                   |
| EGL           | Required when <ApiLink name="LV_USE_DRAW_OPENGLES" /> or <ApiLink name="LV_USE_DRAW_NANOVG" /> is enabled. No fallback is possible.                                              |

Other options [#other-options]

| Symbol                   | Default | Effect                                                                                                 |
| ------------------------ | ------- | ------------------------------------------------------------------------------------------------------ |
| `LV_WAYLAND_DIRECT_EXIT` | on      | Deinitialize LVGL and exit once the last window closes. Turn it off to handle window closure yourself. |

Usage [#usage]

<ApiLink name="lv_wayland_window_create" /> opens a window and returns its display

```c title=" " lineNumbers=1
#define H_RES 800
#define V_RES 480

lv_display_t * disp = lv_wayland_window_create(H_RES, V_RES, "Window Title", NULL);
if(disp == NULL) {
    /* No enabled backend could set up a display */
}
```

Call it again to open more windows; each gets its own display and its own set of input devices.

Input devices [#input-devices]

Each window registers four input devices, wired to the corresponding Wayland events:

| Device  | Wayland source        | Getter                                        |
| ------- | --------------------- | --------------------------------------------- |
| KEYPAD  | keyboard              | <ApiLink name="lv_wayland_get_keyboard" />    |
| POINTER | touch                 | <ApiLink name="lv_wayland_get_touchscreen" /> |
| POINTER | pointer               | <ApiLink name="lv_wayland_get_pointer" />     |
| ENCODER | pointer axis (scroll) | <ApiLink name="lv_wayland_get_pointeraxis" /> |

<Callout type="warning" title="Check the display before querying input devices">
  The getters return `NULL` if called before the display is fully initialised. Always check that the display
  returned by <ApiLink name="lv_wayland_window_create" /> is non-`NULL` first.
</Callout>

To enable keyboard and encoder navigation, put the relevant devices in a group:

```c title=" " lineNumbers=1
/* Create a group and set it as default */
lv_group_t * g = lv_group_create();
lv_group_set_default(g);

/* Assign keyboard and encoder to the group */
lv_indev_set_group(lv_wayland_get_keyboard(disp), g);
lv_indev_set_group(lv_wayland_get_pointeraxis(disp), g);
```

Once a group is the default, every focusable widget created afterwards joins it automatically, so arrow keys
and encoder rotation move focus and Enter activates.

Window state [#window-state]

| Function                                            | Purpose                       |
| --------------------------------------------------- | ----------------------------- |
| <ApiLink name="lv_wayland_window_set_fullscreen" /> | Enter or leave fullscreen     |
| <ApiLink name="lv_wayland_window_set_maximized" />  | Maximize or restore           |
| <ApiLink name="lv_wayland_window_set_minimized" />  | Minimize                      |
| <ApiLink name="lv_wayland_window_close" />          | Close the window              |
| <ApiLink name="lv_wayland_window_is_open" />        | Test whether it is still open |

With multiple physical displays, assign the window to one before going fullscreen so it appears where you
want it:

```c title=" " lineNumbers=1
/* Assign to physical display 0 and go fullscreen there */
lv_wayland_set_physical_display(disp, 0);
lv_wayland_window_set_fullscreen(disp, true);
```

<ApiLink name="LV_WAYLAND_PHYSICAL_DISPLAY_ANY" /> can be used to restore the default behaviour.

```c title=" " lineNumbers=1
lv_wayland_set_physical_display(disp, LV_WAYLAND_PHYSICAL_DISPLAY_ANY);
```

Physical outputs [#physical-outputs]

To target a specific physical output, look it up by its connector name:

| Function                                       | Purpose                                        |
| ---------------------------------------------- | ---------------------------------------------- |
| <ApiLink name="lv_wayland_get_output_count" /> | Number of outputs the compositor advertises    |
| <ApiLink name="lv_wayland_get_output_name" />  | Connector name of an output, e.g. `"HDMI-A-1"` |
| <ApiLink name="lv_wayland_get_output_size" />  | Size of an output in pixels                    |
| <ApiLink name="lv_wayland_get_display_size" /> | Look up an output by name and get its size     |

```c title=" " lineNumbers=1
/* Open a fullscreen window on the HDMI connector */
int32_t width, height;
int32_t output = lv_wayland_get_display_size("HDMI-A-1", &width, &height);

if(output >= 0) {
    lv_display_t * disp = lv_wayland_window_create(width, height, "My window", NULL);
    lv_wayland_set_physical_display(disp, output);
    lv_wayland_window_set_fullscreen(disp, true);
}
```

The names come from the `xdg-output` protocol, or from the `wl_output` v4 `name` event when the compositor doesn't support xdg-output.
If neither is available the name is empty and the lookup returns -1; the size falls back to the output's current mode.

Event loop [#event-loop]

You can integrate LVGL into your own event loop, <ApiLink name="lv_wayland_get_fd" /> gives you the
compositor's file descriptor to poll.

Rendering Backends [#rendering-backends]

EGL [#egl]

Renders through OpenGL ES 2.0 and EGL, and is the backend to use with LVGL's GPU draw units and
[3D/glTF content](/integration/embedded_linux/opengl). Rotation is done by the GPU at no extra copy.

Requires linking with `wayland-egl` and OpenGL ES 2.0 on the target.

G2D [#g2d]

Uses NXP's G2D 2D accelerator, available on i.MX 6 and i.MX 8 parts, for hardware-accelerated blits and
rotation, presenting frames through DMA-BUF. It needs the `g2d` library on the system and the
[G2D draw unit](/integration/chip_vendors/nxp/g2d_gpu) enabled alongside the display path.

Requires linking with `g2d` and a compositor with `zwp_linux_dmabuf_v1` support.

DMA-BUF [#dma-buf]

A software backend that changes how finished frames reach the compositor rather than how they are drawn.
LVGL renders on the CPU as usual; on flush the frame is copied into a linear DMA-BUF allocated through GBM
and posted with `zwp_linux_dmabuf_v1`. The compositor can scan that buffer out directly, which saves it the
copy out of shared memory that `wl_shm` costs, the win is on the compositor's side of the connection, not
in LVGL's rendering.

It needs no GPU API of any kind, only `libdrm` and `libgbm` to allocate the buffers, so it is available on
plenty of targets that cannot run the EGL backend. What it does need at runtime is a DRM primary node it
can open, which means the process must have permission to do so.

Rotation is handled in software, folded into that flush copy — unlike SHM there is no extra full-frame
buffer and no additional pass.

Requires linking with `libdrm` and `libgbm` and a compositor with `zwp_linux_dmabuf_v1` support.

SHM (default) [#shm-default]

Renders into `wl_shm` shared-memory buffers.

No extra requirements.

Window Decorations [#window-decorations]

<Callout type="info">
  As of LVGL v9.5 the `LV_WAYLAND_WINDOW_DECORATIONS` option has been removed. Setting it produces a build
  warning.
</Callout>

Title bars, borders and close buttons are now the application's responsibility. Build them from LVGL
widgets. For fullscreen and kiosk applications, you can simply omit them.

Support [#support]

| Capability                  | DMA-BUF                                                 | EGL                                                                 | G2D                        | SHM                              |
| --------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------- | -------------------------- | -------------------------------- |
| Rotation                    | Software (folded into the flush copy)                   | Hardware (GPU)                                                      | Hardware (G2D)             | Software (extra full-frame copy) |
| Runtime resolution change   | Yes                                                     | Yes                                                                 | Yes                        | Yes                              |
| Runtime color format change | Yes                                                     | Yes                                                                 | Yes                        | Yes                              |
| Color formats               | RGB565, XRGB8888, ARGB8888                              | RGB565 or ARGB8888, from <ApiLink name="LV_COLOR_FORMAT_DEFAULT" /> | RGB565, XRGB8888, ARGB8888 | RGB565, XRGB8888, ARGB8888       |
| Multiple displays / windows | Yes                                                     | Yes                                                                 | Yes                        | Yes                              |
| Hardware acceleration       | None — presentation only                                | OpenGL ES                                                           | NXP G2D                    | None                             |
| Render mode                 | Direct — fixed                                          | Full with NanoVG, otherwise Direct — fixed                          | Direct — fixed             | Direct — fixed                   |
| Input                       | Built in — keyboard, pointer, touch, encoder per window | Built in                                                            | Built in                   | Built in                         |

Reporting Bugs [#reporting-bugs]

The Wayland driver is under active development; bug reports and feedback are welcome. Detailed issues help
most, enable <ApiLink name="LV_USE_LOG" /> and capture both LVGL's log and the Wayland message stream:

```sh title="sh" lineNumbers=1
WAYLAND_DEBUG=1 ./path/to/executable > /tmp/debug 2>&1
```

Attach `/tmp/debug` to the issue. Reproduce the problem quickly as the log file grows very quickly.

Say which `LV_WAYLAND_USE_*` backends you enabled, and which one the log reports as having taken the display.

See Also [#see-also]

* [lv\_port\_linux](https://github.com/lvgl/lv_port_linux) - reference project with a working Wayland build
* [Dependency Management](/integration/building/cmake) - how LVGL resolves and links the Wayland libraries
* [OpenGL Overview](/integration/embedded_linux/opengl) - the GPU rendering path used by the EGL backend
* [EGL](/integration/embedded_linux/drivers/egl) - the EGL layer underneath the EGL backend
* [X11](/integration/embedded_linux/drivers/X11) - alternative for X-based desktops
* [DRM](/integration/embedded_linux/drivers/drm) - alternative when there is no compositor at all
