Your First Program
A complete LVGL Safe application in one file - framebuffer, display, screen, widgets, and the main loop you own.
Every LVGL Safe program has the same shape: give a display a framebuffer you own, init a screen, create widgets onto it, configure them by writing struct fields, then loop over input, render, and flush.
#include "ls_display.h"
#include "ls_screen.h"
#include "ls_render.h"
#include "ls_input.h"
#include "ls_button.h"
#include "ls_label.h"
#define HOR_RES 800
#define VER_RES 480
/* Generated ahead of time by scripts/font_conv.py — there is no built-in font */
extern ls_font_t my_font;
/* The framebuffer is owned by the application — the library allocates nothing */
static ls_frame_buffer_color_t fb[HOR_RES * VER_RES];
/* Widgets are caller-owned structs that must outlive the screen */
static ls_display_t display;
static ls_screen_t screen;
static ls_button_t button;
static ls_label_t label;
int main(void)
{
/* 1. Display */
if(ls_display_init(&display) != LS_ERROR_CODE_OK) return 1;
display.hor_res = HOR_RES;
display.ver_res = VER_RES;
display.stride = HOR_RES * LS_BYTE_PER_PIXEL; /* bytes per line */
display.frame_buffer = fb;
/* 2. Screen */
if(ls_screen_init(&screen) != LS_ERROR_CODE_OK) return 1;
display.screen_active = &screen;
/* 3. Widgets — created onto the screen, then configured by direct field access */
if(ls_button_create(&screen, &button) != LS_ERROR_CODE_OK) return 1;
button.x = 100;
button.y = 100;
button.width = 200;
button.height = 75;
button.bg_color_normal = LS_COLOR_HEX(0xffaaaa);
button.bg_color_pressed = LS_COLOR_HEX(0xff0000);
if(ls_label_create(&screen, &label) != LS_ERROR_CODE_OK) return 1;
label.x = 120;
label.y = 105;
label.width = 160;
label.align = LS_LABEL_ALIGN_CENTER;
label.font = &my_font;
label.text = "Hello\nLVGL Safe";
/* 4. Main loop — owned by the application */
while(1) {
/* touch_x/touch_y/state come from your own touch driver */
if(ls_indev_process(&display, touch_x, touch_y, state, prev_state) != LS_ERROR_CODE_OK) return 1;
prev_state = state;
if(ls_render(&display) != LS_ERROR_CODE_OK) return 1;
my_flush_framebuffer_to_screen(fb); /* your display driver */
}
}Two things to notice: every fallible call returns an ls_error_code_t that is
checked at the call site, and every struct - framebuffer included - has static
storage duration. That is the shape of every LVGL Safe program.
A complete, runnable SDL2 integration (framebuffer flush plus mouse/touch/keyboard input) is in examples/basic_example/main.c.
Next steps
- How it works - what the display, screen, and widget layers actually do, and the rules the lifecycle imposes.
- API reference - every field of every type, with the defaults each widget starts from.
- Assets - generating the font this example assumes, plus images and translations.
Last updated on
Build and Run
Install the dependencies, build the two example applications, and run them on your desktop in an SDL2 window - no target hardware needed.
How It Works
Displays, screens, and widgets; the lifecycle rules that follow from never allocating; how rendering and colors work; and how input reaches a widget.