LvglPkg - Exploring Modern Firmware User Interfaces with LVGL and EDK II

Hello everyone,

I would like to share a project that I have been working on recently:

LvglPkg is an EDK II package that integrates the LVGL graphics library into the UEFI environment.

The project started as an experiment to investigate whether modern embedded UI frameworks could be used to build richer firmware user interfaces beyond the traditional SetupBrowser approach.

While EDK II already provides several mechanisms for building setup applications, most firmware interfaces today are still constrained by workflows and design concepts that have remained largely unchanged for many years. My goal was to explore what a modern firmware UI stack might look like when built on top of a widely adopted graphics framework.

Current work includes:

  • LVGL integration into EDK II
  • Graphics output backend implementation for UEFI
  • Input handling integration
  • LVGL application support within firmware
  • Experiments with modern firmware user interface concepts

As part of this effort, I have also been experimenting with an LVGL-based alternative to DisplayEngineDxe. The intention is not to replace existing EDK II solutions, but to explore whether a more flexible rendering and widget framework could simplify the development of richer firmware interfaces, especially on higher-resolution displays.

Some areas that I find particularly interesting are:

  • Modern BIOS/Setup user interface concepts
  • Responsive layouts for different display resolutions
  • Improved visual consistency across firmware applications
  • Reusable UI components and widgets
  • Alternative rendering architectures for firmware environments

The repository is still under active development and should be considered experimental. There are many areas that can be improved, and I would greatly appreciate feedback from the community regarding architecture, integration approaches, portability, performance considerations, and potential use cases.

Repository:

Feedback, suggestions, and contributions are welcome.

Thanks,

Hamit Can Karaca

1 Like

Hi @hamitcan99,

Congrats, it’s an interesting project.

1 Like

It looks really nice!

Did you know that LVGL also has a built-in UEFI driver?

Is it something you can use?

Hi @kisvegabor, thanks so much, and yes! Your message actually nudged me to adopt it. Since then I’ve migrated the display backend to LVGL’s built-in UEFI driver: lv_conf.h now sets LV_USE_UEFI = 1, and I call lv_uefi_init() + lv_uefi_display_create(), which replaced the ~80-line hand-written GOP-flush glue. Works great and is one less thing for me to maintain.

For input it’s a more nuanced story. I tried the built-in indevs too, but ended up keeping custom callbacks for two concrete reasons on our QEMU/OVMF setup:

  1. Pointer / mouse wheel — the built-in absolute-pointer indev discards the Z axis, so wheel scrolling is lost. On UEFI, EFI_ABSOLUTE_POINTER_PROTOCOL’s GetState() is single-consumer (it clears StateChanged on each read), so a separate wheel poller would race the cursor reads. I kept one custom read that pulls X/Y, buttons, and CurrentZ in a single GetState() call.

  2. Keyboard — the built-in lv_uefi_simple_text_input_indev queues PRESSED+RELEASED for each keystroke within the same LVGL tick, so indev_keypad_proc never sees the key as held and long_press_repeat throttling never engages. With EFI auto-repeat that caused runaway navigation. The custom keypad_read returns PRESSED while the EFI input buffer is non-empty and RELEASED when it drains which matches UEFI’s press-only (no key-up) model.

So: built-in display driver, adopted and happily recommended. Built-in input, I’d love to use it, but the discarded Z axis and the PRESSED/RELEASED-in-one-tick behavior are the two blockers for me right now. Happy to share a minimal repro if that’d be useful for the driver.