docs(linux): standardize and update embedded linux docs

This commit is contained in:
André Costa
2026-08-10 13:28:52 +02:00
parent 6e20aab4c5
commit 63a714b1aa
16 changed files with 981 additions and 760 deletions
@@ -1,92 +1,107 @@
---
title: NanoVG Draw Unit
description: "NanoVG is a lightweight, antialiased 2D vector graphics library built on top of OpenGL/OpenGL ES. The NanoVG draw unit integrates NanoVG as a hardware-accelerated rendering backend for LVGL, provide..."
description: "The NanoVG draw unit renders LVGL on the GPU through NanoVG, a lightweight antialiased 2D vector graphics library built on OpenGL ES. It is the recommended GPU renderer on Linux."
---
## Introduction
## Overview
NanoVG is a lightweight, antialiased 2D vector graphics library built on top of OpenGL/OpenGL ES.
The NanoVG draw unit integrates NanoVG as a hardware-accelerated rendering backend for LVGL,
providing GPU-accelerated drawing for all standard LVGL widgets and graphics primitives.
NanoVG is a lightweight, antialiased 2D vector graphics library built on OpenGL / OpenGL ES. The NanoVG draw
unit uses it as LVGL's renderer, so widgets and primitives are drawn by the GPU instead of the CPU.
Unlike the software renderer, NanoVG leverages the GPU for:
It is the **recommended** GPU renderer on Linux: compared with the
[OpenGL ES draw unit](/integration/embedded_linux/draw_units/draw_opengl) it makes better use of the GPU and
covers more LVGL features. Where the software renderer rasterizes every pixel on the CPU, NanoVG gives you:
- Antialiased path rendering (rectangles, arcs, lines, triangles)
- Hardware-accelerated image compositing with rotation and scaling
- Efficient text rendering with font texture caching
- Image compositing with rotation and scaling
- Text rendering with a font texture atlas
- Box shadows and gradients
- Vector graphics support
- Vector graphics, and 3D rendering
## Requirements
### Requirements
- OpenGL 2.0+ / OpenGL ES 2.0+ / OpenGL ES 3.0+
- An initialized OpenGL context (via GLFW, EGL, or custom setup)
- Stencil buffer support (8-bit recommended)
- OpenGL 2.0+, OpenGL ES 2.0+, or OpenGL ES 3.0+
- An OpenGL context, created by one of the display drivers listed in
[OpenGL Overview](/integration/embedded_linux/opengl) or by your own code
- A config with an 8-bit stencil buffer. LVGL's EGL backends require stencil 8 and 4× multisampling when
choosing a config for NanoVG, so a config lacking them will not be selected.
## Configuration
Enable the NanoVG draw unit in `lv_conf.h`:
Enable <ApiLink name="LV_USE_DRAW_NANOVG" />.
```c
/* Enable NanoVG library */
#define LV_USE_NANOVG 1
### Options
/* Enable NanoVG draw unit */
#define LV_USE_DRAW_NANOVG 1
| Symbol | Default | Meaning |
|---|---|---|
| `LV_NANOVG_BACKEND` | `LV_NANOVG_BACKEND_GLES2` | Which shader flavour to compile. Must match the context the display driver creates. |
| `LV_NANOVG_IMAGE_CACHE_CNT` | `128` | Number of decoded images kept as GPU textures. |
| `LV_NANOVG_LETTER_CACHE_CNT` | `512` | Number of rendered glyphs kept for text drawing. |
/* Select OpenGL backend (choose one):
* - LV_NANOVG_BACKEND_GL2: OpenGL 2.0
* - LV_NANOVG_BACKEND_GL3: OpenGL 3.0+
* - LV_NANOVG_BACKEND_GLES2: OpenGL ES 2.0
* - LV_NANOVG_BACKEND_GLES3: OpenGL ES 3.0+
*/
#define LV_NANOVG_BACKEND LV_NANOVG_BACKEND_GLES2
`LV_NANOVG_BACKEND` accepts:
/* Optional: Adjust cache sizes */
#define LV_NANOVG_IMAGE_CACHE_CNT 32 /* Image texture cache entries */
#define LV_NANOVG_FBO_CACHE_CNT 8 /* Framebuffer object cache entries */
```
| Value | Context |
|---|---|
| `LV_NANOVG_BACKEND_GL2` | OpenGL 2.0 |
| `LV_NANOVG_BACKEND_GL3` | OpenGL 3.0+ |
| `LV_NANOVG_BACKEND_GLES2` | OpenGL ES 2.0 |
| `LV_NANOVG_BACKEND_GLES3` | OpenGL ES 3.0+ |
<Callout type="warning" title="The backend must match the context">
`LV_NANOVG_BACKEND` selects which shaders are compiled; it does not create or negotiate a context. Setting a
flavour the driver's context does not provide gives you shader compilation failures at run time, not a build
error. The EGL-based drivers request OpenGL ES 3 and fall back to ES 2, so `LV_NANOVG_BACKEND_GLES2` is the
safe default.
</Callout>
## Supported Features
The NanoVG draw unit supports all standard LVGL drawing operations:
| Feature | Description |
| --- | --- |
| Fill | Solid colors, gradients (linear/radial) |
| Border | Rounded rectangles with customizable width |
| Box Shadow | Hardware-accelerated shadow rendering |
| Feature | Notes |
|---|---|
| Fill | Solid colors, linear and radial gradients |
| Border | Rounded rectangles with configurable width |
| Box Shadow | Hardware-accelerated |
| Images | Rotation, scaling, tiling, recoloring |
| Labels | Font rendering with texture atlas caching |
| Lines | Antialiased lines with configurable width |
| Labels | Font rendering with a texture atlas |
| Lines | Antialiased, configurable width |
| Arcs | Antialiased arc segments |
| Triangles | Filled triangles |
| Triangles | Filled |
| Masks | Rectangle masks for clipping |
| Layers | Off-screen rendering with FBO |
| Canvas | Direct drawing to canvas buffers |
| Vector Graphics | SVG-style path rendering (requires `LV_USE_VECTOR_GRAPHIC`) |
| Layers | Off-screen rendering into an FBO |
| Canvas | Direct drawing into canvas buffers |
| Vector Graphics | Path rendering — requires <ApiLink name="LV_USE_VECTOR_GRAPHIC" /> |
| 3D | glTF models, see [glTF](/libs/gltf) |
## Supported Image Formats
### Image formats uploaded without conversion
NanoVG supports zero-copy texture upload for these LVGL color formats:
| LVGL format | GL handling | Notes |
|---|---|---|
| <ApiLink name="LV_COLOR_FORMAT_A8" /> | Alpha texture | Tinted in the shader |
| <ApiLink name="LV_COLOR_FORMAT_ARGB8888" /> | BGR→RGB swizzle | Premultiplication handled in the shader |
| <ApiLink name="LV_COLOR_FORMAT_XRGB8888" /> | BGR→RGB, alpha forced to 1 | X channel ignored |
| <ApiLink name="LV_COLOR_FORMAT_RGB888" /> | BGR→RGB swizzle | No alpha |
| <ApiLink name="LV_COLOR_FORMAT_RGB565" /> | Direct upload | LVGL uses a BGR565 layout |
| LVGL Format | GL Processing | Notes |
| --- | --- | --- |
| `LV_COLOR_FORMAT_A8` | Alpha texture | Color tinting via shader |
| `LV_COLOR_FORMAT_ARGB8888` | BGR→RGB swizzle | Premultiplication handled in shader |
| `LV_COLOR_FORMAT_XRGB8888` | BGR→RGB + alpha=1 | X channel ignored |
| `LV_COLOR_FORMAT_RGB888` | BGR→RGB swizzle | No alpha channel |
| `LV_COLOR_FORMAT_RGB565` | Direct upload | Note: LVGL uses BGR565 layout |
Anything else is converted before upload, which costs CPU time per image. Prefer one of the formats above for
assets you draw often.
## Performance Tips
1. **Minimize Layer Usage**: Each layer requires a framebuffer object (FBO) switch
2. **Use Premultiplied Alpha**: Set `LV_IMAGE_FLAGS_PREMULTIPLIED` for pre-processed images
3. **Cache Static Content**: NanoVG caches textures automatically; avoid recreating images
4. **Batch Similar Operations**: Group widgets with similar styles for better GPU batching
- **Minimize layer usage.** Each layer means a framebuffer object switch.
- **Use premultiplied alpha.** Set `LV_IMAGE_FLAGS_PREMULTIPLIED` on images you have pre-processed.
- **Let the caches work.** Textures and glyphs are cached automatically; recreating images defeats that.
Raise `LV_NANOVG_IMAGE_CACHE_CNT` or `LV_NANOVG_LETTER_CACHE_CNT` if your UI requires more than the default value.
- **Batch similar styles.** Widgets with matching styles batch better on the GPU.
## Limitations
- **Blur**: Not natively supported; Using this style will not affect the rendering results.
- **Complex Gradients**: Limited to 2-color gradients (LVGL supports multi-stop)
- **Layer Readback**: `glReadPixels` for canvas/layer is relatively slow
- **Gradients** are limited to two colors; LVGL's API allows more stops.
- **Layer and canvas readback** goes through `glReadPixels`, which is slow. Avoid it per frame.
## See Also
- [OpenGL Overview](/integration/embedded_linux/opengl) - how drivers and draw units fit together
- [OpenGL ES Draw Unit](/integration/embedded_linux/draw_units/draw_opengl) - the texture-caching alternative
- [glTF](/libs/gltf) - 3D model rendering, which requires a GPU renderer
- [EGL](/integration/embedded_linux/drivers/egl) - context creation, including headless off-screen rendering
- [DRM](/integration/embedded_linux/drivers/drm) - production display driver with an EGL backend
@@ -1,51 +1,52 @@
---
title: OpenGL ES Draw Unit
description: "The OpenGL ES Draw Unit provides a hardware-accelerated rendering backend for LVGL that leverages OpenGL ES capabilities."
description: "The OpenGL ES draw unit caches software-rendered areas as GPU textures and composites them with OpenGL ES, avoiding redraws of content that has not changed."
---
## Overview
The OpenGL ES Draw Unit provides a hardware-accelerated rendering backend for LVGL that leverages OpenGL ES capabilities.
The OpenGL ES draw unit renders widgets in software, caches the result as an OpenGL texture, and composites
those textures on the GPU. Whenever a later frame asks for something already in the cache, the texture is
reused instead of redrawn, which is most of the screen, most of the time, in a typical UI.
OpenGL ES (OpenGL for Embedded Systems) is a subset of OpenGL designed for embedded devices, mobile phones, and other resource-constrained platforms.
The OpenGL ES Draw Unit brings GPU-accelerated rendering to LVGL applications on these platforms.
That makes it fast for static and slowly-changing content, and slower than software rendering for content
that changes every frame.
### Key Features
<Callout type="info" title="Prefer NanoVG">
[NanoVG](/integration/embedded_linux/draw_units/draw_nanovg) is the recommended GPU renderer on Linux: it
performs better and supports more LVGL features. Reach for this draw unit when you specifically want the
texture cache, or when NanoVG's limitations do not suit you.
</Callout>
- **Hardware Acceleration**: Direct GPU acceleration via OpenGL ES 2.0+
- **Texture Caching**: Rendered elements are cached as OpenGL textures for efficient reuse
- **GPU Blending**: Hardware-accelerated texture composition and blending
- **Embedded-Friendly**: Optimized for resource-constrained embedded systems
- **Wide Platform Support**: Works on mobile, embedded Linux, and other OpenGL ES-compatible platforms
### Requirements
### Performance Characteristics
The OpenGL ES Draw Unit provides excellent performance for:
- **Best Performance**: Static UI elements that benefit from texture caching
- **Good Performance**: UIs with moderate animation and dynamic content
- **Embedded Optimization**: Efficient memory usage suitable for embedded systems
## Prerequisites
- OpenGL ES 2.0 or higher support on your platform
- A driver which supports OpenGL configured (see [OpenGL Overview](/integration/embedded_linux/opengl))
- OpenGL ES 2.0 or newer
- An OpenGL context, created by one of the display drivers listed in
[OpenGL Overview](/integration/embedded_linux/opengl) or by your own code
## Configuration
Enable the OpenGL ES draw unit in `lv_conf.h`:
Enable <ApiLink name="LV_USE_DRAW_OPENGLES" />
```c
#define LV_USE_OPENGLES 1
### Options
#define LV_USE_DRAW_OPENGLES 1
| Symbol | Default | Meaning |
|---|---|---|
| `LV_DRAW_OPENGLES_TEXTURE_CACHE_COUNT` | `64` | Number of textures kept in the cache. More entries mean fewer redraws but more GPU memory. |
/* Configurable cache count. Bigger cache will improve performance */
#define LV_DRAW_OPENGLES_TEXTURE_CACHE_COUNT 64
```
## Limitations
- **No gain without cache hits.** Performance is worse when drawn areas are never found
in the cache, e.g. widgets whose color or shape changes every frame
- **Layer transparency.** Layers that have both transparent pixels and an overall layer opacity do not blend
correctly
- **No layer rotation.** Rotated layers are unsupported. Rotated *images* are fine.
[NanoVG](/integration/embedded_linux/draw_units/draw_nanovg) has none of these limitations.
## See Also
- [OpenGL Overview](/integration/embedded_linux/opengl) - Complete OpenGL integration overview
- [EGL](/integration/embedded_linux/drivers/egl) - EGL Display Driver documentation
- [NanoVG Draw Unit](/integration/embedded_linux/draw_units/draw_nanovg) - Vector graphics rendering option
- [NanoVG Draw Unit](/integration/embedded_linux/draw_units/draw_nanovg) - the recommended GPU renderer
- [OpenGL Overview](/integration/embedded_linux/opengl) - how drivers and draw units fit together
- [OpenGL Driver](/integration/embedded_linux/drivers/opengl_driver) - texture-only driver for an existing context
- [EGL](/integration/embedded_linux/drivers/egl) - context creation on embedded targets
@@ -1,42 +1,40 @@
---
title: SDL Draw Unit
description: "The SDL Draw Unit provides a hardware-accelerated rendering backend for LVGL that leverages SDL2's texture system. It uses software rendering to create SDL textures which are then cached and effici..."
description: "The SDL draw unit renders widgets in software, caches them as SDL textures, and lets SDL's renderer composite them on the GPU."
---
## Overview
The SDL Draw Unit provides a hardware-accelerated rendering backend for LVGL that leverages SDL2's texture system.
It uses software rendering to create SDL textures which are then cached and efficiently blended together by the GPU to compose the final UI.
This approach combines the flexibility of software rendering with the performance benefits of hardware-accelerated texture blending.
The SDL draw unit renders widgets in software, caches the result as an SDL texture, and lets SDL's own
renderer blend those textures into the final frame. It is a hybrid: software rasterization,
hardware compositing.
### Key Features
It is the natural choice when your application already uses SDL and you want cheap acceleration without
introducing an OpenGL dependency. It works best with static or slowly-changing UIs, where most textures are
reused between frames, and offers little for content that changes every frame.
- **Texture Caching**: Rendered elements are cached as SDL textures, reducing redundant rendering operations
- **Hardware Blending**: SDL's GPU-accelerated texture blending provides smooth compositing
- **Cross-Platform**: Works on any platform that supports SDL2
- **Easy Integration**: Seamless integration with SDL-based LVGL applications
### Performance Characteristics
The SDL Draw Unit excels in scenarios with:
- **Best Performance**: Static or infrequently changing UI elements that benefit from texture caching
- **Good Performance**: UIs with moderate animation and updates
- **Consider Alternatives**: Heavily dynamic content that changes every frame may not benefit as much from caching
## Prerequisites
- SDL2 library installed (see [SDL Driver](/integration/pc/sdl) for installation instructions)
- LVGL configured with SDL support (`LV_USE_SDL 1`)
For genuinely GPU-accelerated rendering, use [NanoVG](/integration/embedded_linux/draw_units/draw_nanovg)
through the SDL driver's EGL backend instead. It rasterizes on the GPU rather than only compositing there.
## Configuration
Enable the SDL draw unit in `lv_conf.h`:
Enable <ApiLink name="LV_USE_DRAW_SDL" /> and <ApiLink name="LV_USE_SDL" />. It has no options of its
Select `LV_SDL_BACKEND_TEXTURE` as your <ApiLink name="LV_SDL_BACKEND" />
```c
#define LV_USE_SDL 1
#define LV_USE_DRAW_SDL 1
#define LV_USE_DRAW_SW 1
```
## Building
The SDL Draw Unit automatically integrates with the SDL display driver when both are enabled.
The renderer requires `sdl2_image`.
<Callout type="tip">
LVGL's CMake integration resolves it for you.
It can be fetched at compile time by LVGL so it doesn't have to be present on your system.
See [Dependency Management](/integration/building/cmake).
</Callout>
## See Also
- [SDL Driver](/integration/pc/sdl) - the display driver this draw unit pairs with
- [NanoVG Draw Unit](/integration/embedded_linux/draw_units/draw_nanovg) - true GPU rasterization, via SDL's EGL backend
- [OpenGL Overview](/integration/embedded_linux/opengl) - how drivers and draw units fit together
- [Dependency Management](/integration/building/cmake) - how LVGL resolves or fetches SDL2_image
@@ -1,129 +1,121 @@
---
title: X11
description: The X11 display/input driver offers support for simulating the LVGL display and keyboard/mouse inputs in an X11 desktop window.
description: The X11 driver opens a window on an X11 desktop and reads mouse, mousewheel and keyboard input, mainly for simulating an LVGL application on a Linux development machine.
---
## Overview
The **X11** display/input [driver](https://github.com/lvgl/lvgl/tree/master/src/drivers/x11)
offers support for simulating the LVGL display and keyboard/mouse inputs in an X11
desktop window.
The [**X11** display/input driver](https://en.wikipedia.org/wiki/X_Window_System) opens a window
on an X11 desktop through Xlib and reads mouse, mousewheel and keyboard input.
It is an alternative to **Wayland**, **XCB**, **SDL** or **Qt**.
X11 is a CPU-blit driver with no GPU path. For production embedded targets prefer
[DRM](/integration/embedded_linux/drivers/drm) or [Wayland](/integration/embedded_linux/drivers/wayland).
The main purpose for this driver is for testing/debugging the LVGL application in a
**Linux** simulation window.
## Configuration
## Prerequisites
Enable <ApiLink name="LV_USE_X11" />.
The X11 driver uses XLib to access the linux window manager.
<ApiLink name="LV_COLOR_DEPTH" /> must be `16`, `24` or `32`.
1. Install XLib: `sudo apt-get install libx11-6` (should be installed already)
2. Install XLib development package: `sudo apt-get install libx11-dev`
### Options
## Configure X11 driver
| Symbol | Default | Effect |
|---|---|---|
| `LV_X11_RENDER_MODE` | `LV_DISPLAY_RENDER_MODE_PARTIAL` | Render mode |
| `LV_X11_DOUBLE_BUFFER` | on | Use two buffers |
| `LV_X11_DIRECT_EXIT` | on | Exit the application once all windows are closed |
1. Enable the X11 driver support in lv_conf.h, by cmake compiler define or by KConfig
## Building
```c
#define LV_USE_X11 1
```
The driver always requires `xlib`.
2. Optional configuration options:
- Direct Exit
<Callout type="tip">
LVGL's CMake integration resolves it for you.
Make sure the required libraries are present on your system or in your sysroot.
```c
#define LV_X11_DIRECT_EXIT 1 /* preferred default - ends the application automatically if last window has been closed */
// or
#define LV_X11_DIRECT_EXIT 0 /* application is responsible for ending the application (e.g. by own LV_EVENT_DELETE handler) */
```
- Double buffering
```c
#define LV_X11_DOUBLE_BUFFER 1 /* preferred default */
// or
#define LV_X11_DOUBLE_BUFFER 0 /* not recommended */
```
- Render mode
```c
#define LV_X11_RENDER_MODE_PARTIAL 1 /* LV_DISPLAY_RENDER_MODE_PARTIAL, preferred default */
// or
#define LV_X11_RENDER_MODE_DIRECT 1 /* LV_DISPLAY_RENDER_MODE_DIRECT, not recommended for X11 driver */
// or
#define LV_X11_RENDER_MODE_FULL 1 /* LV_DISPLAY_RENDER_MODE_FULL, not recommended for X11 driver */
```
See [Dependency Management](/integration/building/cmake).
</Callout>
## Usage
The minimal initialisation opening a window and enabling keyboard/mouse support
(e.g. in main.c, LV_X11_DIRECT_EXIT must be 1):
Create the window with <ApiLink name="lv_x11_window_create" />, then attach input with
<ApiLink name="lv_x11_inputs_create" />. The second argument is an optional cursor image; pass `NULL` for
none.
```c
int main(int argc, char ** argv)
{
...
lv_init();
/* initialize X11 display driver */
lv_display_t * disp = lv_x11_window_create("LVGL X11 Simulation", monitor_hor_res, monitor_ver_res);
lv_display_t * disp = lv_x11_window_create("LVGL X11 Simulation", 800, 480);
/* initialize X11 input drivers (for keyboard, mouse & mousewheel) */
/* Keyboard, mouse and mousewheel */
lv_x11_inputs_create(disp, NULL);
...
lv_demo_widgets();
while(true)
{
...
/* Periodically call the lv_timer handler */
lv_timer_handler();
while(1) {
uint32_t time_until_next = lv_timer_handler();
if(time_until_next == LV_NO_TIMER_READY) time_until_next = LV_DEF_REFR_PERIOD;
lv_delay_ms(time_until_next);
}
}
```
Full initialisation with mouse pointer symbol and own application exit handling
(dependent on LV_X11_DIRECT_EXIT (can be 1 or 0))
Call <ApiLink name="lv_x11_window_create" /> again to open more windows; each returns its own display.
### Handling window closure yourself
With `LV_X11_DIRECT_EXIT` off, the application decides what happens when a window closes. Register an
<ApiLink name="LV_EVENT_DELETE" /> handler on the display and run your own loop condition:
```c
bool terminated = false;
static bool terminated = false;
#if !LV_X11_DIRECT_EXIT
static void on_close_cb(lv_event_t * e)
{
...
LV_UNUSED(e);
terminated = true;
}
#endif
int main(int argc, char ** argv)
{
...
lv_init();
/* initialize X11 display driver */
lv_display_t * disp = lv_x11_window_create("LVGL X11 Simulation", monitor_hor_res, monitor_ver_res);
lv_display_t * disp = lv_x11_window_create("LVGL X11 Simulation", 800, 480);
lv_display_add_event_cb(disp, on_close_cb, LV_EVENT_DELETE, disp);
/* initialize X11 input drivers (for keyboard, mouse & mousewheel) */
LV_IMAGE_DECLARE(my_mouse_cursor_icon);
lv_x11_inputs_create(disp, &my_mouse_cursor_icon);
/* Attach inputs, optionally with a cursor image */
LV_IMAGE_DECLARE(mouse_cursor_icon);
lv_x11_inputs_create(disp, &mouse_cursor_icon);
#if !LV_X11_DIRECT_EXIT
/* set optional window close callback to enable application cleanup and exit */
lv_x11_window_set_close_cb(disp, on_close_cb, disp);
#endif
...
while(!terminated)
{
...
/* Periodically call the lv_timer handler */
lv_timer_handler();
while(!terminated) {
uint32_t time_until_next = lv_timer_handler();
if(time_until_next == LV_NO_TIMER_READY) time_until_next = LV_DEF_REFR_PERIOD;
lv_delay_ms(time_until_next);
}
/* Clean up here */
}
```
## Support
| Capability | Value |
|---|---|
| Rotation | None |
| Runtime resolution change | Yes |
| Runtime color format change | No |
| Color formats | Derived from <ApiLink name="LV_COLOR_DEPTH" /> (`16`, `24` or `32`) |
| Multiple displays / windows | Yes, one display per window |
| Hardware acceleration | None |
| Render mode | Configurable via `LV_X11_RENDER_MODE` |
| Input | Built in. Mouse, mousewheel and keyboard via <ApiLink name="lv_x11_inputs_create" /> |
## See Also
- [Dependency Management](/integration/building/cmake) - how LVGL resolves Xlib
- [Wayland](/integration/embedded_linux/drivers/wayland) - the modern alternative on Linux desktops
- [SDL Driver](/integration/pc/sdl) - cross-platform alternative for development
- [GLFW](/integration/embedded_linux/drivers/glfw) - development driver with an OpenGL context
- [DRM](/integration/embedded_linux/drivers/drm) - production alternative with no display server
@@ -1,73 +1,92 @@
---
title: DRM
description: "The DRM (Direct Rendering Manager) display driver provides support for rendering LVGL directly to Linux framebuffer devices through the DRM/KMS subsystem. It enables running LVGL without a windowin..."
description: "The DRM display driver renders LVGL straight to a display through the Linux DRM/KMS subsystem, with no windowing system in between. It supports dumb buffers, GBM buffers and hardware-accelerated EGL rendering."
---
## Overview
The **DRM** (Direct Rendering Manager) display driver provides support for rendering
LVGL directly to Linux framebuffer devices through the DRM/KMS subsystem.
It enables running LVGL without a windowing system such as X11 or Wayland,
making it suitable for embedded devices, single-board computers, and direct-to-display
applications.
The **DRM** (Direct Rendering Manager) display driver renders LVGL straight to a display through the Linux
[DRM/KMS subsystem](https://en.wikipedia.org/wiki/Direct_Rendering_Manager), talking to the GPU or display
controller through a `/dev/dri/cardX` node. No windowing system is involved, which makes it the recommended
choice for production embedded targets: single-board computers, panels, and any direct-to-display application.
The DRM driver interacts directly with the GPU or display controller through
`/dev/dri/cardX` nodes.
The driver offers three backends:
## Getting Started with DRM
1. **Dumb buffers**: software rendering into DRM/KMS dumb framebuffers
2. **GBM**: (GPU-friendly DMA buffers)
3. **EGL** (OpenGL ES rendering on the GPU).
Compared with the [framebuffer driver](/integration/embedded_linux/drivers/fbdev), DRM gives you proper
modesetting, atomic page flips, and a path to GPU acceleration.
### Prerequisites
The DRM driver requires:
- A kernel with DRM/KMS support.
- A DRM device node, typically `/dev/dri/card0`.
- Permission to open it, either run as root, or add the user to the `video` group.
- A Linux system with DRM/KMS support enabled in the kernel.
- Access to a DRM device node, typically `/dev/dri/card0`.
- Proper permissions to access DRM devices (e.g. running as root or adding the user
to the `video` group).
## Configuration
On Debian/Ubuntu-based systems:
Enable <ApiLink name="LV_USE_LINUX_DRM" />, and set <ApiLink name="LV_COLOR_DEPTH" /> to `32` or `16`.
```bash
sudo apt-get install libdrm-dev
```
## Building
### Configure DRM Driver
The driver always requires `libdrm`.
The GBM and EGL backends additionally require `libgbm`.
1. Enable the DRM driver support in `lv_conf.h`, by CMake compiler define, or by KConfig:
<Callout type="tip">
LVGL's CMake integration resolves both.
Make sure the required libraries are present on your system or in your sysroot.
See [Dependency Management](/integration/building/cmake).
</Callout>
## Selecting a rendering backend
Disable `LV_LINUX_DRM_AUTO_BACKEND` and set `LV_LINUX_DRM_BACKEND` to one of:
| Value | Backend |
|---|---|
| `LV_LINUX_DRM_BACKEND_FBDEV` | Software rendering into DRM/KMS dumb buffers |
| `LV_LINUX_DRM_BACKEND_GBM` | Buffers allocated through Mesa GBM as DMA buffers |
| `LV_LINUX_DRM_BACKEND_EGL` | Hardware-accelerated rendering via OpenGL ES 2.0 and EGL |
<Callout type="warning" title="Turn off the legacy auto-backend first">
`LV_LINUX_DRM_AUTO_BACKEND` infers the backend from <ApiLink name="LV_USE_OPENGLES" />,
picking EGL when that is enabled and dumb buffers otherwise.
Set `LV_LINUX_DRM_AUTO_BACKEND` to `0` whenever you select a backend explicitly. Auto mode will be removed in LVGL v10.
</Callout>
For the GPU draw units that run on top of the EGL backend, see
[OpenGL Overview](/integration/embedded_linux/opengl).
## Usage
<Callout type="tip">
[LVGL's linux port](https://github.com/lvgl/lv_port_linux) can be used to quickly get started with LVGL's DRM backend.
</Callout>
Create the display with <ApiLink name="lv_linux_drm_create" />, then bind it to a device node and connector
with <ApiLink name="lv_linux_drm_set_file" />.
```c
#define LV_USE_LINUX_DRM 1
```
2. Link against `libdrm` when building.
### Basic Usage
```c
#include "lvgl/lvgl.h"
#include <lvgl/lvgl.h>
#include "lvgl/demos/lv_demos.h"
int main(void)
{
/* Initialize LVGL */
lv_init();
/* DRM device node */
const char *device = "/dev/dri/card0";
lv_display_t * disp = lv_linux_drm_create();
/* Create a DRM display */
lv_display_t *disp = lv_linux_drm_create();
/* 2nd argument: DRM device path
* 3rd argument: connector id, or -1 to auto-select the first available one */
lv_linux_drm_set_file(disp, "/dev/dri/card0", -1);
/* Set DRM device file and connector */
/* The 2nd argument is the DRM device path */
/* The 3rd argument is the connector_id (-1 = auto-select first available) */
lv_linux_drm_set_file(disp, device, -1);
/* Create demo widgets */
lv_demo_widgets();
/* Handle LVGL tasks */
while (1) {
while(1) {
uint32_t time_until_next = lv_timer_handler();
if(time_until_next == LV_NO_TIMER_READY) {
time_until_next = LV_DEF_REFR_PERIOD;
@@ -79,93 +98,45 @@ int main(void)
}
```
### Notes
The connector id selects which output (HDMI, eDP, DP, ...) to drive. Passing `-1` picks the first available
one.
- `connector_id` specifies which display output (HDMI, eDP, DP, etc.) should be used.
If `-1` is passed, the DRM driver will try to automatically pick the first available connector.
- DRM requires proper modesetting. By default, LVGL will select a preferred display mode.
In order to avoid hard coding the device card path, you can ask LVGL to find a connected one for you using <ApiLink name="lv_linux_drm_find_device_path" />.
It will return the first connected card it can find.
Rather than hard-coding the card path, ask LVGL to find a connected one with
<ApiLink name="lv_linux_drm_find_device_path" />. It scans `/sys/class/drm` and returns the first connected
card; free the result with `lv_free`.
```c
lv_display_t * disp = lv_linux_drm_create();
/* Find the first connected card in /sys/class/drm */
char * device = lv_linux_drm_find_device_path();
lv_linux_drm_set_file(disp, device, -1);
/* Free the path pointer */
lv_free(device);
```
## Using DRM with GBM
## Selecting a Display Mode
The DRM driver can optionally use **GBM** (Generic Buffer Management) for buffer allocation.
This allows the driver to use GPU-friendly buffer objects instead of simple dumb framebuffers.
1. Enable the following option in your `lv_conf.h` (or via Kconfig/CMake):
```c
#define LV_USE_LINUX_DRM_GBM_BUFFERS 1
```
2. Link against `libgbm` when building.
When this option is enabled:
- Buffers will be allocated using GBM.
- This can improve performance and compatibility on platforms where GBM is supported.
## Using DRM with EGL
The DRM driver can also be combined with [EGL](/integration/embedded_linux/drivers/egl) for hardware-accelerated
rendering via EGL/GLES.
To enable this, set the following options in your `lv_conf.h` (or via Kconfig/CMake):
```c
#define LV_USE_LINUX_DRM 1
#define LV_USE_OPENGLES 1
#define LV_USE_DRAW_OPENGLES 1 /* optional but recommended for performance */
```
When <ApiLink name="LV_USE_OPENGLES" /> is enabled, the DRM driver will automatically initialize EGL.
No special setup is required beyond the basic DRM initialization shown in [Basic Usage](/integration/embedded_linux/drivers/drm).
For a detailed overview of EGL usage and configuration, see [EGL](/integration/embedded_linux/drivers/egl).
## Selecting Display Mode
By default the driver uses the display's preferred mode. To choose differently, register a callback with
<ApiLink name="lv_linux_drm_set_mode_cb" /> before calling <ApiLink name="lv_linux_drm_set_file" />. It
receives every mode the connector reports and returns the index of the one to use.
<Callout type="info">
Custom mode selection is currently only supported when using DRM with EGL
(<ApiLink name="LV_USE_OPENGLES" /> enabled). When using DRM without EGL, the driver
will always use the preferred display mode.
Custom mode selection only takes effect with the EGL backend. With dumb buffers or GBM the driver always
uses the preferred mode.
</Callout>
By default, the DRM driver automatically selects the preferred display mode for the connected display. However, you can customize this behavior by providing a mode selection callback.
### Custom Mode Selection
To implement custom mode selection logic, define a callback function and register it with <ApiLink name="lv_linux_drm_set_mode_cb" />:
```c
#include "lvgl/lvgl.h"
/* Custom mode selection callback */
size_t my_mode_selector(lv_display_t * disp, const lv_linux_drm_mode_t * modes, size_t mode_count)
static size_t my_mode_selector(lv_display_t * disp, const lv_linux_drm_mode_t * modes, size_t mode_count)
{
/* Example: Select the first 1920x1080@60Hz mode */
/* Pick 1920x1080@60Hz if the display offers it */
for(size_t i = 0; i < mode_count; i++) {
int32_t width = lv_linux_drm_mode_get_horizontal_resolution(&modes[i]);
int32_t height = lv_linux_drm_mode_get_vertical_resolution(&modes[i]);
int32_t refresh = lv_linux_drm_mode_get_refresh_rate(&modes[i]);
if(width == 1920 && height == 1080 && refresh == 60) {
return i; /* Return the index of the selected mode */
if(lv_linux_drm_mode_get_horizontal_resolution(&modes[i]) == 1920 &&
lv_linux_drm_mode_get_vertical_resolution(&modes[i]) == 1080 &&
lv_linux_drm_mode_get_refresh_rate(&modes[i]) == 60) {
return i;
}
}
/* Fallback: return the first mode */
/* Fall back to the first mode */
return 0;
}
@@ -174,70 +145,45 @@ int main(void)
lv_init();
lv_display_t * disp = lv_linux_drm_create();
/* Set custom mode selection callback */
lv_linux_drm_set_mode_cb(disp, my_mode_selector);
lv_linux_drm_set_file(disp, "/dev/dri/card0", -1);
/* ... rest of your application ... */
/* ... */
}
```
The callback receives an array of available modes and must return the index of the desired mode.
These functions query a mode:
### Mode Information API
| Function | Returns |
|---|---|
| <ApiLink name="lv_linux_drm_mode_get_horizontal_resolution" /> | Width in pixels |
| <ApiLink name="lv_linux_drm_mode_get_vertical_resolution" /> | Height in pixels |
| <ApiLink name="lv_linux_drm_mode_get_refresh_rate" /> | Refresh rate in Hz |
| <ApiLink name="lv_linux_drm_mode_is_preferred" /> | Whether this is the display's native mode |
| <ApiLink name="lv_linux_drm_mode_get_raw" /> | The underlying mode info, for anything not covered above |
The following functions are available to query mode properties:
The callback must always return a valid index in `0 .. mode_count - 1`. Passing `NULL` to
<ApiLink name="lv_linux_drm_set_mode_cb" /> restores the default behaviour.
- <ApiLink name="lv_linux_drm_mode_get_horizontal_resolution" /> - Get width in pixels
- <ApiLink name="lv_linux_drm_mode_get_vertical_resolution" /> - Get height in pixels
- <ApiLink name="lv_linux_drm_mode_get_refresh_rate" /> - Get refresh rate in Hz
- <ApiLink name="lv_linux_drm_mode_is_preferred" /> - Check if mode is the display's preferred/native mode
## Support
### Example: Selecting Preferred Mode
| Capability | Dumb buffers | GBM | EGL |
|---|---|---|---|
| Rotation | None | None | Hardware (GPU) |
| Runtime resolution change | No | No | Yes |
| Runtime color format change | No | No | No |
| Color formats | RGB565 or XRGB8888, from <ApiLink name="LV_COLOR_DEPTH" /> | RGB565 or XRGB8888, from <ApiLink name="LV_COLOR_DEPTH" /> | RGB565 or ARGB8888, from <ApiLink name="LV_COLOR_DEPTH" /> |
| Multiple displays / windows | One display per DRM device | One display per DRM device | One display per DRM device |
| Hardware acceleration | None | Buffer allocation only | OpenGL ES |
| Render mode | Direct | Direct | Full with NanoVG, otherwise Direct |
| Input | None built in. Pair with [evdev](/integration/embedded_linux/drivers/evdev) or [libinput](/integration/embedded_linux/drivers/libinput) | Same | Same |
| Custom mode selection | No | No | Yes |
```c
size_t select_preferred_mode(lv_display_t * disp, const lv_linux_drm_mode_t * modes, size_t mode_count)
{
/* Find and select the preferred mode */
for(size_t i = 0; i < mode_count; i++) {
if(lv_linux_drm_mode_is_preferred(&modes[i])) {
return i;
}
}
## See Also
/* If no preferred mode found, return the first mode */
return 0;
}
```
### Example: Selecting Highest Resolution
```c
size_t select_highest_resolution(lv_display_t * disp, const lv_linux_drm_mode_t * modes, size_t mode_count)
{
size_t best_index = 0;
int32_t max_pixels = 0;
for(size_t i = 0; i < mode_count; i++) {
int32_t width = lv_linux_drm_mode_get_horizontal_resolution(&modes[i]);
int32_t height = lv_linux_drm_mode_get_vertical_resolution(&modes[i]);
int32_t pixels = width * height;
if(pixels > max_pixels) {
max_pixels = pixels;
best_index = i;
}
}
return best_index;
}
```
### Notes
- The mode selection callback is called before the display is initialized.
- If no callback is set, the driver uses the preferred mode by default.
- Ensure the callback always returns a valid index (0 to `mode_count - 1`).
- To restore default behavior, call <ApiLink name="lv_linux_drm_set_mode_cb" /> with `NULL` as the callback.
- [lv_port_linux](https://github.com/lvgl/lv_port_linux): reference project with a working DRM build
- [Dependency Management](/integration/building/cmake): how LVGL resolves `libdrm` and `libgbm`
- [EGL](/integration/embedded_linux/drivers/egl): the EGL layer underneath the EGL backend
- [OpenGL Overview](/integration/embedded_linux/opengl): GPU draw units usable with the EGL backend
- [Linux Framebuffer](/integration/embedded_linux/drivers/fbdev): simpler alternative when only `/dev/fb` exists
- [evdev](/integration/embedded_linux/drivers/evdev): input to pair with this driver
@@ -1,45 +1,45 @@
---
title: EGL
description: "The EGL driver provides support for creating LVGL displays using the EGL (Embedded-System Graphics Library) API. EGL is a lower-level API that is more closely tied to the underlying drivers of the ..."
description: "EGL is the platform layer that binds OpenGL ES to a native windowing system. In LVGL it underpins the hardware-accelerated backends of the DRM, Wayland and SDL drivers, and can also render off-screen with no display server at all."
---
## Overview
The **EGL** driver provides support for creating LVGL displays using the EGL (Embedded-System Graphics Library) API.
EGL is a lower-level API that is more closely tied to the underlying drivers of the platform.
The OpenGL support in LVGL is intended to be portable between different APIs. Currently, there is support for GLFW and EGL.
Using EGL requires some additional platform integration.
[**EGL**](https://en.wikipedia.org/wiki/EGL_(API)) (Embedded-System Graphics Library) is the platform layer that binds OpenGL ES to a native windowing
system. It sits lower than GLFW and is more closely tied to the platform's own drivers, which is why it is
the path LVGL uses for GPU rendering on embedded Linux.
`lv_opengles_egl_window_create` can be used to create a <ApiLink name="lv_opengles_window_t" />
which can be used with the same generic LVGL OpenGL APIs as a GLFW window.
You rarely enable EGL on its own. It underpins the hardware-accelerated backends of the other drivers:
## EGL with DRM
- [DRM](/integration/embedded_linux/drivers/drm) with `LV_LINUX_DRM_BACKEND_EGL`
- [Wayland](/integration/embedded_linux/drivers/wayland) with `LV_WAYLAND_BACKEND_EGL`
- [SDL](/integration/pc/sdl) with `LV_SDL_BACKEND_EGL`
EGL can be used together with the DRM driver for hardware-accelerated rendering.
Selecting any of those backends turns EGL on for you and sets up the context during normal driver
initialization. Beyond that, EGL offers two things the other drivers do
not: a standalone window path for platforms LVGL does not otherwise cover, and off-screen rendering with no
display server at all.
When <ApiLink name="LV_USE_OPENGLES" /> is enabled, the DRM driver will automatically set up EGL.
No additional initialization is required beyond the normal DRM setup.
## Building
See [DRM](/integration/embedded_linux/drivers/drm) for configuration and a basic usage example.
LVGL loads `libEGL.so` and `libGLESv2.so` at runtime with `dlopen`, so EGL doesn't have any build-time dependencies.
## EGL without DRM (Experimental)
<Callout type="warn">
This feature is experimental and the API is private. Expect breaking changes.
<Callout type="warning" title="EGL and GLFW are mutually exclusive">
<ApiLink name="LV_USE_GLFW" /> cannot be combined with <ApiLink name="LV_USE_EGL" />
each wants to own context creation.
</Callout>
If you want to use EGL without being tied to DRM, you can enable <ApiLink name="LV_USE_EGL" /> using a compiler definition.
This API is currently private and experimental, and people should expect breaking changes.
## Usage
```bash
# Enable standalone EGL (experimental)
-DLV_USE_EGL=1
```
With a driver backend, EGL is invisible: configure the driver as its own page describes and it initializes
EGL during setup. See [DRM](/integration/embedded_linux/drivers/drm) or
[Wayland](/integration/embedded_linux/drivers/wayland) for working examples.
This allows you to use EGL with your own context management or other platforms, but the API may change
without notice in future versions.
## See Also
## Improving Performance
There is a renderer in LVGL which caches software-rendered areas as OpenGL textures.
See [OpenGL Texture Caching Renderer](/integration/embedded_linux/drivers/opengl_driver) to learn more about it.
- [DRM](/integration/embedded_linux/drivers/drm) - EGL backend for direct-to-display rendering
- [Wayland](/integration/embedded_linux/drivers/wayland) - EGL backend under a compositor
- [OpenGL Driver](/integration/embedded_linux/drivers/opengl_driver) - use when you already own the context
- [OpenGL Overview](/integration/embedded_linux/opengl) - how the drivers and draw units fit together
- [NanoVG Draw Unit](/integration/embedded_linux/draw_units/draw_nanovg) - the renderer used for off-screen rendering
- [glTF](/libs/gltf) - 3D content, the strongest case for headless GPU rendering
@@ -1,63 +1,91 @@
---
title: evdev
description: "The Linux event device (evdev) is a hardware-independent API that gives access to input events from, for example, a mouse or touchscreen. It is exposed via the Linux device file system interface."
description: "The evdev driver reads touchscreen, mouse and keyboard input straight from Linux /dev/input event devices, with no user-space input library required."
---
## Overview
The Linux event device (evdev) is a hardware-independent API that gives access to input events from,
for example, a mouse or touchscreen. It is exposed via the Linux device file system interface.
The [Linux event device](https://en.wikipedia.org/wiki/Evdev) (evdev) interface exposes input events from a touchscreen,
mouse, keyboard or anything else the kernel recognizes, through device files under `/dev/input/`.
The evdev driver reads them directly, so it needs no user-space input library at all.
## Prerequisites
That makes it the lightest input option on Linux and the natural pairing for the
[framebuffer](/integration/embedded_linux/drivers/fbdev) and [DRM](/integration/embedded_linux/drivers/drm)
display drivers, neither of which brings its own input.
Your system has an input device configured (usually under `/dev/input/` such as `/dev/input/event0`).
### Prerequisites
## Configuring the driver
An input device under `/dev/input/`, typically `/dev/input/event0`, and permission to read it.
Enable the Linux LVGL evdev driver support in `lv_conf.h`.
## Configuration
```c
#define LV_USE_EVDEV 1
```
Enable <ApiLink name="LV_USE_EVDEV" />.
## Building
The driver always requires `libevdev`.
<Callout type="tip">
LVGL's CMake integration resolves both.
Make sure the required libraries are present on your system or in your sysroot.
See [Dependency Management](/integration/building/cmake).
</Callout>
## Usage
To set up an event input, first create an input device with <ApiLink name="lv_evdev_create" />, setting it to the correct Linux event device.
Then link this to the LVGL display with <ApiLink name="lv_indev_set_display" />.
```c
lv_indev_t *touch = lv_evdev_create(LV_INDEV_TYPE_POINTER, "/dev/input/event0");
lv_indev_set_display(touch, disp);
```
Ensure that an <ApiLink name="lv_display_t" /> object is already created for `disp`. An example for this is shown below, using the [Linux framebuffer](/integration/embedded_linux/drivers/fbdev) driver.
Create an input device with <ApiLink name="lv_evdev_create" />, giving it the device type and node path, then
attach it to a display with <ApiLink name="lv_indev_set_display" />.
```c
lv_display_t * disp = lv_linux_fbdev_create();
lv_linux_fbdev_set_file(disp, "/dev/fb0");
lv_indev_t * touch = lv_evdev_create(LV_INDEV_TYPE_POINTER, "/dev/input/event0");
lv_indev_set_display(touch, disp);
```
## Locating your input device
The type is <ApiLink name="LV_INDEV_TYPE_POINTER" /> for mice and touchscreens, or
<ApiLink name="LV_INDEV_TYPE_KEYPAD" /> for keyboards. <ApiLink name="lv_evdev_create_fd" /> takes an already
open file descriptor instead of a path, and <ApiLink name="lv_evdev_delete" /> removes a device.
If you can't determine your input device, first run
### Finding your input device
```$cat /proc/bus/input/devices```
List what the kernel sees:
This should show input devices and there will be entries with the word `event` which give a clue as to the device to use eg. `event1` would be `/dev/input/event1`.
```sh
cat /proc/bus/input/devices
```
You can use `evtest` to show data from that event source to see if it is actually the one you want.
Entries mention an `event` handler. `event1` corresponds to `/dev/input/event1`. Confirm you picked the
right one by watching its events:
Try:
```sh
evtest /dev/input/event1
```
`$evtest /dev/input/event1` replacing `eventX` with your event device from above.
### Calibrating a touchscreen
## Automatic input device discovery
Raw touchscreens report device coordinates, not screen coordinates. Map them with
<ApiLink name="lv_evdev_set_calibration" />, and correct a transposed panel with
<ApiLink name="lv_evdev_set_swap_axes" />.
There is support for automatically finding and adding input devices in `/dev/input/`. New devices will automatically be added
when they are connected. To enable this feature, you can simply call <ApiLink name="lv_evdev_discovery_start" display="lv_evdev_discovery_start(NULL, NULL)" />.
```c
lv_evdev_set_swap_axes(touch, true);
lv_evdev_set_calibration(touch, min_x, min_y, max_x, max_y);
```
You may want to react to a new device being added so that a cursor image can be applied, for example. You can provide a callback
function which will be called when a new device is added.
### Automatic device discovery
Rather than naming device nodes, let the driver find them. <ApiLink name="lv_evdev_discovery_start" /> scans
`/dev/input/` and keeps watching it, so devices plugged in later are added automatically.
Passing `NULL` for both arguments is enough to enable it:
```c
lv_evdev_discovery_start(NULL, NULL);
```
Supply a callback when you need to react to a new device — to attach a cursor image to a mouse, for example:
```c
#include "lvgl/src/core/lv_global.h"
@@ -72,9 +100,9 @@ static void indev_deleted_cb(lv_event_t * e)
static void discovery_cb(lv_indev_t * indev, lv_evdev_type_t type, void * user_data)
{
LV_LOG_USER("new '%s' device discovered", type == LV_EVDEV_TYPE_REL ? "REL" :
type == LV_EVDEV_TYPE_ABS ? "ABS" :
type == LV_EVDEV_TYPE_KEY ? "KEY" :
"unknown");
type == LV_EVDEV_TYPE_ABS ? "ABS" :
type == LV_EVDEV_TYPE_KEY ? "KEY" :
"unknown");
if(type == LV_EVDEV_TYPE_REL) {
LV_IMAGE_DECLARE(mouse_cursor_icon);
@@ -85,7 +113,7 @@ static void discovery_cb(lv_indev_t * indev, lv_evdev_type_t type, void * user_d
}
}
int main()
int main(void)
{
/* ... */
lv_evdev_discovery_start(discovery_cb, NULL);
@@ -93,4 +121,34 @@ int main()
}
```
At the time of writing, this feature is not supported in BSD.
<ApiLink name="lv_evdev_discovery_stop" /> turns it off again.
<Callout type="info">
Discovery relies on `inotify` and is not available on BSD. Create devices explicitly there.
</Callout>
### Raw key codes
For keys LVGL does not map to one of its own, <ApiLink name="lv_evdev_is_raw_key" /> tells you an event
carries a raw code and <ApiLink name="lv_evdev_get_raw_key" /> returns it. Use these when you need keys
outside LVGL's navigation set.
## Support
| Capability | Value |
|---|---|
| Device types | Pointer (mouse, touchscreen) and keypad |
| Automatic discovery | Yes — <ApiLink name="lv_evdev_discovery_start" /> |
| Hotplug | Yes — devices connected later are added automatically |
| Full keyboard (layouts, modifiers) | No — use [libinput](/integration/embedded_linux/drivers/libinput) with XKB |
| Touch calibration | Yes — <ApiLink name="lv_evdev_set_calibration" /> |
| Axis swap | Yes — <ApiLink name="lv_evdev_set_swap_axes" /> |
| Find device by capability | No — pass a node path, or use discovery |
| BSD | Yes, except discovery and hotplug |
## See Also
- [libinput](/integration/embedded_linux/drivers/libinput) - richer input stack, with XKB keyboard support
- [Linux Framebuffer](/integration/embedded_linux/drivers/fbdev) - display driver commonly paired with evdev
- [DRM](/integration/embedded_linux/drivers/drm) - display driver commonly paired with evdev
- [Dependency Management](/integration/building/cmake) - how LVGL resolves the evdev library
@@ -1,76 +1,101 @@
---
title: Linux Framebuffer
description: "The Linux framebuffer (fbdev) is a linux subsystem used to display graphics. It is a hardware-independent API that gives user space software access to the framebuffer (the part of a computer's vide..."
description: "The Linux framebuffer (fbdev) driver renders LVGL into /dev/fb through the kernel's framebuffer interface, with no libraries and no display server required."
---
## Overview
The Linux framebuffer (fbdev) is a linux subsystem used to display graphics. It is a hardware-independent API that gives user space software
access to the framebuffer (the part of a computer's video memory containing a current video frame) using only the Linux kernel's own basic
facilities and its device file system interface, avoiding the need for libraries that implement video drivers in user space.
The [Linux framebuffer (fbdev)](https://en.wikipedia.org/wiki/Linux_framebuffer) is a kernel subsystem that
exposes a video framebuffer to user space through a device file, typically `/dev/fb0`. It is hardware-independent
and needs no user-space graphics libraries at all, which makes this the simplest way to put LVGL on a screen:
open the device, write pixels.
## Prerequisites
That simplicity is also its limit. fbdev has no modesetting, no GPU path and no page-flip guarantees. It is
the right choice for minimal systems and for kernels that expose nothing else; on anything modern prefer
[DRM/KMS](/integration/embedded_linux/drivers/drm), which supersedes fbdev and can reach the GPU.
Your system has a framebuffer device configured (usually under `/dev/fb0`).
### Prerequisites
## Configuring the driver
A framebuffer device, usually `/dev/fb0`, and permission to open it.
Enable the framebuffer driver support in lv_conf.h, by cmake compiler define or by KConfig. Additionally you may configure the rendering
mode.
## Configuration
```c
#define LV_USE_LINUX_FBDEV 1
#define LV_LINUX_FBDEV_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL
```
Enable <ApiLink name="LV_USE_LINUX_FBDEV" />.
### Options
| Symbol | Default | Effect |
|---|---|---|
| `LV_LINUX_FBDEV_RENDER_MODE` | `LV_DISPLAY_RENDER_MODE_PARTIAL` | Render mode; see below. |
| `LV_LINUX_FBDEV_BUFFER_COUNT` | `1` | `1` or `2` screen-sized buffers, or `0` for a custom-sized partial buffer. |
| `LV_LINUX_FBDEV_BUFFER_SIZE` | `60` | Height in rows of the partial buffer. Only used when the buffer count is `0`. |
| `LV_LINUX_FBDEV_MMAP` | on | Access the framebuffer via `mmap()` instead of `write()` calls. |
| `LV_LINUX_FBDEV_BSD` | off | Use the BSD flavour of the framebuffer device. |
`LV_LINUX_FBDEV_RENDER_MODE` accepts <ApiLink name="LV_DISPLAY_RENDER_MODE_PARTIAL" />,
<ApiLink name="LV_DISPLAY_RENDER_MODE_DIRECT" /> or <ApiLink name="LV_DISPLAY_RENDER_MODE_FULL" />.
The buffer count and the render mode constrain each other: two screen-sized buffers require a non-partial
render mode, and a custom-sized buffer requires the partial one.
## Usage
To set up a framebuffer-based display, first create a display with <ApiLink name="lv_linux_fbdev_create" />. Afterwards set the framebuffer device
node on the display with <ApiLink name="lv_linux_fbdev_set_file" /> (usually this is `/dev/fb0`).
<Callout type="tip">
[LVGL's linux port](https://github.com/lvgl/lv_port_linux) can be used to quickly get started with LVGL's FBDEV backend.
</Callout>
Create the display with <ApiLink name="lv_linux_fbdev_create" />, then point it at a device node with
<ApiLink name="lv_linux_fbdev_set_file" />.
```c
lv_display_t *disp = lv_linux_fbdev_create();
lv_display_t * disp = lv_linux_fbdev_create();
lv_linux_fbdev_set_file(disp, "/dev/fb0");
```
If your screen stays black or only draws partially, you can try enabling direct
rendering via <ApiLink name="LV_DISPLAY_RENDER_MODE_DIRECT" />. Additionally, you can
activate a force refresh mode with <ApiLink name="lv_linux_fbdev_set_force_refresh" />. This
usually has a performance impact though and shouldn't be enabled unless really needed.
The resolution and color format come from the device, the driver reads them from the framebuffer's
`ioctl` info, so there is nothing to configure.
## Hide the cursor
### If the screen stays black or draws partially
You may encounter a blinking cursor on the screen. The method to hide it
varies depending on the platform. For instance, here is how it can be done
on a Raspberry Pi:
Try <ApiLink name="LV_DISPLAY_RENDER_MODE_DIRECT" /> first. If that does not help, force a refresh of the
whole screen on every flush with <ApiLink name="lv_linux_fbdev_set_force_refresh" />. This costs performance,
so only enable it when it is genuinely needed.
1. Edit `/boot/cmdline.txt` file.
2. Add `vt.global_cursor_default=0`.
### Hide the blinking cursor
## Common mistakes
The kernel console leaves a blinking cursor on the framebuffer. How to disable it depends on the platform;
on a Raspberry Pi, add `vt.global_cursor_default=0` to `/boot/cmdline.txt`.
### Default resolution issue
### Wrong resolution after boot
When the Linux kernel initializes, it sets up subsystems like the framebuffer
(fbdev) to manage display output. If an HDMI display is connected, the kernel
detects it and allocates a portion of RAM as the framebuffer, which holds the
pixel data for rendering images.
The kernel sizes the framebuffer when it initializes, based on what the display reports. If the board powers
up before the screen (common with HDMI) the kernel may pick the wrong resolution, leaving both the visible
and virtual resolutions incorrect.
However, a common issue arises when the display is not powered on during the
boot process. If the board is powered on before the screen, the kernel may
incorrectly configure the framebuffer resolution. As a result, both the visible
and virtual resolutions can be incorrect, leading to display problems.
Check what the kernel actually chose:
This issue often occurs with HDMI connections where the display is powered up
after the system has already booted.
The following command outputs the current settings of the specified framebuffer
device, such as resolution, pixel depth, and timings.
```c
```sh
fbset -fb /dev/fb0
```
To prevent display-related issues, it is recommended to ensure all devices,
including the HDMI display, are connected and powered on before powering up
the board.
The fix is procedural: make sure the display is connected and powered before the board boots.
## Support
| Capability | Value |
|---|---|
| Rotation | Software |
| Runtime resolution change | No |
| Runtime color format change | No |
| Color formats | RGB565, RGB888 or XRGB8888, chosen from the framebuffer's bit depth (16, 24 or 32) |
| Multiple displays / windows | Yes |
| Hardware acceleration | None |
| Render mode | Partial (default) — configurable via `LV_LINUX_FBDEV_RENDER_MODE` |
| Input | None built in. Pair with [evdev](/integration/embedded_linux/drivers/evdev) or [libinput](/integration/embedded_linux/drivers/libinput) |
## See Also
- [DRM](/integration/embedded_linux/drivers/drm) - the modern replacement, with modesetting and a GPU path
- [evdev](/integration/embedded_linux/drivers/evdev) - simplest input pairing for this driver
- [libinput](/integration/embedded_linux/drivers/libinput) - input with device quirks and full keyboard support
- [lv_port_linux](https://github.com/lvgl/lv_port_linux) - reference project
@@ -1,74 +1,86 @@
---
title: GLFW
description: "The GLFW display/input driver offers support for creating LVGL displays and keyboard/mouse inputs that can be used in an OpenGL context. It can be used like Wayland, XCB, SDL or Qt or it can be use..."
description: "The GLFW driver opens a window with an OpenGL context and reads mouse and keyboard input. It is the quickest way to get LVGL running with GPU rendering on a PC-like platform."
---
## Overview
The **GLFW** display/input driver offers support for creating
LVGL displays and keyboard/mouse inputs that can be used in an OpenGL context.
It can be used like **Wayland**, **XCB**, **SDL** or **Qt** or it can be used for more embedded applications.
The **GLFW** display/input driver opens a window with an OpenGL context and reads mouse and keyboard input.
The GLFW driver is a quick way to get started on PC-like platforms.
## Configuration
## Getting Started with GLFW
Enable <ApiLink name="LV_USE_GLFW" />.
### Prerequisites
<Callout type="warning" title="GLFW and EGL are mutually exclusive">
<ApiLink name="LV_USE_GLFW" /> cannot be combined with `EGL`.
GLFW manages the context itself, so the two window-system layers would collide.
</Callout>
Install GLFW: `sudo apt-get install libglfw3-dev`
### Configure GLFW Driver
## Building
1. Required linked libraries: -lglfw
2. Enable the GLFW driver and Opengles support in `lv_conf.h`, via cmake compiler define or via KConfig
The driver always requires `GLFW`.
```c
#define LV_USE_GLFW 1
#define LV_USE_OPENGLES 1
```
<Callout type="tip">
LVGL's CMake integration resolves it for you.
It can be fetched at compile time by LVGL so it doesn't have to be present on your system.
### Basic Usage
See [Dependency Management](/integration/building/cmake).
</Callout>
## GPU Rendering
To render on the GPU rather than blitting software-rendered pixels, also enable one of the draw units:
| Symbol | Effect |
|---|---|
| <ApiLink name="LV_USE_DRAW_NANOVG" /> | Vector rendering on the GPU. Recommended — best performance and feature coverage. |
| <ApiLink name="LV_USE_DRAW_OPENGLES" /> | Caches software-rendered areas as GPU textures. |
Neither is required: with no draw unit enabled the driver still works, rendering in software and uploading
the result to the window.
## Usage
<Callout type="tip">
[LVGL's linux port](https://github.com/lvgl/lv_port_linux) can be used to quickly get started with LVGL's GLFW backend.
</Callout>
<ApiLink name="lv_opengles_glfw_window_create" /> opens the window and initializes OpenGL. The third
argument enables the mouse input device. <ApiLink name="lv_opengles_window_display_create" /> then gives you
a display that renders into that window.
```c
#include "lvgl/lvgl.h"
#include "lvgl/examples/lv_examples.h"
#include "lvgl/demos/lv_demos.h"
#define WIDTH 640
#define WIDTH 640
#define HEIGHT 480
int main()
int main(void)
{
/* initialize lvgl */
lv_init();
/* create a window and initialize OpenGL */
/* Create a window and initialize OpenGL */
lv_opengles_window_t * window = lv_opengles_glfw_window_create(WIDTH, HEIGHT, true);
/* create a display that flushes to a texture */
lv_display_t * texture = lv_opengles_texture_create(WIDTH, HEIGHT);
lv_display_set_default(texture);
/* Create a display that renders into the window */
lv_display_t * disp = lv_opengles_window_display_create(window, WIDTH, HEIGHT);
lv_display_set_default(disp);
/* add the texture to the window */
unsigned int texture_id = lv_opengles_texture_get_texture_id(texture);
lv_opengles_window_texture_t * window_texture = lv_opengles_window_add_texture(window, texture_id, WIDTH, HEIGHT);
/* get the mouse indev of the window texture */
lv_indev_t * mouse = lv_opengles_window_texture_get_mouse_indev(window_texture);
/* add a cursor to the mouse indev */
/* Add a cursor to the window's mouse indev */
lv_opengles_window_texture_t * wt = lv_opengles_window_display_get_window_texture(disp);
lv_indev_t * mouse = lv_opengles_window_texture_get_mouse_indev(wt);
LV_IMAGE_DECLARE(mouse_cursor_icon);
lv_obj_t * cursor_obj = lv_image_create(lv_screen_active());
lv_image_set_src(cursor_obj, &mouse_cursor_icon);
lv_indev_set_cursor(mouse, cursor_obj);
/* create Widgets on the screen */
lv_demo_widgets();
while (1)
{
while(1) {
uint32_t time_until_next = lv_timer_handler();
if(time_until_next == LV_NO_TIMER_READY) time_until_next = LV_DEF_REFR_PERIOD; /*handle LV_NO_TIMER_READY. Another option is to `sleep` for longer*/
if(time_until_next == LV_NO_TIMER_READY) time_until_next = LV_DEF_REFR_PERIOD;
lv_delay_ms(time_until_next);
}
@@ -76,125 +88,75 @@ int main()
}
```
### Advanced Usage
Multiple windows can be created, each with its own displays.
The GLFW driver can draw textures from the user. A third-party library could be
used to add content to a texture and the driver will draw the texture in the window.
## Compositing Textures in a Window
A GLFW window can draw any number of OpenGL textures, positioned and blended independently. That covers two
cases: several LVGL displays in one window, and LVGL displaying textures produced by another library.
Use <ApiLink name="lv_opengles_texture_create" /> for an LVGL display that renders into its own texture, and
<ApiLink name="lv_opengles_window_add_texture" /> to place any texture id in the window.
```c
#include "lvgl/lvgl.h"
#include <GLFW/glfw3.h>
#define WIDTH 640
#define WIDTH 640
#define HEIGHT 480
void custom_texture_example(void)
void composite_example(lv_opengles_window_t * window)
{
/*****************
* MAIN WINDOW
*****************/
/* A second LVGL display, rendering into its own texture */
const int32_t sub_w = 300, sub_h = 300;
lv_display_t * sub = lv_opengles_texture_create(sub_w, sub_h);
unsigned int sub_id = lv_opengles_texture_get_texture_id(sub);
lv_opengles_window_texture_t * sub_wt =
lv_opengles_window_add_texture(window, sub_id, sub_w, sub_h);
/* create a window and initialize OpenGL */
/* multiple windows can be created */
lv_opengles_window_t * window = lv_opengles_glfw_window_create(WIDTH, HEIGHT, true);
/****************************
* OPTIONAL MAIN TEXTURE
****************************/
/* create a main display that flushes to a texture */
lv_display_t * main_texture = lv_opengles_texture_create(WIDTH, HEIGHT);
lv_display_set_default(main_texture);
/* add the main texture to the window */
unsigned int main_texture_id = lv_opengles_texture_get_texture_id(main_texture);
lv_opengles_window_texture_t * window_main_texture = lv_opengles_window_add_texture(window, main_texture_id, WIDTH, HEIGHT);
/* get the mouse indev of this main texture */
lv_indev_t * main_texture_mouse = lv_opengles_window_texture_get_mouse_indev(window_main_texture);
/* add a cursor to the mouse indev */
LV_IMAGE_DECLARE(mouse_cursor_icon);
lv_obj_t * cursor_obj = lv_image_create(lv_screen_active());
lv_image_set_src(cursor_obj, &mouse_cursor_icon);
lv_indev_set_cursor(main_texture_mouse, cursor_obj);
/* create Widgets on the screen of the main texture */
lv_demo_widgets();
/**********************
* ANOTHER TEXTURE
**********************/
/* create a sub display that flushes to a texture */
const int32_t sub_texture_w = 300;
const int32_t sub_texture_h = 300;
lv_display_t * sub_texture = lv_opengles_texture_create(sub_texture_w, sub_texture_h);
/* add the sub texture to the window */
unsigned int sub_texture_id = lv_opengles_texture_get_texture_id(sub_texture);
lv_opengles_window_texture_t * window_sub_texture = lv_opengles_window_add_texture(window, sub_texture_id, sub_texture_w, sub_texture_h);
/* create Widgets on the screen of the sub texture */
lv_display_set_default(sub_texture);
/* Build its UI */
lv_display_t * saved = lv_display_get_default();
lv_display_set_default(sub);
lv_example_keyboard_2();
lv_display_set_default(main_texture);
lv_display_set_default(saved);
/* position the sub texture within the window */
lv_opengles_window_texture_set_x(window_sub_texture, 250);
lv_opengles_window_texture_set_y(window_sub_texture, 150);
/* Position it in the window and make it translucent */
lv_opengles_window_texture_set_x(sub_wt, 250);
lv_opengles_window_texture_set_y(sub_wt, 150);
lv_opengles_window_texture_set_opa(sub_wt, LV_OPA_80);
/* optionally change the opacity of the sub texture */
lv_opengles_window_texture_set_opa(window_sub_texture, LV_OPA_80);
/* A texture owned by some other library can be added the same way */
lv_opengles_window_add_texture(window, external_texture_id, ext_w, ext_h);
/*********************************************
* USE AN EXTERNAL OPENGL TEXTURE IN LVGL
*********************************************/
unsigned int external_texture_id;
glGenTextures(1, &external_texture_id);
glBindTexture(GL_TEXTURE_2D, external_texture_id);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
LV_IMAGE_DECLARE(img_cogwheel_argb);
#if LV_COLOR_DEPTH == 8
const int texture_format = GL_R8;
#elif LV_COLOR_DEPTH == 16
const int texture_format = GL_RGB565;
#elif LV_COLOR_DEPTH == 24
const int texture_format = GL_RGB;
#elif LV_COLOR_DEPTH == 32
const int texture_format = GL_RGBA;
#else
#error("Unsupported color format")
#endif
glTexImage2D(GL_TEXTURE_2D, 0, texture_format, img_cogwheel_argb.header.w, img_cogwheel_argb.header.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_cogwheel_argb.data);
glGenerateMipmap(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, 0);
/* add the external texture to the window */
lv_opengles_window_texture_t * window_external_texture = lv_opengles_window_add_texture(window, external_texture_id, img_cogwheel_argb.header.w, img_cogwheel_argb.header.h);
/* set the position and opacity of the external texture within the window */
lv_opengles_window_texture_set_x(window_external_texture, 20);
lv_opengles_window_texture_set_y(window_external_texture, 20);
lv_opengles_window_texture_set_opa(window_external_texture, LV_OPA_70);
/*********************************************
* USE AN LVGL TEXTURE IN ANOTHER LIBRARY
*********************************************/
lv_refr_now(sub_texture);
/* the texture is drawn on by LVGL and can be used by anything that uses OpenGL textures */
third_party_lib_use_texture(sub_texture_id);
/* Going the other way: render the LVGL texture, then hand it to another library */
lv_refr_now(sub);
third_party_lib_use_texture(sub_id);
}
```
## Improving Performance
<ApiLink name="lv_opengles_window_texture_remove" /> takes a texture back out of the window.
There is a renderer in LVGL which caches software-rendered areas as OpenGL textures.
See [OpenGL Texture Caching Renderer](/integration/embedded_linux/drivers/opengl_driver) to learn more about it.
To wrap a texture your own code allocated in an LVGL display instead, use
<ApiLink name="lv_opengles_texture_create_from_texture_id" />. See
[OpenGL Driver](/integration/embedded_linux/drivers/opengl_driver) for the texture-only driver, which
assumes you manage the context and the window yourself.
## Support
| Capability | Value |
|---|---|
| Rotation | Hardware (GPU) |
| Runtime resolution change | No |
| Runtime color format change | No |
| Color formats | Derived from <ApiLink name="LV_COLOR_DEPTH" /> |
| Multiple displays / windows | Yes |
| Hardware acceleration | OpenGL ES, via <ApiLink name="LV_USE_DRAW_NANOVG" /> or <ApiLink name="LV_USE_DRAW_OPENGLES" /> |
| Render mode | Full with <ApiLink name="LV_USE_DRAW_OPENGLES" />, otherwise Direct |
| Input | Built in, mouse per window, keyboard |
## See Also
- [Dependency Management](/integration/building/cmake) - how LVGL resolves or fetches GLFW
- [OpenGL Driver](/integration/embedded_linux/drivers/opengl_driver) - texture-only driver for an existing context
- [OpenGL Overview](/integration/embedded_linux/opengl) - how the drivers and draw units fit together
- [NanoVG Draw Unit](/integration/embedded_linux/draw_units/draw_nanovg) - the recommended GPU renderer
- [EGL](/integration/embedded_linux/drivers/egl) - use instead of GLFW when you need EGL
@@ -1,6 +1,84 @@
---
title: Drivers
description: "LVGL's Linux drivers come in two kinds: display drivers that put pixels on a screen, and input drivers that read touch, mouse and keyboard events. Most setups need one of each."
---
## Overview
LVGL's Linux drivers come in two kinds, and most setups need one of each:
- **Display drivers** get rendered pixels onto a screen — or into a texture, or a file.
- **Input drivers** read touch, mouse and keyboard events.
Some display drivers bring their own input, so pairing is not always necessary. The tables below say which.
If you are not sure which display driver you want, start from
[Choosing a Display Driver](/integration/embedded_linux#choosing-a-display-driver), which picks one based on
what your system already has.
## Display Drivers
| Driver | Best for | Rotation | Hardware acceleration | Runtime resolution change | Input |
|---|---|---|---|---|---|
| **[fbdev](/integration/embedded_linux/drivers/fbdev)** | Minimal systems exposing only `/dev/fb` | Software, but it disables direct mode | None | No | Pair separately |
| **[DRM](/integration/embedded_linux/drivers/drm)** | Production embedded targets | EGL backend only | EGL backend | EGL backend only | Pair separately |
| **[Wayland](/integration/embedded_linux/drivers/wayland)** | Systems running a compositor | Yes, all backends | EGL or G2D backend | Yes | Built in |
| **[X11](/integration/embedded_linux/drivers/X11)** | Development on an X desktop | None | None | Yes | Built in |
| **[GLFW](/integration/embedded_linux/drivers/glfw)** | Development with an instant GL context | Hardware | OpenGL ES | No | Built in |
| **[OpenGL Driver](/integration/embedded_linux/drivers/opengl_driver)** | Embedding LVGL in your own GL application | Hardware | OpenGL ES | Manual | None — you own the window |
| **[EGL](/integration/embedded_linux/drivers/egl)** | The layer under the accelerated backends, and headless rendering | Hardware | OpenGL ES | Depends on the driver | Depends on the driver |
Each driver page carries a fuller **Support** table, including color formats, multiple-display support and the
render mode.
<Callout type="info" title="SDL is documented elsewhere">
The [SDL driver](/integration/pc/sdl) is a perfectly good option on Linux and is often the quickest way to get
a window during development. Because it is cross-platform it lives under
[Running on PC](/integration/pc) rather than here. Its
[draw unit](/integration/embedded_linux/draw_units/draw_sdl) is documented with the other draw units.
</Callout>
### A note on EGL
[EGL](/integration/embedded_linux/drivers/egl) is not usually a driver you select. It is the OpenGL ES
platform layer that the DRM, Wayland and SDL drivers use for their hardware-accelerated backends, so
selecting one of those backends turns it on for you. You reach for the EGL page directly in two cases:
custom platform integration, and off-screen rendering with no display server at all.
## Input Drivers
| Driver | Best for | Discovery and hotplug | Full keyboard | Touch calibration | Find device by capability |
|---|---|---|---|---|---|
| **[evdev](/integration/embedded_linux/drivers/evdev)** | The common case — a known touchscreen or mouse | Yes | No | Yes | No |
| **[libinput](/integration/embedded_linux/drivers/libinput)** | Text entry, or devices needing quirks | No | Yes, via XKB | Not needed | Yes |
Use **evdev** unless you need real keyboard support — layouts and modifiers — or your device requires
libinput's quirk handling. evdev is lighter and is the only one of the two with automatic device discovery and
hotplug.
Pair an input driver with a display driver by attaching it to the display:
```c
lv_indev_t * touch = lv_evdev_create(LV_INDEV_TYPE_POINTER, "/dev/input/event0");
lv_indev_set_display(touch, disp);
```
## Rendering
Drivers decide how a frame reaches the screen; **draw units** decide who rasterizes it. The two are chosen
independently — see [Draw Units](/integration/embedded_linux/draw_units) and
[OpenGL Overview](/integration/embedded_linux/opengl).
One consequence worth knowing before you start: on every GPU and compositor path the render mode is fixed by
the driver rather than configurable, because something other than LVGL owns the buffers. Only the CPU-blit
drivers — fbdev, X11 and SDL's software backend — let you choose it.
## See Also
- [Choosing a Display Driver](/integration/embedded_linux#choosing-a-display-driver) - decision table
- [Draw Units](/integration/embedded_linux/draw_units) - who does the rasterizing
- [OpenGL Overview](/integration/embedded_linux/opengl) - which drivers provide a GL context
- [Dependency Management](/integration/building/cmake) - how enabling a driver pulls in its libraries
- [lv_port_linux](https://github.com/lvgl/lv_port_linux) - reference project with every driver preconfigured
<DirectoryIndex />
@@ -1,21 +1,29 @@
---
title: libinput
description: "Libinput is an input stack for processes that need to provide events from commonly used input devices. That includes mice, keyboards, touchpads, touchscreens and graphics tablets."
description: "The libinput driver reads pointer, touch and keyboard input through libinput, which handles device-specific quirks and offers full keyboard support through XKB."
---
## Overview
Libinput is an input stack for processes that need to provide events from commonly used input devices. That includes mice, keyboards, touchpads,
touchscreens and graphics tablets. Libinput handles device-specific quirks and provides an easy-to-use API to receive events from devices.
[libinput](https://wiki.archlinux.org/title/Libinput) is the input stack used by most Linux desktops.
It covers mice, keyboards, touchpads, touchscreens and graphics tablets, absorbing device-specific quirks
and normalizing everything into a uniform event stream.
## Prerequisites
Compared with [evdev](/integration/embedded_linux/drivers/evdev), libinput gives you two things that matter:
quirk handling for devices that need it, and real keyboard support (layouts, modifiers and non-US
keyboards) through XKB. In exchange it adds a dependency and gives up automatic device discovery.
Use [evdev](/integration/embedded_linux/drivers/evdev) for a minimal system with a known touchscreen;
use libinput when you need proper text entry or a device with quirks.
You have the development version of libinput installed (usually `libinput-dev`). If your input device requires quirks, make sure they are
installed as well (usually in `/usr/share/libinput/*.quirks`). To test if your device is set up correctly for use with libinput, you can
run `libinput list-devices`.
### Prerequisites
```console
$ sudo libinput list-devices
libinput must be available in the build (usually the `libinput` development package). If your device needs
quirks, make sure those are installed too, normally under `/usr/share/libinput/*.quirks`.
Check that libinput sees your device:
```bash
libinput list-devices
...
Device: ETPS/2 Elantech Touchpad
Kernel: /dev/input/event5
@@ -28,56 +36,102 @@ Tap-and-drag: enabled
...
```
If your device doesn't show up, you may have to configure udev and the appropriate udev rules to connect it.
If it does not appear, you likely need udev rules to connect it.
Additionally, if you want full keyboard support, including letters and modifiers, you'll need the development version of libxkbcommon
installed (usually `libxkbcommon-dev`).
## Configuration
## Configuring the driver
Enable <ApiLink name="LV_USE_LIBINPUT" />.
Enable the libinput driver support in lv_conf.h, by cmake compiler define or by KConfig.
### Full keyboard support
```c
#define LV_USE_LIBINPUT 1
```
Letters, modifiers and non-US layouts need XKB, which is off by default. Enable `LV_LIBINPUT_XKB`, then
describe your keyboard with these options:
Full keyboard support needs to be enabled separately.
| Symbol | Default | Meaning |
|---|---|---|
| `LV_LIBINPUT_XKB_RULES` | `""` | XKB rules file |
| `LV_LIBINPUT_XKB_MODEL` | `"pc101"` | Keyboard model |
| `LV_LIBINPUT_XKB_LAYOUT` | `"us"` | Layout |
| `LV_LIBINPUT_XKB_VARIANT` | `""` | Layout variant |
| `LV_LIBINPUT_XKB_OPTIONS_USE_DEFAULT` | on | Use the system default options, e.g. from `XKB_DEFAULT_OPTIONS` |
| `LV_LIBINPUT_XKB_OPTIONS` | `""` | Explicit options; only used when the above is off |
```c
#define LV_LIBINPUT_XKB 1
#define LV_LIBINPUT_XKB_KEY_MAP { .rules = NULL, .model = "pc101", .layout = "us", .variant = NULL, .options = NULL }
```
Run `setxkbmap -query` on a working system to find the right values.
To find the right key map values, you may use the `setxkbmap -query` command.
### Other options
| Symbol | Default | Effect |
|---|---|---|
| `LV_LIBINPUT_BSD` | off | Use the BSD variant of libinput. |
## Building
The driver always requires `libinput`.
Additionally you may use XKB support which requires `libxbkcommon`.
<Callout type="tip">
LVGL's CMake integration resolves both.
Make sure the required libraries are present on your system or in your sysroot.
See [Dependency Management](/integration/building/cmake).
</Callout>
## Usage
To set up an input device via the libinput driver, all you need to do is call <ApiLink name="lv_libinput_create" /> with the respective device type
(<ApiLink name="LV_INDEV_TYPE_POINTER" /> or <ApiLink name="LV_INDEV_TYPE_KEYPAD" />) and device node path (e.g. `/dev/input/event5`).
Call <ApiLink name="lv_libinput_create" /> with the device type and the node path.
```c
lv_indev_t *indev = lv_libinput_create(LV_INDEV_TYPE_POINTER, "/dev/input/event5");
lv_indev_t * indev = lv_libinput_create(LV_INDEV_TYPE_POINTER, "/dev/input/event5");
```
Note that touchscreens are treated as (absolute) pointer devices by the libinput driver and require <ApiLink name="LV_INDEV_TYPE_POINTER" />.
The type is <ApiLink name="LV_INDEV_TYPE_POINTER" /> or <ApiLink name="LV_INDEV_TYPE_KEYPAD" />. Note that
libinput treats touchscreens as absolute pointer devices, so a touchscreen also needs
<ApiLink name="LV_INDEV_TYPE_POINTER" />. <ApiLink name="lv_libinput_delete" /> removes a device.
Depending on your system, the device node paths might not be stable across reboots. If this is the case, you can use <ApiLink name="lv_libinput_find_dev" />
to find the first device that has a specific capability.
### Finding devices without hard-coded paths
Device node paths are not necessarily stable across reboots. Look a device up by what it can do instead, with
<ApiLink name="lv_libinput_find_dev" />:
```c
char *path = lv_libinput_find_dev(LV_LIBINPUT_CAPABILITY_TOUCH, true);
char * path = lv_libinput_find_dev(LV_LIBINPUT_CAPABILITY_TOUCH, true);
```
The second argument controls whether or not all devices are rescanned. If you have many devices connected this can get quite slow.
Therefore, you should only specify `true` on the first call when calling this method multiple times in a row. If you want to find
all devices that have a specific capability, use <ApiLink name="lv_libinput_find_devs" />.
The capability is one of `LV_LIBINPUT_CAPABILITY_KEYBOARD`, `LV_LIBINPUT_CAPABILITY_POINTER` or
`LV_LIBINPUT_CAPABILITY_TOUCH`. The second argument controls whether all devices are rescanned; scanning is
slow with many devices, so pass `true` only on the first of a series of calls.
If you want to connect a keyboard device to a textarea, create a dedicated input group and set it on both the indev and textarea.
<ApiLink name="lv_libinput_find_devs" /> returns every device with a given capability, and
<ApiLink name="lv_libinput_query_capability" /> reports what a specific device supports.
### Connecting a keyboard to a text area
Keyboard input reaches a widget through a group. Create one, set it on the input device, and add the widget:
```c
lv_obj_t *textarea = lv_textarea_create(...);
...
lv_group_t *keyboard_input_group = lv_group_create();
lv_indev_set_group(indev, keyboard_input_group);
lv_group_add_obj(keyboard_input_group, textarea);
lv_obj_t * textarea = lv_textarea_create(lv_screen_active());
lv_group_t * g = lv_group_create();
lv_indev_set_group(indev, g);
lv_group_add_obj(g, textarea);
```
## Support
| Capability | Value |
|---|---|
| Device types | Pointer (mouse, touchpad, touchscreen) and keypad |
| Automatic discovery | No |
| Hotplug | No |
| Full keyboard (layouts, modifiers) | With `LV_LIBINPUT_XKB` |
| Touch calibration | Automatic |
| Axis swap | With libinput quirks or udev rules |
| Find device by capability | <ApiLink name="lv_libinput_find_dev" /> and <ApiLink name="lv_libinput_find_devs" /> |
| BSD | With `LV_LIBINPUT_BSD` |
## See Also
- [evdev](/integration/embedded_linux/drivers/evdev) - lighter alternative, with discovery and hotplug
- [Linux Framebuffer](/integration/embedded_linux/drivers/fbdev) - display driver needing a separate input driver
- [DRM](/integration/embedded_linux/drivers/drm) - display driver needing a separate input driver
- [Dependency Management](/integration/building/cmake) - how LVGL resolves libinput and xkbcommon
@@ -3,11 +3,11 @@
"pages": [
"fbdev",
"drm",
"opengl_driver",
"glfw",
"egl",
"wayland",
"X11",
"glfw",
"opengl_driver",
"egl",
"evdev",
"libinput"
]
@@ -1,109 +1,155 @@
---
title: OpenGL Driver
description: The OpenGL display driver is a generic driver that creates textures for embedding LVGL content in other applications. The goal is to create textures that people can embed in other applications.
description: "The OpenGL driver renders an LVGL display into an OpenGL texture, for embedding LVGL inside an application that already owns the OpenGL context."
---
## Overview
The **OpenGL** display driver is a generic driver that creates textures for embedding
LVGL content in other applications. The goal is to create textures that people can
embed in other applications. The OpenGL context must be created by the user or they
can use GLFW or EGL as backends.
The **OpenGL** display driver renders an LVGL display into an OpenGL texture and hands you the texture id.
It creates no window and no context: it is the driver to use when something else, a game, a visualization
tool or your own engine already owns the OpenGL context and you want LVGL content inside it.
## Getting Started with OpenGL
If you do not already have a context, use a driver that creates one for you instead:
### Prerequisites
- [GLFW](/integration/embedded_linux/drivers/glfw) on PC-like platforms
- [EGL](/integration/embedded_linux/drivers/egl), or [DRM](/integration/embedded_linux/drivers/drm) and
[Wayland](/integration/embedded_linux/drivers/wayland) with their EGL backends, on embedded targets
- [SDL](/integration/pc/sdl) with its EGL backend
An OpenGL context must be created before using the OpenGL driver. You can create this using:
## Configuration
- GLFW (see [GLFW driver](/integration/embedded_linux/drivers/glfw))
- EGL (see [EGL driver](/integration/embedded_linux/drivers/egl))
- Your own OpenGL context management
Enable <ApiLink name="LV_USE_OPENGLES" />.
### Configure OpenGL Driver
There are no libraries for LVGL to resolve, the GL entry points come from the context you already created.
Enable the OpenGL driver support in lv_conf.h, by cmake compiler define or by KConfig:
To render on the GPU rather than uploading software-rendered pixels into the texture, also enable a draw
unit: <ApiLink name="LV_USE_DRAW_NANOVG" /> (recommended) or <ApiLink name="LV_USE_DRAW_OPENGLES" />. See
[OpenGL Overview](/integration/embedded_linux/opengl).
```c
#define LV_USE_OPENGLES 1
```
## Usage
### Basic Usage
The OpenGL context must already be current when you call <ApiLink name="lv_opengles_texture_create" />. It
allocates the texture for you; <ApiLink name="lv_opengles_texture_get_texture_id" /> returns the id to use in
your own rendering.
```c
#include "lvgl/lvgl.h"
#define WIDTH 640
#define WIDTH 640
#define HEIGHT 480
/* This flush callback works with both FULL and DIRECT render modes*/
static void flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
int main(void)
{
if (lv_display_flush_is_last(disp)) {
const int32_t disp_width = lv_display_get_horizontal_resolution(disp);
const int32_t disp_height = lv_display_get_vertical_resolution(disp);
/* The texture occupies the full screen even if `area` is not the full screen which happens with RENDER_MODE_DIRECT */
lv_area_t full_area;
lv_area_set(&full_area, 0, 0, disp_width, disp_height);
/* Get the texture id containing LVGL generated UI */
unsigned int texture_id = lv_opengles_texture_get_texture_id(disp);
/* This function will render to the current context */
lv_opengles_render_texture(texture_id, &full_area, LV_OPA_COVER, disp_width, disp_height, &full_area, false, true);
}
lv_display_flush_ready(disp);
}
int main()
{
/* initialize lvgl */
lv_init();
/* Don't forget the tick callback */
/* NOTE: OpenGL context must be created before this point */
/* NOTE: the OpenGL context must be created and current before this point */
/* Create a display that flushes to a texture. The OpenGL texture will be created for you */
lv_display_t * texture = lv_opengles_texture_create(WIDTH, HEIGHT);
/* If you already have an OpenGL texture ready, you can use it instead:
* lv_display_t * texture = lv_opengles_texture_create_from_texture_id(WIDTH, HEIGHT, my_texture_id); */
lv_display_t * disp = lv_opengles_texture_create(WIDTH, HEIGHT);
/* Set the display render mode and flush callback
* lv_display_set_render_mode(texture, LV_DISPLAY_RENDER_MODE_FULL);
* lv_display_set_flush_cb(texture, flush_cb); */
/* The texture id, for use in your application's rendering */
unsigned int texture_id = lv_opengles_texture_get_texture_id(disp);
/* get the texture ID for use in your application */
unsigned int texture_id = lv_opengles_texture_get_texture_id(texture);
/* create Widgets on the screen */
lv_demo_widgets();
while (1)
{
while(1) {
uint32_t time_until_next = lv_timer_handler();
if(time_until_next == LV_NO_TIMER_READY) time_until_next = LV_DEF_REFR_PERIOD;
lv_delay_ms(time_until_next);
/* Draw texture_id in your own scene, then swap buffers */
}
return 0;
}
```
## OpenGL Texture Caching Renderer
If you have already allocated a texture yourself, wrap it instead of letting LVGL create one with
<ApiLink name="lv_opengles_texture_create_from_texture_id" />.
<ApiLink name="lv_opengles_texture_get_from_texture_id" /> looks up the display belonging to a texture id,
and <ApiLink name="lv_opengles_texture_reshape" /> resizes a texture and its display together.
There is a renderer in LVGL which caches software-rendered areas as OpenGL textures.
The textures are retrieved from the cache and reused when there is a match.
The performance will be drastically improved in most cases.
Create as many displays as you need, each gets its own texture.
### Drawing the texture yourself
The driver flushes into the texture and stops there; putting it on screen is your job.
<ApiLink name="lv_opengles_render_texture" /> draws a texture into the current framebuffer if you want LVGL
to do it, and <ApiLink name="lv_opengles_render_display_texture" /> does the same for a display's own
texture.
To take over presentation entirely, set your own flush callback. This one works in both the full and direct
render modes:
```c
#define LV_USE_DRAW_OPENGLES 1
static void flush_cb(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map)
{
LV_UNUSED(area);
LV_UNUSED(px_map);
if(lv_display_flush_is_last(disp)) {
const int32_t w = lv_display_get_horizontal_resolution(disp);
const int32_t h = lv_display_get_vertical_resolution(disp);
/* The texture always covers the full screen, even when `area` does not,
* which is the case in the direct render mode */
lv_area_t full_area;
lv_area_set(&full_area, 0, 0, w, h);
unsigned int texture_id = lv_opengles_texture_get_texture_id(disp);
/* Renders into the current context */
lv_opengles_render_texture(texture_id, &full_area, LV_OPA_COVER, w, h, &full_area, false, true);
}
lv_display_flush_ready(disp);
}
```
Register it after creating the display, together with the render mode you want:
```c
lv_display_set_render_mode(disp, LV_DISPLAY_RENDER_MODE_FULL);
lv_display_set_flush_cb(disp, flush_cb);
```
<ApiLink name="lv_opengles_viewport" /> sets the viewport LVGL renders through, if your application needs
LVGL confined to part of the framebuffer.
## OpenGL Texture Caching Renderer
<ApiLink name="LV_USE_DRAW_OPENGLES" /> enables a renderer that caches software-rendered areas as OpenGL
textures and reuses them whenever a later frame asks for the same thing. In most UIs this is a large
improvement, because most of the screen does not change between frames.
### Known Limitations
- Performance will be the same or slightly worse if the drawn areas are never found in the cache
due to Widgets with continuously varying colors or shapes. One example is a label whose color
is set to a random value every frame, as in the "Multiple labels" scene of the benchmark demo.
- Layers with transparent pixels and an overall layer transparency will not blend correctly.
The effect can be observed in the "Containers with opa_layer" scene of the benchmark demo
in the border corners.
- Layers with rotation are not currently supported. Images with rotation are fine.
- Performance is the same or slightly worse when drawn areas never hit the cache — widgets whose color or
shape changes every frame, such as a label recolored randomly each frame (the "Multiple labels" scene of
the benchmark demo).
- Layers that have both transparent pixels and an overall layer opacity do not blend correctly. Visible in
the border corners of the benchmark demo's "Containers with opa_layer" scene.
- Layers with rotation are not supported. Rotated *images* are fine.
<ApiLink name="LV_USE_DRAW_NANOVG" /> avoids these limitations and performs better; prefer it unless you
specifically need the texture cache. See
[NanoVG Draw Unit](/integration/embedded_linux/draw_units/draw_nanovg).
## Support
| Capability | Value |
|---|---|
| Rotation | Hardware (GPU) |
| Runtime resolution change | With <ApiLink name="lv_opengles_texture_reshape" /> |
| Runtime color format change | No |
| Color formats | Derived from <ApiLink name="LV_COLOR_DEPTH" /> |
| Multiple displays / windows | Yes. One display per texture. Windows are not this driver's concern |
| Hardware acceleration | OpenGL ES, via <ApiLink name="LV_USE_DRAW_NANOVG" /> or <ApiLink name="LV_USE_DRAW_OPENGLES" /> |
| Render mode | Direct by default |
| Input | None built in. The window belongs to your application, so feed LVGL input yourself |
## See Also
- [GLFW](/integration/embedded_linux/drivers/glfw) - creates the window and context for you, and can composite these textures
- [EGL](/integration/embedded_linux/drivers/egl) - context creation on embedded targets, including off-screen rendering
- [OpenGL Overview](/integration/embedded_linux/opengl) - how the drivers and draw units fit together
- [NanoVG Draw Unit](/integration/embedded_linux/draw_units/draw_nanovg) - the recommended GPU renderer
- [OpenGL ES Draw Unit](/integration/embedded_linux/draw_units/draw_opengl) - the texture-caching renderer
@@ -5,7 +5,7 @@ description: "The Wayland driver renders LVGL into a Wayland surface and reads k
## Overview
The Wayland driver renders LVGL into a Wayland surface and reads keyboard, pointer and touch input from the compositor.
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.
+44 -1
View File
@@ -12,6 +12,32 @@ flexibility to target anything from a minimal framebuffer setup to a fully GPU-a
The recommended way to get started is the [lv_port_linux](https://github.com/lvgl/lv_port_linux) reference project. It provides a
ready-to-build CMake project with the most common drivers pre-configured, and is the fastest path to a running LVGL application on Linux hardware.
## Choosing a Display Driver
Pick the driver from what your system already has, not from what sounds fastest. On Linux there is usually
exactly one right answer.
| What you have | Use | Why |
|---|---|---|
| A modern kernel with DRM/KMS, no compositor | **[DRM](/integration/embedded_linux/drivers/drm)** | The production default. Real modesetting, and an EGL backend when there is a GPU. |
| Only `/dev/fb`, no DRM | **[fbdev](/integration/embedded_linux/drivers/fbdev)** | Simplest possible path. No libraries, no GPU, no modesetting. |
| A Wayland compositor already running | **[Wayland](/integration/embedded_linux/drivers/wayland)** | SHM everywhere, EGL for the GPU, G2D on NXP i.MX. |
| An X11 desktop, for development | **[X11](/integration/embedded_linux/drivers/X11)** | Window on your dev machine. CPU-only, no rotation. |
| A dev machine, want a window fast | **[SDL](/integration/pc/sdl)** or **[GLFW](/integration/embedded_linux/drivers/glfw)** | Cross-platform. GLFW brings an OpenGL context with it. |
| An OpenGL context you already own | **[OpenGL Driver](/integration/embedded_linux/drivers/opengl_driver)** | Renders into a texture for embedding in your own GL application. |
| No display at all — CI, thumbnails, servers | **[EGL pbuffer](/integration/embedded_linux/drivers/egl)** | The only way to get GPU-rendered frames with no display server. |
Then decide whether you want GPU rendering on top: see
[Rendering acceleration](#rendering-acceleration) below. Enabling a driver's EGL backend without a GPU draw
unit is valid — LVGL renders in software and uses OpenGL only to present the result.
<Callout type="info" title="Input is separate">
Only the Wayland, X11, SDL and GLFW drivers come with input. With DRM or fbdev you pair a display driver
with an input driver yourself — [evdev](/integration/embedded_linux/drivers/evdev) for the simple case,
[libinput](/integration/embedded_linux/drivers/libinput) when you need full keyboard layouts or device
quirks.
</Callout>
## What LVGL supports
### Display and input drivers
@@ -22,10 +48,14 @@ LVGL provides drivers for the most common Linux display and input interfaces:
- **[DRM/KMS](/integration/embedded_linux/drivers/drm)**: modern direct rendering, recommended for production embedded targets
- **[Wayland](/integration/embedded_linux/drivers/wayland)**: suitable for systems running a Wayland compositor, with SHM, EGL, and G2D backends. Also useful for development
- **[X11](/integration/embedded_linux/drivers/X11)**: useful for development and desktop simulation
- **[EGL](/integration/embedded_linux/drivers/egl)**: hardware-accelerated OpenGL ES rendering, usable via DRM or Wayland
- **[EGL](/integration/embedded_linux/drivers/egl)**: the OpenGL ES layer under the DRM, Wayland and SDL hardware-accelerated backends, and the way to render off-screen with no display server
- **[evdev](/integration/embedded_linux/drivers/evdev) / [libinput](/integration/embedded_linux/drivers/libinput)**: touchscreen, mouse, and keyboard input
- **[GLFW](/integration/embedded_linux/drivers/glfw)**: cross-platform windowing, primarily for development
Each driver page carries a **Support** table listing what it can and cannot do — rotation, runtime resolution
and color format changes, multiple displays, hardware acceleration and input. The
[driver index](/integration/embedded_linux/drivers) collects the highlights into one comparison table.
### Rendering acceleration
Beyond the CPU-based software renderer, LVGL supports GPU-accelerated draw units on Linux:
@@ -34,6 +64,13 @@ Beyond the CPU-based software renderer, LVGL supports GPU-accelerated draw units
- **[OpenGL](/integration/embedded_linux/draw_units/draw_opengl)** (`draw_opengl`): direct OpenGL ES rendering, including 3D/glTF support
- **[SDL](/integration/embedded_linux/draw_units/draw_sdl)** (`draw_sdl`): SDL2-based rendering
A draw unit needs a driver that provides an OpenGL context — see
[OpenGL Overview](/integration/embedded_linux/opengl) for which drivers do, and for why the render mode is
fixed on those paths rather than something you choose.
3D and [glTF](/libs/gltf) content requires one of the GPU draw units; the software renderer cannot draw it
at all.
### OS and distribution support
LVGL works with standard Linux distributions and embedded build systems. Dedicated integration guides are available for:
@@ -43,6 +80,12 @@ LVGL works with standard Linux distributions and embedded build systems. Dedicat
- **[Yocto](/integration/embedded_linux/distros/yocto)**: layer-based embedded Linux build system
- **[Torizon](/integration/embedded_linux/distros/torizon)**: container-based embedded Linux platform by Toradex
### Building and dependencies
Enabling a driver's config symbol is also what pulls its libraries into the build — LVGL resolves them with
`find_package`, then `pkg-config`, then by fetching sources where that is possible. See
[Dependency Management](/integration/building/cmake) rather than installing packages by hand.
### Multimedia
LVGL supports hardware-accelerated video playback on Linux through:
+41 -38
View File
@@ -1,60 +1,63 @@
---
title: OpenGL Overview
description: "OpenGL (Open Graphics Library) is a cross-platform, hardware-accelerated graphics API that provides a standardized interface for rendering 2D and 3D graphics. Originally developed by Silicon Graphi..."
description: "How LVGL's OpenGL support fits together on Linux: which display drivers create a context, which draw units render on the GPU, and how the two combine."
---
## Introduction
## Overview
### Overview
OpenGL is a cross-platform, hardware-accelerated graphics API, and OpenGL ES is the subset designed for
embedded and mobile hardware. LVGL uses it on Linux for three reasons: to offload rendering to the GPU, to
embed LVGL content inside existing OpenGL applications, and to render 3D content.
OpenGL (Open Graphics Library) is a cross-platform, hardware-accelerated graphics API that provides a standardized interface for rendering 2D and 3D graphics.
Originally developed by Silicon Graphics in 1992, OpenGL has become one of the most widely adopted graphics APIs across desktop, mobile, and embedded platforms.
LVGL's OpenGL support comes in two halves, and you generally need one of each:
### OpenGL Support in LVGL
- **Display drivers** create and own the OpenGL context, and put the finished frame on screen.
- **Draw units** do the actual rendering with OpenGL instead of the CPU.
The OpenGL integration consists of two main components:
Enabling a driver with an OpenGL backend but no GPU draw unit is valid and sometimes what you want,
LVGL can then render using software and use OpenGL to present the result to the display.
- OpenGL Display Drivers: Handle output by showing the LVGL rendered content on the display, window, or texture in OpenGL-based environments
- OpenGL Draw Units: When enabled, LVGL uses OpenGL for hardware-accelerated rendering operations.
## Drivers That Create a Context
OpenGL support addresses several key use cases:
These drivers create and manage an OpenGL context for you:
1. Performance optimization: Leverage GPU acceleration for rendering.
2. Cross-Platform compatibility: OpenGL's wide platform support enables LVGL UIs on desktop, mobile, and embedded systems.
3. Integration with existing applications: Embed LVGL UI elements into games, visualization tools, or other OpenGL applications.
| Driver | How to get the OpenGL path |
|---|---|
| [DRM](/integration/embedded_linux/drivers/drm) | `LV_LINUX_DRM_BACKEND_EGL` |
| [Wayland](/integration/embedded_linux/drivers/wayland) | `LV_WAYLAND_USE_EGL` |
| [SDL](/integration/pc/sdl) | `LV_SDL_BACKEND_EGL` |
| [GLFW](/integration/embedded_linux/drivers/glfw) | Always |
## OpenGL Driver Options
Every one of them except GLFW uses [EGL](/integration/embedded_linux/drivers/egl) underneath.
The following drivers can be used and will automatically create and maintain an OpenGL context.
<Callout type="warning" title="GLFW and EGL are mutually exclusive">
<ApiLink name="LV_USE_GLFW" /> cannot be combined with <ApiLink name="LV_USE_EGL" />
</Callout>
- [SDL driver](/integration/pc/sdl)
- [Wayland driver](/integration/embedded_linux/drivers/wayland)
- [DRM driver](/integration/embedded_linux/drivers/drm)
- [GLFW driver](/integration/embedded_linux/drivers/glfw)
Additionally there's also a generic [OpenGL Driver](/integration/embedded_linux/drivers/opengl_driver)
which can be use to embed LVGL in an application that already owns the context.
All drivers except the GLFW driver use EGL (Embedded-System Graphics Library) under the hood.
## Draw Units
Additionally, LVGL provides a generic OpenGL driver which the user may use to embed
OpenGL textures in existing OpenGL applications. This driver assumes an existing OpenGL context
and generates textures with hardware acceleration for integration into custom graphics pipelines.
| Draw unit | Notes |
|---|---|
| [NanoVG](/integration/embedded_linux/draw_units/draw_nanovg) | **Recommended.** Rasterizes on the GPU, best performance and widest feature coverage |
| [OpenGL ES](/integration/embedded_linux/draw_units/draw_opengl) | Caches software-rendered areas as GPU textures. Slower, fewer features. |
For complete implementation details, see [OpenGL driver](/integration/embedded_linux/drivers/opengl_driver).
## 3D and glTF
## OpenGL Draw Unit Options
The glTF module loads and renders 3D models per the glTF 2.0 specification, with PBR materials, animations and
camera control. It needs OpenGL ES 2.0 with some extra extensions.
When one of these draw units is enabled as well as one of the supporting drivers, LVGL will use OpenGL for
hardware-accelerated rendering.
3D support comes from the GL renderers so a GPU draw unit is required.
- [NanoVG](/integration/embedded_linux/draw_units/draw_nanovg)
- [OpenGL](/integration/embedded_linux/draw_units/draw_opengl)
For details, see [glTF](/libs/gltf).
[NanoVG](/integration/embedded_linux/draw_units/draw_nanovg) is the recommended choice. It makes better use of the GPU
and supports more LVGL features. OpenGL is also available but the performance is worse and it supports fewer features.
## See Also
## 3D/glTF Support
The glTF module provides support for loading and rendering 3D models using the glTF 2.0 specification within LVGL applications.
This support requires OpenGL ES 2.0 with some extra extensions and provides comprehensive 3D rendering capabilities including PBR materials,
animations, and interactive camera controls for embedded 3D visualization.
For complete implementation details, see [glTF](/libs/gltf).
- [NanoVG Draw Unit](/integration/embedded_linux/draw_units/draw_nanovg) - the recommended GPU renderer
- [EGL](/integration/embedded_linux/drivers/egl) - the layer under most OpenGL paths on Linux
- [DRM](/integration/embedded_linux/drivers/drm) - production driver with an EGL backend
- [GLFW](/integration/embedded_linux/drivers/glfw) - quickest OpenGL setup for development
- [OpenGL Driver](/integration/embedded_linux/drivers/opengl_driver) - embedding LVGL in an existing context
- [glTF](/libs/gltf) - 3D model rendering