diff --git a/.gitmodules b/.gitmodules index bc21444..c10b07a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "External/edk2"] path = External/edk2 url = https://github.com/tianocore/edk2.git +[submodule "External/lvgl"] + path = External/lvgl + url = https://github.com/lvgl/lvgl.git diff --git a/CHANGELOG.md b/CHANGELOG.md index 971d923..88fd351 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,67 @@ this file as both a release log and a lightweight development progress record. ### Added +- DisplayEngine per-opcode control affordances: each editable FormBrowser + statement now shows a distinct, non-semantic cue glyph keyed on its control + kind — checkbox box, numeric `+`, one-of/choice `▼`, ordered-list up/down, + string caret, date/time segment ticks, and reference/action `▶`. Cues are + drawn purely from renderer primitives, so they render identically through the + GOP and LVGL backends, and are composited by a post-text overlay + (`ModernDisplayDrawStatementRowCue`, called at the end of `DisplayOneMenu`) so + the native highlight text background no longer overpaints them. Read-only and + grayed/disabled rows intentionally get no cue. edk2 keeps all HII/value/storage + ownership; the cue helpers are smoke-guarded against + `ConfigAccess`/`RouteConfig`/`ExtractConfig`/`SetVariable`/`HiiGetString`. +- Unified control-affordance vocabulary across the front-page App and the + in-setup DisplayEngine: the per-control cue shapes now live in one shared + function, `ModernUiEngineDrawControlCue` (`ModernUiEngineLib`), keyed on + `MODERN_UI_VALUE_TYPE` and built from a new shared `ModernUiFillTriangle` + renderer primitive plus `ModernUiFillRect`/`ModernUiStrokeRect`. The App value + lane (`ModernUiEngineDrawValue`) now paints the matching cue just left of the + value box, and the DisplayEngine row overlay maps its row kind to the value + type (`ModernDisplayKindToValueType`) and delegates to the same function — so a + checkbox/drop-down/numeric/date-time/password/string/ordered-list/action + control reads identically in both surfaces. The DisplayEngine's former private + `ModernDisplayDrawKindCue`/`ModernDisplayCueTriangle` shape copies are removed. +- `build-ovmf-x64.sh` gains `MODERN_SETUP_DEMO_DRIVER_SAMPLE=0|1` (default `0`): + when `1`, edk2 `DriverSampleDxe` is added to the OVMF overlay (reachable via + Device Manager) as a control-rich VFR for exercising the DisplayEngine control + affordances. Off by default; never in a shipped overlay. +- Experimental LVGL renderer-swap mode (`experimental/lvgl-spike` branch only): + `MODERN_SETUP_DISPLAY_ENGINE=lvgl` now keeps the existing + `ModernDisplayEngineDxe` (all FormBrowser/HII/interaction ownership unchanged) + but resolves the `ModernUiRendererLib` class to a new LVGL-backed + implementation, `Library/ModernUiLvglRendererLib`. Every primitive composites + through LVGL's software draw pipeline into a persistent full-screen XRGB8888 + shadow canvas, then BLTs only the touched region to GOP: geometry (fills, + borders, panels, rows, cards, progress, value boxes, drop-downs) via + `lv_draw_rect`, ASCII text via `lv_draw_label` (Montserrat), and non-ASCII + (CJK) runs via the firmware HII font composited into the same canvas (LVGL + bundles no CJK coverage). The library keeps the exact `ModernUiRenderer.h` API + and the original 8 px-cell text-measurement model, so layouts in the display + engine and `ModernSetupApp` are unchanged. Verified on OVMF X64: the live UiApp + FormBrowser front page renders end-to-end through LVGL. `LvglCoreLib` + + `IntrinsicLib` are force-linked only into the lvgl-mode overlay; never in a + default overlay. The earlier standalone `LvglDisplayEngineDxe` remains as a + separate from-scratch reference and is no longer wired into the OVMF overlay. + CJK is handled by compositing the package's embedded bitmap glyphs + (`ModernUiGlyphs.c`) into the LVGL canvas, with the firmware HII font as a + secondary fallback for code points without an embedded glyph. `lvgl` mode also + composes with `MODERN_SETUP_REPLACE_UIAPP=1`: the `ModernSetupApp` front-page + shell then renders through the same LVGL pipeline (IntrinsicLib is force-linked + into the app component in lvgl mode). Verified on OVMF X64 — both the UiApp + FormBrowser front page and the full ModernSetupApp dashboard (zh labels + + English values + all chrome) render end-to-end through LVGL. +- Experimental LVGL rendering-backend spike (`experimental/lvgl-spike` branch only; + `Experimental/LvglSpikePkg`, never in a default overlay or `ModernSetupApp`). + Pins `External/lvgl` at a tagged v9.5.0-derived baseline and validates that + LVGL core + software renderer + its upstream UEFI port build under edk2 GCC on + a hard architecture: `LvglSpikeProbe.efi` compiles for LoongArch64 and was run + on real LoongArch hardware, drawing an LVGL UI straight to GOP (standalone-app + path, not via any DisplayEngine). The ~3-site LoongArch64/RISC-V64 UEFI + arch-gate change was contributed upstream and is now in the pinned baseline, so + `External/lvgl` is consumed pristine (no local patch, no build-time patching). + See `Experimental/LvglSpikePkg/README.md`. - ModernSetupApp header clock now updates live while the front page is idle. The app loop arms a one-second periodic timer and waits on it alongside the keyboard/pointer sources, repainting only the header clock text in place on diff --git a/Docs/MODULE_BOUNDARIES.md b/Docs/MODULE_BOUNDARIES.md index 47920d6..c01120f 100644 --- a/Docs/MODULE_BOUNDARIES.md +++ b/Docs/MODULE_BOUNDARIES.md @@ -30,6 +30,53 @@ ModernSetupPkg is easiest to maintain when each layer keeps a small, concrete co | HII bridge | Stay isolated as experimental parser/adapter research | Become the default compatibility path, force writes, treat unsupported opcodes as safe | | Platform/CI | Build scripts, overlays, QEMU/manual validation, release packaging | Hide behavior changes inside scripts or docs-only PRs | +## Renderer backends + +`ModernUiRendererLib` is a class with two interchangeable implementations selected +by the build (`MODERN_SETUP_DISPLAY_ENGINE`); both expose the identical +`Include/ModernUi/ModernUiRenderer.h` API: + +- **GOP backend** (`Library/ModernUiRendererLib`) — the default. Draws straight to + `EFI_GRAPHICS_OUTPUT_PROTOCOL` (BLT fills, HII-font text, built-in bitmap glyphs). +- **LVGL backend** (`Library/ModernUiLvglRendererLib`, `=lvgl`, experimental) — every + primitive is composited by LVGL's software renderer into a persistent full-screen + XRGB8888 **shadow canvas**, then only the touched region is BLT'd to GOP. Geometry + uses `lv_draw_rect`; ASCII text uses `lv_draw_label` (Montserrat); non-ASCII (CJK) + runs use the package's embedded bitmap glyphs (`ModernUiGlyphs.c`), with the + firmware HII font as a secondary fallback (LVGL ships no CJK font). + +Both backends share, verbatim, the backend-agnostic surface +`Library/ModernUiRendererLib/ModernUiRendererCommon.c` (geometry compositions, text +measurement, themed widgets, the `ModernUiFillTriangle` shape primitive) and the +glyph table `ModernUiGlyphs.c`. A backend provides only three primitives — +`ModernUiRendererInit`, `ModernUiFillRect`, `ModernUiDrawText` — declared with the +shared helpers in `ModernUiRendererInternal.h`. Keep the 8 px-cell / +fixed-CJK-cell measurement model identical across backends so caller layouts are +backend-stable. + +## Control-affordance vocabulary + +The per-control cue shapes (checkbox box, one-of `▼`, ordered-list up/down, +numeric `+`, date/time segment ticks, password dots, string caret, action `▶`) +are defined **once**, in `ModernUiEngineDrawControlCue` (`ModernUiEngineLib`), +keyed on `MODERN_UI_VALUE_TYPE` and built only from renderer primitives +(`ModernUiFillRect` / `ModernUiStrokeRect` / `ModernUiFillTriangle`). Both +surfaces that show controls delegate to it, so a given control type reads +identically in each: + +- **Front-page App value lane** — `ModernUiEngineDrawValue` paints the cue just + left of the value box. +- **In-setup DisplayEngine row** — `ModernDisplayDrawStatementRowCue` maps its + `MODERN_DISPLAY_FORM_ROW_KIND` to the value type via + `ModernDisplayKindToValueType` and draws the cue at the row's right edge, + **after** native FormBrowser prints the row text (so it composites on top + instead of being overpainted). It classifies already-materialized statement + data only and never touches HII/config/storage; read-only and disabled rows + get no cue. + +Do not add a second copy of these shapes in either consumer — extend the shared +vocabulary and add the value-type mapping instead. + ## What not to do - App code must not parse IFR or write HII varstores. Real setup pages should enter native FormBrowser with `EFI_FORM_BROWSER2_PROTOCOL.SendForm()`. diff --git a/Experimental/LvglSpikePkg/Include/Library/LvglCoreLib.h b/Experimental/LvglSpikePkg/Include/Library/LvglCoreLib.h new file mode 100644 index 0000000..1f00545 --- /dev/null +++ b/Experimental/LvglSpikePkg/Include/Library/LvglCoreLib.h @@ -0,0 +1,18 @@ +/** @file + LvglCoreLib -- convenience umbrella header for the shared LVGL build (core + + software renderer + upstream UEFI port). Consumers include this to get the LVGL + and lv_uefi APIs. experimental/lvgl-spike only. + + Copyright (c) 2026, MarsDoge. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef LVGL_CORE_LIB_H_ +#define LVGL_CORE_LIB_H_ + +#include "lvgl/lvgl.h" +#include "lvgl/src/drivers/uefi/lv_uefi.h" +#include "lvgl/src/drivers/uefi/lv_uefi_context.h" +#include "lvgl/src/drivers/uefi/lv_uefi_display.h" + +#endif // LVGL_CORE_LIB_H_ diff --git a/Experimental/LvglSpikePkg/Include/Library/LvglLib.h b/Experimental/LvglSpikePkg/Include/Library/LvglLib.h new file mode 100644 index 0000000..031dbe7 --- /dev/null +++ b/Experimental/LvglSpikePkg/Include/Library/LvglLib.h @@ -0,0 +1,69 @@ +/** @file + LvglLib class with APIs from the openssl project + + Copyright (c) 2024, Yang Gang. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef __LVGL_LIB_H__ +#define __LVGL_LIB_H__ + +#if defined(_MSC_VER) +#pragma warning(disable: 4244) // workaround for misc/lv_color.h(355), remove after lvgl update +#endif + +#include + +// +// Custom LVGL key codes for EFI function keys F1-F12. +// EFI SCAN_F1..F12 (0x000B-0x0016) overlap with LVGL reserved key values, +// so we remap them to a safe range above 0x0100. +// +#define LV_KEY_F1 0x0101U +#define LV_KEY_F2 0x0102U +#define LV_KEY_F3 0x0103U +#define LV_KEY_F4 0x0104U +#define LV_KEY_F5 0x0105U +#define LV_KEY_F6 0x0106U +#define LV_KEY_F7 0x0107U +#define LV_KEY_F8 0x0108U +#define LV_KEY_F9 0x0109U +#define LV_KEY_F10 0x010AU +#define LV_KEY_F11 0x010BU +#define LV_KEY_F12 0x010CU + +typedef +VOID +(EFIAPI *EFI_LVGL_APP_FUNCTION)( + VOID + ); + +EFI_STATUS +EFIAPI +UefiLvglInit ( + VOID + ); + +EFI_STATUS +EFIAPI +UefiLvglDeinit ( + VOID + ); + +EFI_STATUS +EFIAPI +UefiLvglAppRegister ( + IN EFI_LVGL_APP_FUNCTION AppRegister + ); + +/** + Drain the EFI keyboard buffer and reset the LVGL keypad indev state so that + no pending key-press leaks into the next event loop. +**/ +void +lv_uefi_keypad_drain ( + void + ); + +#endif \ No newline at end of file diff --git a/Experimental/LvglSpikePkg/Include/LvglTheme.h b/Experimental/LvglSpikePkg/Include/LvglTheme.h new file mode 100644 index 0000000..0b8f8f8 --- /dev/null +++ b/Experimental/LvglSpikePkg/Include/LvglTheme.h @@ -0,0 +1,157 @@ +/** @file + LvglTheme.h - User-customizable theme for LvglDisplayEngineDxe. + + Edit the macros in this file and rebuild to restyle the LVGL HII form + renderer. All colors, fonts, padding, and radius values used by + LvglFormRenderer.c flow through these macros. + + Fonts referenced here must be enabled in LvglPkg/lv_conf.h + (LV_FONT_MONTSERRAT_*). +**/ + +#ifndef LVGL_THEME_H_ +#define LVGL_THEME_H_ + +#include + +// +// Background colors (24-bit RGB; passed to lv_color_hex()) +// +// NovaCore palette: near-black canvas, blue-tinted panels, accent blue. +// +#define THEME_COLOR_BG_SCREEN 0x0F141A +#define THEME_COLOR_BG_PANEL 0x182328 +#define THEME_COLOR_BG_PANEL_ELEV 0x23304A +#define THEME_COLOR_BG_DIALOG 0x1B2A3A +#define THEME_COLOR_BG_SEPARATOR 0x2A3A52 + +// +// Text colors +// +#define THEME_COLOR_TEXT_TITLE 0xFFFFFF +#define THEME_COLOR_TEXT_PRIMARY 0xEAF2FF +#define THEME_COLOR_TEXT_SECONDARY 0x9FB0C8 +#define THEME_COLOR_TEXT_DISABLED 0x6A7B92 +#define THEME_COLOR_TEXT_LABEL THEME_COLOR_TEXT_PRIMARY +#define THEME_COLOR_TEXT_POPUP THEME_COLOR_TEXT_PRIMARY + +// +// Row colors (default vs focused). +// +#define THEME_COLOR_ROW_BG THEME_COLOR_BG_PANEL +#define THEME_COLOR_ROW_TEXT THEME_COLOR_TEXT_PRIMARY +#define THEME_COLOR_ROW_TEXT_FOCUSED 0xFFFFFF +#define THEME_COLOR_ROW_TEXT_DISABLED THEME_COLOR_TEXT_DISABLED + +// +// Accent (focus ring + active selection). +// +#define THEME_COLOR_ACCENT 0x1A6FD8 +#define THEME_COLOR_ACCENT_HOVER 0x2A85F0 +#define THEME_COLOR_SUCCESS 0x2BC79F +#define THEME_COLOR_WARNING 0xE9A23B +#define THEME_COLOR_DANGER 0xE25555 + +// +// Accent (subtitle / focus highlight) — LV_PALETTE_* enum value +// +#define THEME_ACCENT_PALETTE LV_PALETTE_BLUE + +// +// Fonts — must be enabled in lv_conf.h +// +#define THEME_FONT_TITLE (&lv_font_montserrat_20) +#define THEME_FONT_BODY (&lv_font_montserrat_16) +#define THEME_FONT_POPUP (&lv_font_montserrat_16) + +// +// Spacing & shape (pixels) +// +#define THEME_PAD_SCREEN 16 +#define THEME_PAD_PANEL 8 +#define THEME_PAD_ROW 4 +#define THEME_PAD_ROW_TIGHT 2 +#define THEME_PAD_DIALOG 16 +#define THEME_PAD_DIALOG_ROW_GAP 10 +#define THEME_PAD_DIALOG_COL_GAP 8 +#define THEME_PAD_SCREEN_ROW_GAP 6 +#define THEME_PAD_PANEL_ROW_GAP 4 +#define THEME_PAD_LABEL_TOP 8 +#define THEME_RADIUS 8 +#define THEME_OVERLAY_OPA LV_OPA_50 + +// +// Aptio-style chrome (header / subtitle / footer / wallpaper). +// Used by LvglDisplayEngineDxe/LvglAptioChrome.c. +// +#define THEME_COLOR_HEADER_BG_TOP 0x0B1218 +#define THEME_COLOR_HEADER_BG_BOTTOM 0x16263F +#define THEME_COLOR_HEADER_TEXT 0xFFFFFF +#define THEME_COLOR_HEADER_DIM 0x9FB0C8 +#define THEME_COLOR_SUBTITLE_BG 0x182328 +#define THEME_COLOR_SUBTITLE_TEXT 0xEAF2FF +#define THEME_COLOR_FOOTER_BG 0x0B1218 +#define THEME_COLOR_FOOTER_TEXT 0xEAF2FF +#define THEME_COLOR_FOOTER_DIM 0x6A7B92 +#define THEME_COLOR_HIGHLIGHT_ROW THEME_COLOR_ACCENT + +#define THEME_HEADER_HEIGHT 44 +#define THEME_SUBTITLE_HEIGHT 28 +#define THEME_FOOTER_HEIGHT 32 + +// +// Chrome — help pane (right column). +// +#define THEME_HELPPANE_WIDTH 240 +#define THEME_PAD_HELPPANE_X 14 +#define THEME_PAD_HELPPANE_Y 12 +#define THEME_PAD_HELPPANE_ROW_GAP 8 + +// +// Chrome — header / subtitle / content / footer paddings. +// +#define THEME_PAD_HEADER_X 16 +#define THEME_PAD_SUBTITLE_X 16 +#define THEME_PAD_CONTENT_LEFT 24 +#define THEME_PAD_CONTENT_RIGHT 16 +#define THEME_PAD_CONTENT_Y 12 +#define THEME_PAD_CONTENT_ROW_GAP 6 +#define THEME_PAD_FOOTER_X 12 +#define THEME_PAD_FOOTER_Y 2 +#define THEME_PAD_FOOTER_COL_GAP 6 + +// +// Footer "chip" widgets (one per registered hotkey + nav primitives). +// +#define THEME_PAD_CHIP_X 8 +#define THEME_PAD_CHIP_Y 4 +#define THEME_PAD_CHIP_COL_GAP 6 +#define THEME_RADIUS_CHIP 4 + +// +// Form rows (StyleRow). +// +#define THEME_RADIUS_ROW 4 +#define THEME_PAD_ROW_X 16 +#define THEME_PAD_ROW_Y 10 +#define THEME_BORDER_FOCUS 3 + +// +// Generic 1 px separator/border thickness used between panes. +// +#define THEME_BORDER_PANE 1 + +// +// Confirm/discard dialog card width as a percentage of the screen. +// +#define THEME_DIALOG_WIDTH_PCT 50 + +// +// Static strings shown in the chrome (no SMBIOS lookup yet). +// +#define APTIO_HEADER_TITLE "EDK II Boot Manager" +#define APTIO_HEADER_VENDOR "UEFI v2.90 | EDK II" +#define APTIO_FOOTER_NAV_HINTS LV_SYMBOL_UP LV_SYMBOL_DOWN " Select Enter Confirm Esc Exit F9 Defaults F10 Save" +#define APTIO_FOOTER_SYSINFO "QEMU x86_64 | OVMF" + +#endif // LVGL_THEME_H_ diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/EscExitHandler.c b/Experimental/LvglSpikePkg/Library/LvglLib/EscExitHandler.c new file mode 100644 index 0000000..20d9454 --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/EscExitHandler.c @@ -0,0 +1,155 @@ +#include "LvglLibCommon.h" +#include + + +UINT8 mExitBtnYes = EXIT_BTN_NONE; +STATIC BOOLEAN mPopUpActive = FALSE; +VOID *mEscNotifyHandle = NULL; + +static lv_group_t *g_default_group = NULL; + +void exit_confirm_cb(lv_event_t * e) +{ + + lv_obj_t * btn = lv_event_get_target_obj(e); + lv_obj_t * label = lv_obj_get_child(btn, 0); + LV_UNUSED(label); + + const char * txt = lv_label_get_text(label); + + if (strcmp(txt, "Yes") == 0) { + mExitBtnYes = EXIT_BTN_YES; + } else if (strcmp(txt, "No") == 0) { + mExitBtnYes = EXIT_BTN_NO; + } else { + mExitBtnYes = EXIT_BTN_NONE; + } + + lv_obj_t * msgbox = lv_event_get_user_data(e); + + lv_group_t *msgbox_group = (lv_group_t *)lv_obj_get_user_data(msgbox); + + lv_indev_t *indev = NULL; + for(;;) { + indev = lv_indev_get_next(indev); + if(!indev) { + break; + } + + if(lv_indev_get_type(indev) == LV_INDEV_TYPE_KEYPAD) { + lv_indev_set_group(indev, g_default_group); + } + } + + lv_group_del(msgbox_group); + + lv_msgbox_close(msgbox); + + mPopUpActive = false; +} + + +EFI_STATUS +EFIAPI +EscKeyNotifyCallBack ( + IN EFI_KEY_DATA *KeyData + ) +{ + if (mPopUpActive) { + return EFI_ALREADY_STARTED; + } + + mPopUpActive = true; + + g_default_group = lv_group_get_default(); + + lv_group_t *msgbox_group = lv_group_create(); + + lv_obj_t * mbox = lv_msgbox_create(NULL); + + lv_msgbox_add_title(mbox, "Exit?"); + lv_msgbox_add_text(mbox, "Exit this APP?"); + + lv_obj_t * btn_yes = lv_msgbox_add_footer_button(mbox, "Yes"); + lv_obj_add_event_cb(btn_yes, exit_confirm_cb, LV_EVENT_CLICKED, mbox); + lv_group_add_obj(msgbox_group, btn_yes); + + lv_obj_t * btn_no = lv_msgbox_add_footer_button(mbox, "No"); + lv_obj_add_event_cb(btn_no, exit_confirm_cb, LV_EVENT_CLICKED, mbox); + lv_group_add_obj(msgbox_group, btn_no); + + lv_group_focus_obj(btn_yes); + + lv_indev_t *indev = NULL; + for(;;) { + indev = lv_indev_get_next(indev); + if(!indev) { + break; + } + + if(lv_indev_get_type(indev) == LV_INDEV_TYPE_KEYPAD) { + lv_indev_set_group(indev, msgbox_group); + } + } + + lv_obj_set_user_data(mbox, msgbox_group); + + lv_obj_align(mbox, LV_ALIGN_CENTER, 0, 0); + + lv_obj_t * bg = lv_obj_get_parent(mbox); + lv_obj_set_style_bg_opa(bg, LV_OPA_70, 0); + lv_obj_set_style_bg_color(bg, lv_palette_main(LV_PALETTE_GREY), 0); + + return EFI_SUCCESS; + +} + +VOID +EFIAPI +LvglUefiEscExitRegister ( + VOID + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInEx; + EFI_KEY_DATA KeyData; + + Status = gBS->HandleProtocol(gST->ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID**)&TextInEx); + if (EFI_ERROR(Status)) { + return; + } + + ZeroMem(&KeyData, sizeof(KeyData)); + KeyData.Key.ScanCode = SCAN_ESC; + Status = TextInEx->RegisterKeyNotify ( + TextInEx, + &KeyData, + EscKeyNotifyCallBack, + &mEscNotifyHandle + ); + +} + +VOID +EFIAPI +LvglUefiEscExitUnregister ( + VOID + ) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TextInEx; + + if (mEscNotifyHandle == NULL) { + return; + } + + Status = gBS->HandleProtocol(gST->ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID**)&TextInEx); + if (EFI_ERROR(Status)) { + return; + } + + Status = TextInEx->UnregisterKeyNotify (TextInEx, mEscNotifyHandle); + if (!EFI_ERROR(Status)) { + mEscNotifyHandle = NULL; + } +} diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/LvglCoreLib.inf b/Experimental/LvglSpikePkg/Library/LvglLib/LvglCoreLib.inf new file mode 100644 index 0000000..81c00b9 --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/LvglCoreLib.inf @@ -0,0 +1,499 @@ +## @file +# LvglCoreLib -- LVGL core + software renderer + upstream UEFI port as an EDK2 +# library class, so multiple modules (the app probe and LvglDisplayEngineDxe) +# can share one LVGL build. experimental/lvgl-spike only. +# +# Copyright (c) 2026, MarsDoge. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = LvglCoreLib + FILE_GUID = 6F3B1C0A-2D54-4E19-9A7C-1B8E0D22AB03 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = LvglCoreLib + +[Sources] + lv_conf.h + lvgl/src/core/lv_group.c + lvgl/src/core/lv_obj.c + lvgl/src/core/lv_obj_class.c + lvgl/src/core/lv_obj_draw.c + lvgl/src/core/lv_obj_event.c + lvgl/src/core/lv_obj_id_builtin.c + lvgl/src/core/lv_obj_pos.c + lvgl/src/core/lv_obj_property.c + lvgl/src/core/lv_obj_scroll.c + lvgl/src/core/lv_obj_style.c + lvgl/src/core/lv_obj_style_gen.c + lvgl/src/core/lv_obj_tree.c + lvgl/src/core/lv_observer.c + lvgl/src/core/lv_refr.c + lvgl/src/debugging/monkey/lv_monkey.c + lvgl/src/debugging/sysmon/lv_sysmon.c + lvgl/src/debugging/test/lv_test_display.c + lvgl/src/debugging/test/lv_test_fs.c + lvgl/src/debugging/test/lv_test_helpers.c + lvgl/src/debugging/test/lv_test_indev.c + lvgl/src/debugging/test/lv_test_indev_gesture.c + lvgl/src/debugging/test/lv_test_screenshot_compare.c + lvgl/src/debugging/vg_lite_tvg/vg_lite_matrix.c + lvgl/src/display/lv_display.c + lvgl/src/draw/convert/helium/lv_draw_buf_convert_helium.c + lvgl/src/draw/convert/lv_draw_buf_convert.c + lvgl/src/draw/convert/neon/lv_draw_buf_convert_neon.c + lvgl/src/draw/dma2d/lv_draw_dma2d.c + lvgl/src/draw/dma2d/lv_draw_dma2d_fill.c + lvgl/src/draw/dma2d/lv_draw_dma2d_img.c + lvgl/src/draw/espressif/ppa/lv_draw_ppa_buf.c + lvgl/src/draw/espressif/ppa/lv_draw_ppa.c + lvgl/src/draw/espressif/ppa/lv_draw_ppa_fill.c + lvgl/src/draw/espressif/ppa/lv_draw_ppa_img.c + lvgl/src/draw/eve/lv_draw_eve_arc.c + lvgl/src/draw/eve/lv_draw_eve.c + lvgl/src/draw/eve/lv_draw_eve_fill.c + lvgl/src/draw/eve/lv_draw_eve_image.c + lvgl/src/draw/eve/lv_draw_eve_letter.c + lvgl/src/draw/eve/lv_draw_eve_line.c + lvgl/src/draw/eve/lv_draw_eve_ram_g.c + lvgl/src/draw/eve/lv_draw_eve_triangle.c + lvgl/src/draw/eve/lv_eve.c + lvgl/src/draw/lv_draw_3d.c + lvgl/src/draw/lv_draw_arc.c + lvgl/src/draw/lv_draw_blur.c + lvgl/src/draw/lv_draw_buf.c + lvgl/src/draw/lv_draw.c + lvgl/src/draw/lv_draw_image.c + lvgl/src/draw/lv_draw_label.c + lvgl/src/draw/lv_draw_line.c + lvgl/src/draw/lv_draw_mask.c + lvgl/src/draw/lv_draw_rect.c + lvgl/src/draw/lv_draw_triangle.c + lvgl/src/draw/lv_draw_vector.c + lvgl/src/draw/lv_image_decoder.c + lvgl/src/draw/nanovg/lv_draw_nanovg_3d.c + lvgl/src/draw/nanovg/lv_draw_nanovg_arc.c + lvgl/src/draw/nanovg/lv_draw_nanovg_border.c + lvgl/src/draw/nanovg/lv_draw_nanovg_box_shadow.c + lvgl/src/draw/nanovg/lv_draw_nanovg.c + lvgl/src/draw/nanovg/lv_draw_nanovg_fill.c + lvgl/src/draw/nanovg/lv_draw_nanovg_grad.c + lvgl/src/draw/nanovg/lv_draw_nanovg_image.c + lvgl/src/draw/nanovg/lv_draw_nanovg_label.c + lvgl/src/draw/nanovg/lv_draw_nanovg_layer.c + lvgl/src/draw/nanovg/lv_draw_nanovg_line.c + lvgl/src/draw/nanovg/lv_draw_nanovg_mask_rect.c + lvgl/src/draw/nanovg/lv_draw_nanovg_triangle.c + lvgl/src/draw/nanovg/lv_draw_nanovg_vector.c + lvgl/src/draw/nanovg/lv_nanovg_fbo_cache.c + lvgl/src/draw/nanovg/lv_nanovg_image_cache.c + lvgl/src/draw/nanovg/lv_nanovg_utils.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_arc.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_border.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_fill.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_img.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_label.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_layer.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_line.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_stm32_hal.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_triangle.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_utils.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_vector.c + lvgl/src/draw/nema_gfx/lv_nema_gfx_path.c + lvgl/src/draw/nxp/g2d/lv_draw_buf_g2d.c + lvgl/src/draw/nxp/g2d/lv_draw_g2d.c + lvgl/src/draw/nxp/g2d/lv_draw_g2d_fill.c + lvgl/src/draw/nxp/g2d/lv_draw_g2d_img.c + lvgl/src/draw/nxp/g2d/lv_g2d_buf_map.c + lvgl/src/draw/nxp/g2d/lv_g2d_utils.c + lvgl/src/draw/nxp/pxp/lv_draw_buf_pxp.c + lvgl/src/draw/nxp/pxp/lv_draw_pxp.c + lvgl/src/draw/nxp/pxp/lv_draw_pxp_fill.c + lvgl/src/draw/nxp/pxp/lv_draw_pxp_img.c + lvgl/src/draw/nxp/pxp/lv_draw_pxp_layer.c + lvgl/src/draw/nxp/pxp/lv_pxp_cfg.c + lvgl/src/draw/nxp/pxp/lv_pxp_osa.c + lvgl/src/draw/nxp/pxp/lv_pxp_utils.c + lvgl/src/draw/opengles/lv_draw_opengles.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_arc.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_border.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_fill.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_image.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_label.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_line.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_mask_rectangle.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_triangle.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_utils.c + lvgl/src/draw/sdl/lv_draw_sdl.c + lvgl/src/draw/snapshot/lv_snapshot.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_a8.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_al88.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_argb8888.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_argb8888_premultiplied.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_i1.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_l8.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_rgb565.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_rgb565_swapped.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_rgb888.c + lvgl/src/draw/sw/blend/neon/lv_draw_sw_blend_neon_to_rgb565.c + lvgl/src/draw/sw/blend/neon/lv_draw_sw_blend_neon_to_rgb888.c + lvgl/src/draw/sw/blend/riscv_v/lv_draw_sw_blend_riscv_v_to_rgb888.c + lvgl/src/draw/sw/lv_draw_sw_arc.c + lvgl/src/draw/sw/lv_draw_sw_blur.c + lvgl/src/draw/sw/lv_draw_sw_border.c + lvgl/src/draw/sw/lv_draw_sw_box_shadow.c + lvgl/src/draw/sw/lv_draw_sw.c + lvgl/src/draw/sw/lv_draw_sw_fill.c + lvgl/src/draw/sw/lv_draw_sw_grad.c + lvgl/src/draw/sw/lv_draw_sw_img.c + lvgl/src/draw/sw/lv_draw_sw_letter.c + lvgl/src/draw/sw/lv_draw_sw_line.c + lvgl/src/draw/sw/lv_draw_sw_mask.c + lvgl/src/draw/sw/lv_draw_sw_mask_rect.c + lvgl/src/draw/sw/lv_draw_sw_transform.c + lvgl/src/draw/sw/lv_draw_sw_triangle.c + lvgl/src/draw/sw/lv_draw_sw_utils.c + lvgl/src/draw/sw/lv_draw_sw_vector.c + lvgl/src/draw/vg_lite/lv_draw_buf_vg_lite.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_arc.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_border.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_box_shadow.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_fill.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_img.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_label.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_layer.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_line.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_mask_rect.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_triangle.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_vector.c + lvgl/src/draw/vg_lite/lv_vg_lite_bitmap_font_cache.c + lvgl/src/draw/vg_lite/lv_vg_lite_decoder.c + lvgl/src/draw/vg_lite/lv_vg_lite_grad.c + lvgl/src/draw/vg_lite/lv_vg_lite_math.c + lvgl/src/draw/vg_lite/lv_vg_lite_path.c + lvgl/src/draw/vg_lite/lv_vg_lite_pending.c + lvgl/src/draw/vg_lite/lv_vg_lite_stroke.c + lvgl/src/draw/vg_lite/lv_vg_lite_utils.c + lvgl/src/drivers/display/drm/lv_linux_drm.c + lvgl/src/drivers/display/drm/lv_linux_drm_common.c + lvgl/src/drivers/display/drm/lv_linux_drm_egl.c + lvgl/src/drivers/display/fb/lv_linux_fbdev.c + lvgl/src/drivers/display/ft81x/lv_ft81x.c + lvgl/src/drivers/display/ili9341/lv_ili9341.c + lvgl/src/drivers/display/lcd/lv_lcd_generic_mipi.c + lvgl/src/drivers/display/nv3007/lv_nv3007.c + lvgl/src/drivers/display/nxp_elcdif/lv_nxp_elcdif.c + lvgl/src/drivers/display/renesas_glcdc/lv_renesas_glcdc.c + lvgl/src/drivers/display/st7735/lv_st7735.c + lvgl/src/drivers/display/st7789/lv_st7789.c + lvgl/src/drivers/display/st7796/lv_st7796.c + lvgl/src/drivers/display/st_ltdc/lv_st_ltdc.c + lvgl/src/drivers/draw/eve/lv_draw_eve_display.c + lvgl/src/drivers/evdev/lv_evdev.c + lvgl/src/drivers/libinput/lv_libinput.c + lvgl/src/drivers/libinput/lv_xkb.c + lvgl/src/drivers/nuttx/lv_nuttx_cache.c + lvgl/src/drivers/nuttx/lv_nuttx_entry.c + lvgl/src/drivers/nuttx/lv_nuttx_fbdev.c + lvgl/src/drivers/nuttx/lv_nuttx_image_cache.c + lvgl/src/drivers/nuttx/lv_nuttx_lcd.c + lvgl/src/drivers/nuttx/lv_nuttx_libuv.c + lvgl/src/drivers/nuttx/lv_nuttx_mouse.c + lvgl/src/drivers/nuttx/lv_nuttx_profiler.c + lvgl/src/drivers/nuttx/lv_nuttx_touchscreen.c + lvgl/src/drivers/opengles/assets/lv_opengles_shader.c + lvgl/src/drivers/opengles/glad/src/egl.c + lvgl/src/drivers/opengles/glad/src/gl.c + lvgl/src/drivers/opengles/glad/src/gles2.c + lvgl/src/drivers/opengles/lv_opengles_debug.c + lvgl/src/drivers/opengles/lv_opengles_driver.c + lvgl/src/drivers/opengles/lv_opengles_egl.c + lvgl/src/drivers/opengles/lv_opengles_glfw.c + lvgl/src/drivers/opengles/lv_opengles_texture.c + lvgl/src/drivers/opengles/opengl_shader/lv_opengl_shader_manager.c + lvgl/src/drivers/opengles/opengl_shader/lv_opengl_shader_program.c + lvgl/src/drivers/qnx/lv_qnx.c + lvgl/src/drivers/sdl/lv_sdl_egl.c + lvgl/src/drivers/sdl/lv_sdl_keyboard.c + lvgl/src/drivers/sdl/lv_sdl_mouse.c + lvgl/src/drivers/sdl/lv_sdl_mousewheel.c + lvgl/src/drivers/sdl/lv_sdl_sw.c + lvgl/src/drivers/sdl/lv_sdl_texture.c + lvgl/src/drivers/sdl/lv_sdl_window.c + lvgl/src/drivers/uefi/lv_uefi_context.c + lvgl/src/drivers/uefi/lv_uefi_display.c + lvgl/src/drivers/uefi/lv_uefi_indev_keyboard.c + lvgl/src/drivers/uefi/lv_uefi_indev_pointer.c + lvgl/src/drivers/uefi/lv_uefi_indev_touch.c + lvgl/src/drivers/uefi/lv_uefi_private.c + lvgl/src/drivers/wayland/lv_wayland.c + lvgl/src/drivers/wayland/lv_wl_egl_backend.c + lvgl/src/drivers/wayland/lv_wl_g2d_backend.c + lvgl/src/drivers/wayland/lv_wl_keyboard.c + lvgl/src/drivers/wayland/lv_wl_pointer.c + lvgl/src/drivers/wayland/lv_wl_seat.c + lvgl/src/drivers/wayland/lv_wl_shm_backend.c + lvgl/src/drivers/wayland/lv_wl_touch.c + lvgl/src/drivers/wayland/lv_wl_window.c + lvgl/src/drivers/wayland/lv_wl_xdg_shell.c + lvgl/src/drivers/windows/lv_windows_context.c + lvgl/src/drivers/windows/lv_windows_display.c + lvgl/src/drivers/windows/lv_windows_input.c + lvgl/src/drivers/x11/lv_x11_display.c + lvgl/src/drivers/x11/lv_x11_input.c + lvgl/src/font/binfont_loader/lv_binfont_loader.c + lvgl/src/font/fmt_txt/lv_font_fmt_txt.c + lvgl/src/font/font_manager/lv_font_manager.c + lvgl/src/font/font_manager/lv_font_manager_recycle.c + lvgl/src/font/imgfont/lv_imgfont.c + lvgl/src/font/lv_font.c + lvgl/src/font/lv_font_dejavu_16_persian_hebrew.c + lvgl/src/font/lv_font_montserrat_10.c + lvgl/src/font/lv_font_montserrat_12.c + lvgl/src/font/lv_font_montserrat_14_aligned.c + lvgl/src/font/lv_font_montserrat_14.c + lvgl/src/font/lv_font_montserrat_16.c + lvgl/src/font/lv_font_montserrat_18.c + lvgl/src/font/lv_font_montserrat_20.c + lvgl/src/font/lv_font_montserrat_22.c + lvgl/src/font/lv_font_montserrat_24.c + lvgl/src/font/lv_font_montserrat_26.c + lvgl/src/font/lv_font_montserrat_28.c + lvgl/src/font/lv_font_montserrat_28_compressed.c + lvgl/src/font/lv_font_montserrat_30.c + lvgl/src/font/lv_font_montserrat_32.c + lvgl/src/font/lv_font_montserrat_34.c + lvgl/src/font/lv_font_montserrat_36.c + lvgl/src/font/lv_font_montserrat_38.c + lvgl/src/font/lv_font_montserrat_40.c + lvgl/src/font/lv_font_montserrat_42.c + lvgl/src/font/lv_font_montserrat_44.c + lvgl/src/font/lv_font_montserrat_46.c + lvgl/src/font/lv_font_montserrat_48.c + lvgl/src/font/lv_font_montserrat_8.c + lvgl/src/font/lv_font_source_han_sans_sc_14_cjk.c + lvgl/src/font/lv_font_source_han_sans_sc_16_cjk.c + lvgl/src/font/lv_font_unscii_16.c + lvgl/src/font/lv_font_unscii_8.c + lvgl/src/indev/lv_gridnav.c + lvgl/src/indev/lv_indev.c + lvgl/src/indev/lv_indev_gesture.c + lvgl/src/indev/lv_indev_scroll.c + lvgl/src/layouts/flex/lv_flex.c + lvgl/src/layouts/grid/lv_grid.c + lvgl/src/layouts/lv_layout.c + lvgl/src/libs/barcode/code128.c + lvgl/src/libs/barcode/lv_barcode.c + lvgl/src/libs/bin_decoder/lv_bin_decoder.c + lvgl/src/libs/bmp/lv_bmp.c + lvgl/src/libs/ffmpeg/lv_ffmpeg.c + lvgl/src/libs/freetype/lv_freetype.c + lvgl/src/libs/freetype/lv_freetype_glyph.c + lvgl/src/libs/freetype/lv_freetype_image.c + lvgl/src/libs/freetype/lv_freetype_outline.c + lvgl/src/libs/freetype/lv_ftsystem.c + lvgl/src/libs/frogfs/src/decomp_raw.c + lvgl/src/libs/frogfs/src/frogfs.c + lvgl/src/libs/fsdrv/lv_fs_cbfs.c + lvgl/src/libs/fsdrv/lv_fs_fatfs.c + lvgl/src/libs/fsdrv/lv_fs_frogfs.c + lvgl/src/libs/fsdrv/lv_fs_littlefs.c + lvgl/src/libs/fsdrv/lv_fs_memfs.c + lvgl/src/libs/fsdrv/lv_fs_posix.c + lvgl/src/libs/fsdrv/lv_fs_stdio.c + lvgl/src/libs/fsdrv/lv_fs_uefi.c + lvgl/src/libs/fsdrv/lv_fs_win32.c + lvgl/src/libs/FT800-FT813/EVE_commands.c + lvgl/src/libs/FT800-FT813/EVE_supplemental.c + lvgl/src/libs/gif/gif.c + lvgl/src/libs/gltf/gltf_environment/lv_gltf_ibl_sampler.c + lvgl/src/libs/gltf/gltf_view/assets/chromatic.c + lvgl/src/libs/gltf/gltf_view/assets/lv_gltf_view_shader.c + lvgl/src/libs/gltf/math/lv_3dmath.c + lvgl/src/libs/gstreamer/lv_gstreamer.c + lvgl/src/libs/libjpeg_turbo/lv_libjpeg_turbo.c + lvgl/src/libs/libpng/lv_libpng.c + lvgl/src/libs/libwebp/lv_libwebp.c + lvgl/src/libs/lodepng/lodepng.c + lvgl/src/libs/lodepng/lv_lodepng.c + lvgl/src/libs/lz4/lz4.c + lvgl/src/libs/nanovg/nanovg.c + lvgl/src/libs/qrcode/lv_qrcode.c + lvgl/src/libs/qrcode/qrcodegen.c + lvgl/src/libs/rle/lv_rle.c + lvgl/src/libs/rlottie/lv_rlottie.c + lvgl/src/libs/svg/lv_svg.c + lvgl/src/libs/svg/lv_svg_decoder.c + lvgl/src/libs/svg/lv_svg_parser.c + lvgl/src/libs/svg/lv_svg_render.c + lvgl/src/libs/svg/lv_svg_token.c + lvgl/src/libs/tiny_ttf/lv_tiny_ttf.c + lvgl/src/libs/tjpgd/lv_tjpgd.c + lvgl/src/libs/tjpgd/tjpgd.c + lvgl/src/libs/vg_lite_driver/lv_vg_lite_hal/lv_vg_lite_hal.c + lvgl/src/libs/vg_lite_driver/lv_vg_lite_hal/vg_lite_os.c + lvgl/src/libs/vg_lite_driver/VGLiteKernel/vg_lite_kernel.c + lvgl/src/libs/vg_lite_driver/VGLite/vg_lite.c + lvgl/src/libs/vg_lite_driver/VGLite/vg_lite_image.c + lvgl/src/libs/vg_lite_driver/VGLite/vg_lite_matrix.c + lvgl/src/libs/vg_lite_driver/VGLite/vg_lite_path.c + lvgl/src/libs/vg_lite_driver/VGLite/vg_lite_stroke.c + lvgl/src/lv_init.c + lvgl/src/misc/cache/class/lv_cache_lru_ll.c + lvgl/src/misc/cache/class/lv_cache_lru_rb.c + lvgl/src/misc/cache/class/lv_cache_sc_da.c + lvgl/src/misc/cache/instance/lv_image_cache.c + lvgl/src/misc/cache/instance/lv_image_header_cache.c + lvgl/src/misc/cache/lv_cache.c + lvgl/src/misc/cache/lv_cache_entry.c + lvgl/src/misc/lv_anim.c + lvgl/src/misc/lv_anim_timeline.c + lvgl/src/misc/lv_area.c + lvgl/src/misc/lv_array.c + lvgl/src/misc/lv_async.c + lvgl/src/misc/lv_bidi.c + lvgl/src/misc/lv_circle_buf.c + lvgl/src/misc/lv_color.c + lvgl/src/misc/lv_color_op.c + lvgl/src/misc/lv_event.c + lvgl/src/misc/lv_fs.c + lvgl/src/misc/lv_grad.c + lvgl/src/misc/lv_iter.c + lvgl/src/misc/lv_ll.c + lvgl/src/misc/lv_log.c + lvgl/src/misc/lv_lru.c + lvgl/src/misc/lv_math.c + lvgl/src/misc/lv_matrix.c + lvgl/src/misc/lv_palette.c + lvgl/src/misc/lv_pending.c + lvgl/src/misc/lv_profiler_builtin.c + lvgl/src/misc/lv_profiler_builtin_posix.c + lvgl/src/misc/lv_rb.c + lvgl/src/misc/lv_style.c + lvgl/src/misc/lv_style_gen.c + lvgl/src/misc/lv_templ.c + lvgl/src/misc/lv_text_ap.c + lvgl/src/misc/lv_text.c + lvgl/src/misc/lv_timer.c + lvgl/src/misc/lv_tree.c + lvgl/src/misc/lv_utils.c + lvgl/src/osal/lv_cmsis_rtos2.c + lvgl/src/osal/lv_freertos.c + lvgl/src/osal/lv_linux.c + lvgl/src/osal/lv_mqx.c + lvgl/src/osal/lv_os.c + lvgl/src/osal/lv_os_none.c + lvgl/src/osal/lv_pthread.c + lvgl/src/osal/lv_rtthread.c + lvgl/src/osal/lv_sdl2.c + lvgl/src/osal/lv_windows.c + lvgl/src/others/file_explorer/lv_file_explorer.c + lvgl/src/others/fragment/lv_fragment.c + lvgl/src/others/fragment/lv_fragment_manager.c + lvgl/src/others/translation/lv_translation.c + lvgl/src/stdlib/builtin/lv_mem_core_builtin.c + lvgl/src/stdlib/builtin/lv_sprintf_builtin.c + lvgl/src/stdlib/builtin/lv_string_builtin.c + lvgl/src/stdlib/builtin/lv_tlsf.c + lvgl/src/stdlib/clib/lv_mem_core_clib.c + lvgl/src/stdlib/clib/lv_sprintf_clib.c + lvgl/src/stdlib/clib/lv_string_clib.c + lvgl/src/stdlib/lv_mem.c + lvgl/src/stdlib/micropython/lv_mem_core_micropython.c + lvgl/src/stdlib/rtthread/lv_mem_core_rtthread.c + lvgl/src/stdlib/rtthread/lv_sprintf_rtthread.c + lvgl/src/stdlib/rtthread/lv_string_rtthread.c + lvgl/src/stdlib/uefi/lv_mem_core_uefi.c + lvgl/src/themes/default/lv_theme_default.c + lvgl/src/themes/lv_theme.c + lvgl/src/themes/mono/lv_theme_mono.c + lvgl/src/themes/simple/lv_theme_simple.c + lvgl/src/tick/lv_tick.c + lvgl/src/widgets/3dtexture/lv_3dtexture.c + lvgl/src/widgets/animimage/lv_animimage.c + lvgl/src/widgets/arclabel/lv_arclabel.c + lvgl/src/widgets/arc/lv_arc.c + lvgl/src/widgets/bar/lv_bar.c + lvgl/src/widgets/button/lv_button.c + lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.c + lvgl/src/widgets/calendar/lv_calendar.c + lvgl/src/widgets/calendar/lv_calendar_chinese.c + lvgl/src/widgets/calendar/lv_calendar_header_arrow.c + lvgl/src/widgets/calendar/lv_calendar_header_dropdown.c + lvgl/src/widgets/canvas/lv_canvas.c + lvgl/src/widgets/chart/lv_chart.c + lvgl/src/widgets/checkbox/lv_checkbox.c + lvgl/src/widgets/dropdown/lv_dropdown.c + lvgl/src/widgets/gif/lv_gif.c + lvgl/src/widgets/imagebutton/lv_imagebutton.c + lvgl/src/widgets/image/lv_image.c + lvgl/src/widgets/ime/lv_ime_pinyin.c + lvgl/src/widgets/keyboard/lv_keyboard.c + lvgl/src/widgets/label/lv_label.c + lvgl/src/widgets/led/lv_led.c + lvgl/src/widgets/line/lv_line.c + lvgl/src/widgets/list/lv_list.c + lvgl/src/widgets/lottie/lv_lottie.c + lvgl/src/widgets/menu/lv_menu.c + lvgl/src/widgets/msgbox/lv_msgbox.c + lvgl/src/widgets/objx_templ/lv_objx_templ.c + lvgl/src/widgets/property/lv_animimage_properties.c + lvgl/src/widgets/property/lv_arc_properties.c + lvgl/src/widgets/property/lv_bar_properties.c + lvgl/src/widgets/property/lv_buttonmatrix_properties.c + lvgl/src/widgets/property/lv_chart_properties.c + lvgl/src/widgets/property/lv_checkbox_properties.c + lvgl/src/widgets/property/lv_dropdown_properties.c + lvgl/src/widgets/property/lv_image_properties.c + lvgl/src/widgets/property/lv_keyboard_properties.c + lvgl/src/widgets/property/lv_label_properties.c + lvgl/src/widgets/property/lv_led_properties.c + lvgl/src/widgets/property/lv_line_properties.c + lvgl/src/widgets/property/lv_menu_properties.c + lvgl/src/widgets/property/lv_obj_properties.c + lvgl/src/widgets/property/lv_roller_properties.c + lvgl/src/widgets/property/lv_scale_properties.c + lvgl/src/widgets/property/lv_slider_properties.c + lvgl/src/widgets/property/lv_span_properties.c + lvgl/src/widgets/property/lv_spinbox_properties.c + lvgl/src/widgets/property/lv_spinner_properties.c + lvgl/src/widgets/property/lv_style_properties.c + lvgl/src/widgets/property/lv_switch_properties.c + lvgl/src/widgets/property/lv_table_properties.c + lvgl/src/widgets/property/lv_tabview_properties.c + lvgl/src/widgets/property/lv_textarea_properties.c + lvgl/src/widgets/roller/lv_roller.c + lvgl/src/widgets/scale/lv_scale.c + lvgl/src/widgets/slider/lv_slider.c + lvgl/src/widgets/span/lv_span.c + lvgl/src/widgets/spinbox/lv_spinbox.c + lvgl/src/widgets/spinner/lv_spinner.c + lvgl/src/widgets/switch/lv_switch.c + lvgl/src/widgets/table/lv_table.c + lvgl/src/widgets/tabview/lv_tabview.c + lvgl/src/widgets/textarea/lv_textarea.c + lvgl/src/widgets/tileview/lv_tileview.c + lvgl/src/widgets/win/lv_win.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + LvglSpikePkg/LvglSpikePkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + MemoryAllocationLib + UefiBootServicesTableLib + DebugLib + PrintLib + +[BuildOptions] + # See README: LV_CONF_INCLUDE_SIMPLE (symlinked lvgl/), warning relaxers. + GCC:*_*_*_CC_FLAGS = -DLV_CONF_INCLUDE_SIMPLE -Wno-error -Wno-format -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-function -Wno-sign-compare -Wno-type-limits -Wno-unused-parameter -Wno-array-bounds -Wno-maybe-uninitialized -Wno-incompatible-pointer-types diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/LvglLib.c b/Experimental/LvglSpikePkg/Library/LvglLib/LvglLib.c new file mode 100644 index 0000000..52e6550 --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/LvglLib.c @@ -0,0 +1,176 @@ + +#include "LvglLibCommon.h" + +#include + +extern UINT8 mExitBtnYes; + +BOOLEAN mTickSupport = FALSE; +STATIC BOOLEAN mUefiLvglInitDone = FALSE; + +#if LV_USE_LOG +static void efi_lv_log_print(lv_log_level_t level, const char * buf) +{ + static const int priority[LV_LOG_LEVEL_NUM] = { + DEBUG_VERBOSE|DEBUG_INFO|DEBUG_WARN|DEBUG_ERROR, DEBUG_INFO, DEBUG_WARN, DEBUG_ERROR, DEBUG_INFO + }; + + DebugPrint (priority[level], "[LVGL] %a\n", buf); +} +#endif + + +static uint32_t tick_get_cb(void) +{ + return (UINT32) DivU64x32 (GetTimeInNanoSecond (GetPerformanceCounter()), 1000 * 1000); +} + +VOID +EFIAPI +UefiLvglTickInit ( + VOID + ) +{ + if (GetPerformanceCounter()) { + mTickSupport = TRUE; + lv_tick_set_cb(tick_get_cb); + } +} + + +EFI_STATUS +EFIAPI +UefiLvglInit ( + VOID + ) +{ + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + EFI_STATUS Status; + UINTN Width, Heigth; + + if (mUefiLvglInitDone) { + return EFI_SUCCESS; + } + + Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput); + if (EFI_ERROR(Status)) { + return EFI_UNSUPPORTED; + } + + lv_init(); + +#if 0 + // Need real TimerLib + UefiLvglTickInit(); +#endif + +#if LV_USE_LOG + lv_log_register_print_cb (efi_lv_log_print); +#endif + + Width = GraphicsOutput->Mode->Info->HorizontalResolution; + Heigth = GraphicsOutput->Mode->Info->VerticalResolution; + + lv_disp_t *display = lv_uefi_disp_create (Width, Heigth); + + lv_port_indev_init(display); + + mUefiLvglInitDone = TRUE; + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +UefiLvglDeinit ( + VOID + ) +{ + + if (!mUefiLvglInitDone) { + return EFI_SUCCESS; + } + + LvglUefiEscExitUnregister (); + + lv_deinit(); + + lv_port_indev_close(); + + gST->ConOut->ClearScreen (gST->ConOut); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, 0); + gST->ConOut->EnableCursor (gST->ConOut, TRUE); + + mUefiLvglInitDone = FALSE; + + return EFI_SUCCESS; +} + +EFI_STATUS +EFIAPI +UefiLvglAppRegister ( + IN EFI_LVGL_APP_FUNCTION AppRegister + ) +{ + if (!mUefiLvglInitDone) { + if (UefiLvglInit() != EFI_SUCCESS) { + return EFI_UNSUPPORTED; + } + } + + if (AppRegister != NULL) { + gST->ConOut->ClearScreen (gST->ConOut); + gST->ConOut->EnableCursor (gST->ConOut, FALSE); + + // call user GUI APP + AppRegister(); + + LvglUefiEscExitRegister (); + + while (1) { + if (mExitBtnYes == EXIT_BTN_YES) { + break; + } + + lv_timer_handler(); + + gBS->Stall (10 * 1000); + if (!mTickSupport) { + lv_tick_inc(10); + } + } + } else { + UefiLvglDeinit(); + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + + +EFI_STATUS +EFIAPI +LvglLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + + UefiLvglInit (); + + return EFI_SUCCESS; +} + + +EFI_STATUS +EFIAPI +LvglLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + + UefiLvglDeinit(); + + return EFI_SUCCESS; +} diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/LvglLib.inf b/Experimental/LvglSpikePkg/Library/LvglLib/LvglLib.inf new file mode 100644 index 0000000..89af6be --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/LvglLib.inf @@ -0,0 +1,543 @@ +## @file +# LVGL as UEFI Library Class. +# +# Copyright (c) 2024, Yang Gang. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = LvglLib + FILE_GUID = DB82E73C-46EF-4490-9E6E-70E7AE65CCBB + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = LvglLib|DXE_DRIVER UEFI_APPLICATION + CONSTRUCTOR = LvglLibConstructor + DESTRUCTOR = LvglLibDestructor + +[Sources] + LvglLib.c + LvglUefiPort.h + LvglUefiPort.c + + EscExitHandler.c + lv_uefi_display.c + MouseCursorIcon.c + lv_port_indev.c + lv_conf.h + +# Wrapper header files start # + limits.h + stdint.h + stdio.h + stdarg.h + stddef.h + stdlib.h + string.h + config.h +# Wrapper header files end # + +# Upstream Lvgl code + lvgl/src/lv_init.c + lvgl/src/core/lv_group.c + lvgl/src/core/lv_obj.c + lvgl/src/core/lv_obj_class.c + lvgl/src/core/lv_obj_draw.c + lvgl/src/core/lv_obj_event.c + lvgl/src/core/lv_obj_id_builtin.c + lvgl/src/core/lv_obj_pos.c + lvgl/src/core/lv_obj_property.c + lvgl/src/core/lv_obj_scroll.c + lvgl/src/core/lv_obj_style.c + lvgl/src/core/lv_obj_style_gen.c + lvgl/src/core/lv_obj_tree.c + lvgl/src/core/lv_observer.c + lvgl/src/core/lv_refr.c + lvgl/src/debugging/monkey/lv_monkey.c + lvgl/src/debugging/sysmon/lv_sysmon.c + lvgl/src/debugging/test/lv_test_display.c + lvgl/src/debugging/test/lv_test_fs.c + lvgl/src/debugging/test/lv_test_helpers.c + lvgl/src/debugging/test/lv_test_indev.c + lvgl/src/debugging/test/lv_test_indev_gesture.c + lvgl/src/debugging/test/lv_test_screenshot_compare.c + lvgl/src/debugging/vg_lite_tvg/vg_lite_matrix.c + lvgl/src/display/lv_display.c + lvgl/src/draw/lv_draw.c + lvgl/src/draw/lv_draw_3d.c + lvgl/src/draw/lv_draw_arc.c + lvgl/src/draw/lv_draw_blur.c + lvgl/src/draw/lv_draw_buf.c + lvgl/src/draw/lv_draw_image.c + lvgl/src/draw/lv_draw_label.c + lvgl/src/draw/lv_draw_line.c + lvgl/src/draw/lv_draw_mask.c + lvgl/src/draw/lv_draw_rect.c + lvgl/src/draw/lv_draw_triangle.c + lvgl/src/draw/lv_draw_vector.c + lvgl/src/draw/lv_image_decoder.c + lvgl/src/draw/convert/lv_draw_buf_convert.c + lvgl/src/draw/convert/helium/lv_draw_buf_convert_helium.c + lvgl/src/draw/convert/neon/lv_draw_buf_convert_neon.c + lvgl/src/draw/dma2d/lv_draw_dma2d.c + lvgl/src/draw/dma2d/lv_draw_dma2d_fill.c + lvgl/src/draw/dma2d/lv_draw_dma2d_img.c + lvgl/src/draw/espressif/ppa/lv_draw_ppa.c + lvgl/src/draw/espressif/ppa/lv_draw_ppa_buf.c + lvgl/src/draw/espressif/ppa/lv_draw_ppa_fill.c + lvgl/src/draw/espressif/ppa/lv_draw_ppa_img.c + lvgl/src/draw/eve/lv_draw_eve.c + lvgl/src/draw/eve/lv_draw_eve_arc.c + lvgl/src/draw/eve/lv_draw_eve_fill.c + lvgl/src/draw/eve/lv_draw_eve_image.c + lvgl/src/draw/eve/lv_draw_eve_letter.c + lvgl/src/draw/eve/lv_draw_eve_line.c + lvgl/src/draw/eve/lv_draw_eve_ram_g.c + lvgl/src/draw/eve/lv_draw_eve_triangle.c + lvgl/src/draw/eve/lv_eve.c + lvgl/src/draw/nanovg/lv_draw_nanovg.c + lvgl/src/draw/nanovg/lv_draw_nanovg_3d.c + lvgl/src/draw/nanovg/lv_draw_nanovg_arc.c + lvgl/src/draw/nanovg/lv_draw_nanovg_border.c + lvgl/src/draw/nanovg/lv_draw_nanovg_box_shadow.c + lvgl/src/draw/nanovg/lv_draw_nanovg_fill.c + lvgl/src/draw/nanovg/lv_draw_nanovg_grad.c + lvgl/src/draw/nanovg/lv_draw_nanovg_image.c + lvgl/src/draw/nanovg/lv_draw_nanovg_label.c + lvgl/src/draw/nanovg/lv_draw_nanovg_layer.c + lvgl/src/draw/nanovg/lv_draw_nanovg_line.c + lvgl/src/draw/nanovg/lv_draw_nanovg_mask_rect.c + lvgl/src/draw/nanovg/lv_draw_nanovg_triangle.c + lvgl/src/draw/nanovg/lv_draw_nanovg_vector.c + lvgl/src/draw/nanovg/lv_nanovg_fbo_cache.c + lvgl/src/draw/nanovg/lv_nanovg_image_cache.c + lvgl/src/draw/nanovg/lv_nanovg_utils.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_arc.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_border.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_fill.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_img.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_label.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_layer.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_line.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_stm32_hal.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_triangle.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_utils.c + lvgl/src/draw/nema_gfx/lv_draw_nema_gfx_vector.c + lvgl/src/draw/nema_gfx/lv_nema_gfx_path.c + lvgl/src/draw/nxp/g2d/lv_draw_buf_g2d.c + lvgl/src/draw/nxp/g2d/lv_draw_g2d.c + lvgl/src/draw/nxp/g2d/lv_draw_g2d_fill.c + lvgl/src/draw/nxp/g2d/lv_draw_g2d_img.c + lvgl/src/draw/nxp/g2d/lv_g2d_buf_map.c + lvgl/src/draw/nxp/g2d/lv_g2d_utils.c + lvgl/src/draw/nxp/pxp/lv_draw_buf_pxp.c + lvgl/src/draw/nxp/pxp/lv_draw_pxp.c + lvgl/src/draw/nxp/pxp/lv_draw_pxp_fill.c + lvgl/src/draw/nxp/pxp/lv_draw_pxp_img.c + lvgl/src/draw/nxp/pxp/lv_draw_pxp_layer.c + lvgl/src/draw/nxp/pxp/lv_pxp_cfg.c + lvgl/src/draw/nxp/pxp/lv_pxp_osa.c + lvgl/src/draw/nxp/pxp/lv_pxp_utils.c + lvgl/src/draw/opengles/lv_draw_opengles.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_arc.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_border.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_fill.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_image.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_label.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_line.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_mask_rectangle.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_triangle.c + lvgl/src/draw/renesas/dave2d/lv_draw_dave2d_utils.c + lvgl/src/draw/sdl/lv_draw_sdl.c + lvgl/src/draw/snapshot/lv_snapshot.c + lvgl/src/draw/sw/lv_draw_sw.c + lvgl/src/draw/sw/lv_draw_sw_arc.c + lvgl/src/draw/sw/lv_draw_sw_blur.c + lvgl/src/draw/sw/lv_draw_sw_border.c + lvgl/src/draw/sw/lv_draw_sw_box_shadow.c + lvgl/src/draw/sw/lv_draw_sw_fill.c + lvgl/src/draw/sw/lv_draw_sw_grad.c + lvgl/src/draw/sw/lv_draw_sw_img.c + lvgl/src/draw/sw/lv_draw_sw_letter.c + lvgl/src/draw/sw/lv_draw_sw_line.c + lvgl/src/draw/sw/lv_draw_sw_mask.c + lvgl/src/draw/sw/lv_draw_sw_mask_rect.c + lvgl/src/draw/sw/lv_draw_sw_transform.c + lvgl/src/draw/sw/lv_draw_sw_triangle.c + lvgl/src/draw/sw/lv_draw_sw_utils.c + lvgl/src/draw/sw/lv_draw_sw_vector.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_a8.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_al88.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_argb8888.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_argb8888_premultiplied.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_i1.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_l8.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_rgb565.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_rgb565_swapped.c + lvgl/src/draw/sw/blend/lv_draw_sw_blend_to_rgb888.c + lvgl/src/draw/sw/blend/helium/lv_blend_helium.S + lvgl/src/draw/sw/blend/neon/lv_draw_sw_blend_neon_to_rgb565.c + lvgl/src/draw/sw/blend/neon/lv_draw_sw_blend_neon_to_rgb888.c + lvgl/src/draw/sw/blend/riscv_v/lv_draw_sw_blend_riscv_v_to_rgb888.c + lvgl/src/draw/vg_lite/lv_draw_buf_vg_lite.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_arc.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_border.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_box_shadow.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_fill.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_img.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_label.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_layer.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_line.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_mask_rect.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_triangle.c + lvgl/src/draw/vg_lite/lv_draw_vg_lite_vector.c + lvgl/src/draw/vg_lite/lv_vg_lite_bitmap_font_cache.c + lvgl/src/draw/vg_lite/lv_vg_lite_decoder.c + lvgl/src/draw/vg_lite/lv_vg_lite_grad.c + lvgl/src/draw/vg_lite/lv_vg_lite_math.c + lvgl/src/draw/vg_lite/lv_vg_lite_path.c + lvgl/src/draw/vg_lite/lv_vg_lite_pending.c + lvgl/src/draw/vg_lite/lv_vg_lite_stroke.c + lvgl/src/draw/vg_lite/lv_vg_lite_utils.c + lvgl/src/drivers/display/drm/lv_linux_drm.c + lvgl/src/drivers/display/drm/lv_linux_drm_common.c + lvgl/src/drivers/display/drm/lv_linux_drm_egl.c + lvgl/src/drivers/display/fb/lv_linux_fbdev.c + lvgl/src/drivers/display/ft81x/lv_ft81x.c + lvgl/src/drivers/display/ili9341/lv_ili9341.c + lvgl/src/drivers/display/lcd/lv_lcd_generic_mipi.c + lvgl/src/drivers/display/nv3007/lv_nv3007.c + lvgl/src/drivers/display/nxp_elcdif/lv_nxp_elcdif.c + lvgl/src/drivers/display/renesas_glcdc/lv_renesas_glcdc.c + lvgl/src/drivers/display/st7735/lv_st7735.c + lvgl/src/drivers/display/st7789/lv_st7789.c + lvgl/src/drivers/display/st7796/lv_st7796.c + lvgl/src/drivers/display/st_ltdc/lv_st_ltdc.c + lvgl/src/drivers/draw/eve/lv_draw_eve_display.c + lvgl/src/drivers/evdev/lv_evdev.c + lvgl/src/drivers/libinput/lv_libinput.c + lvgl/src/drivers/libinput/lv_xkb.c + lvgl/src/drivers/nuttx/lv_nuttx_cache.c + lvgl/src/drivers/nuttx/lv_nuttx_entry.c + lvgl/src/drivers/nuttx/lv_nuttx_fbdev.c + lvgl/src/drivers/nuttx/lv_nuttx_image_cache.c + lvgl/src/drivers/nuttx/lv_nuttx_lcd.c + lvgl/src/drivers/nuttx/lv_nuttx_libuv.c + lvgl/src/drivers/nuttx/lv_nuttx_mouse.c + lvgl/src/drivers/nuttx/lv_nuttx_profiler.c + lvgl/src/drivers/nuttx/lv_nuttx_touchscreen.c + lvgl/src/drivers/opengles/lv_opengles_debug.c + lvgl/src/drivers/opengles/lv_opengles_driver.c + lvgl/src/drivers/opengles/lv_opengles_egl.c + lvgl/src/drivers/opengles/lv_opengles_glfw.c + lvgl/src/drivers/opengles/lv_opengles_texture.c + lvgl/src/drivers/opengles/assets/lv_opengles_shader.c + lvgl/src/drivers/opengles/glad/src/egl.c + lvgl/src/drivers/opengles/glad/src/gl.c + lvgl/src/drivers/opengles/glad/src/gles2.c + lvgl/src/drivers/opengles/opengl_shader/lv_opengl_shader_manager.c + lvgl/src/drivers/opengles/opengl_shader/lv_opengl_shader_program.c + lvgl/src/drivers/qnx/lv_qnx.c + lvgl/src/drivers/sdl/lv_sdl_egl.c + lvgl/src/drivers/sdl/lv_sdl_keyboard.c + lvgl/src/drivers/sdl/lv_sdl_mouse.c + lvgl/src/drivers/sdl/lv_sdl_mousewheel.c + lvgl/src/drivers/sdl/lv_sdl_sw.c + lvgl/src/drivers/sdl/lv_sdl_texture.c + lvgl/src/drivers/sdl/lv_sdl_window.c + lvgl/src/drivers/uefi/lv_uefi_context.c + lvgl/src/drivers/uefi/lv_uefi_display.c + lvgl/src/drivers/uefi/lv_uefi_indev_keyboard.c + lvgl/src/drivers/uefi/lv_uefi_indev_pointer.c + lvgl/src/drivers/uefi/lv_uefi_indev_touch.c + lvgl/src/drivers/uefi/lv_uefi_private.c + lvgl/src/drivers/wayland/lv_wayland.c + lvgl/src/drivers/wayland/lv_wl_egl_backend.c + lvgl/src/drivers/wayland/lv_wl_g2d_backend.c + lvgl/src/drivers/wayland/lv_wl_keyboard.c + lvgl/src/drivers/wayland/lv_wl_pointer.c + lvgl/src/drivers/wayland/lv_wl_seat.c + lvgl/src/drivers/wayland/lv_wl_shm_backend.c + lvgl/src/drivers/wayland/lv_wl_touch.c + lvgl/src/drivers/wayland/lv_wl_window.c + lvgl/src/drivers/wayland/lv_wl_xdg_shell.c + lvgl/src/drivers/windows/lv_windows_context.c + lvgl/src/drivers/windows/lv_windows_display.c + lvgl/src/drivers/windows/lv_windows_input.c + lvgl/src/drivers/x11/lv_x11_display.c + lvgl/src/drivers/x11/lv_x11_input.c + lvgl/src/font/lv_font.c + lvgl/src/font/lv_font_dejavu_16_persian_hebrew.c + lvgl/src/font/lv_font_montserrat_10.c + lvgl/src/font/lv_font_montserrat_12.c + lvgl/src/font/lv_font_montserrat_14.c + lvgl/src/font/lv_font_montserrat_14_aligned.c + lvgl/src/font/lv_font_montserrat_16.c + lvgl/src/font/lv_font_montserrat_18.c + lvgl/src/font/lv_font_montserrat_20.c + lvgl/src/font/lv_font_montserrat_22.c + lvgl/src/font/lv_font_montserrat_24.c + lvgl/src/font/lv_font_montserrat_26.c + lvgl/src/font/lv_font_montserrat_28.c + lvgl/src/font/lv_font_montserrat_28_compressed.c + lvgl/src/font/lv_font_montserrat_30.c + lvgl/src/font/lv_font_montserrat_32.c + lvgl/src/font/lv_font_montserrat_34.c + lvgl/src/font/lv_font_montserrat_36.c + lvgl/src/font/lv_font_montserrat_38.c + lvgl/src/font/lv_font_montserrat_40.c + lvgl/src/font/lv_font_montserrat_42.c + lvgl/src/font/lv_font_montserrat_44.c + lvgl/src/font/lv_font_montserrat_46.c + lvgl/src/font/lv_font_montserrat_48.c + lvgl/src/font/lv_font_montserrat_8.c + lvgl/src/font/lv_font_source_han_sans_sc_14_cjk.c + lvgl/src/font/lv_font_source_han_sans_sc_16_cjk.c + lvgl/src/font/lv_font_unscii_16.c + lvgl/src/font/lv_font_unscii_8.c + lvgl/src/font/binfont_loader/lv_binfont_loader.c + lvgl/src/font/fmt_txt/lv_font_fmt_txt.c + lvgl/src/font/font_manager/lv_font_manager.c + lvgl/src/font/font_manager/lv_font_manager_recycle.c + lvgl/src/font/imgfont/lv_imgfont.c + lvgl/src/indev/lv_gridnav.c + lvgl/src/indev/lv_indev.c + lvgl/src/indev/lv_indev_gesture.c + lvgl/src/indev/lv_indev_scroll.c + lvgl/src/layouts/lv_layout.c + lvgl/src/layouts/flex/lv_flex.c + lvgl/src/layouts/grid/lv_grid.c + lvgl/src/libs/barcode/code128.c + lvgl/src/libs/barcode/lv_barcode.c + lvgl/src/libs/bin_decoder/lv_bin_decoder.c + lvgl/src/libs/bmp/lv_bmp.c + lvgl/src/libs/ffmpeg/lv_ffmpeg.c + lvgl/src/libs/freetype/lv_freetype.c + lvgl/src/libs/freetype/lv_freetype_glyph.c + lvgl/src/libs/freetype/lv_freetype_image.c + lvgl/src/libs/freetype/lv_freetype_outline.c + lvgl/src/libs/freetype/lv_ftsystem.c + lvgl/src/libs/frogfs/src/decomp_raw.c + lvgl/src/libs/frogfs/src/frogfs.c + lvgl/src/libs/fsdrv/lv_fs_cbfs.c + lvgl/src/libs/fsdrv/lv_fs_fatfs.c + lvgl/src/libs/fsdrv/lv_fs_frogfs.c + lvgl/src/libs/fsdrv/lv_fs_littlefs.c + lvgl/src/libs/fsdrv/lv_fs_memfs.c + lvgl/src/libs/fsdrv/lv_fs_posix.c + lvgl/src/libs/fsdrv/lv_fs_stdio.c + lvgl/src/libs/fsdrv/lv_fs_uefi.c + lvgl/src/libs/fsdrv/lv_fs_win32.c + lvgl/src/libs/FT800-FT813/EVE_commands.c + lvgl/src/libs/FT800-FT813/EVE_supplemental.c + lvgl/src/libs/gif/gif.c + lvgl/src/libs/gltf/gltf_environment/lv_gltf_ibl_sampler.c + lvgl/src/libs/gltf/gltf_view/assets/chromatic.c + lvgl/src/libs/gltf/gltf_view/assets/lv_gltf_view_shader.c + lvgl/src/libs/gltf/math/lv_3dmath.c + lvgl/src/libs/gstreamer/lv_gstreamer.c + lvgl/src/libs/libjpeg_turbo/lv_libjpeg_turbo.c + lvgl/src/libs/libpng/lv_libpng.c + lvgl/src/libs/libwebp/lv_libwebp.c + lvgl/src/libs/lodepng/lodepng.c + lvgl/src/libs/lodepng/lv_lodepng.c + lvgl/src/libs/lz4/lz4.c + lvgl/src/libs/nanovg/nanovg.c + lvgl/src/libs/qrcode/lv_qrcode.c + lvgl/src/libs/qrcode/qrcodegen.c + lvgl/src/libs/rle/lv_rle.c + lvgl/src/libs/rlottie/lv_rlottie.c + lvgl/src/libs/svg/lv_svg.c + lvgl/src/libs/svg/lv_svg_decoder.c + lvgl/src/libs/svg/lv_svg_parser.c + lvgl/src/libs/svg/lv_svg_render.c + lvgl/src/libs/svg/lv_svg_token.c + lvgl/src/libs/tiny_ttf/lv_tiny_ttf.c + lvgl/src/libs/tjpgd/lv_tjpgd.c + lvgl/src/libs/tjpgd/tjpgd.c + lvgl/src/libs/vg_lite_driver/lv_vg_lite_hal/lv_vg_lite_hal.c + lvgl/src/libs/vg_lite_driver/lv_vg_lite_hal/vg_lite_os.c + lvgl/src/libs/vg_lite_driver/VGLite/vg_lite.c + lvgl/src/libs/vg_lite_driver/VGLite/vg_lite_image.c + lvgl/src/libs/vg_lite_driver/VGLite/vg_lite_matrix.c + lvgl/src/libs/vg_lite_driver/VGLite/vg_lite_path.c + lvgl/src/libs/vg_lite_driver/VGLite/vg_lite_stroke.c + lvgl/src/libs/vg_lite_driver/VGLiteKernel/vg_lite_kernel.c + lvgl/src/misc/lv_anim.c + lvgl/src/misc/lv_anim_timeline.c + lvgl/src/misc/lv_area.c + lvgl/src/misc/lv_array.c + lvgl/src/misc/lv_async.c + lvgl/src/misc/lv_bidi.c + lvgl/src/misc/lv_circle_buf.c + lvgl/src/misc/lv_color.c + lvgl/src/misc/lv_color_op.c + lvgl/src/misc/lv_event.c + lvgl/src/misc/lv_fs.c + lvgl/src/misc/lv_grad.c + lvgl/src/misc/lv_iter.c + lvgl/src/misc/lv_ll.c + lvgl/src/misc/lv_log.c + lvgl/src/misc/lv_lru.c + lvgl/src/misc/lv_math.c + lvgl/src/misc/lv_matrix.c + lvgl/src/misc/lv_palette.c + lvgl/src/misc/lv_pending.c + lvgl/src/misc/lv_profiler_builtin.c + lvgl/src/misc/lv_profiler_builtin_posix.c + lvgl/src/misc/lv_rb.c + lvgl/src/misc/lv_style.c + lvgl/src/misc/lv_style_gen.c + lvgl/src/misc/lv_templ.c + lvgl/src/misc/lv_text.c + lvgl/src/misc/lv_text_ap.c + lvgl/src/misc/lv_timer.c + lvgl/src/misc/lv_tree.c + lvgl/src/misc/lv_utils.c + lvgl/src/misc/cache/lv_cache.c + lvgl/src/misc/cache/lv_cache_entry.c + lvgl/src/misc/cache/class/lv_cache_lru_ll.c + lvgl/src/misc/cache/class/lv_cache_lru_rb.c + lvgl/src/misc/cache/class/lv_cache_sc_da.c + lvgl/src/misc/cache/instance/lv_image_cache.c + lvgl/src/misc/cache/instance/lv_image_header_cache.c + lvgl/src/osal/lv_cmsis_rtos2.c + lvgl/src/osal/lv_freertos.c + lvgl/src/osal/lv_linux.c + lvgl/src/osal/lv_mqx.c + lvgl/src/osal/lv_os.c + lvgl/src/osal/lv_os_none.c + lvgl/src/osal/lv_pthread.c + lvgl/src/osal/lv_rtthread.c + lvgl/src/osal/lv_sdl2.c + lvgl/src/osal/lv_windows.c + lvgl/src/others/file_explorer/lv_file_explorer.c + lvgl/src/others/fragment/lv_fragment.c + lvgl/src/others/fragment/lv_fragment_manager.c + lvgl/src/others/translation/lv_translation.c + lvgl/src/stdlib/lv_mem.c + lvgl/src/stdlib/builtin/lv_mem_core_builtin.c + lvgl/src/stdlib/builtin/lv_sprintf_builtin.c + lvgl/src/stdlib/builtin/lv_string_builtin.c + lvgl/src/stdlib/builtin/lv_tlsf.c + lvgl/src/stdlib/clib/lv_mem_core_clib.c + lvgl/src/stdlib/clib/lv_sprintf_clib.c + lvgl/src/stdlib/clib/lv_string_clib.c + lvgl/src/stdlib/micropython/lv_mem_core_micropython.c + lvgl/src/stdlib/rtthread/lv_mem_core_rtthread.c + lvgl/src/stdlib/rtthread/lv_sprintf_rtthread.c + lvgl/src/stdlib/rtthread/lv_string_rtthread.c + lvgl/src/stdlib/uefi/lv_mem_core_uefi.c + lvgl/src/themes/lv_theme.c + lvgl/src/themes/default/lv_theme_default.c + lvgl/src/themes/mono/lv_theme_mono.c + lvgl/src/themes/simple/lv_theme_simple.c + lvgl/src/tick/lv_tick.c + lvgl/src/widgets/3dtexture/lv_3dtexture.c + lvgl/src/widgets/animimage/lv_animimage.c + lvgl/src/widgets/arc/lv_arc.c + lvgl/src/widgets/arclabel/lv_arclabel.c + lvgl/src/widgets/bar/lv_bar.c + lvgl/src/widgets/button/lv_button.c + lvgl/src/widgets/buttonmatrix/lv_buttonmatrix.c + lvgl/src/widgets/calendar/lv_calendar.c + lvgl/src/widgets/calendar/lv_calendar_chinese.c + lvgl/src/widgets/calendar/lv_calendar_header_arrow.c + lvgl/src/widgets/calendar/lv_calendar_header_dropdown.c + lvgl/src/widgets/canvas/lv_canvas.c + lvgl/src/widgets/chart/lv_chart.c + lvgl/src/widgets/checkbox/lv_checkbox.c + lvgl/src/widgets/dropdown/lv_dropdown.c + lvgl/src/widgets/gif/lv_gif.c + lvgl/src/widgets/image/lv_image.c + lvgl/src/widgets/imagebutton/lv_imagebutton.c + lvgl/src/widgets/ime/lv_ime_pinyin.c + lvgl/src/widgets/keyboard/lv_keyboard.c + lvgl/src/widgets/label/lv_label.c + lvgl/src/widgets/led/lv_led.c + lvgl/src/widgets/line/lv_line.c + lvgl/src/widgets/list/lv_list.c + lvgl/src/widgets/lottie/lv_lottie.c + lvgl/src/widgets/menu/lv_menu.c + lvgl/src/widgets/msgbox/lv_msgbox.c + lvgl/src/widgets/objx_templ/lv_objx_templ.c + lvgl/src/widgets/property/lv_animimage_properties.c + lvgl/src/widgets/property/lv_arc_properties.c + lvgl/src/widgets/property/lv_bar_properties.c + lvgl/src/widgets/property/lv_buttonmatrix_properties.c + lvgl/src/widgets/property/lv_chart_properties.c + lvgl/src/widgets/property/lv_checkbox_properties.c + lvgl/src/widgets/property/lv_dropdown_properties.c + lvgl/src/widgets/property/lv_image_properties.c + lvgl/src/widgets/property/lv_keyboard_properties.c + lvgl/src/widgets/property/lv_label_properties.c + lvgl/src/widgets/property/lv_led_properties.c + lvgl/src/widgets/property/lv_line_properties.c + lvgl/src/widgets/property/lv_menu_properties.c + lvgl/src/widgets/property/lv_obj_properties.c + lvgl/src/widgets/property/lv_roller_properties.c + lvgl/src/widgets/property/lv_scale_properties.c + lvgl/src/widgets/property/lv_slider_properties.c + lvgl/src/widgets/property/lv_span_properties.c + lvgl/src/widgets/property/lv_spinbox_properties.c + lvgl/src/widgets/property/lv_spinner_properties.c + lvgl/src/widgets/property/lv_style_properties.c + lvgl/src/widgets/property/lv_switch_properties.c + lvgl/src/widgets/property/lv_table_properties.c + lvgl/src/widgets/property/lv_tabview_properties.c + lvgl/src/widgets/property/lv_textarea_properties.c + lvgl/src/widgets/roller/lv_roller.c + lvgl/src/widgets/scale/lv_scale.c + lvgl/src/widgets/slider/lv_slider.c + lvgl/src/widgets/span/lv_span.c + lvgl/src/widgets/spinbox/lv_spinbox.c + lvgl/src/widgets/spinner/lv_spinner.c + lvgl/src/widgets/switch/lv_switch.c + lvgl/src/widgets/table/lv_table.c + lvgl/src/widgets/tabview/lv_tabview.c + lvgl/src/widgets/textarea/lv_textarea.c + lvgl/src/widgets/tileview/lv_tileview.c + lvgl/src/widgets/win/lv_win.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + LvglSpikePkg/LvglSpikePkg.dec + +[LibraryClasses] + UefiLib + UefiBootServicesTableLib + MemoryAllocationLib + BaseMemoryLib + DebugLib + PrintLib + BaseLib + TimerLib + +[Guids] + +[Protocols] + gEfiGraphicsOutputProtocolGuid + gEfiAbsolutePointerProtocolGuid + gEfiSimplePointerProtocolGuid + gEfiDevicePathProtocolGuid + gEfiSimpleTextInputExProtocolGuid + +[BuildOptions] + + # /wd5287: warning C5287: operands are different enum types 'lv_part_t' and 'lv_state_t'; `LV_PART_ANY | LV_STATE_ANY` + MSFT:*_*_*_CC_FLAGS = /wd4244 /wd4204 /wd4389 /wd4221 /wd4800 /wd4267 /wd4018 /wd4047 /wd4245 /wd4003 /wd4702 /wd4718 /wd4706 /wd4819 /wd4028 /wd5287 + + MSFT:*_*_*_CC_FLAGS = /GL- + + # Spike: keep the probe focused on hard compile/link errors (e.g. soft-float, + # arch gates), not on LVGL's many style warnings, so a real XArch blocker is + # not masked by -Werror tripping on an unused-variable. + GCC:*_*_*_CC_FLAGS = -Wno-format -Wno-error -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-function -Wno-sign-compare -Wno-type-limits -Wno-unused-parameter -Wno-array-bounds -Wno-maybe-uninitialized \ No newline at end of file diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/LvglLibCommon.h b/Experimental/LvglSpikePkg/Library/LvglLib/LvglLibCommon.h new file mode 100644 index 0000000..2fe3aac --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/LvglLibCommon.h @@ -0,0 +1,45 @@ + + +#ifndef __LVGL_LIB_COMMON_H__ +#define __LVGL_LIB_COMMON_H__ + +#include "lvgl/lvgl.h" +#include "lv_port_indev.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "LvglUefiPort.h" + + +#define EXIT_BTN_NONE 0x0 +#define EXIT_BTN_YES 0x1 +#define EXIT_BTN_NO 0x2 + + +lv_display_t * lv_uefi_disp_create(int32_t hor_res, int32_t ver_res); + +VOID +EFIAPI +LvglUefiEscExitRegister ( + VOID + ); + +VOID +EFIAPI +LvglUefiEscExitUnregister ( + VOID + ); + +#endif diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/LvglSpikeProbe.c b/Experimental/LvglSpikePkg/Library/LvglLib/LvglSpikeProbe.c new file mode 100644 index 0000000..eccd952 --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/LvglSpikeProbe.c @@ -0,0 +1,133 @@ +/** @file + LvglSpikeProbe -- experimental LVGL-on-LoongArch render demo. + + Brings up LVGL's upstream UEFI backend on a GOP display and draws a small demo + screen (themed background + title + subtitle + rounded button), then runs the + LVGL handler loop holding the frame until ESC is pressed. This both forces the + full LVGL closure (core + software renderer + UEFI port, with the + LoongArch64/RISC-V64 arch-gate patch) to compile/link AND gives a visible + on-hardware result. + + experimental/lvgl-spike only; never ship, never default overlay. + + Copyright (c) 2026, MarsDoge. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include + +#include "lvgl/lvgl.h" +#include "lvgl/src/drivers/uefi/lv_uefi.h" +#include "lvgl/src/drivers/uefi/lv_uefi_context.h" +#include "lvgl/src/drivers/uefi/lv_uefi_display.h" + +/** + Build the demo UI on the active screen: dark background, a large title, a + subtitle line, and a rounded accent button with a centered caption. + + @param[in] Screen The LVGL screen object to populate. Must be non-NULL. +**/ +STATIC +VOID +LvglSpikeBuildUi ( + IN lv_obj_t *Screen + ) +{ + lv_obj_t *Title; + lv_obj_t *Subtitle; + lv_obj_t *Button; + lv_obj_t *ButtonLabel; + + lv_obj_set_style_bg_color (Screen, lv_color_hex (0x0E1116), LV_PART_MAIN); + lv_obj_set_style_bg_opa (Screen, LV_OPA_COVER, LV_PART_MAIN); + + Title = lv_label_create (Screen); + lv_label_set_text (Title, "ModernSetupPkg x LVGL"); + lv_obj_set_style_text_color (Title, lv_color_hex (0xF2C14E), LV_PART_MAIN); + lv_obj_set_style_text_font (Title, &lv_font_montserrat_24, LV_PART_MAIN); + lv_obj_align (Title, LV_ALIGN_CENTER, 0, -60); + + Subtitle = lv_label_create (Screen); + lv_label_set_text (Subtitle, "LVGL v9.5.0 software renderer on LoongArch64 UEFI"); + lv_obj_set_style_text_color (Subtitle, lv_color_hex (0xB8C0CC), LV_PART_MAIN); + lv_obj_align (Subtitle, LV_ALIGN_CENTER, 0, -20); + + Button = lv_button_create (Screen); + lv_obj_set_size (Button, 220, 56); + lv_obj_align (Button, LV_ALIGN_CENTER, 0, 50); + lv_obj_set_style_radius (Button, 12, LV_PART_MAIN); + lv_obj_set_style_bg_color (Button, lv_color_hex (0xF2C14E), LV_PART_MAIN); + + ButtonLabel = lv_label_create (Button); + lv_label_set_text (ButtonLabel, "Press ESC to exit"); + lv_obj_set_style_text_color (ButtonLabel, lv_color_hex (0x10141A), LV_PART_MAIN); + lv_obj_center (ButtonLabel); +} + +/** + Entry point. Initializes the LVGL UEFI backend + core, creates one display on + a GOP-backed handle, draws the demo UI, then pumps lv_timer_handler() in a + loop (advancing the tick manually) until the user presses ESC. Falls back to a + text message on ConOut when no GOP display can be found. + + @param[in] ImageHandle Firmware-allocated image handle. + @param[in] SystemTable Pointer to the EFI System Table. + + @retval EFI_SUCCESS Always (build/render probe, not a product). +**/ +EFI_STATUS +EFIAPI +LvglSpikeProbeMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + void *DisplayHandle; + lv_display_t *Display; + EFI_INPUT_KEY Key; + EFI_STATUS Status; + + Print (L"LvglSpikeProbe: start\n"); + lv_uefi_init (ImageHandle, SystemTable); + Print (L"LvglSpikeProbe: lv_uefi_init done\n"); + lv_init (); + Print (L"LvglSpikeProbe: lv_init done\n"); + + DisplayHandle = lv_uefi_display_get_any (); + Print (L"LvglSpikeProbe: display handle = %p\n", DisplayHandle); + if (DisplayHandle == NULL) { + Print (L"LvglSpikeProbe: no EFI_GRAPHICS_OUTPUT_PROTOCOL display found.\n"); + return EFI_UNSUPPORTED; + } + + Display = lv_uefi_display_create (DisplayHandle); + Print (L"LvglSpikeProbe: display created = %p\n", Display); + if (Display == NULL) { + Print (L"LvglSpikeProbe: lv_uefi_display_create failed.\n"); + return EFI_DEVICE_ERROR; + } + + LvglSpikeBuildUi (lv_screen_active ()); + Print (L"LvglSpikeProbe: UI built; rendering -- press ESC to exit\n"); + + // + // Hold the rendered frame; ESC exits. Tick is advanced manually so LVGL + // timers/refresh run even if no timestamp tick source is registered. + // + for ( ; ; ) { + lv_timer_handler (); + gBS->Stall (10 * 1000); + lv_tick_inc (10); + + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + if (!EFI_ERROR (Status) && (Key.ScanCode == SCAN_ESC)) { + break; + } + } + + gST->ConOut->ClearScreen (gST->ConOut); + return EFI_SUCCESS; +} diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/LvglSpikeProbe.inf b/Experimental/LvglSpikePkg/Library/LvglLib/LvglSpikeProbe.inf new file mode 100644 index 0000000..75f0640 --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/LvglSpikeProbe.inf @@ -0,0 +1,58 @@ +## @file +# LvglSpikeProbe -- experimental standalone LVGL-on-GOP render probe. +# +# Forces the shared LVGL build (LvglCoreLib) to compile/link and draws a demo +# UI straight to GOP (NOT via any DisplayEngine). experimental/lvgl-spike only; +# never ship, never default overlay. +# +# Copyright (c) 2026, MarsDoge. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = LvglSpikeProbe + FILE_GUID = 7C2A9E14-3B6D-4F08-A1C2-9E5D0B71AA02 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = LvglSpikeProbeMain + +[Sources] + LvglSpikeProbe.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + LvglSpikePkg/LvglSpikePkg.dec + +[LibraryClasses] + UefiApplicationEntryPoint + UefiBootServicesTableLib + UefiLib + BaseLib + BaseMemoryLib + MemoryAllocationLib + DebugLib + PrintLib + TimerLib + LvglCoreLib + +[Protocols] + gEfiGraphicsOutputProtocolGuid + gEfiSimplePointerProtocolGuid + gEfiAbsolutePointerProtocolGuid + gEfiSimpleTextInputExProtocolGuid + gEfiSimpleTextInProtocolGuid + gEfiEdidActiveProtocolGuid + gEfiTimestampProtocolGuid + gEfiLoadedImageProtocolGuid + gEfiSimpleFileSystemProtocolGuid + gEfiDevicePathProtocolGuid + +[Guids] + gEfiFileInfoGuid + +[BuildOptions] + # LvglSpikeProbe.c includes , which pulls lv_conf.h via + # LV_CONF_INCLUDE_SIMPLE (found in this module's own dir). + GCC:*_*_*_CC_FLAGS = -DLV_CONF_INCLUDE_SIMPLE diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/LvglUefiPort.c b/Experimental/LvglSpikePkg/Library/LvglLib/LvglUefiPort.c new file mode 100644 index 0000000..3c4b568 --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/LvglUefiPort.c @@ -0,0 +1,212 @@ + +#include "LvglUefiPort.h" + +#define LVGL_HEAD_SIGNATURE SIGNATURE_32('l','v','g','l') + +typedef struct { + UINT32 Signature; + UINTN Size; +} LVGL_HEAD; + +#define LVGL_OVERHEAD sizeof(LVGL_HEAD) + +void* memset (void *dest, char ch, unsigned int count) +{ + return SetMem (dest, count, ch); +} + + +void * +malloc ( + size_t size + ) +{ + LVGL_HEAD *PoolHdr; + UINTN NewSize; + VOID *Data; + + NewSize = (UINTN)(size) + LVGL_OVERHEAD; + + Data = AllocatePool (NewSize); + if (Data != NULL) { + PoolHdr = (LVGL_HEAD *)Data; + PoolHdr->Signature = LVGL_HEAD_SIGNATURE; + PoolHdr->Size = size; + + return (VOID *)(PoolHdr + 1); + } + + return NULL; +} + +void * +realloc ( + void *ptr, + size_t size + ) +{ + LVGL_HEAD *OldPoolHdr; + LVGL_HEAD *NewPoolHdr; + UINTN OldSize; + UINTN NewSize; + VOID *Data; + + NewSize = (UINTN)size + LVGL_OVERHEAD; + Data = AllocatePool (NewSize); + if (Data != NULL) { + NewPoolHdr = (LVGL_HEAD *)Data; + NewPoolHdr->Signature = LVGL_HEAD_SIGNATURE; + NewPoolHdr->Size = size; + if (ptr != NULL) { + OldPoolHdr = (LVGL_HEAD *)ptr - 1; + ASSERT (OldPoolHdr->Signature == LVGL_HEAD_SIGNATURE); + OldSize = OldPoolHdr->Size; + + CopyMem ((VOID *)(NewPoolHdr + 1), ptr, MIN (OldSize, size)); + FreePool ((VOID *)OldPoolHdr); + } + + return (VOID *)(NewPoolHdr + 1); + } + + return NULL; +} + +void +free ( + void *ptr + ) +{ + VOID *EvalOnce; + LVGL_HEAD *PoolHdr; + + EvalOnce = ptr; + if (EvalOnce == NULL) { + return; + } + + PoolHdr = (LVGL_HEAD *)EvalOnce - 1; + if (PoolHdr->Signature == LVGL_HEAD_SIGNATURE) { + FreePool (PoolHdr); + } else { + FreePool (EvalOnce); + } +} + + +/* Return the absolute value of I. */ +long int +labs (long int i) +{ + return i < 0 ? -i : i; +} + +/* Return the absolute value of I. */ +int +abs (int i) +{ + return i < 0 ? -i : i; +} + +char *strchr(const char *str, int ch) +{ + return ScanMem8 (str, AsciiStrSize (str), (UINT8)ch); +} + + +char * +strcpy ( + char *strDest, + const char *strSource + ) +{ + AsciiStrCpyS (strDest, AsciiStrnSizeS (strSource, MAX_STRING_SIZE - 1), strSource); + return strDest; +} + +char * +strncpy ( + char *strDest, + const char *strSource, + size_t count + ) +{ + UINTN DestMax = MAX_STRING_SIZE; + + if (count < MAX_STRING_SIZE) { + DestMax = count + 1; + } else { + count = MAX_STRING_SIZE-1; + } + + AsciiStrnCpyS (strDest, DestMax, strSource, (UINTN)count); + + return strDest; +} + +char * +strcat ( + char *strDest, + const char *strSource + ) +{ + UINTN DestMax; + + DestMax = AsciiStrnLenS (strDest, MAX_STRING_SIZE) + AsciiStrnSizeS (strSource, MAX_STRING_SIZE); + + if (DestMax > MAX_STRING_SIZE) { + DestMax = MAX_STRING_SIZE; + } + + AsciiStrCatS (strDest, DestMax, strSource); + + return strDest; +} + + +char * +strncat ( + char *strDest, + const char *strSource, + size_t count + ) +{ + UINTN DestMax = MAX_STRING_SIZE; + + DestMax = AsciiStrnLenS (strDest, MAX_STRING_SIZE) + AsciiStrnSizeS (strSource, MAX_STRING_SIZE); + + if (DestMax > MAX_STRING_SIZE) { + DestMax = MAX_STRING_SIZE; + } + + AsciiStrnCatS (strDest, DestMax, strSource, (UINTN)count); + + return strDest; +} + +FILE * +fopen ( + const char *filename, + const char *mode + ) +{ + return NULL; +} + +int +fclose ( + FILE *stream + ) +{ + return EOF; +} + +int +fscanf ( + FILE *stream, + const char *format, + ... + ) +{ + return EOF; +} diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/LvglUefiPort.h b/Experimental/LvglSpikePkg/Library/LvglLib/LvglUefiPort.h new file mode 100644 index 0000000..56474c9 --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/LvglUefiPort.h @@ -0,0 +1,164 @@ + +#ifndef __LVGL_UEFI_PORT_H__ +#define __LVGL_UEFI_PORT_H__ + +#include +#include +#include +#include +#include +#include + + +typedef INT8 int8_t; +typedef UINT8 uint8_t; +typedef INT16 int16_t; +typedef UINT16 uint16_t; +typedef INT32 int32_t; +typedef UINT32 uint32_t; +typedef INT64 int64_t; +typedef UINT64 uint64_t; +typedef INTN intmax_t; +typedef UINTN uintmax_t; +typedef INTN intptr_t; +typedef UINTN uintptr_t; +typedef UINTN size_t; + +typedef INTN ptrdiff_t; + +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +#define INT8_MAX MAX_INT8 +#define INT16_MAX MAX_INT16 +#define INT32_MAX MAX_INT32 +#define INT64_MAX MAX_INT64 +#define UINT8_MAX MAX_UINT8 +#define UINT16_MAX MAX_UINT16 +#define UINT32_MAX MAX_UINT32 +#define UINT64_MAX MAX_UINT64 + +#define INT8_MIN MIN_INT8 +#define INT16_MIN MIN_INT16 +#define INT32_MIN MIN_INT32 +#define INT64_MIN MIN_INT64 +#define UINT8_MIN MIN_UINT8 +#define UINT16_MIN MIN_UINT16 +#define UINT32_MIN MIN_UINT32 +#define UINT64_MIN MIN_UINT64 + +#ifndef offsetof +#define offsetof OFFSET_OF +#endif + + +#define calloc(n, s) AllocateZeroPool((n)*(s)) +#define memcpy(dest,source,count) CopyMem(dest,source,(UINTN)(count)) +// #define memset(dest,ch,count) SetMem(dest,(UINTN)(count),(UINT8)(ch)) +#define memchr(buf,ch,count) ScanMem8(buf,(UINTN)(count),(UINT8)ch) +#define memcmp(buf1,buf2,count) (int)(CompareMem(buf1,buf2,(UINTN)(count))) +#define memmove(dest,source,count) CopyMem(dest,source,(UINTN)(count)) + +#define va_init_list(a, b) VA_START(a,b) +#define va_list VA_LIST +#define va_arg(a, b) VA_ARG(a,b) +#define va_end(a) VA_END(a) +#define va_start VA_START +#define va_copy VA_COPY + +#define FILE VOID +#define stdout NULL +#define fprintf(...) +#define vsnprintf (int)AsciiVSPrint + +#define EOF (-1) + +char * +strcpy ( + char *strDest, + const char *strSource + ); + +char * +strncpy ( + char *strDest, + const char *strSource, + size_t count + ); + +char * +strcat ( + char *strDest, + const char *strSource + ); + +char * +strncat ( + char *strDest, + const char *strSource, + size_t count + ); + +#define MAX_STRING_SIZE 0x1000 +#define strlen(str) (size_t)(AsciiStrnLenS(str,MAX_STRING_SIZE)) +#define strnlen(str, count) (size_t)(AsciiStrnLenS(str, count)) +// #define strcpy(strDest,strSource) AsciiStrCpyS(strDest,MAX_STRING_SIZE,strSource) +// #define strncpy(strDest,strSource,count) AsciiStrnCpyS(strDest,MAX_STRING_SIZE,strSource,(UINTN)count) +// #define strcat(strDest,strSource) AsciiStrCatS(strDest,MAX_STRING_SIZE,strSource) +// #define strncat(strDest,strSource,count) AsciiStrnCatS(strDest,MAX_STRING_SIZE,strSource,(UINTN)count) +#define strncmp(string1,string2,count) (int)(AsciiStrnCmp(string1,string2,(UINTN)(count))) +#define strcasecmp(str1,str2) (int)AsciiStriCmp(str1,str2) +#define strcmp(str1,str2) (int)AsciiStrCmp (str1,str2) + +void * +malloc ( + size_t size + ); + +void * +realloc ( + void *ptr, + size_t size + ); + + +void +free ( + void *ptr + ); + +long int labs (long int i); + +int abs (int i); + +char *strchr(const char *str, int ch); + +void* memset (void *dest, char ch, unsigned int count); + +FILE * +fopen ( + const char *filename, + const char *mode + ); + +int +fclose ( + FILE * + ); + +int +fscanf ( + FILE *stream, + const char *format, + ... + ); + +#define exit(n) ASSERT(FALSE); + +#endif diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/MouseCursorIcon.c b/Experimental/LvglSpikePkg/Library/LvglLib/MouseCursorIcon.c new file mode 100644 index 0000000..eef5797 --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/MouseCursorIcon.c @@ -0,0 +1,43 @@ +#include "lv_port_indev.h" + +// https://github.com/lvgl/lv_web_emscripten/blob/master/mouse_cursor_icon.c +#ifndef LV_ATTRIBUTE_MEM_ALIGN +#define LV_ATTRIBUTE_MEM_ALIGN +#endif + +#ifndef LV_ATTRIBUTE_IMG_MOUSE_CURSOR_ICON +#define LV_ATTRIBUTE_IMG_MOUSE_CURSOR_ICON +#endif + +const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_MOUSE_CURSOR_ICON uint8_t mouse_cursor_icon_map[] = { + 0x78, 0x78, 0x78, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x69, 0x69, 0x69, 0xff, 0xed, 0xed, 0xed, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x1b, 0x1b, 0x1b, 0xff, 0x6c, 0x6c, 0x6c, 0xff, 0xec, 0xec, 0xec, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0x16, 0x16, 0x16, 0xff, 0x75, 0x75, 0x75, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xd6, 0xd6, 0xd6, 0xff, 0x13, 0x13, 0x13, 0xff, 0x7f, 0x7f, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd0, 0xd0, 0xd0, 0xff, 0x11, 0x11, 0x11, 0xff, 0x87, 0x87, 0x87, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xf7, 0xf7, 0xf7, 0xff, 0xfe, 0xfe, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0xc9, 0xc9, 0xff, 0x0f, 0x0f, 0x0f, 0xff, 0x8f, 0x8f, 0x8f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xf4, 0xf4, 0xf4, 0xff, 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0xc3, 0xc3, 0xff, 0x0f, 0x0f, 0x0f, 0xff, 0x97, 0x97, 0x97, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xf0, 0xf0, 0xf0, 0xff, 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbb, 0xbb, 0xbb, 0xff, 0x0f, 0x0f, 0x0f, 0xff, 0x9f, 0x9f, 0x9f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xed, 0xed, 0xed, 0xff, 0xf4, 0xf4, 0xf4, 0xff, 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb4, 0xb4, 0xb4, 0xff, 0x10, 0x10, 0x10, 0xff, 0xa6, 0xa6, 0xa6, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xe9, 0xe9, 0xe9, 0xff, 0xf1, 0xf1, 0xf1, 0xff, 0xf8, 0xf8, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xaa, 0xaa, 0xff, 0x11, 0x11, 0x11, 0xff, 0xae, 0xae, 0xae, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xe6, 0xe6, 0xe6, 0xff, 0xed, 0xed, 0xed, 0xff, 0xf4, 0xf4, 0xf4, 0xff, 0xfb, 0xfb, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa3, 0xa3, 0xa3, 0xff, 0x13, 0x13, 0x13, 0xff, 0xb4, 0xb4, 0xb4, 0xff, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xe3, 0xe3, 0xe3, 0xff, 0xea, 0xea, 0xea, 0xff, 0xf1, 0xf1, 0xf1, 0xff, 0xf8, 0xf8, 0xf8, 0xff, 0xfd, 0xfd, 0xfd, 0xff, 0x2d, 0x2d, 0x2d, 0xff, 0x0b, 0x0b, 0x0b, 0xff, 0x0b, 0x0b, 0x0b, 0xff, 0x0b, 0x0b, 0x0b, 0xff, 0x01, 0x01, 0x01, 0xff, 0x13, 0x13, 0x13, 0xff, 0xc5, 0xc5, 0xc5, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xdf, 0xdf, 0xdf, 0xff, 0xe6, 0xe6, 0xe6, 0xff, 0xd7, 0xd7, 0xd7, 0xff, 0x9c, 0x9c, 0x9c, 0xff, 0xfc, 0xfc, 0xfc, 0xff, 0x91, 0x91, 0x91, 0xff, 0x2a, 0x2a, 0x2a, 0xff, 0x94, 0x94, 0x94, 0xff, 0xa3, 0xa3, 0xa3, 0xff, 0xa6, 0xa6, 0xa6, 0xff, 0xac, 0xac, 0xac, 0xff, 0xce, 0xce, 0xce, 0xff, + 0x00, 0x00, 0x00, 0xff, 0xdc, 0xdc, 0xdc, 0xff, 0xcb, 0xcb, 0xcb, 0xff, 0x1f, 0x1f, 0x1f, 0xff, 0x0f, 0x0f, 0x0f, 0xff, 0xeb, 0xeb, 0xeb, 0xff, 0xf5, 0xf5, 0xf5, 0xff, 0x13, 0x13, 0x13, 0xff, 0x98, 0x98, 0x98, 0xff, 0xe5, 0xe5, 0xe5, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0xc0, 0xc0, 0xc0, 0xff, 0x1b, 0x1b, 0x1b, 0xff, 0x4e, 0x4e, 0x4e, 0xff, 0x38, 0x38, 0x38, 0xff, 0x80, 0x80, 0x80, 0xff, 0xfc, 0xfc, 0xfc, 0xff, 0x81, 0x81, 0x81, 0xff, 0x40, 0x40, 0x40, 0xff, 0xe4, 0xe4, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xff, 0x19, 0x19, 0x19, 0xff, 0x54, 0x54, 0x54, 0xff, 0xba, 0xba, 0xba, 0xff, 0xb1, 0xb1, 0xb1, 0xff, 0x12, 0x12, 0x12, 0xff, 0xed, 0xed, 0xed, 0xff, 0xef, 0xef, 0xef, 0xff, 0x0f, 0x0f, 0x0f, 0xff, 0xb2, 0xb2, 0xb2, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x01, 0xff, 0x5d, 0x5d, 0x5d, 0xff, 0xbe, 0xbe, 0xbe, 0xff, 0xed, 0xed, 0xed, 0x54, 0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x44, 0xff, 0x83, 0x83, 0x83, 0xff, 0xfc, 0xfc, 0xfc, 0xff, 0x40, 0x40, 0x40, 0xff, 0x76, 0x76, 0x76, 0xff, 0xec, 0xec, 0xec, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x81, 0x81, 0xff, 0xca, 0xca, 0xca, 0xff, 0xec, 0xec, 0xec, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0xbd, 0xbd, 0xff, 0x0f, 0x0f, 0x0f, 0xff, 0x45, 0x45, 0x45, 0xff, 0x14, 0x14, 0x14, 0xff, 0x91, 0x91, 0x91, 0xff, 0xf3, 0xf3, 0xf3, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0xb3, 0xb3, 0xff, 0x6f, 0x6f, 0x6f, 0xff, 0x9a, 0x9a, 0x9a, 0xff, 0xd7, 0xd7, 0xd7, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0xec, 0xec, 0x35, 0xf3, 0xf3, 0xf3, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +const lv_img_dsc_t mouse_cursor_icon = { + .header.cf = LV_COLOR_FORMAT_ARGB8888, + .header.magic = LV_IMAGE_HEADER_MAGIC, + .header.w = 13, + .header.h = 21, + .data_size = 273 * 4, + .data = mouse_cursor_icon_map, +}; diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/lv_conf.h b/Experimental/LvglSpikePkg/Library/LvglLib/lv_conf.h new file mode 100644 index 0000000..0343725 --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/lv_conf.h @@ -0,0 +1,1526 @@ +/** + * @file lv_conf.h + * Configuration file for v9.5.0 + */ + +/* + * Copy this file as `lv_conf.h` + * 1. simply next to `lvgl` folder + * 2. or to any other place and + * - define `LV_CONF_INCLUDE_SIMPLE`; + * - add the path as an include path. + */ + +/* clang-format off */ +#if 1 /* Set this to "1" to enable content */ + +#ifndef LV_CONF_H +#define LV_CONF_H + +/* If you need to include anything here, do it inside the `__ASSEMBLY__` guard */ +#if 0 && defined(__ASSEMBLY__) +#include "my_include.h" +#endif + +/*==================== + COLOR SETTINGS + *====================*/ + +/** Color depth: 1 (I1), 8 (L8), 16 (RGB565), 24 (RGB888), 32 (XRGB8888) */ +#define LV_COLOR_DEPTH 32 + +/*========================= + STDLIB WRAPPER SETTINGS + *=========================*/ + +/** Possible values + * - LV_STDLIB_BUILTIN: LVGL's built in implementation + * - LV_STDLIB_CLIB: Standard C functions, like malloc, strlen, etc + * - LV_STDLIB_MICROPYTHON: MicroPython implementation + * - LV_STDLIB_RTTHREAD: RT-Thread implementation + * - LV_STDLIB_CUSTOM: Implement the functions externally + */ +#define LV_USE_STDLIB_MALLOC LV_STDLIB_CUSTOM + +/** Possible values + * - LV_STDLIB_BUILTIN: LVGL's built in implementation + * - LV_STDLIB_CLIB: Standard C functions, like malloc, strlen, etc + * - LV_STDLIB_MICROPYTHON: MicroPython implementation + * - LV_STDLIB_RTTHREAD: RT-Thread implementation + * - LV_STDLIB_CUSTOM: Implement the functions externally + */ +#define LV_USE_STDLIB_STRING LV_STDLIB_BUILTIN + +/** Possible values + * - LV_STDLIB_BUILTIN: LVGL's built in implementation + * - LV_STDLIB_CLIB: Standard C functions, like malloc, strlen, etc + * - LV_STDLIB_MICROPYTHON: MicroPython implementation + * - LV_STDLIB_RTTHREAD: RT-Thread implementation + * - LV_STDLIB_CUSTOM: Implement the functions externally + */ +#define LV_USE_STDLIB_SPRINTF LV_STDLIB_BUILTIN + +#define LV_STDINT_INCLUDE +#define LV_STDDEF_INCLUDE +#define LV_STDBOOL_INCLUDE +#define LV_INTTYPES_INCLUDE +#define LV_LIMITS_INCLUDE +#define LV_STDARG_INCLUDE + +#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN + /** Size of memory available for `lv_malloc()` in bytes (>= 2kB) */ + #define LV_MEM_SIZE (64 * 1024U) /**< [bytes] */ + + /** Size of the memory expand for `lv_malloc()` in bytes */ + #define LV_MEM_POOL_EXPAND_SIZE 0 + + /** Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too. */ + #define LV_MEM_ADR 0 /**< 0: unused*/ + /* Instead of an address give a memory allocator that will be called to get a memory pool for LVGL. E.g. my_malloc */ + #if LV_MEM_ADR == 0 + #undef LV_MEM_POOL_INCLUDE + #undef LV_MEM_POOL_ALLOC + #endif +#endif /*LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN*/ + +/*==================== + HAL SETTINGS + *====================*/ + +/** Default display refresh, input device read and animation step period. */ +#define LV_DEF_REFR_PERIOD 10 /**< [ms] */ + +/** Default Dots Per Inch. Used to initialize default sizes such as widgets sized, style paddings. + * (Not so important, you can adjust it to modify default sizes and spaces.) */ +#define LV_DPI_DEF 130 /**< [px/inch] */ + +/*================= + * OPERATING SYSTEM + *=================*/ +/** Select operating system to use. Possible options: + * - LV_OS_NONE + * - LV_OS_PTHREAD + * - LV_OS_FREERTOS + * - LV_OS_CMSIS_RTOS2 + * - LV_OS_RTTHREAD + * - LV_OS_WINDOWS + * - LV_OS_MQX + * - LV_OS_SDL2 + * - LV_OS_CUSTOM */ +#define LV_USE_OS LV_OS_NONE + +#if LV_USE_OS == LV_OS_CUSTOM + #define LV_OS_CUSTOM_INCLUDE +#endif +#if LV_USE_OS == LV_OS_FREERTOS + /* + * Unblocking an RTOS task with a direct notification is 45% faster and uses less RAM + * than unblocking a task using an intermediary object such as a binary semaphore. + * RTOS task notifications can only be used when there is only one task that can be the recipient of the event. + */ + #define LV_USE_FREERTOS_TASK_NOTIFY 1 +#endif + +/*======================== + * RENDERING CONFIGURATION + *========================*/ + +/** Align stride of all layers and images to this bytes */ +#define LV_DRAW_BUF_STRIDE_ALIGN 1 + +/** Align start address of draw_buf addresses to this bytes*/ +#define LV_DRAW_BUF_ALIGN 4 + +/** Using matrix for transformations. + * Requirements: + * - `LV_USE_MATRIX = 1`. + * - Rendering engine needs to support 3x3 matrix transformations. */ +#define LV_DRAW_TRANSFORM_USE_MATRIX 0 + +/* If a widget has `style_opa < 255` (not `bg_opa`, `text_opa` etc) or not NORMAL blend mode + * it is buffered into a "simple" layer before rendering. The widget can be buffered in smaller chunks. + * "Transformed layers" (if `transform_angle/zoom` are set) use larger buffers + * and can't be drawn in chunks. */ + +/** The target buffer size for simple layer chunks. */ +#define LV_DRAW_LAYER_SIMPLE_BUF_SIZE (24 * 1024) /**< [bytes]*/ + +/* Limit the max allocated memory for simple and transformed layers. + * It should be at least `LV_DRAW_LAYER_SIMPLE_BUF_SIZE` sized but if transformed layers are also used + * it should be enough to store the largest widget too (width x height x 4 area). + * Set it to 0 to have no limit. */ +#define LV_DRAW_LAYER_MAX_MEMORY 0 /**< No limit by default [bytes]*/ + +/** Stack size of drawing thread. + * NOTE: If FreeType or ThorVG is enabled, it is recommended to set it to 32KB or more. + */ +#define LV_DRAW_THREAD_STACK_SIZE (8 * 1024) /**< [bytes]*/ + +/** Thread priority of the drawing task. + * Higher values mean higher priority. + * Can use values from lv_thread_prio_t enum in lv_os.h: LV_THREAD_PRIO_LOWEST, + * LV_THREAD_PRIO_LOW, LV_THREAD_PRIO_MID, LV_THREAD_PRIO_HIGH, LV_THREAD_PRIO_HIGHEST + * Make sure the priority value aligns with the OS-specific priority levels. + * On systems with limited priority levels (e.g., FreeRTOS), a higher value can improve + * rendering performance but might cause other tasks to starve. */ +#define LV_DRAW_THREAD_PRIO LV_THREAD_PRIO_HIGH + +#define LV_USE_DRAW_SW 1 +#if LV_USE_DRAW_SW == 1 + /* + * Selectively disable color format support in order to reduce code size. + * NOTE: some features use certain color formats internally, e.g. + * - gradients use RGB888 + * - bitmaps with transparency may use ARGB8888 + */ + #define LV_DRAW_SW_SUPPORT_RGB565 0 + #define LV_DRAW_SW_SUPPORT_RGB565_SWAPPED 0 + #define LV_DRAW_SW_SUPPORT_RGB565A8 0 + #define LV_DRAW_SW_SUPPORT_RGB888 1 + #define LV_DRAW_SW_SUPPORT_XRGB8888 1 + #define LV_DRAW_SW_SUPPORT_ARGB8888 1 + #define LV_DRAW_SW_SUPPORT_ARGB8888_PREMULTIPLIED 0 + #define LV_DRAW_SW_SUPPORT_L8 0 + #define LV_DRAW_SW_SUPPORT_AL88 0 + #define LV_DRAW_SW_SUPPORT_A8 0 + #define LV_DRAW_SW_SUPPORT_I1 0 + + /* The threshold of the luminance to consider a pixel as + * active in indexed color format */ + #define LV_DRAW_SW_I1_LUM_THRESHOLD 127 + + /** Set number of draw units. + * - > 1 requires operating system to be enabled in `LV_USE_OS`. + * - > 1 means multiple threads will render the screen in parallel. */ + #define LV_DRAW_SW_DRAW_UNIT_CNT 1 + + /** Use Arm-2D to accelerate software (sw) rendering. */ + #define LV_USE_DRAW_ARM2D_SYNC 0 + + /** Enable native helium assembly to be compiled. */ + #define LV_USE_NATIVE_HELIUM_ASM 0 + + /** + * - 0: Use a simple renderer capable of drawing only simple rectangles with gradient, images, text, and straight lines only. + * - 1: Use a complex renderer capable of drawing rounded corners, shadow, skew lines, and arcs too. */ + #define LV_DRAW_SW_COMPLEX 1 + + #if LV_DRAW_SW_COMPLEX == 1 + /** Allow buffering some shadow calculation. + * LV_DRAW_SW_SHADOW_CACHE_SIZE is the maximum shadow size to buffer, where shadow size is + * `shadow_width + radius`. Caching has LV_DRAW_SW_SHADOW_CACHE_SIZE^2 RAM cost. */ + #define LV_DRAW_SW_SHADOW_CACHE_SIZE 0 + + /** Set number of maximally-cached circle data. + * The circumference of 1/4 circle are saved for anti-aliasing. + * `radius * 4` bytes are used per circle (the most often used radiuses are saved). + * - 0: disables caching */ + #define LV_DRAW_SW_CIRCLE_CACHE_SIZE 4 + #endif + + #define LV_USE_DRAW_SW_ASM LV_DRAW_SW_ASM_NONE + + #if LV_USE_DRAW_SW_ASM == LV_DRAW_SW_ASM_CUSTOM + #define LV_DRAW_SW_ASM_CUSTOM_INCLUDE "" + #endif + + /** Enable drawing complex gradients in software: linear at an angle, radial or conical */ + #define LV_USE_DRAW_SW_COMPLEX_GRADIENTS 0 + +#endif + +/*Use TSi's aka (Think Silicon) NemaGFX */ +#define LV_USE_NEMA_GFX 0 + +#if LV_USE_NEMA_GFX + /** Select which NemaGFX static library headers to use. Possible options: + * - LV_NEMA_LIB_NONE an alias of LV_NEMA_LIB_M33_REVC + * - LV_NEMA_LIB_M33_REVC + * - LV_NEMA_LIB_M33_NEMAPVG + * - LV_NEMA_LIB_M55 + * - LV_NEMA_LIB_M7 + * You must also take care to link the correct corresponding static library + * in libs/nema_gfx/lib/core/ + */ + #define LV_USE_NEMA_LIB LV_NEMA_LIB_NONE + + /** Select which NemaGFX HAL to use. Possible options: + * - LV_NEMA_HAL_CUSTOM + * - LV_NEMA_HAL_STM32 */ + #define LV_USE_NEMA_HAL LV_NEMA_HAL_CUSTOM + #if LV_USE_NEMA_HAL == LV_NEMA_HAL_STM32 + #define LV_NEMA_STM32_HAL_INCLUDE + + /** Set it to a value like __attribute__((section("Nemagfx_Memory_Pool_Buffer"))) + * and define the section in the linker script if you need the GPU memory to + * be, e.g. in a region where accesses will not be cached. + */ + #define LV_NEMA_STM32_HAL_ATTRIBUTE_POOL_MEM + #endif + + /*Enable Vector Graphics Operations. Available only if NemaVG library is present*/ + #define LV_USE_NEMA_VG 0 + #if LV_USE_NEMA_VG + /*Define application's resolution used for VG related buffer allocation */ + #define LV_NEMA_GFX_MAX_RESX 800 + #define LV_NEMA_GFX_MAX_RESY 600 + #endif +#endif + +/** Use NXP's PXP on iMX RTxxx platforms. */ +#define LV_USE_PXP 0 + +#if LV_USE_PXP + /** Use PXP for drawing.*/ + #define LV_USE_DRAW_PXP 1 + + /** Use PXP to rotate display.*/ + #define LV_USE_ROTATE_PXP 0 + + #if LV_USE_DRAW_PXP && LV_USE_OS + /** Use additional draw thread for PXP processing.*/ + #define LV_USE_PXP_DRAW_THREAD 1 + #endif + + /** Enable PXP asserts. */ + #define LV_USE_PXP_ASSERT 0 +#endif + +/** Use NXP's G2D on MPU platforms. */ +#define LV_USE_G2D 0 + +#if LV_USE_G2D + /** Use G2D for drawing. **/ + #define LV_USE_DRAW_G2D 1 + + /** Use G2D to rotate display. **/ + #define LV_USE_ROTATE_G2D 0 + + /** Maximum number of buffers that can be stored for G2D draw unit. + * Includes the frame buffers and assets. */ + #define LV_G2D_HASH_TABLE_SIZE 50 + + #if LV_USE_DRAW_G2D && LV_USE_OS + /** Use additional draw thread for G2D processing.*/ + #define LV_USE_G2D_DRAW_THREAD 1 + #endif + + /** Enable G2D asserts. */ + #define LV_USE_G2D_ASSERT 0 +#endif + +/** Use Renesas Dave2D on RA platforms. */ +#define LV_USE_DRAW_DAVE2D 0 + +/** Draw using cached SDL textures*/ +#define LV_USE_DRAW_SDL 0 + +/** Use VG-Lite GPU. */ +#define LV_USE_DRAW_VG_LITE 0 +#if LV_USE_DRAW_VG_LITE + /** Enable VG-Lite custom external 'gpu_init()' function */ + #define LV_VG_LITE_USE_GPU_INIT 0 + + /** Enable VG-Lite assert. */ + #define LV_VG_LITE_USE_ASSERT 0 + + /** VG-Lite flush commit trigger threshold. GPU will try to batch these many draw tasks. */ + #define LV_VG_LITE_FLUSH_MAX_COUNT 8 + + /** Enable border to simulate shadow. + * NOTE: which usually improves performance, + * but does not guarantee the same rendering quality as the software. */ + #define LV_VG_LITE_USE_BOX_SHADOW 1 + + /** VG-Lite gradient maximum cache number. + * @note The memory usage of a single gradient image is 4K bytes. */ + #define LV_VG_LITE_GRAD_CACHE_CNT 32 + + /** VG-Lite stroke maximum cache number. */ + #define LV_VG_LITE_STROKE_CACHE_CNT 32 + + /** VG-Lite unaligned bitmap font maximum cache number. */ + #define LV_VG_LITE_BITMAP_FONT_CACHE_CNT 256 + + /** Remove VLC_OP_CLOSE path instruction (Workaround for NXP) **/ + #define LV_VG_LITE_DISABLE_VLC_OP_CLOSE 0 + + /** Disable blit rectangular offset to resolve certain hardware errors. */ + #define LV_VG_LITE_DISABLE_BLIT_RECT_OFFSET 0 + + /** Disable linear gradient extension for some older versions of drivers. */ + #define LV_VG_LITE_DISABLE_LINEAR_GRADIENT_EXT 0 + + /** Maximum path dump print length (in points) */ + #define LV_VG_LITE_PATH_DUMP_MAX_LEN 1000 + + /** Enable usage of the LVGL's built-in vg_lite driver */ + #define LV_USE_VG_LITE_DRIVER 0 + #if LV_USE_VG_LITE_DRIVER + /** Used to pick the correct GPU series folder valid options are gc255, gc355 and gc555*/ + #define LV_VG_LITE_HAL_GPU_SERIES gc255 + + /** Used to pick the correct GPU revision header it depends on the vendor */ + #define LV_VG_LITE_HAL_GPU_REVISION 0x40 + + /** Base memory address of the GPU IP it depends on SoC, + * default value is for NXP based devices */ + #define LV_VG_LITE_HAL_GPU_BASE_ADDRESS 0x40240000 + #endif /*LV_USE_VG_LITE_DRIVER*/ + + /** Use ThorVG (a software vector library) as VG-Lite driver to allow testing VGLite on PC + * Requires: LV_USE_THORVG_INTERNAL or LV_USE_THORVG_EXTERNAL */ + #define LV_USE_VG_LITE_THORVG 0 + #if LV_USE_VG_LITE_THORVG + /** Enable LVGL's blend mode support */ + #define LV_VG_LITE_THORVG_LVGL_BLEND_SUPPORT 0 + + /** Enable YUV color format support */ + #define LV_VG_LITE_THORVG_YUV_SUPPORT 0 + + /** Enable Linear gradient extension support */ + #define LV_VG_LITE_THORVG_LINEAR_GRADIENT_EXT_SUPPORT 0 + + /** Enable alignment on 16 pixels */ + #define LV_VG_LITE_THORVG_16PIXELS_ALIGN 1 + + /** Buffer address alignment */ + #define LV_VG_LITE_THORVG_BUF_ADDR_ALIGN 64 + + /** Enable multi-thread render */ + #define LV_VG_LITE_THORVG_THREAD_RENDER 0 + #endif /*LV_USE_VG_LITE_THORVG*/ +#endif + +/** Accelerate blends, fills, etc. with STM32 DMA2D */ +#define LV_USE_DRAW_DMA2D 0 +#if LV_USE_DRAW_DMA2D + #define LV_DRAW_DMA2D_HAL_INCLUDE "stm32h7xx_hal.h" + + /* if enabled, the user is required to call `lv_draw_dma2d_transfer_complete_interrupt_handler` + * upon receiving the DMA2D global interrupt + */ + #define LV_USE_DRAW_DMA2D_INTERRUPT 0 +#endif + +/** Draw using cached OpenGLES textures. Requires LV_USE_OPENGLES */ +#define LV_USE_DRAW_OPENGLES 0 +#if LV_USE_DRAW_OPENGLES + #define LV_DRAW_OPENGLES_TEXTURE_CACHE_COUNT 64 +#endif + +/** Draw using espressif PPA accelerator */ +#define LV_USE_PPA 0 +#if LV_USE_PPA + #define LV_USE_PPA_IMG 0 + #define LV_PPA_BURST_LENGTH 128 +#endif + +/* Use EVE FT81X GPU. */ +#define LV_USE_DRAW_EVE 0 +#if LV_USE_DRAW_EVE + /* EVE_GEN value: 2, 3, or 4 */ + #define LV_DRAW_EVE_EVE_GENERATION 4 + + /* The maximum number of bytes to buffer before a single SPI transmission. + * Set it to 0 to disable write buffering. + */ + #define LV_DRAW_EVE_WRITE_BUFFER_SIZE 2048 +#endif + +/** Use NanoVG Renderer + * - Requires LV_USE_NANOVG, LV_USE_MATRIX. + */ +#define LV_USE_DRAW_NANOVG 0 +#if LV_USE_DRAW_NANOVG + /** Select OpenGL backend for NanoVG: + * - 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 + + /** Draw image texture cache count. */ + #define LV_NANOVG_IMAGE_CACHE_CNT 128 + + /** Draw letter texture cache count. */ + #define LV_NANOVG_LETTER_CACHE_CNT 512 +#endif + +/*======================= + * FEATURE CONFIGURATION + *=======================*/ + +/*------------- + * Logging + *-----------*/ + +/** Enable log module */ +#define LV_USE_LOG 0 +#if LV_USE_LOG + /** Set value to one of the following levels of logging detail: + * - LV_LOG_LEVEL_TRACE Log detailed information. + * - LV_LOG_LEVEL_INFO Log important events. + * - LV_LOG_LEVEL_WARN Log if something unwanted happened but didn't cause a problem. + * - LV_LOG_LEVEL_ERROR Log only critical issues, when system may fail. + * - LV_LOG_LEVEL_USER Log only custom log messages added by the user. + * - LV_LOG_LEVEL_NONE Do not log anything. */ + #define LV_LOG_LEVEL LV_LOG_LEVEL_WARN + + /** - 1: Print log with 'printf'; + * - 0: User needs to register a callback with `lv_log_register_print_cb()`. */ + #define LV_LOG_PRINTF 0 + + /** Set callback to print logs. + * E.g `my_print`. The prototype should be `void my_print(lv_log_level_t level, const char * buf)`. + * Can be overwritten by `lv_log_register_print_cb`. */ + //#define LV_LOG_PRINT_CB + + /** - 1: Enable printing timestamp; + * - 0: Disable printing timestamp. */ + #define LV_LOG_USE_TIMESTAMP 1 + + /** - 1: Print file and line number of the log; + * - 0: Do not print file and line number of the log. */ + #define LV_LOG_USE_FILE_LINE 1 + + /* Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs. */ + #define LV_LOG_TRACE_MEM 1 /**< Enable/disable trace logs in memory operations. */ + #define LV_LOG_TRACE_TIMER 1 /**< Enable/disable trace logs in timer operations. */ + #define LV_LOG_TRACE_INDEV 1 /**< Enable/disable trace logs in input device operations. */ + #define LV_LOG_TRACE_DISP_REFR 1 /**< Enable/disable trace logs in display re-draw operations. */ + #define LV_LOG_TRACE_EVENT 1 /**< Enable/disable trace logs in event dispatch logic. */ + #define LV_LOG_TRACE_OBJ_CREATE 1 /**< Enable/disable trace logs in object creation (core `obj` creation plus every widget). */ + #define LV_LOG_TRACE_LAYOUT 1 /**< Enable/disable trace logs in flex- and grid-layout operations. */ + #define LV_LOG_TRACE_ANIM 1 /**< Enable/disable trace logs in animation logic. */ + #define LV_LOG_TRACE_CACHE 1 /**< Enable/disable trace logs in cache operations. */ +#endif /*LV_USE_LOG*/ + +/*------------- + * Asserts + *-----------*/ + +/* Enable assertion failures if an operation fails or invalid data is found. + * If LV_USE_LOG is enabled, an error message will be printed on failure. */ +#define LV_USE_ASSERT_NULL 1 /**< Check if the parameter is NULL. (Very fast, recommended) */ +#define LV_USE_ASSERT_MALLOC 1 /**< Checks is the memory is successfully allocated or no. (Very fast, recommended) */ +#define LV_USE_ASSERT_STYLE 0 /**< Check if the styles are properly initialized. (Very fast, recommended) */ +#define LV_USE_ASSERT_MEM_INTEGRITY 0 /**< Check the integrity of `lv_mem` after critical operations. (Slow) */ +#define LV_USE_ASSERT_OBJ 0 /**< Check the object's type and existence (e.g. not deleted). (Slow) */ + +/** Add a custom handler when assert happens e.g. to restart MCU. */ +#define LV_ASSERT_HANDLER_INCLUDE +#define LV_ASSERT_HANDLER while(1); /**< Halt by default */ + +/*------------- + * Debug + *-----------*/ + +/** 1: Draw random colored rectangles over the redrawn areas. */ +#define LV_USE_REFR_DEBUG 0 + +/** 1: Draw a red overlay for ARGB layers and a green overlay for RGB layers*/ +#define LV_USE_LAYER_DEBUG 0 + +/** 1: Adds the following behaviors for debugging: + * - Draw overlays with different colors for each draw_unit's tasks. + * - Draw index number of draw unit on white background. + * - For layers, draws index number of draw unit on black background. */ +#define LV_USE_PARALLEL_DRAW_DEBUG 0 + +/*------------- + * Others + *-----------*/ + +#define LV_ENABLE_GLOBAL_CUSTOM 0 +#if LV_ENABLE_GLOBAL_CUSTOM + /** Header to include for custom 'lv_global' function" */ + #define LV_GLOBAL_CUSTOM_INCLUDE +#endif + +/** Default cache size in bytes. + * Used by image decoders such as `lv_lodepng` to keep the decoded image in memory. + * If size is not set to 0, the decoder will fail to decode when the cache is full. + * If size is 0, the cache function is not enabled and the decoded memory will be + * released immediately after use. */ +#define LV_CACHE_DEF_SIZE 0 + +/** Default number of image header cache entries. The cache is used to store the headers of images + * The main logic is like `LV_CACHE_DEF_SIZE` but for image headers. */ +#define LV_IMAGE_HEADER_CACHE_DEF_CNT 0 + +/** Number of stops allowed per gradient. Increase this to allow more stops. + * This adds (sizeof(lv_color_t) + 1) bytes per additional stop. */ +#define LV_GRADIENT_MAX_STOPS 2 + +/** Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently. + * - 0: round down, + * - 64: round up from x.75, + * - 128: round up from half, + * - 192: round up from x.25, + * - 254: round up */ +#define LV_COLOR_MIX_ROUND_OFS 0 + +/** Add 2 x 32-bit variables to each `lv_obj_t` to speed up getting style properties */ +#define LV_OBJ_STYLE_CACHE 0 + +/** Add `id` field to `lv_obj_t` */ +#define LV_USE_OBJ_ID 0 + +/** Enable support widget names*/ +#define LV_USE_OBJ_NAME 0 + +/** Automatically assign an ID when obj is created */ +#define LV_OBJ_ID_AUTO_ASSIGN LV_USE_OBJ_ID + +/** Use builtin obj ID handler functions: +* - lv_obj_assign_id: Called when a widget is created. Use a separate counter for each widget class as an ID. +* - lv_obj_id_compare: Compare the ID to decide if it matches with a requested value. +* - lv_obj_stringify_id: Return string-ified identifier, e.g. "button3". +* - lv_obj_free_id: Does nothing, as there is no memory allocation for the ID. +* When disabled these functions needs to be implemented by the user.*/ +#define LV_USE_OBJ_ID_BUILTIN 1 + +/** Use obj property set/get API. */ +#define LV_USE_OBJ_PROPERTY 0 + +/** Enable property name support. */ +#define LV_USE_OBJ_PROPERTY_NAME 1 + +/* Enable the multi-touch gesture recognition feature */ +/* Gesture recognition requires the use of floats */ +#define LV_USE_GESTURE_RECOGNITION 0 + +/*===================== + * COMPILER SETTINGS + *====================*/ + +/** For big endian systems set to 1 */ +#define LV_BIG_ENDIAN_SYSTEM 0 + +/** Define a custom attribute for `lv_tick_inc` function */ +#define LV_ATTRIBUTE_TICK_INC + +/** Define a custom attribute for `lv_timer_handler` function */ +#define LV_ATTRIBUTE_TIMER_HANDLER + +/** Define a custom attribute for `lv_display_flush_ready` function */ +#define LV_ATTRIBUTE_FLUSH_READY + +/** Align VG_LITE buffers on this number of bytes. + * @note vglite_src_buf_aligned() uses this value to validate alignment of passed buffer pointers. */ +#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1 + +/** Will be added where memory needs to be aligned (with -Os data might not be aligned to boundary by default). + * E.g. __attribute__((aligned(4)))*/ +#define LV_ATTRIBUTE_MEM_ALIGN + +/** Attribute to mark large constant arrays, for example for font bitmaps */ +#define LV_ATTRIBUTE_LARGE_CONST + +/** Compiler prefix for a large array declaration in RAM */ +#define LV_ATTRIBUTE_LARGE_RAM_ARRAY + +/** Place performance critical functions into a faster memory (e.g RAM) */ +#define LV_ATTRIBUTE_FAST_MEM + +/** Export integer constant to binding. This macro is used with constants in the form of LV_ that + * should also appear on LVGL binding API such as MicroPython. */ +#define LV_EXPORT_CONST_INT(int_value) struct _silence_gcc_warning /**< The default value just prevents GCC warning */ + +/** Prefix all global extern data with this */ +#define LV_ATTRIBUTE_EXTERN_DATA + +/** Use `float` as `lv_value_precise_t` */ +#define LV_USE_FLOAT 0 + +/** Enable matrix support + * - Requires `LV_USE_FLOAT = 1` */ +#define LV_USE_MATRIX 0 + +/** Include `lvgl_private.h` in `lvgl.h` to access internal data and functions by default */ +#ifndef LV_USE_PRIVATE_API + #define LV_USE_PRIVATE_API 1 +#endif + +/*================== + * FONT USAGE + *===================*/ + +/* Montserrat fonts with ASCII range and some symbols using bpp = 4 + * https://fonts.google.com/specimen/Montserrat */ +#define LV_FONT_MONTSERRAT_8 0 +#define LV_FONT_MONTSERRAT_10 0 +#define LV_FONT_MONTSERRAT_12 0 +#define LV_FONT_MONTSERRAT_14 1 +#define LV_FONT_MONTSERRAT_16 1 +#define LV_FONT_MONTSERRAT_18 1 +#define LV_FONT_MONTSERRAT_20 1 +#define LV_FONT_MONTSERRAT_22 1 +#define LV_FONT_MONTSERRAT_24 1 +#define LV_FONT_MONTSERRAT_26 0 +#define LV_FONT_MONTSERRAT_28 0 +#define LV_FONT_MONTSERRAT_30 0 +#define LV_FONT_MONTSERRAT_32 0 +#define LV_FONT_MONTSERRAT_34 0 +#define LV_FONT_MONTSERRAT_36 0 +#define LV_FONT_MONTSERRAT_38 0 +#define LV_FONT_MONTSERRAT_40 0 +#define LV_FONT_MONTSERRAT_42 0 +#define LV_FONT_MONTSERRAT_44 0 +#define LV_FONT_MONTSERRAT_46 0 +#define LV_FONT_MONTSERRAT_48 0 + +/* Demonstrate special features */ +#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /**< bpp = 3 */ +#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /**< Hebrew, Arabic, Persian letters and all their forms */ +#define LV_FONT_SOURCE_HAN_SANS_SC_14_CJK 0 /**< 1338 most common CJK radicals */ +#define LV_FONT_SOURCE_HAN_SANS_SC_16_CJK 0 /**< 1338 most common CJK radicals */ + +/** Pixel perfect monospaced fonts */ +#define LV_FONT_UNSCII_8 0 +#define LV_FONT_UNSCII_16 0 + +/** Optionally declare custom fonts here. + * + * You can use any of these fonts as the default font too and they will be available + * globally. Example: + * + * @code + * #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2) + * @endcode + */ +#define LV_FONT_CUSTOM_DECLARE + +/** Always set a default font */ +#define LV_FONT_DEFAULT &lv_font_montserrat_14 + +/** Enable handling large font and/or fonts with a lot of characters. + * The limit depends on the font size, font face and bpp. + * A compiler error will be triggered if a font needs it. */ +#define LV_FONT_FMT_TXT_LARGE 0 + +/** Enables/disables support for compressed fonts. */ +#define LV_USE_FONT_COMPRESSED 0 + +/** Enable drawing placeholders when glyph dsc is not found. */ +#define LV_USE_FONT_PLACEHOLDER 1 + +/*================= + * TEXT SETTINGS + *=================*/ + +/** + * Select a character encoding for strings. + * Your IDE or editor should have the same character encoding. + * - LV_TXT_ENC_UTF8 + * - LV_TXT_ENC_ASCII + */ +#define LV_TXT_ENC LV_TXT_ENC_UTF8 + +/** While rendering text strings, break (wrap) text on these chars. */ +#define LV_TXT_BREAK_CHARS " ,.;:-_)]}" + +/** If a word is at least this long, will break wherever "prettiest". + * To disable, set to a value <= 0. */ +#define LV_TXT_LINE_BREAK_LONG_LEN 0 + +/** Minimum number of characters in a long word to put on a line before a break. + * Depends on LV_TXT_LINE_BREAK_LONG_LEN. */ +#define LV_TXT_LINE_BREAK_LONG_PRE_MIN_LEN 3 + +/** Minimum number of characters in a long word to put on a line after a break. + * Depends on LV_TXT_LINE_BREAK_LONG_LEN. */ +#define LV_TXT_LINE_BREAK_LONG_POST_MIN_LEN 3 + +/** Support bidirectional text. Allows mixing Left-to-Right and Right-to-Left text. + * The direction will be processed according to the Unicode Bidirectional Algorithm: + * https://www.w3.org/International/articles/inline-bidi-markup/uba-basics */ +#define LV_USE_BIDI 0 +#if LV_USE_BIDI + /*Set the default direction. Supported values: + *`LV_BASE_DIR_LTR` Left-to-Right + *`LV_BASE_DIR_RTL` Right-to-Left + *`LV_BASE_DIR_AUTO` detect text base direction*/ + #define LV_BIDI_BASE_DIR_DEF LV_BASE_DIR_AUTO +#endif + +/** Enable Arabic/Persian processing + * In these languages characters should be replaced with another form based on their position in the text */ +#define LV_USE_ARABIC_PERSIAN_CHARS 0 + +/*The control character to use for signaling text recoloring*/ +#define LV_TXT_COLOR_CMD "#" + +/*================== + * WIDGETS + *================*/ +/* Documentation for widgets can be found here: https://docs.lvgl.io/master/widgets/index.html . */ + +/** 1: Causes these widgets to be given default values at creation time. + * - lv_buttonmatrix_t: Get default maps: {"Btn1", "Btn2", "Btn3", "\n", "Btn4", "Btn5", ""}, else map not set. + * - lv_checkbox_t : String label set to "Check box", else set to empty string. + * - lv_dropdown_t : Options set to "Option 1", "Option 2", "Option 3", else no values are set. + * - lv_roller_t : Options set to "Option 1", "Option 2", "Option 3", "Option 4", "Option 5", else no values are set. + * - lv_label_t : Text set to "Text", else empty string. + * - lv_arclabel_t : Text set to "Arced Text", else empty string. + * */ +#define LV_WIDGETS_HAS_DEFAULT_VALUE 1 + +#define LV_USE_ANIMIMG 1 + +#define LV_USE_ARC 1 + +#define LV_USE_ARCLABEL 1 + +#define LV_USE_BAR 1 + +#define LV_USE_BUTTON 1 + +#define LV_USE_BUTTONMATRIX 1 + +#define LV_USE_CALENDAR 1 +#if LV_USE_CALENDAR + #define LV_CALENDAR_WEEK_STARTS_MONDAY 0 + #if LV_CALENDAR_WEEK_STARTS_MONDAY + #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"} + #else + #define LV_CALENDAR_DEFAULT_DAY_NAMES {"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"} + #endif + + #define LV_CALENDAR_DEFAULT_MONTH_NAMES {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"} + #define LV_USE_CALENDAR_HEADER_ARROW 1 + #define LV_USE_CALENDAR_HEADER_DROPDOWN 1 + #define LV_USE_CALENDAR_CHINESE 0 +#endif /*LV_USE_CALENDAR*/ + +#define LV_USE_CANVAS 1 + +#define LV_USE_CHART 1 + +#define LV_USE_CHECKBOX 1 + +#define LV_USE_DROPDOWN 1 /**< Requires: lv_label */ + +#define LV_USE_IMAGE 1 /**< Requires: lv_label */ + +#define LV_USE_IMAGEBUTTON 1 + +#define LV_USE_KEYBOARD 1 + +#define LV_USE_LABEL 1 +#if LV_USE_LABEL + #define LV_LABEL_TEXT_SELECTION 1 /**< Enable selecting text of the label */ + #define LV_LABEL_LONG_TXT_HINT 1 /**< Store some extra info in labels to speed up drawing of very long text */ + #define LV_LABEL_WAIT_CHAR_COUNT 3 /**< The count of wait chart */ +#endif + +#define LV_USE_LED 1 + +#define LV_USE_LINE 1 + +#define LV_USE_LIST 1 + +#define LV_USE_LOTTIE 0 /**< Requires: lv_canvas, thorvg */ + +#define LV_USE_MENU 1 + +#define LV_USE_MSGBOX 1 + +#define LV_USE_ROLLER 1 /**< Requires: lv_label */ + +#define LV_USE_SCALE 1 + +#define LV_USE_SLIDER 1 /**< Requires: lv_bar */ + +#define LV_USE_SPAN 1 +#if LV_USE_SPAN + /** A line of text can contain this maximum number of span descriptors. */ + #define LV_SPAN_SNIPPET_STACK_SIZE 64 +#endif + +#define LV_USE_SPINBOX 1 + +#define LV_USE_SPINNER 1 + +#define LV_USE_SWITCH 1 + +#define LV_USE_TABLE 1 + +#define LV_USE_TABVIEW 1 + +#define LV_USE_TEXTAREA 1 /**< Requires: lv_label */ +#if LV_USE_TEXTAREA != 0 + #define LV_TEXTAREA_DEF_PWD_SHOW_TIME 1500 /**< [ms] */ +#endif + +#define LV_USE_TILEVIEW 1 + +#define LV_USE_WIN 1 + +#define LV_USE_3DTEXTURE 0 + +/*================== + * THEMES + *==================*/ +/* Documentation for themes can be found here: https://docs.lvgl.io/master/common-widget-features/styles/styles.html#themes . */ + +/** A simple, impressive and very complete theme */ +#define LV_USE_THEME_DEFAULT 1 +#if LV_USE_THEME_DEFAULT + /** 0: Light mode; 1: Dark mode */ + #define LV_THEME_DEFAULT_DARK 0 + + /** 1: Enable grow on press */ + #define LV_THEME_DEFAULT_GROW 1 + + /** Default transition time in ms. */ + #define LV_THEME_DEFAULT_TRANSITION_TIME 80 +#endif /*LV_USE_THEME_DEFAULT*/ + +/** A very simple theme that is a good starting point for a custom theme */ +#define LV_USE_THEME_SIMPLE 1 + +/** A theme designed for monochrome displays */ +#define LV_USE_THEME_MONO 1 + +/*================== + * LAYOUTS + *==================*/ +/* Documentation for layouts can be found here: https://docs.lvgl.io/master/common-widget-features/layouts/index.html . */ + +/** A layout similar to Flexbox in CSS. */ +#define LV_USE_FLEX 1 + +/** A layout similar to Grid in CSS. */ +#define LV_USE_GRID 1 + +/*==================== + * 3RD PARTS LIBRARIES + *====================*/ +/* Documentation for libraries can be found here: https://docs.lvgl.io/master/libs/index.html . */ + +/* File system interfaces for common APIs */ + +/** Setting a default driver letter allows skipping the driver prefix in filepaths. + * Documentation about how to use the below driver-identifier letters can be found at + * https://docs.lvgl.io/master/main-modules/fs.html#lv-fs-identifier-letters . */ +#define LV_FS_DEFAULT_DRIVER_LETTER '\0' + +/** API for fopen, fread, etc. */ +#define LV_USE_FS_STDIO 0 +#if LV_USE_FS_STDIO + #define LV_FS_STDIO_LETTER '\0' /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */ + #define LV_FS_STDIO_PATH "" /**< Set the working directory. File/directory paths will be appended to it. */ + #define LV_FS_STDIO_CACHE_SIZE 0 /**< >0 to cache this number of bytes in lv_fs_read() */ +#endif + +/** API for open, read, etc. */ +#define LV_USE_FS_POSIX 0 +#if LV_USE_FS_POSIX + #define LV_FS_POSIX_LETTER '\0' /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */ + #define LV_FS_POSIX_PATH "" /**< Set the working directory. File/directory paths will be appended to it. */ + #define LV_FS_POSIX_CACHE_SIZE 0 /**< >0 to cache this number of bytes in lv_fs_read() */ +#endif + +/** API for CreateFile, ReadFile, etc. */ +#define LV_USE_FS_WIN32 0 +#if LV_USE_FS_WIN32 + #define LV_FS_WIN32_LETTER '\0' /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */ + #define LV_FS_WIN32_PATH "" /**< Set the working directory. File/directory paths will be appended to it. */ + #define LV_FS_WIN32_CACHE_SIZE 0 /**< >0 to cache this number of bytes in lv_fs_read() */ +#endif + +/** API for FATFS (needs to be added separately). Uses f_open, f_read, etc. */ +#define LV_USE_FS_FATFS 0 +#if LV_USE_FS_FATFS + #define LV_FS_FATFS_LETTER '\0' /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */ + #define LV_FS_FATFS_PATH "" /**< Set the working directory. File/directory paths will be appended to it. */ + #define LV_FS_FATFS_CACHE_SIZE 0 /**< >0 to cache this number of bytes in lv_fs_read() */ +#endif + +/** API for memory-mapped file access. */ +#define LV_USE_FS_MEMFS 0 +#if LV_USE_FS_MEMFS + #define LV_FS_MEMFS_LETTER '\0' /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */ +#endif + +/** API for LittleFs. */ +#define LV_USE_FS_LITTLEFS 0 +#if LV_USE_FS_LITTLEFS + #define LV_FS_LITTLEFS_LETTER '\0' /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */ + #define LV_FS_LITTLEFS_PATH "" /**< Set the working directory. File/directory paths will be appended to it. */ +#endif + +/** API for Arduino LittleFs. */ +#define LV_USE_FS_ARDUINO_ESP_LITTLEFS 0 +#if LV_USE_FS_ARDUINO_ESP_LITTLEFS + #define LV_FS_ARDUINO_ESP_LITTLEFS_LETTER '\0' /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */ + #define LV_FS_ARDUINO_ESP_LITTLEFS_PATH "" /**< Set the working directory. File/directory paths will be appended to it. */ +#endif + +/** API for Arduino Sd. */ +#define LV_USE_FS_ARDUINO_SD 0 +#if LV_USE_FS_ARDUINO_SD + #define LV_FS_ARDUINO_SD_LETTER '\0' /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */ + #define LV_FS_ARDUINO_SD_PATH "" /**< Set the working directory. File/directory paths will be appended to it. */ +#endif + +/** API for UEFI */ +#define LV_USE_FS_UEFI 0 +#if LV_USE_FS_UEFI + #define LV_FS_UEFI_LETTER '\0' /**< Set an upper-case driver-identifier letter for this driver (e.g. 'A'). */ +#endif + +#define LV_USE_FS_FROGFS 0 +#if LV_USE_FS_FROGFS + #define LV_FS_FROGFS_LETTER '\0' +#endif + +/** LODEPNG decoder library */ +#define LV_USE_LODEPNG 0 + +/** PNG decoder(libpng) library */ +#define LV_USE_LIBPNG 0 + +/** BMP decoder library */ +#define LV_USE_BMP 0 + +/** JPG + split JPG decoder library. + * Split JPG is a custom format optimized for embedded systems. */ +#define LV_USE_TJPGD 0 + +/** libjpeg-turbo decoder library. + * - Supports complete JPEG specifications and high-performance JPEG decoding. */ +#define LV_USE_LIBJPEG_TURBO 0 + +/** WebP decoder library */ +#define LV_USE_LIBWEBP 0 + +/** GIF decoder library */ +#define LV_USE_GIF 0 +#if LV_USE_GIF + /** GIF decoder accelerate */ + #define LV_GIF_CACHE_DECODE_DATA 0 +#endif + +/** GStreamer library */ +#define LV_USE_GSTREAMER 0 + +/** Decode bin images to RAM */ +#define LV_BIN_DECODER_RAM_LOAD 0 + +/** RLE decompress library */ +#define LV_USE_RLE 0 + +/** QR code library */ +#define LV_USE_QRCODE 0 + +/** Barcode code library */ +#define LV_USE_BARCODE 0 + +/** FreeType library */ +#define LV_USE_FREETYPE 0 +#if LV_USE_FREETYPE + /** Let FreeType use LVGL memory and file porting */ + #define LV_FREETYPE_USE_LVGL_PORT 0 + + /** Cache count of glyphs in FreeType, i.e. number of glyphs that can be cached. + * The higher the value, the more memory will be used. */ + #define LV_FREETYPE_CACHE_FT_GLYPH_CNT 256 +#endif + +/** Built-in TTF decoder */ +#define LV_USE_TINY_TTF 0 +#if LV_USE_TINY_TTF + /* Enable loading TTF data from files */ + #define LV_TINY_TTF_FILE_SUPPORT 0 + #define LV_TINY_TTF_CACHE_GLYPH_CNT 128 + #define LV_TINY_TTF_CACHE_KERNING_CNT 256 +#endif + +/** Rlottie library */ +#define LV_USE_RLOTTIE 0 + +/** Requires `LV_USE_3DTEXTURE = 1` */ +#define LV_USE_GLTF 0 + +/** Enable Vector Graphic APIs + * Requires `LV_USE_MATRIX = 1` + * and a rendering engine supporting vector graphics, e.g. + * (LV_USE_DRAW_SW and LV_USE_THORVG) or LV_USE_DRAW_VG_LITE or LV_USE_NEMA_VG. */ +#define LV_USE_VECTOR_GRAPHIC 0 + +/** Enable ThorVG (vector graphics library) from the src/libs folder. + * Requires LV_USE_VECTOR_GRAPHIC */ +#define LV_USE_THORVG_INTERNAL 0 + +/** Enable ThorVG by assuming that its installed and linked to the project + * Requires LV_USE_VECTOR_GRAPHIC */ +#define LV_USE_THORVG_EXTERNAL 0 + +/** Enable NanoVG (vector graphics library) */ +#define LV_USE_NANOVG 0 + +/** Use lvgl built-in LZ4 lib */ +#define LV_USE_LZ4_INTERNAL 0 + +/** Use external LZ4 library */ +#define LV_USE_LZ4_EXTERNAL 0 + +/*SVG library + * - Requires `LV_USE_VECTOR_GRAPHIC = 1` */ +#define LV_USE_SVG 0 +#define LV_USE_SVG_ANIMATION 0 +#define LV_USE_SVG_DEBUG 0 + +/** FFmpeg library for image decoding and playing videos. + * Supports all major image formats so do not enable other image decoder with it. */ +#define LV_USE_FFMPEG 0 +#if LV_USE_FFMPEG + /** Dump input information to stderr */ + #define LV_FFMPEG_DUMP_FORMAT 0 + /** Use lvgl file path in FFmpeg Player widget + * You won't be able to open URLs after enabling this feature. + * Note that FFmpeg image decoder will always use lvgl file system. */ + #define LV_FFMPEG_PLAYER_USE_LV_FS 0 +#endif + +/*================== + * OTHERS + *==================*/ +/* Documentation for several of the below items can be found here: https://docs.lvgl.io/master/auxiliary-modules/index.html . */ + +/** 1: Enable API to take snapshot for object */ +#define LV_USE_SNAPSHOT 0 + +/** 1: Enable system monitor component */ +#define LV_USE_SYSMON 0 +#if LV_USE_SYSMON + /** Get the idle percentage. E.g. uint32_t my_get_idle(void); */ + #define LV_SYSMON_GET_IDLE lv_os_get_idle_percent + /** 1: Enable usage of lv_os_get_proc_idle_percent.*/ + #define LV_SYSMON_PROC_IDLE_AVAILABLE 0 + #if LV_SYSMON_PROC_IDLE_AVAILABLE + /** Get the applications idle percentage. + * - Requires `LV_USE_OS == LV_OS_PTHREAD` */ + #define LV_SYSMON_GET_PROC_IDLE lv_os_get_proc_idle_percent + #endif + + /** 1: Show CPU usage and FPS count. + * - Requires `LV_USE_SYSMON = 1` */ + #define LV_USE_PERF_MONITOR 1 + #if LV_USE_PERF_MONITOR + #define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT + + /** 0: Displays performance data on the screen; 1: Prints performance data using log. */ + #define LV_USE_PERF_MONITOR_LOG_MODE 0 + #endif + + /** 1: Show used memory and memory fragmentation. + * - Requires `LV_USE_STDLIB_MALLOC = LV_STDLIB_BUILTIN` + * - Requires `LV_USE_SYSMON = 1`*/ + #define LV_USE_MEM_MONITOR 1 + #if LV_USE_MEM_MONITOR + #define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT + #endif +#endif /*LV_USE_SYSMON*/ + +/** 1: Enable runtime performance profiler */ +#define LV_USE_PROFILER 0 +#if LV_USE_PROFILER + /** 1: Enable the built-in profiler */ + #define LV_USE_PROFILER_BUILTIN 1 + #if LV_USE_PROFILER_BUILTIN + /** Default profiler trace buffer size */ + #define LV_PROFILER_BUILTIN_BUF_SIZE (16 * 1024) /**< [bytes] */ + #define LV_PROFILER_BUILTIN_DEFAULT_ENABLE 1 + #define LV_USE_PROFILER_BUILTIN_POSIX 0 /**< Enable POSIX profiler port */ + #endif + + /** Header to include for profiler */ + #define LV_PROFILER_INCLUDE "lvgl/src/misc/lv_profiler_builtin.h" + + /** Profiler start point function */ + #define LV_PROFILER_BEGIN LV_PROFILER_BUILTIN_BEGIN + + /** Profiler end point function */ + #define LV_PROFILER_END LV_PROFILER_BUILTIN_END + + /** Profiler start point function with custom tag */ + #define LV_PROFILER_BEGIN_TAG LV_PROFILER_BUILTIN_BEGIN_TAG + + /** Profiler end point function with custom tag */ + #define LV_PROFILER_END_TAG LV_PROFILER_BUILTIN_END_TAG + + /*Enable layout profiler*/ + #define LV_PROFILER_LAYOUT 1 + + /*Enable disp refr profiler*/ + #define LV_PROFILER_REFR 1 + + /*Enable draw profiler*/ + #define LV_PROFILER_DRAW 1 + + /*Enable indev profiler*/ + #define LV_PROFILER_INDEV 1 + + /*Enable decoder profiler*/ + #define LV_PROFILER_DECODER 1 + + /*Enable font profiler*/ + #define LV_PROFILER_FONT 1 + + /*Enable fs profiler*/ + #define LV_PROFILER_FS 1 + + /*Enable style profiler*/ + #define LV_PROFILER_STYLE 0 + + /*Enable timer profiler*/ + #define LV_PROFILER_TIMER 1 + + /*Enable cache profiler*/ + #define LV_PROFILER_CACHE 1 + + /*Enable event profiler*/ + #define LV_PROFILER_EVENT 1 +#endif + +/** 1: Enable Monkey test */ +#define LV_USE_MONKEY 0 + +/** 1: Enable grid navigation */ +#define LV_USE_GRIDNAV 0 + +/** 1: Enable `lv_obj` fragment logic */ +#define LV_USE_FRAGMENT 0 + +/** 1: Support using images as font in label or span widgets */ +#define LV_USE_IMGFONT 0 + +/** 1: Enable an observer pattern implementation */ +#define LV_USE_OBSERVER 1 + +/** 1: Enable Pinyin input method + * - Requires: lv_keyboard */ +#define LV_USE_IME_PINYIN 0 +#if LV_USE_IME_PINYIN + /** 1: Use default thesaurus. + * @note If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesaurus. */ + #define LV_IME_PINYIN_USE_DEFAULT_DICT 1 + /** Set maximum number of candidate panels that can be displayed. + * @note This needs to be adjusted according to size of screen. */ + #define LV_IME_PINYIN_CAND_TEXT_NUM 6 + + /** Use 9-key input (k9). */ + #define LV_IME_PINYIN_USE_K9_MODE 1 + #if LV_IME_PINYIN_USE_K9_MODE == 1 + #define LV_IME_PINYIN_K9_CAND_TEXT_NUM 3 + #endif /*LV_IME_PINYIN_USE_K9_MODE*/ +#endif + +/** 1: Enable file explorer. + * - Requires: lv_table */ +#define LV_USE_FILE_EXPLORER 0 +#if LV_USE_FILE_EXPLORER + /** Maximum length of path */ + #define LV_FILE_EXPLORER_PATH_MAX_LEN (128) + /** Quick access bar, 1:use, 0:do not use. + * - Requires: lv_list */ + #define LV_FILE_EXPLORER_QUICK_ACCESS 1 +#endif + +/** 1: Enable Font manager */ +#define LV_USE_FONT_MANAGER 0 +#if LV_USE_FONT_MANAGER + +/**Font manager name max length*/ +#define LV_FONT_MANAGER_NAME_MAX_LEN 32 + +#endif + +/** Enable emulated input devices, time emulation, and screenshot compares. */ +#define LV_USE_TEST 0 +#if LV_USE_TEST + +/** Enable `lv_test_screenshot_compare`. + * Requires lodepng and a few MB of extra RAM. */ +#define LV_USE_TEST_SCREENSHOT_COMPARE 0 + +#if LV_USE_TEST_SCREENSHOT_COMPARE + /** 1: Automatically create missing reference images*/ + #define LV_TEST_SCREENSHOT_CREATE_REFERENCE_IMAGE 1 +#endif /*LV_USE_TEST_SCREENSHOT_COMPARE*/ + +#endif /*LV_USE_TEST*/ + +/** 1: Enable text translation support */ +#define LV_USE_TRANSLATION 0 + +/*1: Enable color filter style*/ +#define LV_USE_COLOR_FILTER 0 + +/*================== + * DEVICES + *==================*/ + +/** Use SDL to open window on PC and handle mouse and keyboard. */ +#define LV_USE_SDL 0 +#if LV_USE_SDL + #define LV_SDL_INCLUDE_PATH + #define LV_SDL_RENDER_MODE LV_DISPLAY_RENDER_MODE_DIRECT /**< LV_DISPLAY_RENDER_MODE_DIRECT is recommended for best performance */ + #define LV_SDL_BUF_COUNT 1 /**< 1 or 2 */ + #define LV_SDL_ACCELERATED 1 /**< 1: Use hardware acceleration*/ + #define LV_SDL_FULLSCREEN 0 /**< 1: Make the window full screen by default */ + #define LV_SDL_DIRECT_EXIT 1 /**< 1: Exit the application when all SDL windows are closed */ + #define LV_SDL_MOUSEWHEEL_MODE LV_SDL_MOUSEWHEEL_MODE_ENCODER /*LV_SDL_MOUSEWHEEL_MODE_ENCODER/CROWN*/ +#endif + +/** Use X11 to open window on Linux desktop and handle mouse and keyboard */ +#define LV_USE_X11 0 +#if LV_USE_X11 + #define LV_X11_DIRECT_EXIT 1 /**< Exit application when all X11 windows have been closed */ + #define LV_X11_DOUBLE_BUFFER 1 /**< Use double buffers for rendering */ + /* Select only 1 of the following render modes (LV_X11_RENDER_MODE_PARTIAL preferred!). */ + #define LV_X11_RENDER_MODE_PARTIAL 1 /**< Partial render mode (preferred) */ + #define LV_X11_RENDER_MODE_DIRECT 0 /**< Direct render mode */ + #define LV_X11_RENDER_MODE_FULL 0 /**< Full render mode */ +#endif + +/** Use Wayland to open a window and handle input on Linux or BSD desktops */ +#define LV_USE_WAYLAND 0 +#if LV_USE_WAYLAND + #define LV_WAYLAND_DIRECT_EXIT 1 /**< 1: Exit the application when all Wayland windows are closed */ +#endif + +/** Driver for /dev/fb */ +#define LV_USE_LINUX_FBDEV 0 +#if LV_USE_LINUX_FBDEV + #define LV_LINUX_FBDEV_BSD 0 + #define LV_LINUX_FBDEV_RENDER_MODE LV_DISPLAY_RENDER_MODE_PARTIAL + #define LV_LINUX_FBDEV_BUFFER_COUNT 0 + #define LV_LINUX_FBDEV_BUFFER_SIZE 60 + #define LV_LINUX_FBDEV_MMAP 1 +#endif + +/** Use Nuttx to open window and handle touchscreen */ +#define LV_USE_NUTTX 0 + +#if LV_USE_NUTTX + #define LV_USE_NUTTX_INDEPENDENT_IMAGE_HEAP 0 + + /** Use independent image heap for default draw buffer */ + #define LV_NUTTX_DEFAULT_DRAW_BUF_USE_INDEPENDENT_IMAGE_HEAP 0 + + #define LV_USE_NUTTX_LIBUV 0 + + /** Use Nuttx custom init API to open window and handle touchscreen */ + #define LV_USE_NUTTX_CUSTOM_INIT 0 + + /** Driver for /dev/lcd */ + #define LV_USE_NUTTX_LCD 0 + #if LV_USE_NUTTX_LCD + #define LV_NUTTX_LCD_BUFFER_COUNT 0 + #define LV_NUTTX_LCD_BUFFER_SIZE 60 + #endif + + /** Driver for /dev/input */ + #define LV_USE_NUTTX_TOUCHSCREEN 0 + + /** Touchscreen cursor size in pixels(<=0: disable cursor) */ + #define LV_NUTTX_TOUCHSCREEN_CURSOR_SIZE 0 + + /** Driver for /dev/mouse */ + #define LV_USE_NUTTX_MOUSE 0 + + /** Mouse movement step (pixels) */ + #define LV_USE_NUTTX_MOUSE_MOVE_STEP 1 + + /*NuttX trace file and its path*/ + #define LV_USE_NUTTX_TRACE_FILE 0 + #if LV_USE_NUTTX_TRACE_FILE + #define LV_NUTTX_TRACE_FILE_PATH "/data/lvgl-trace.log" + #endif + +#endif + +/** Driver for /dev/dri/card */ +#define LV_USE_LINUX_DRM 0 + +#if LV_USE_LINUX_DRM + + /* Use the MESA GBM library to allocate DMA buffers that can be + * shared across sub-systems and libraries using the Linux DMA-BUF API. + * The GBM library aims to provide a platform independent memory management system + * it supports the major GPU vendors - This option requires linking with libgbm */ + #define LV_USE_LINUX_DRM_GBM_BUFFERS 0 +#endif + +/** Interface for TFT_eSPI */ +#define LV_USE_TFT_ESPI 0 + +/** Interface for Lovyan_GFX */ +#define LV_USE_LOVYAN_GFX 0 + +#if LV_USE_LOVYAN_GFX + #define LV_LGFX_USER_INCLUDE "lv_lgfx_user.hpp" + +#endif /*LV_USE_LOVYAN_GFX*/ + +/** Driver for evdev input devices */ +#define LV_USE_EVDEV 0 + +/** Driver for libinput input devices */ +#define LV_USE_LIBINPUT 0 + +#if LV_USE_LIBINPUT + #define LV_LIBINPUT_BSD 0 + + /** Full keyboard support */ + #define LV_LIBINPUT_XKB 0 + #if LV_LIBINPUT_XKB + /** "setxkbmap -query" can help find the right values for your keyboard */ + #define LV_LIBINPUT_XKB_KEY_MAP { .rules = NULL, .model = "pc101", .layout = "us", .variant = NULL, .options = NULL } + #endif +#endif + +/* Drivers for LCD devices connected via SPI/parallel port */ +#define LV_USE_ST7735 0 +#define LV_USE_ST7789 0 +#define LV_USE_ST7796 0 +#define LV_USE_ILI9341 0 +#define LV_USE_FT81X 0 +#define LV_USE_NV3007 0 + +#if (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341 | LV_USE_NV3007) + #define LV_USE_GENERIC_MIPI 1 +#else + #define LV_USE_GENERIC_MIPI 0 +#endif + +/** Driver for Renesas GLCD */ +#define LV_USE_RENESAS_GLCDC 0 + +/** Driver for ST LTDC */ +#define LV_USE_ST_LTDC 0 +#if LV_USE_ST_LTDC + /* Only used for partial. */ + #define LV_ST_LTDC_USE_DMA2D_FLUSH 0 +#endif + +/** Driver for NXP ELCDIF */ +#define LV_USE_NXP_ELCDIF 0 + +/** LVGL Windows backend */ +#define LV_USE_WINDOWS 0 + +/** LVGL UEFI backend */ +#define LV_USE_UEFI 1 +#if LV_USE_UEFI + #define LV_USE_UEFI_INCLUDE "lv_uefi_edk2.h" /**< Header that hides the actual framework (EDK2, gnu-efi, ...) */ + #define LV_UEFI_USE_MEMORY_SERVICES 1 /**< Use the memory functions from the boot services table */ +#endif + +/** Use a generic OpenGL driver that can be used to embed in other applications or used with GLFW/EGL + * - Requires LV_USE_MATRIX. + */ +#define LV_USE_OPENGLES 0 +#if LV_USE_OPENGLES + #define LV_USE_OPENGLES_DEBUG 1 /**< Enable or disable debug for opengles */ +#endif + +/** Use GLFW to open window on PC and handle mouse and keyboard. Requires*/ +#define LV_USE_GLFW 0 + + +/** QNX Screen display and input drivers */ +#define LV_USE_QNX 0 +#if LV_USE_QNX + #define LV_QNX_BUF_COUNT 1 /**< 1 or 2 */ +#endif + +/** Enable or disable for external data and destructor function */ +#define LV_USE_EXT_DATA 0 + +/*===================== +* BUILD OPTIONS +*======================*/ + +/** Enable examples to be built with the library. */ +#define LV_BUILD_EXAMPLES 1 + +/** Build the demos */ +#define LV_BUILD_DEMOS 1 + +/*=================== + * DEMO USAGE + ====================*/ + +#if LV_BUILD_DEMOS + /** Show some widgets. This might be required to increase `LV_MEM_SIZE`. */ + #define LV_USE_DEMO_WIDGETS 0 + + /** Demonstrate usage of encoder and keyboard. */ + #define LV_USE_DEMO_KEYPAD_AND_ENCODER 1 + + /** Benchmark your system */ + #define LV_USE_DEMO_BENCHMARK 0 + + #if LV_USE_DEMO_BENCHMARK + /** Use fonts where bitmaps are aligned 16 byte and has Nx16 byte stride */ + #define LV_DEMO_BENCHMARK_ALIGNED_FONTS 0 + #endif + + /** Render test for each primitive. + * - Requires at least 480x272 display. */ + #define LV_USE_DEMO_RENDER 0 + + /** Stress test for LVGL */ + #define LV_USE_DEMO_STRESS 0 + + /** Music player demo */ + #define LV_USE_DEMO_MUSIC 0 + #if LV_USE_DEMO_MUSIC + #define LV_DEMO_MUSIC_SQUARE 0 + #define LV_DEMO_MUSIC_LANDSCAPE 0 + #define LV_DEMO_MUSIC_ROUND 0 + #define LV_DEMO_MUSIC_LARGE 0 + #define LV_DEMO_MUSIC_AUTO_PLAY 0 + #endif + + /** Vector graphic demo */ + #define LV_USE_DEMO_VECTOR_GRAPHIC 0 + + /** GLTF demo */ + #define LV_USE_DEMO_GLTF 0 + + /*--------------------------- + * Demos from lvgl/lv_demos + ---------------------------*/ + + /** Flex layout demo */ + #define LV_USE_DEMO_FLEX_LAYOUT 0 + + /** Smart-phone like multi-language demo */ + #define LV_USE_DEMO_MULTILANG 0 + + /*E-bike demo with Lottie animations (if LV_USE_LOTTIE is enabled)*/ + #define LV_USE_DEMO_EBIKE 0 + #if LV_USE_DEMO_EBIKE + #define LV_DEMO_EBIKE_PORTRAIT 0 /*0: for 480x270..480x320, 1: for 480x800..720x1280*/ + #endif + + /** High-resolution demo */ + #define LV_USE_DEMO_HIGH_RES 0 + + /* Smart watch demo */ + #define LV_USE_DEMO_SMARTWATCH 0 +#endif /* LV_BUILD_DEMOS */ + +/*--END OF LV_CONF_H--*/ + +#endif /*LV_CONF_H*/ + +#endif /*End of "Content enable"*/ diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/lv_port_indev.c b/Experimental/LvglSpikePkg/Library/LvglLib/lv_port_indev.c new file mode 100644 index 0000000..5a8f926 --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/lv_port_indev.c @@ -0,0 +1,567 @@ +/** + * @file lv_port_indev.c + * + */ + + +/********************* + * INCLUDES + *********************/ +#include "lv_port_indev.h" + +#include "lvgl/src/indev/lv_indev_private.h" + +/********************* + * DEFINES + *********************/ + +extern const lv_img_dsc_t mouse_cursor_icon; + +typedef struct { + EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer; + EFI_ABSOLUTE_POINTER_PROTOCOL *AbsPointer; + INTN LastCursorX; + INTN LastCursorY; + UINT64 LastAbsZ; + INT32 WheelDelta; + UINT32 ActiveButtons; + BOOLEAN LeftButton; + BOOLEAN RightButton; +} LVGL_UEFI_MOUSE; + +// +// LVGL treats any non-zero pointer.diff as one full detent and scrolls by +// scroll_limit (default 10px) per call (lv_indev.c:1638). QEMU usb-mouse can +// send several wheel reports per perceived host wheel motion, so we ratchet: +// accumulate raw Z counts and only emit one ±1 detent per N counts. +// Lower this number = more sensitive. Raise = less sensitive. +// +#define LVGL_WHEEL_COUNTS_PER_DETENT 8 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ + +static void mouse_read(lv_indev_t * indev, lv_indev_data_t * data); + +static void keypad_read(lv_indev_t * indev, lv_indev_data_t * data); + + +/********************** + * STATIC VARIABLES + **********************/ +lv_indev_t * indev_mouse; +lv_indev_t * indev_keypad; + +LVGL_UEFI_MOUSE mLvglUefiMouse; + +// +// Protocol-install notification: fires when any driver installs +// EFI_ABSOLUTE_POINTER_PROTOCOL (e.g. when USB mouse binds during BDS +// ConnectAll). At constructor time USB is not yet enumerated, so the +// initial lv_uefi_mouse_create() call fails; this callback retries +// once the pointer protocol actually appears. +// +STATIC EFI_EVENT mPointerNotifyEvent = NULL; +STATIC VOID *mPointerNotifyRegistration = NULL; +STATIC lv_display_t *mPointerNotifyDisplay = NULL; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +static void keypad_read(lv_indev_t * indev_drv, lv_indev_data_t * data) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx; + EFI_KEY_DATA KeyData; + UINT32 KeyShift; + + data->key = 0; + + Status = gBS->HandleProtocol (gST->ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID **)&TxtInEx); + Status = TxtInEx->ReadKeyStrokeEx (TxtInEx, &KeyData); + if (!EFI_ERROR (Status)) { + switch (KeyData.Key.UnicodeChar) { + case CHAR_CARRIAGE_RETURN: + data->key = LV_KEY_ENTER; + break; + + case CHAR_BACKSPACE: + data->key = LV_KEY_BACKSPACE; + break; + + case CHAR_TAB: + KeyShift = KeyData.KeyState.KeyShiftState; + data->key = (KeyShift & EFI_SHIFT_STATE_VALID) && (KeyShift & (EFI_RIGHT_SHIFT_PRESSED | EFI_LEFT_SHIFT_PRESSED)) ? LV_KEY_PREV : LV_KEY_NEXT; + break; + + case CHAR_NULL: + switch (KeyData.Key.ScanCode) { + case SCAN_UP: + data->key = LV_KEY_UP; + break; + + case SCAN_DOWN: + data->key = LV_KEY_DOWN; + break; + + case SCAN_RIGHT: + data->key = LV_KEY_RIGHT; + break; + + case SCAN_LEFT: + data->key = LV_KEY_LEFT; + break; + + case SCAN_ESC: + data->key = LV_KEY_ESC; + break; + + case SCAN_DELETE: + data->key = LV_KEY_DEL; + break; + + case SCAN_PAGE_DOWN: + data->key = LV_KEY_NEXT; + break; + + case SCAN_PAGE_UP: + data->key = LV_KEY_PREV; + break; + + case SCAN_HOME: + data->key = LV_KEY_HOME; + break; + + case SCAN_END: + data->key = LV_KEY_END; + break; + + case SCAN_F1: data->key = LV_KEY_F1; break; + case SCAN_F2: data->key = LV_KEY_F2; break; + case SCAN_F3: data->key = LV_KEY_F3; break; + case SCAN_F4: data->key = LV_KEY_F4; break; + case SCAN_F5: data->key = LV_KEY_F5; break; + case SCAN_F6: data->key = LV_KEY_F6; break; + case SCAN_F7: data->key = LV_KEY_F7; break; + case SCAN_F8: data->key = LV_KEY_F8; break; + case SCAN_F9: data->key = LV_KEY_F9; break; + case SCAN_F10: data->key = LV_KEY_F10; break; + case SCAN_F11: data->key = LV_KEY_F11; break; + case SCAN_F12: data->key = LV_KEY_F12; break; + + default: + break; + } + break; + + case CHAR_LINEFEED: + break; + + default: + data->key = KeyData.Key.UnicodeChar; + break; + } + + data->state = LV_INDEV_STATE_PRESSED; + } else { + data->state = LV_INDEV_STATE_RELEASED; + } + +} + + +lv_indev_t * lv_uefi_keyboard_create(void) +{ + lv_indev_t * indev = lv_indev_create(); + LV_ASSERT_MALLOC(indev); + if(indev == NULL) { + return NULL; + } + + lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD); + lv_indev_set_read_cb(indev, keypad_read); + + return indev; +} + + +EFI_STATUS +EFIAPI +GetXYZ ( + lv_indev_t * indev_drv + ) +{ + EFI_STATUS Status; + EFI_ABSOLUTE_POINTER_PROTOCOL *AbsPointer = NULL; + EFI_ABSOLUTE_POINTER_STATE AbsState; + EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer = NULL; + EFI_SIMPLE_POINTER_STATE SimpleState; + + lv_display_t *disp = lv_indev_get_display(indev_drv); + + int32_t hor_res = lv_display_get_horizontal_resolution(disp); + int32_t ver_res = lv_display_get_vertical_resolution(disp); + + if (mLvglUefiMouse.AbsPointer != NULL) { + AbsPointer = mLvglUefiMouse.AbsPointer; + Status = AbsPointer->GetState (AbsPointer, &AbsState); + if (!EFI_ERROR (Status)) { + mLvglUefiMouse.LastCursorX = (AbsState.CurrentX * hor_res) / (AbsPointer->Mode->AbsoluteMaxX - AbsPointer->Mode->AbsoluteMinX); + if (mLvglUefiMouse.LastCursorX > hor_res - 1) { + mLvglUefiMouse.LastCursorX = hor_res - 1; + } + mLvglUefiMouse.LastCursorY = (AbsState.CurrentY * ver_res) / (AbsPointer->Mode->AbsoluteMaxY - AbsPointer->Mode->AbsoluteMinY); + if (mLvglUefiMouse.LastCursorY > ver_res - 1) { + mLvglUefiMouse.LastCursorY = ver_res - 1; + } + mLvglUefiMouse.LeftButton = AbsState.ActiveButtons & BIT0; + + mLvglUefiMouse.WheelDelta += (INT32)(AbsState.CurrentZ - mLvglUefiMouse.LastAbsZ); + mLvglUefiMouse.LastAbsZ = AbsState.CurrentZ; + + return EFI_SUCCESS; + } + } else if (mLvglUefiMouse.SimplePointer != NULL) { + SimplePointer = mLvglUefiMouse.SimplePointer; + Status = SimplePointer->GetState (SimplePointer, &SimpleState); + if (!EFI_ERROR (Status)) { + mLvglUefiMouse.LastCursorX += (SimpleState.RelativeMovementX * hor_res) / (INT32)(50 * SimplePointer->Mode->ResolutionX); + if (mLvglUefiMouse.LastCursorX > hor_res - 1) { + mLvglUefiMouse.LastCursorX = hor_res - 1; + } + if (mLvglUefiMouse.LastCursorX < 0) { + mLvglUefiMouse.LastCursorX = 0; + } + mLvglUefiMouse.LastCursorY += (SimpleState.RelativeMovementY * ver_res) / (INT32)(50 * SimplePointer->Mode->ResolutionY); + if (mLvglUefiMouse.LastCursorY > ver_res - 1) { + mLvglUefiMouse.LastCursorY = ver_res - 1; + } + if (mLvglUefiMouse.LastCursorY < 0) { + mLvglUefiMouse.LastCursorY = 0; + } + + mLvglUefiMouse.LeftButton = SimpleState.LeftButton; + mLvglUefiMouse.RightButton = SimpleState.RightButton; + + mLvglUefiMouse.WheelDelta += SimpleState.RelativeMovementZ; + + return EFI_SUCCESS; + } + } + + return EFI_NOT_READY; +} + + +EFI_STATUS +EFIAPI +EfiMouseInit ( + VOID + ) +{ + EFI_STATUS Status; + EFI_ABSOLUTE_POINTER_PROTOCOL *AbsPointer = NULL; + EFI_SIMPLE_POINTER_PROTOCOL *SimplePointer = NULL; + EFI_HANDLE *HandleBuffer = NULL; + UINTN HandleCount, Index; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + BOOLEAN AbsPointerSupport = FALSE; + BOOLEAN SimplePointerSupport = FALSE; + + HandleCount = 0; + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiAbsolutePointerProtocolGuid, NULL, &HandleCount, &HandleBuffer); + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath); + if (!EFI_ERROR (Status)) { + AbsPointerSupport = TRUE; + break; + } + } + if (HandleBuffer!= NULL) { + FreePool (HandleBuffer); + HandleBuffer = NULL; + } + + if (AbsPointerSupport) { + goto StartInit; + } + + HandleCount = 0; + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimplePointerProtocolGuid, NULL, &HandleCount, &HandleBuffer); + for (Index = 0; Index < HandleCount; Index++) { + Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&DevicePath); + if (!EFI_ERROR (Status)) { + SimplePointerSupport = TRUE; + break; + } + } + if (HandleBuffer!= NULL) { + FreePool (HandleBuffer); + HandleBuffer = NULL; + } + + if (!AbsPointerSupport && !SimplePointerSupport) { + return EFI_UNSUPPORTED; + } + +StartInit: + DebugPrint (DEBUG_INFO, "EfiMouseInit()\n"); + + DebugPrint (DEBUG_INFO, "Abs Pointer: %d\n", AbsPointerSupport); + DebugPrint (DEBUG_INFO, "Simple Pointer: %d\n", SimplePointerSupport); + + mLvglUefiMouse.AbsPointer = NULL; + mLvglUefiMouse.SimplePointer = NULL; + mLvglUefiMouse.LastAbsZ = 0; + mLvglUefiMouse.WheelDelta = 0; + mLvglUefiMouse.ActiveButtons = 0; + mLvglUefiMouse.LeftButton = FALSE; + mLvglUefiMouse.RightButton = FALSE; + + if (AbsPointerSupport) { + Status = gBS->HandleProtocol (gST->ConsoleInHandle, &gEfiAbsolutePointerProtocolGuid, (VOID **)&AbsPointer); + if (!EFI_ERROR (Status) && AbsPointer != NULL) { + mLvglUefiMouse.AbsPointer = AbsPointer; + } + } + + if (SimplePointerSupport && !AbsPointerSupport) { + Status = gBS->HandleProtocol (gST->ConsoleInHandle, &gEfiSimplePointerProtocolGuid, (VOID **)&SimplePointer); + if (!EFI_ERROR (Status) && SimplePointer != NULL) { + mLvglUefiMouse.SimplePointer = SimplePointer; + } + } + + return Status; +} + + +// +// Scroll amount in pixels per wheel detent. LVGL's built-in +// indev_proc_pointer_diff path uses indev->scroll_limit (default 10px) but +// only fires when pointer.last_pressed is set — i.e. after the user has +// clicked or dragged once. We bypass that by directly scrolling the +// nearest scrollable ancestor of the obj under the cursor. +// +#define LVGL_WHEEL_SCROLL_PIXELS 40 + +static lv_obj_t * find_scrollable_at_point(lv_display_t * disp, lv_point_t * p) +{ + lv_obj_t *screen = lv_display_get_screen_active(disp); + if (screen == NULL) return NULL; + + lv_obj_t *hit = lv_indev_search_obj(screen, p); + if (hit == NULL) return NULL; + + for (lv_obj_t *o = hit; o != NULL; o = lv_obj_get_parent(o)) { + if (lv_obj_has_flag(o, LV_OBJ_FLAG_SCROLLABLE) && + lv_obj_get_scroll_top(o) + lv_obj_get_scroll_bottom(o) > 0) + { + return o; + } + } + return NULL; +} + +static void mouse_read(lv_indev_t * indev_drv, lv_indev_data_t * data) +{ + int wheel_step = 0; + + GetXYZ(indev_drv); + data->point.x = mLvglUefiMouse.LastCursorX; + data->point.y = mLvglUefiMouse.LastCursorY; + if (mLvglUefiMouse.LeftButton) { + data->state = LV_INDEV_STATE_PRESSED; + } else { + data->state = LV_INDEV_STATE_RELEASED; + } + + if (mLvglUefiMouse.WheelDelta >= LVGL_WHEEL_COUNTS_PER_DETENT) { + wheel_step = 1; + mLvglUefiMouse.WheelDelta -= LVGL_WHEEL_COUNTS_PER_DETENT; + } else if (mLvglUefiMouse.WheelDelta <= -LVGL_WHEEL_COUNTS_PER_DETENT) { + wheel_step = -1; + mLvglUefiMouse.WheelDelta += LVGL_WHEEL_COUNTS_PER_DETENT; + } + + data->enc_diff = 0; + + if (wheel_step != 0) { + lv_display_t *disp = lv_indev_get_display(indev_drv); + lv_point_t p = { (int32_t)data->point.x, (int32_t)data->point.y }; + lv_obj_t *target = find_scrollable_at_point(disp, &p); + if (target != NULL) { + lv_obj_scroll_by_bounded( + target, + 0, + wheel_step * LVGL_WHEEL_SCROLL_PIXELS, + LV_ANIM_OFF + ); + } + } +} + + +void lv_indev_set_cusor_start(lv_indev_t * indev) +{ + if(indev == NULL) return; + + lv_display_t *disp = lv_indev_get_display(indev); + + int32_t hor_res = lv_display_get_horizontal_resolution(disp); + int32_t ver_res = lv_display_get_vertical_resolution(disp); + + indev->pointer.act_point.x = hor_res / 2; + indev->pointer.act_point.y = ver_res / 2; + mLvglUefiMouse.LastCursorX = hor_res / 2; + mLvglUefiMouse.LastCursorY = ver_res / 2; + mLvglUefiMouse.WheelDelta = 0; + mLvglUefiMouse.ActiveButtons = 0; + mLvglUefiMouse.LeftButton = FALSE; + mLvglUefiMouse.RightButton = FALSE; +} + +lv_indev_t * lv_uefi_mouse_create(lv_display_t * disp) +{ + // + // Idempotent: if a pointer indev already exists, return it. + // This lets callers invoke lv_uefi_mouse_create() lazily — e.g. after BDS + // has connected USB — without worrying about whether the constructor-time + // attempt succeeded. + // + lv_indev_t * existing = NULL; + while ((existing = lv_indev_get_next(existing)) != NULL) { + if (lv_indev_get_type(existing) == LV_INDEV_TYPE_POINTER) { + return existing; + } + } + + if (EfiMouseInit() != EFI_SUCCESS) { + return NULL; + } + + lv_indev_t * indev = lv_indev_create(); + LV_ASSERT_MALLOC(indev); + if(indev == NULL) { + return NULL; + } + + DebugPrint (DEBUG_INFO, "Create Mouse\n"); + + lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); + lv_indev_set_read_cb(indev, mouse_read); + lv_indev_set_display (indev, disp); + + LV_IMG_DECLARE(mouse_cursor_icon); + lv_obj_t * mouse_cursor = lv_image_create(lv_screen_active()); + lv_image_set_src(mouse_cursor, &mouse_cursor_icon); + lv_indev_set_cusor_start(indev); + lv_indev_set_cursor(indev, mouse_cursor); + + return indev; +} + +STATIC +VOID +EFIAPI +OnPointerProtocolInstalled ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + if (mPointerNotifyDisplay != NULL) { + lv_uefi_mouse_create (mPointerNotifyDisplay); + } +} + +void lv_port_indev_init(lv_display_t * disp) +{ + lv_uefi_mouse_create(disp); + + lv_uefi_keyboard_create(); + + // + // If the mouse couldn't be created yet (USB not connected at this point + // in boot), register for notification when the pointer protocol shows up. + // + if (mPointerNotifyEvent == NULL) { + EFI_STATUS Status; + + mPointerNotifyDisplay = disp; + + Status = gBS->CreateEvent ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + OnPointerProtocolInstalled, + NULL, + &mPointerNotifyEvent + ); + if (!EFI_ERROR (Status)) { + gBS->RegisterProtocolNotify ( + &gEfiAbsolutePointerProtocolGuid, + mPointerNotifyEvent, + &mPointerNotifyRegistration + ); + } + } +} + +void lv_uefi_keypad_drain(void) +{ + EFI_STATUS Status; + EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx; + EFI_KEY_DATA KeyData; + lv_indev_t *Indev; + + // + // Drain all pending keystrokes from the EFI console. + // + Status = gBS->HandleProtocol (gST->ConsoleInHandle, &gEfiSimpleTextInputExProtocolGuid, (VOID **)&TxtInEx); + if (!EFI_ERROR (Status)) { + while (!EFI_ERROR (TxtInEx->ReadKeyStrokeEx (TxtInEx, &KeyData))) { + } + } + + // + // Force every keypad indev to RELEASED so LVGL doesn't interpret the + // pending ENTER release as a click on the next focused widget. + // + Indev = NULL; + while ((Indev = lv_indev_get_next (Indev)) != NULL) { + if (lv_indev_get_type (Indev) == LV_INDEV_TYPE_KEYPAD) { + Indev->keypad.last_state = LV_INDEV_STATE_RELEASED; + Indev->keypad.last_key = 0; + } + } +} + +void lv_port_indev_close() +{ + if (mPointerNotifyEvent != NULL) { + gBS->CloseEvent (mPointerNotifyEvent); + mPointerNotifyEvent = NULL; + mPointerNotifyRegistration = NULL; + mPointerNotifyDisplay = NULL; + } + + mLvglUefiMouse.AbsPointer = NULL; + mLvglUefiMouse.SimplePointer = NULL; + mLvglUefiMouse.LastCursorX = 0; + mLvglUefiMouse.LastCursorY = 0; + mLvglUefiMouse.LastAbsZ = 0; + mLvglUefiMouse.WheelDelta = 0; + mLvglUefiMouse.ActiveButtons = 0; + mLvglUefiMouse.LeftButton = FALSE; + mLvglUefiMouse.RightButton = FALSE; +} + diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/lv_port_indev.h b/Experimental/LvglSpikePkg/Library/LvglLib/lv_port_indev.h new file mode 100644 index 0000000..3b32a61 --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/lv_port_indev.h @@ -0,0 +1,71 @@ + +/** + * @file lv_port_indev.h + * + */ + +#ifndef LV_PORT_INDEV_H +#define LV_PORT_INDEV_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#if defined(LV_LVGL_H_INCLUDE_SIMPLE) +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +#include "LvglLibCommon.h" + +/********************* + * DEFINES + *********************/ + +// +// Custom LVGL key codes for EFI function keys F1-F12. +// Mirror of the defines in . +// +#define LV_KEY_F1 0x0101U +#define LV_KEY_F2 0x0102U +#define LV_KEY_F3 0x0103U +#define LV_KEY_F4 0x0104U +#define LV_KEY_F5 0x0105U +#define LV_KEY_F6 0x0106U +#define LV_KEY_F7 0x0107U +#define LV_KEY_F8 0x0108U +#define LV_KEY_F9 0x0109U +#define LV_KEY_F10 0x010AU +#define LV_KEY_F11 0x010BU +#define LV_KEY_F12 0x010CU + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +void lv_port_indev_init(lv_display_t * disp); + +void lv_port_indev_close(); + +/** + Drain the EFI keyboard buffer and reset the LVGL keypad indev state so that + no pending key-press leaks into the next event loop. +**/ +void lv_uefi_keypad_drain(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_PORT_INDEV_TEMPL_H*/ diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/lv_uefi_display.c b/Experimental/LvglSpikePkg/Library/LvglLib/lv_uefi_display.c new file mode 100644 index 0000000..4f3c841 --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/lv_uefi_display.c @@ -0,0 +1,83 @@ +#include "LvglLibCommon.h" + + +typedef struct { + EFI_GRAPHICS_OUTPUT_PROTOCOL *EfiGop; + uint8_t *buffer[2]; +} uefi_disp_data_t; + + +static void uefi_disp_delete_evt_cb(lv_event_t * e) +{ + lv_display_t * disp = lv_event_get_user_data(e); + uefi_disp_data_t * uefi_disp_data = lv_display_get_driver_data(disp); + + free(uefi_disp_data->buffer[0]); + free(uefi_disp_data->buffer[1]); + + lv_free(uefi_disp_data); +} + + +void uefi_disp_flush(lv_display_t * disp, const lv_area_t * area, lv_color32_t * color32_p) +{ + UINTN Width, Heigth; + uefi_disp_data_t *uefi_disp_data; + UINTN Delta; + + uefi_disp_data = lv_display_get_driver_data(disp); + + Width = area->x2 - area->x1 + 1; + Heigth = area->y2 - area->y1 + 1; + Delta = uefi_disp_data->EfiGop->Mode->Info->HorizontalResolution * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + + uefi_disp_data->EfiGop->Blt ( + uefi_disp_data->EfiGop, + (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)color32_p, + EfiBltBufferToVideo, + (UINTN)area->x1, + (UINTN)area->y1, + (UINTN)area->x1, + (UINTN)area->y1, + Width, + Heigth, + Delta + ); + + lv_display_flush_ready(disp); +} + + +lv_display_t * lv_uefi_disp_create(int32_t hor_res, int32_t ver_res) +{ + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput; + + Status = gBS->LocateProtocol (&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput); + if (EFI_ERROR(Status)) { + return NULL; + } + + uefi_disp_data_t * uefi_disp_data = lv_malloc_zeroed(sizeof(uefi_disp_data_t)); + LV_ASSERT_MALLOC(uefi_disp_data); + if(NULL == uefi_disp_data) return NULL; + + uefi_disp_data->EfiGop = GraphicsOutput; + + lv_display_t * disp = lv_display_create(hor_res, ver_res); + if(NULL == disp) { + lv_free(uefi_disp_data); + return NULL; + } + lv_display_set_driver_data(disp, uefi_disp_data); + lv_display_set_flush_cb(disp, (lv_display_flush_cb_t)uefi_disp_flush); + lv_display_add_event_cb(disp, uefi_disp_delete_evt_cb, LV_EVENT_DELETE, disp); + + UINTN BufSize = hor_res * ver_res * sizeof (lv_color32_t); + uefi_disp_data->buffer[0] = malloc (BufSize); + uefi_disp_data->buffer[1] = malloc (BufSize); + + lv_display_set_buffers(disp, uefi_disp_data->buffer[0], uefi_disp_data->buffer[1], BufSize, LV_DISPLAY_RENDER_MODE_DIRECT); + + return disp; +} \ No newline at end of file diff --git a/Experimental/LvglSpikePkg/Library/LvglLib/lvgl b/Experimental/LvglSpikePkg/Library/LvglLib/lvgl new file mode 120000 index 0000000..a04dfc9 --- /dev/null +++ b/Experimental/LvglSpikePkg/Library/LvglLib/lvgl @@ -0,0 +1 @@ +../../../../External/lvgl \ No newline at end of file diff --git a/Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglDisplayEngineDxe.c b/Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglDisplayEngineDxe.c new file mode 100644 index 0000000..ed1cb35 --- /dev/null +++ b/Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglDisplayEngineDxe.c @@ -0,0 +1,76 @@ +/** @file + LvglDisplayEngineDxe -- driver glue: initializes the LVGL UEFI backend and + installs EDKII_FORM_DISPLAY_ENGINE_PROTOCOL so SetupBrowser drives LVGL instead + of the native text-grid DisplayEngineDxe. + + SetupBrowser keeps all IFR/config/callback ownership; this driver only renders. + + Copyright (c) 2026, MarsDoge. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "LvglDisplayEngineDxe.h" +#include + +typedef struct { + EFI_HANDLE Handle; + EDKII_FORM_DISPLAY_ENGINE_PROTOCOL FormDisplayProt; +} LVGL_DISPLAY_ENGINE_PRIVATE; + +/** + Confirm how to handle changed data when a form session ends. This skeleton + never edits varstore data, so it asks SetupBrowser to discard. SetupBrowser + still owns the actual decision and any writes. + + @retval BROWSER_ACTION_DISCARD Discard pending changes. +**/ +STATIC +UINTN +EFIAPI +LvglConfirmDataChange ( + VOID + ) +{ + return BROWSER_ACTION_DISCARD; +} + +STATIC LVGL_DISPLAY_ENGINE_PRIVATE mPrivate = { + NULL, + { + LvglFormDisplay, + LvglExitDisplay, + LvglConfirmDataChange + } +}; + +/** + Driver entry point. Caches the image handle / system table for the LVGL UEFI + backend, initializes LVGL, and installs the form-display protocol. The LVGL + display itself is created lazily on the first form (GOP may not be up yet). + + @param[in] ImageHandle This driver's image handle. + @param[in] SystemTable The EFI System Table. + + @retval EFI_SUCCESS The protocol was installed. + @retval Others Protocol installation failed. +**/ +EFI_STATUS +EFIAPI +InitializeLvglDisplayEngine ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + // + // Must precede lv_init() per the upstream LVGL UEFI backend contract. + // + lv_uefi_init (ImageHandle, SystemTable); + lv_init (); + + return gBS->InstallProtocolInterface ( + &mPrivate.Handle, + &gEdkiiFormDisplayEngineProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPrivate.FormDisplayProt + ); +} diff --git a/Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglDisplayEngineDxe.h b/Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglDisplayEngineDxe.h new file mode 100644 index 0000000..80094f4 --- /dev/null +++ b/Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglDisplayEngineDxe.h @@ -0,0 +1,60 @@ +/** @file + LvglDisplayEngineDxe -- experimental EDKII_FORM_DISPLAY_ENGINE_PROTOCOL backend + that renders native SetupBrowser forms with LVGL instead of edk2's text-grid + DisplayEngineDxe. + + BOUNDARY: like every DisplayEngine, this only RENDERS and reports user input. + SetupBrowser keeps ownership of IFR parsing, ConfigAccess, callbacks, and + varstore writes. This driver never writes HII variables. + + experimental/lvgl-spike only; never a default overlay. + + Copyright (c) 2026, MarsDoge. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef LVGL_DISPLAY_ENGINE_DXE_H_ +#define LVGL_DISPLAY_ENGINE_DXE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + Render one form with LVGL and block until the user asks to leave it, then + report the result to SetupBrowser. + + @param[in] FormData The form (statements, title, HII handle) to show. + Must be non-NULL. + @param[out] UserInputData Filled with the user's action/selection. Must be + non-NULL. For this skeleton: ESC -> Action = + BROWSER_ACTION_FORM_EXIT, SelectedStatement = NULL. + + @retval EFI_SUCCESS The form was shown and user input was collected. +**/ +EFI_STATUS +EFIAPI +LvglFormDisplay ( + IN FORM_DISPLAY_ENGINE_FORM *FormData, + OUT USER_INPUT *UserInputData + ); + +/** + Tear the LVGL display back down and restore the text console after a form + session ends. +**/ +VOID +EFIAPI +LvglExitDisplay ( + VOID + ); + +#endif // LVGL_DISPLAY_ENGINE_DXE_H_ diff --git a/Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglDisplayEngineDxe.inf b/Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglDisplayEngineDxe.inf new file mode 100644 index 0000000..0a049d9 --- /dev/null +++ b/Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglDisplayEngineDxe.inf @@ -0,0 +1,64 @@ +## @file +# LvglDisplayEngineDxe -- experimental EDKII_FORM_DISPLAY_ENGINE_PROTOCOL backend +# that renders native SetupBrowser forms with LVGL. SetupBrowser keeps IFR/config +# ownership; this only renders. experimental/lvgl-spike only; never default overlay. +# +# Copyright (c) 2026, MarsDoge. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = LvglDisplayEngineDxe + FILE_GUID = 4D9A7E22-6C13-4B80-9F1E-2A7C5D03AB04 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = InitializeLvglDisplayEngine + +[Sources] + LvglDisplayEngineDxe.c + LvglFormRenderer.c + LvglDisplayEngineDxe.h + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + LvglSpikePkg/LvglSpikePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + UefiBootServicesTableLib + UefiLib + BaseLib + BaseMemoryLib + MemoryAllocationLib + DebugLib + PrintLib + HiiLib + UefiHiiServicesLib + LvglCoreLib + +[Protocols] + gEdkiiFormDisplayEngineProtocolGuid ## PRODUCES + gEfiGraphicsOutputProtocolGuid ## CONSUMES + gEfiSimplePointerProtocolGuid ## CONSUMES + gEfiAbsolutePointerProtocolGuid ## CONSUMES + gEfiSimpleTextInputExProtocolGuid ## CONSUMES + gEfiSimpleTextInProtocolGuid ## CONSUMES + gEfiEdidActiveProtocolGuid ## CONSUMES + gEfiTimestampProtocolGuid ## CONSUMES + gEfiLoadedImageProtocolGuid ## CONSUMES + gEfiSimpleFileSystemProtocolGuid ## CONSUMES + gEfiDevicePathProtocolGuid ## CONSUMES + +[Guids] + gEfiFileInfoGuid + +[Depex] + TRUE + +[BuildOptions] + # LvglDisplayEngineDxe.c includes via LvglCoreLib.h, which pulls + # lv_conf.h (in Library/LvglLib, on the package include path) via + # LV_CONF_INCLUDE_SIMPLE. + GCC:*_*_*_CC_FLAGS = -DLV_CONF_INCLUDE_SIMPLE diff --git a/Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglFormRenderer.c b/Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglFormRenderer.c new file mode 100644 index 0000000..a24c045 --- /dev/null +++ b/Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglFormRenderer.c @@ -0,0 +1,229 @@ +/** @file + LvglDisplayEngineDxe -- the form renderer. Maps a SetupBrowser + FORM_DISPLAY_ENGINE_FORM onto LVGL objects and drives the input loop. + + Skeleton scope (experimental/lvgl-spike, steps 1-2): render the form title and + one row per statement (prompt text) as LVGL labels, then hold the frame until + ESC. Richer widgets (dropdown/checkbox/spinbox/textarea) and live editing are + layered on later; this proves "native form -> LVGL -> GOP" end to end without + taking any IFR/config ownership from SetupBrowser. + + Copyright (c) 2026, MarsDoge. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include "LvglDisplayEngineDxe.h" + +// +// One LVGL display, created lazily on the first form (GOP may not be up when the +// driver loads). NULL until then. +// +STATIC lv_display_t *mLvglDisplay = NULL; + +/** + Convert an HII string (returned as a freshly-allocated CHAR16*) to a pool + CHAR8* for LVGL, which consumes UTF-8/ASCII. Non-ASCII code points degrade to + '?' -- acceptable for the skeleton. Frees the input CHAR16 buffer. + + @param[in] Unicode HII string to convert. May be NULL. + + @retval Pool-allocated CHAR8 string (caller frees), or NULL. Caller owns it. +**/ +STATIC +CHAR8 * +HiiStringToAscii ( + IN EFI_STRING Unicode + ) +{ + UINTN Len; + CHAR8 *Ascii; + UINTN Index; + + if (Unicode == NULL) { + return NULL; + } + + Len = StrLen (Unicode); + Ascii = AllocatePool (Len + 1); + if (Ascii != NULL) { + for (Index = 0; Index < Len; Index++) { + Ascii[Index] = (Unicode[Index] < 0x80) ? (CHAR8)Unicode[Index] : '?'; + } + + Ascii[Len] = '\0'; + } + + FreePool (Unicode); + return Ascii; +} + +/** + Read a statement's prompt string id from its IFR opcode. Question, text, and + subtitle opcodes all carry an EFI_IFR_STATEMENT_HEADER immediately after the + op header; its first field is the prompt string id. Display-only read; no + config semantics are touched. + + @param[in] Statement The statement. Must be non-NULL with a non-NULL OpCode. + + @retval The prompt EFI_STRING_ID, or 0 if none. +**/ +STATIC +EFI_STRING_ID +GetStatementPromptId ( + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement + ) +{ + EFI_IFR_STATEMENT_HEADER *Header; + + if ((Statement == NULL) || (Statement->OpCode == NULL)) { + return 0; + } + + Header = (EFI_IFR_STATEMENT_HEADER *)(Statement->OpCode + 1); + return Header->Prompt; +} + +/** + Build the LVGL object tree for one form: a title label plus one prompt label + per statement, on a themed background. + + @param[in] FormData The form to render. Must be non-NULL. +**/ +STATIC +VOID +LvglBuildForm ( + IN FORM_DISPLAY_ENGINE_FORM *FormData + ) +{ + lv_obj_t *Screen; + lv_obj_t *List; + lv_obj_t *Title; + LIST_ENTRY *Link; + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + CHAR8 *Text; + + Screen = lv_screen_active (); + lv_obj_clean (Screen); + lv_obj_set_style_bg_color (Screen, lv_color_hex (0x0E1116), LV_PART_MAIN); + lv_obj_set_style_bg_opa (Screen, LV_OPA_COVER, LV_PART_MAIN); + + // + // Title from the form's HII title string id. + // + Title = lv_label_create (Screen); + Text = HiiStringToAscii (HiiGetString (FormData->HiiHandle, FormData->FormTitle, NULL)); + lv_label_set_text (Title, (Text != NULL) ? Text : "Setup"); + lv_obj_set_style_text_color (Title, lv_color_hex (0xF2C14E), LV_PART_MAIN); + lv_obj_set_style_text_font (Title, &lv_font_montserrat_24, LV_PART_MAIN); + lv_obj_align (Title, LV_ALIGN_TOP_MID, 0, 16); + if (Text != NULL) { + FreePool (Text); + } + + // + // A scrollable column for the statement rows. + // + List = lv_obj_create (Screen); + lv_obj_set_size (List, lv_pct (90), lv_pct (78)); + lv_obj_align (List, LV_ALIGN_BOTTOM_MID, 0, -16); + lv_obj_set_flex_flow (List, LV_FLEX_FLOW_COLUMN); + lv_obj_set_style_bg_opa (List, LV_OPA_TRANSP, LV_PART_MAIN); + lv_obj_set_style_border_width (List, 0, LV_PART_MAIN); + + // + // One label per statement (prompt text). Display only. + // + for (Link = GetFirstNode (&FormData->StatementListHead); + !IsNull (&FormData->StatementListHead, Link); + Link = GetNextNode (&FormData->StatementListHead, Link)) + { + lv_obj_t *Row; + + Statement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (Link); + Text = HiiStringToAscii (HiiGetString (FormData->HiiHandle, GetStatementPromptId (Statement), NULL)); + if (Text == NULL) { + continue; + } + + Row = lv_label_create (List); + lv_label_set_text (Row, Text); + lv_obj_set_style_text_color (Row, lv_color_hex (0xE6EAF0), LV_PART_MAIN); + FreePool (Text); + } +} + +/** + See LvglDisplayEngineDxe.h. +**/ +EFI_STATUS +EFIAPI +LvglFormDisplay ( + IN FORM_DISPLAY_ENGINE_FORM *FormData, + OUT USER_INPUT *UserInputData + ) +{ + EFI_INPUT_KEY Key; + EFI_STATUS Status; + VOID *Handle; + + if ((FormData == NULL) || (UserInputData == NULL)) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (UserInputData, sizeof (USER_INPUT)); + + // + // Lazily create the LVGL display now that a GOP is expected to be present. + // + if (mLvglDisplay == NULL) { + Handle = lv_uefi_display_get_any (); + if (Handle == NULL) { + return EFI_UNSUPPORTED; + } + + mLvglDisplay = lv_uefi_display_create (Handle); + if (mLvglDisplay == NULL) { + return EFI_DEVICE_ERROR; + } + } + + LvglBuildForm (FormData); + + // + // Hold the frame; ESC leaves the form. SetupBrowser owns everything else -- + // we only report the exit action back to it. + // + for ( ; ; ) { + lv_timer_handler (); + gBS->Stall (10 * 1000); + lv_tick_inc (10); + + Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); + if (!EFI_ERROR (Status) && (Key.ScanCode == SCAN_ESC)) { + UserInputData->SelectedStatement = NULL; + UserInputData->Action = BROWSER_ACTION_FORM_EXIT; + break; + } + } + + return EFI_SUCCESS; +} + +/** + See LvglDisplayEngineDxe.h. +**/ +VOID +EFIAPI +LvglExitDisplay ( + VOID + ) +{ + if (mLvglDisplay != NULL) { + lv_obj_clean (lv_screen_active ()); + lv_refr_now (mLvglDisplay); + } + + gST->ConOut->ClearScreen (gST->ConOut); + gST->ConOut->SetCursorPosition (gST->ConOut, 0, 0); + gST->ConOut->EnableCursor (gST->ConOut, TRUE); +} diff --git a/Experimental/LvglSpikePkg/LvglSpikeLoongArch.dsc b/Experimental/LvglSpikePkg/LvglSpikeLoongArch.dsc new file mode 100644 index 0000000..8f02f21 --- /dev/null +++ b/Experimental/LvglSpikePkg/LvglSpikeLoongArch.dsc @@ -0,0 +1,53 @@ +## @file +# Minimal self-contained DSC for the experimental/lvgl-spike LoongArch build. +# Builds the standalone render probe AND the LvglDisplayEngineDxe backend (LVGL +# behind EDKII_FORM_DISPLAY_ENGINE_PROTOCOL) for LOONGARCH64. Not a shipped +# platform; no FD. +# +# Copyright (c) 2026, MarsDoge. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + PLATFORM_NAME = LvglSpike + PLATFORM_GUID = 9B2E4D71-0A3C-4F65-8E2D-1C7A6F0B1102 + PLATFORM_VERSION = 0.01 + DSC_SPECIFICATION = 0x00010005 + OUTPUT_DIRECTORY = Build/LvglSpike + SUPPORTED_ARCHITECTURES = LOONGARCH64 + BUILD_TARGETS = DEBUG|RELEASE + SKUID_IDENTIFIER = DEFAULT + +!include MdePkg/MdeLibs.dsc.inc + +[LibraryClasses] + UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf + UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf + UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf + LvglCoreLib|LvglSpikePkg/Library/LvglLib/LvglCoreLib.inf + +[Components] + # + # Compiler intrinsics (memcpy/memset/...) that GCC emits for aggregate + # copy/zero in LVGL are force-linked per component via NULL|IntrinsicLib. + # + LvglSpikePkg/Library/LvglLib/LvglSpikeProbe.inf { + + NULL|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf + } + LvglSpikePkg/LvglDisplayEngineDxe/LvglDisplayEngineDxe.inf { + + NULL|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf + } diff --git a/Experimental/LvglSpikePkg/LvglSpikePkg.dec b/Experimental/LvglSpikePkg/LvglSpikePkg.dec new file mode 100644 index 0000000..cba93ba --- /dev/null +++ b/Experimental/LvglSpikePkg/LvglSpikePkg.dec @@ -0,0 +1,30 @@ +## @file +# LvglSpikePkg -- experimental evaluation package for an LVGL rendering backend. +# +# EXPERIMENTAL / SPIKE ONLY. Exists on experimental/lvgl-spike to answer one +# question: does LVGL's core + software renderer + upstream UEFI port build (and +# later render) under edk2 on a hard architecture (LoongArch64), not just X64. +# It points the LVGL integration at this repo's pinned External/lvgl submodule +# (LVGL v9.5.0) via a symlink (Library/LvglLib/lvgl). The Aptio-styled chrome +# from downstream LVGL-UEFI ports is intentionally NOT vendored. +# +# Must never appear in a default ModernSetupPkg firmware overlay or ModernSetupApp. +# +# Copyright (c) 2026, MarsDoge. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + DEC_SPECIFICATION = 0x00010005 + PACKAGE_NAME = LvglSpikePkg + PACKAGE_GUID = 0E0B8F2A-4C1D-4E7A-9B3E-7D6A2F11AA01 + PACKAGE_VERSION = 0.01 + +[Includes] + Include + Library/LvglLib + Library/LvglLib/lvgl + +[LibraryClasses] + ## @libraryclass Shared LVGL build (core + SW renderer + UEFI port). + LvglCoreLib|Include/Library/LvglCoreLib.h diff --git a/Experimental/LvglSpikePkg/LvglSpikeRiscV.dsc b/Experimental/LvglSpikePkg/LvglSpikeRiscV.dsc new file mode 100644 index 0000000..9bcfa9f --- /dev/null +++ b/Experimental/LvglSpikePkg/LvglSpikeRiscV.dsc @@ -0,0 +1,44 @@ +## @file +# Minimal self-contained DSC for the experimental/lvgl-spike RISC-V build +# validation. Builds just the LvglSpikeProbe UEFI_APPLICATION (LVGL core + +# software renderer + upstream UEFI port, with the LoongArch64/RISC-V64 +# arch-gate patch) for RISCV64. Per repo convention RISC-V is build-only +# (no run/capture helper), so this validates compile+link, not render. +# +# Copyright (c) 2026, MarsDoge. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + PLATFORM_NAME = LvglSpikeRiscV + PLATFORM_GUID = 9B2E4D71-0A3C-4F65-8E2D-1C7A6F0B1103 + PLATFORM_VERSION = 0.01 + DSC_SPECIFICATION = 0x00010005 + OUTPUT_DIRECTORY = Build/LvglSpikeRiscV + SUPPORTED_ARCHITECTURES = RISCV64 + BUILD_TARGETS = DEBUG|RELEASE + SKUID_IDENTIFIER = DEFAULT + +!include MdePkg/MdeLibs.dsc.inc + +[LibraryClasses] + UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + +[Components] + LvglSpikePkg/Library/LvglLib/LvglSpikeProbe.inf { + + # Force-link the compiler intrinsics (memcpy/memset/memmove/memcmp) GCC + # emits for aggregate copy/zero in LVGL, even under -ffreestanding. + NULL|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf + } diff --git a/Experimental/LvglSpikePkg/README.md b/Experimental/LvglSpikePkg/README.md new file mode 100644 index 0000000..42d5f9b --- /dev/null +++ b/Experimental/LvglSpikePkg/README.md @@ -0,0 +1,103 @@ +# LvglSpikePkg — experimental LVGL rendering-backend evaluation + +**Status: experimental spike. Not shipped. Never in a default firmware overlay or +in `ModernSetupApp`.** Lives only on the `experimental/lvgl-spike` branch. + +## Why this exists + +To answer one question before considering LVGL as an open-source rendering +backend for ModernSetupPkg: + +> Does LVGL (core + software renderer + its upstream UEFI port) **build and +> render under edk2 on a hard architecture** — LoongArch64 — not just on X64 +> where downstream LVGL-UEFI ports already work? + +This is the single objection that actually gated the "switch backend?" decision: +the package's entire value is *one* Setup UX across OVMF-X64 / ArmVirt-AArch64 / +LoongArch64 / RISC-V64. If LVGL could not survive that, the question was moot. + +## Result + +| Arch | Build (edk2 GCC) | Render | Notes | +|---|---|---|---| +| X64 | ✅ (proven upstream by LvglPkg) | ✅ upstream | not re-done here | +| AArch64 | ✅ already in LVGL's UEFI arch allowlist | — | no patch needed | +| **LoongArch64** | ✅ **PASS** — 465 objs, `LvglSpikeProbe.efi` PE32+ LoongArch | ✅ **PASS on real hardware** | the crux; see below | +| RISC-V64 | ⚠️ arch-gate compiles; full build blocked by local toolchain | — (repo is build-only for RISC-V) | environment limit, not LVGL | + +**LoongArch64 is the headline:** `LvglSpikeProbe.efi` compiles, links, and was +run on **real LoongArch silicon**, drawing an LVGL UI (themed background, title, +rounded button) straight to the GOP framebuffer. Soft-float — the one concern +that static reading can't settle — is a non-issue: `LV_USE_FLOAT 0` keeps the SW +renderer integer-only and the whole closure linked cleanly. + +### RISC-V64 caveat (environment, not LVGL) + +The baseline's RISC-V branch compiles (the `lv_uefi.h` gate passes). The full +build fails only because the host has `riscv64-linux-gnu-gcc` (a glibc Linux +cross-compiler) instead of the bare-metal `riscv64-unknown-elf-gcc` edk2 expects: +LVGL's `#include ` chains into glibc, which then needs +`gnu/stubs-lp64.h` — absent because edk2 builds RISC-V with soft-float `-mabi=lp64` +while the installed glibc is `lp64d`. LoongArch avoided this only because its +glibc ABI happened to match edk2's flags. With a bare-metal RISC-V toolchain (or +by redirecting LVGL's libc includes to edk2 types) this clears; it is not an +LVGL problem. + +## The upstream contribution (now in the baseline) + +LVGL's UEFI port originally hard-`#error`ed on any arch outside +`{x86_64, i386, aarch64}`. Adding LoongArch64 + RISC-V64 was a small, coherent +change across exactly three sites: + +| File | Role | Change | +|---|---|---| +| `src/drivers/uefi/lv_uefi.h` | the `#error` gate + 64-bit size assert | add `__loongarch_lp64` / `__riscv && __riscv_xlen==64` branches (separate, to keep the per-arch `__LV_UEFI_ARCH_*` markers correct) | +| `src/misc/lv_types.h` | `LV_ARCH_64` fallback list | append both arches (main path already covered via `__UINTPTR_MAX__`) | +| `tests/makefiles_uefi/efi.h` | CI standalone UEFI test types | fold both LP64 arches into the existing aarch64 type block | + +This support has since landed **upstream** and the pinned `External/lvgl` baseline +is bumped to a commit that includes it. The submodule is consumed **pristine** — +there is no local patch and no build-time patching. (LVGL stays an unmodified +upstream submodule.) + +## Integration notes learned + +- **Memory:** `LV_USE_STDLIB_MALLOC = LV_STDLIB_BUILTIN` gives only a 64 KB static + pool — too small for a display draw buffer; LVGL hangs on allocation. Use + `LV_STDLIB_CUSTOM` + `LV_UEFI_USE_MEMORY_SERVICES 1` so malloc routes to UEFI + `AllocatePool` (`lv_mem_core_uefi.c`). +- **Headers:** removed LvglPkg's libc-shim headers (`limits.h`, `stdint.h`, …) + which shadowed the compiler's freestanding headers (`CHAR_BIT` undeclared). +- **lv_conf discovery:** `-DLV_CONF_INCLUDE_SIMPLE`, because `lvgl/` is a symlink + out to `External/lvgl`, so LVGL's default `"../../lv_conf.h"` escapes the dir. +- **Render path:** this probe is a standalone UEFI application that draws + `lv_draw_sw → EFI_GRAPHICS_OUTPUT_PROTOCOL.Blt()` directly. It does **not** go + through HII / FormBrowser / any DisplayEngine — same shape as `ModernSetupApp`, + not the DisplayEngine-replacement shape (that would be the separate + `LvglDisplayEngineDxe` model, deliberately not vendored here — it carries + AMI-"Aptio" branded chrome we must not reuse). + +## Build + +```sh +Scripts/bootstrap-edk2.sh # if not already done +Scripts/build-lvgl-spike-loongarch.sh # -> Build/LvglSpike/.../LvglSpikeProbe.efi +TARGET=DEBUG Scripts/build-lvgl-spike-riscv.sh # needs a bare-metal riscv64 toolchain +``` +The pinned `External/lvgl` baseline already carries LoongArch64/RISC-V64 UEFI +support upstream, so the scripts consume the submodule pristine — no patching. + +## Recommendation + +The feasibility blocker — "an LVGL backend would break the XArch promise" — is +**empirically cleared on real LoongArch hardware** with only a ~3-site change +that is now upstream in LVGL. This does **not** by itself mandate switching; it means the +decision can now be made on merits (LVGL's widget/text-editing richness and AA +rendering vs. dependency weight, the boundary/maintenance cost, and the existing +`ModernUiEngineLib` investment) rather than being blocked on portability. + +## Attribution + +Builds on LVGL (lvgl/lvgl), LvglPkg (hamitcan99), and the original UEFI port by +YangGangUEFI — all MIT. See [`REFERENCES.md`](REFERENCES.md) for exactly what is +used from each and what is deliberately not reused. diff --git a/Experimental/LvglSpikePkg/REFERENCES.md b/Experimental/LvglSpikePkg/REFERENCES.md new file mode 100644 index 0000000..fde2aed --- /dev/null +++ b/Experimental/LvglSpikePkg/REFERENCES.md @@ -0,0 +1,46 @@ +# References & attribution — LvglSpikePkg + +This experimental package builds on prior open-source work. All third-party code +used here is MIT-licensed; this file records what we use from each project and +what we deliberately do **not** reuse. Copyright headers in copied files are kept +intact, as MIT requires. + +## LVGL — https://github.com/lvgl/lvgl + +- **License:** MIT. +- **Use:** vendored directly as the `External/lvgl` git submodule, pinned to a + tagged **v9.5.0**-derived baseline that already carries LoongArch64/RISC-V64 + UEFI support upstream. This is the actual rendering engine (core, software + renderer, fonts, widgets, and the upstream UEFI port under + `src/drivers/uefi`). The submodule keeps LVGL's own `LICENCE.txt`. +- **Upstream contribution back:** the LoongArch64 / RISC-V64 arch-gate change + was contributed to lvgl/lvgl and is now in the pinned baseline. The submodule + is consumed **pristine** — no local patch, no build-time patching; LVGL stays + an unmodified upstream submodule. + +## LvglPkg — https://github.com/hamitcan99/LvglPkg + +- **License:** MIT. +- **Use:** EDK2-integration reference. The `Library/LvglLib` baseline in this + package (the `lv_conf.h`, the INF source-list shape, and the UEFI port glue) + was adapted from LvglPkg's `Library/LvglLib`; copied files retain their + original `Copyright (c) 2024, Yang Gang` headers. The forthcoming + `LvglDisplayEngineDxe` (LVGL behind `EDKII_FORM_DISPLAY_ENGINE_PROTOCOL`, + mapping `FORM_DISPLAY_ENGINE_STATEMENT` to LVGL widgets) follows the pattern of + LvglPkg's `LvglDisplayEngineDxe` / `LvglFormRenderer.c`. +- **Deliberately NOT reused:** LvglPkg's `AptioWallpaper.c` / `LvglAptioChrome.*`. + "Aptio" is AMI's commercial BIOS brand; per this repo's "no commercial-IBV + asset reuse" rule we use original chrome only. + +## YangGangUEFI — https://github.com/YangGangUEFI + +- **License:** MIT. +- **Use:** Yang Gang is the original author of the LVGL-on-UEFI port that LvglPkg + forked from. Credited here as the origin of the EDK2/LVGL integration approach + this spike learns from. + +--- + +*Visual/architectural references are fine; copied artwork, fonts, icons, strings, +or vendor-branded assets are not. See the package `README.md` for the spike +findings and `CLAUDE.md` for the boundary rules.* diff --git a/External/lvgl b/External/lvgl new file mode 160000 index 0000000..4cdddfe --- /dev/null +++ b/External/lvgl @@ -0,0 +1 @@ +Subproject commit 4cdddfe4ae6aaf08c713d86673f5675875415998 diff --git a/Include/ModernUi/ModernUiEngine.h b/Include/ModernUi/ModernUiEngine.h index 261b454..367e5cf 100644 --- a/Include/ModernUi/ModernUiEngine.h +++ b/Include/ModernUi/ModernUiEngine.h @@ -207,6 +207,38 @@ ModernUiEngineDrawValue ( IN CONST MODERN_UI_THEME *Theme ); +/** + Draw a small non-semantic control-type affordance inside a cue box. + + This is the single shared affordance vocabulary used by both the App value + lane and the in-setup DisplayEngine row cue, so a checkbox, drop-down, + numeric, date/time, password, string, ordered-list, or action control reads + identically in both surfaces. The affordance is composed entirely from + renderer fill/stroke/triangle primitives and reflects only the control kind; + it never reads or mutates any stored value or HII state. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] CueRect Square pixel box to draw the affordance in. Boxes + smaller than 6x6 are ignored. + @param[in] Type Control value type selecting the affordance shape. + ModernUiValueNone / ModernUiValueText draw nothing. + @param[in] CueColor High-contrast mark / stroke color. + @param[in] FillColor Subtle inner fill color (checkbox interior). + + @retval EFI_SUCCESS Affordance was drawn (or nothing was needed). + @retval EFI_INVALID_PARAMETER Context is NULL. + @retval others Status from the first failing renderer primitive. +**/ +EFI_STATUS +EFIAPI +ModernUiEngineDrawControlCue ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT CueRect, + IN MODERN_UI_VALUE_TYPE Type, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL CueColor, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL FillColor + ); + /** Draw a popup surface. diff --git a/Include/ModernUi/ModernUiRenderer.h b/Include/ModernUi/ModernUiRenderer.h index bf004c9..52bd828 100644 --- a/Include/ModernUi/ModernUiRenderer.h +++ b/Include/ModernUi/ModernUiRenderer.h @@ -31,6 +31,12 @@ typedef struct { UINTN Height; } MODERN_UI_RENDER_CONTEXT; +typedef enum { + ModernUiTriDown, ///< Apex points down (drop-down chevron). + ModernUiTriUp, ///< Apex points up. + ModernUiTriRight ///< Apex points right (navigate / activate arrow). +} MODERN_UI_TRI_DIR; + /** Blend two GOP colors by percentage weight. @@ -123,6 +129,31 @@ ModernUiStrokeRect ( IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color ); +/** + Fill an isosceles triangle inscribed in a rectangle. + + The triangle is built from one-pixel renderer fill spans so it renders + identically through the GOP and LVGL backends. Used to compose chevron and + arrow control affordances. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] Rect Bounding rectangle. Zero width or height is ignored. + @param[in] Dir Apex direction (down / up / right). + @param[in] Color Fill color. + + @retval EFI_SUCCESS Triangle was drawn (or Rect was empty). + @retval EFI_INVALID_PARAMETER Context is NULL. + @retval others Status from the first failing fill span. +**/ +EFI_STATUS +EFIAPI +ModernUiFillTriangle ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT Rect, + IN MODERN_UI_TRI_DIR Dir, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color + ); + /** Return the expected pixel width for a UCS-2 string. diff --git a/Library/ModernUiCustomizedDisplayLib/CustomizedDisplayLibInternal.c b/Library/ModernUiCustomizedDisplayLib/CustomizedDisplayLibInternal.c index 4002558..3123826 100644 --- a/Library/ModernUiCustomizedDisplayLib/CustomizedDisplayLibInternal.c +++ b/Library/ModernUiCustomizedDisplayLib/CustomizedDisplayLibInternal.c @@ -54,21 +54,6 @@ ModernDisplayDrawStatementRowAccents ( IN CONST MODERN_UI_THEME *Theme ); -STATIC -VOID -ModernDisplayDrawStatementValueLane ( - IN CONST MODERN_UI_RECT *RowRect, - IN CONST MODERN_DISPLAY_FORM_ROW *FormRow, - IN CONST MODERN_UI_THEME *Theme - ); - -STATIC -VOID -ModernDisplayDrawStatementValueLaneCue ( - IN CONST MODERN_UI_RECT *LaneRect, - IN CONST MODERN_DISPLAY_FORM_ROW *FormRow, - IN CONST MODERN_UI_THEME *Theme - ); STATIC UINTN @@ -896,109 +881,47 @@ ModernDisplayFormRowAccentColor ( } /** - Draw a subtle editable-value cue inside the value lane. - - Native FormBrowser remains responsible for printing the value text. This helper - only adds a GOP outline, underline, and trailing cap so the existing value area - reads as an editable chip without changing statement dimensions or wrapping. - - @param[in] LaneRect Pixel value-lane rectangle. Must not be NULL. - @param[in] FormRow Private row model. Must not be NULL. - @param[in] Theme Theme token table. Must not be NULL. -**/ -STATIC -VOID -ModernDisplayDrawStatementValueLaneCue ( - IN CONST MODERN_UI_RECT *LaneRect, - IN CONST MODERN_DISPLAY_FORM_ROW *FormRow, - IN CONST MODERN_UI_THEME *Theme - ) -{ - EFI_GRAPHICS_OUTPUT_BLT_PIXEL OutlineColor; - EFI_GRAPHICS_OUTPUT_BLT_PIXEL AccentColor; - UINTN CapWidth; - - if ((LaneRect == NULL) || (FormRow == NULL) || (Theme == NULL) || (LaneRect->Width < 24) || (LaneRect->Height < 8)) { - return; - } - - AccentColor = ModernDisplayFormRowAccentColor (FormRow, Theme); - OutlineColor = ((FormRow->State & ModernDisplayFormRowStateSelected) != 0) ? - Theme->AccentYellow : - ModernUiBlendColor (Theme->Border, Theme->BackgroundBlack, 62); - CapWidth = MIN (4, MAX (2, LaneRect->Width / 24)); - - ModernUiStrokeRect (&mModernRenderContext, *LaneRect, OutlineColor); - ModernUiFillRect ( - &mModernRenderContext, - (MODERN_UI_RECT){ LaneRect->X + 2, LaneRect->Y + LaneRect->Height - 2, LaneRect->Width - 4, 1 }, - ModernUiBlendColor (AccentColor, Theme->BackgroundBlack, 60) - ); - ModernUiFillRect ( - &mModernRenderContext, - (MODERN_UI_RECT){ LaneRect->X + LaneRect->Width - CapWidth - 2, LaneRect->Y + 3, CapWidth, LaneRect->Height - 6 }, - ModernUiBlendColor (AccentColor, Theme->BackgroundBlack, 72) - ); -} + Map a DisplayEngine form-row kind to the shared engine control value type. -/** - Draw a subtle value lane on highlighted/editable rows. + This lets the in-setup DisplayEngine reuse the exact same per-control + affordance vocabulary (ModernUiEngineDrawControlCue) that the front-page App + value lane uses, so the same control type reads identically in both surfaces. + Text-only / subtitle / unknown kinds map to ModernUiValueNone (no cue). - Native FormBrowser still prints prompt/value text. This helper only paints a - GOP background hint on the right side of interactive rows so users can - distinguish the value/edit region from the prompt region. + @param[in] Kind DisplayEngine form-row kind. - @param[in] RowRect Pixel row rectangle. Must not be NULL. - @param[in] FormRow Private row model. Must not be NULL. - @param[in] Theme Theme token table. Must not be NULL. + @return The matching MODERN_UI_VALUE_TYPE, or ModernUiValueNone when the kind + carries no control affordance. **/ STATIC -VOID -ModernDisplayDrawStatementValueLane ( - IN CONST MODERN_UI_RECT *RowRect, - IN CONST MODERN_DISPLAY_FORM_ROW *FormRow, - IN CONST MODERN_UI_THEME *Theme +MODERN_UI_VALUE_TYPE +ModernDisplayKindToValueType ( + IN MODERN_DISPLAY_FORM_ROW_KIND Kind ) { - EFI_GRAPHICS_OUTPUT_BLT_PIXEL LaneColor; - MODERN_UI_RECT LaneRect; - UINTN LaneWidth; - UINTN LaneX; - UINTN LaneY; - UINTN LaneHeight; - - if ((RowRect == NULL) || (FormRow == NULL) || (Theme == NULL) || (RowRect->Width < 160) || (RowRect->Height < 12)) { - return; - } - - if (ModernDisplayFormRowIsTextOnly (FormRow->Kind) || ((FormRow->State & ModernDisplayFormRowStateHighlighted) == 0)) { - return; - } - - if (((FormRow->State & ModernDisplayFormRowStateDisabled) != 0) || ((FormRow->State & ModernDisplayFormRowStateReadOnly) != 0)) { - return; - } - - LaneWidth = MIN (MAX (120, RowRect->Width / 3), RowRect->Width - 48); - LaneX = RowRect->X + MAX (160, RowRect->Width / 2); - if ((LaneX + LaneWidth + 10) > (RowRect->X + RowRect->Width)) { - LaneWidth = RowRect->X + RowRect->Width - LaneX - 10; - } - - if (LaneWidth < 64) { - return; + switch (Kind) { + case ModernDisplayFormRowCheckbox: + return ModernUiValueCheckbox; + case ModernDisplayFormRowChoice: + return ModernUiValueOneOf; + case ModernDisplayFormRowOrderedList: + return ModernUiValueOrderedList; + case ModernDisplayFormRowNumeric: + return ModernUiValueNumeric; + case ModernDisplayFormRowDate: + case ModernDisplayFormRowTime: + return ModernUiValueDateTime; + case ModernDisplayFormRowPassword: + return ModernUiValuePassword; + case ModernDisplayFormRowString: + return ModernUiValueString; + case ModernDisplayFormRowReference: + case ModernDisplayFormRowAction: + case ModernDisplayFormRowResetButton: + return ModernUiValueAction; + default: + return ModernUiValueNone; } - - LaneY = RowRect->Y + 4; - LaneHeight = RowRect->Height - 8; - LaneColor = ((FormRow->State & ModernDisplayFormRowStateSelected) != 0) ? - ModernUiBlendColor (Theme->AccentOrange, Theme->BackgroundBlack, 32) : - ModernUiBlendColor (Theme->SurfaceRaised, Theme->BackgroundBlack, 55); - LaneRect = (MODERN_UI_RECT){ LaneX, LaneY, LaneWidth, LaneHeight }; - - ModernUiFillRect (&mModernRenderContext, LaneRect, LaneColor); - ModernUiFillRect (&mModernRenderContext, (MODERN_UI_RECT){ LaneX, LaneY, 3, LaneHeight }, Theme->AccentYellow); - ModernDisplayDrawStatementValueLaneCue (&LaneRect, FormRow, Theme); } /** @@ -1123,10 +1046,96 @@ ModernDisplayDrawStatementRow ( } ModernUiEngineDrawRows (&mModernRenderContext, &RowModel, 1, Theme); - ModernDisplayDrawStatementValueLane (&RowRect, &FormRow, Theme); ModernDisplayDrawStatementRowAccents (&RowRect, &FormRow, Theme); } +/** + Draw a per-opcode control affordance over an already-painted statement row. + + This runs AFTER native FormBrowser has printed the row's prompt/value text + (and its highlight background), so the affordance is composited on top rather + than being overpainted. It paints only a small non-semantic cue glyph at the + row's right edge (clear of the value text) to make each control type read + distinctly. It classifies already-materialized statement data and never reads, + writes, or owns any HII/FormBrowser value or semantics. + + @param[in] FormData DisplayEngine form that owns Statement. May be NULL. + @param[in] Statement Statement to classify. May be NULL (then no cue). + @param[in] Column Text-grid column where the row starts. + @param[in] Row Text-grid row of the statement. + @param[in] Width Text-grid column count of the row. + @param[in] Highlight TRUE when the row currently has keyboard highlight. + @param[in] Selected TRUE when the row is in edit/selection mode. +**/ +VOID +EFIAPI +ModernDisplayDrawStatementRowCue ( + IN FORM_DISPLAY_ENGINE_FORM *FormData OPTIONAL, + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement OPTIONAL, + IN UINTN Column, + IN UINTN Row, + IN UINTN Width, + IN BOOLEAN Highlight, + IN BOOLEAN Selected + ) +{ + CONST MODERN_UI_THEME *Theme; + UINTN CellWidth; + UINTN CellHeight; + UINTN X; + UINTN Y; + UINTN PixelWidth; + UINTN CueSide; + MODERN_DISPLAY_FORM_ROW FormRow; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL CueColor; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL FillColor; + + if ((Statement == NULL) || (Width == 0) || EFI_ERROR (ModernDisplayEnsureRenderer ())) { + return; + } + + if (EFI_ERROR (ModernDisplayClassifyStatementForForm (FormData, Statement, Highlight, Selected, &FormRow))) { + return; + } + + // + // Only editable controls get a type affordance; text/subtitle and + // disabled/read-only rows do not. + // + if (ModernDisplayFormRowIsTextOnly (FormRow.Kind) || + ((FormRow.State & (ModernDisplayFormRowStateDisabled | ModernDisplayFormRowStateReadOnly)) != 0)) + { + return; + } + + Theme = ModernUiGetTheme (); + ModernDisplayGetCellMetrics (&CellWidth, &CellHeight); + + X = Column * CellWidth; + Y = Row * CellHeight; + PixelWidth = Width * CellWidth; + if ((PixelWidth < 48) || (CellHeight < 10)) { + return; + } + + CueSide = MIN (CellHeight - 6, 14); + + // + // High-contrast cue: dark on the bright selected band, bright yellow on the + // dark surface. The cue sits at the row's right edge, clear of value text. + // + CueColor = (Highlight || Selected) ? Theme->BackgroundBlack : Theme->AccentYellow; + FillColor = ModernUiBlendColor (Theme->AccentOrange, Theme->BackgroundBlack, 70); + + ModernUiEngineDrawControlCue ( + &mModernRenderContext, + (MODERN_UI_RECT){ X + PixelWidth - CueSide - 8, Y + (CellHeight - CueSide) / 2, CueSide, CueSide }, + ModernDisplayKindToValueType (FormRow.Kind), + CueColor, + FillColor + ); +} + /** Draw the modern DisplayEngine shell behind the native FormBrowser content. diff --git a/Library/ModernUiEngineLib/ModernUiEngineLib.c b/Library/ModernUiEngineLib/ModernUiEngineLib.c index 95da6ae..5ff3874 100644 --- a/Library/ModernUiEngineLib/ModernUiEngineLib.c +++ b/Library/ModernUiEngineLib/ModernUiEngineLib.c @@ -962,6 +962,7 @@ ModernUiEngineDrawValue ( ) { MODERN_UI_RECT Rect; + UINTN CueSide; if ((Context == NULL) || (Value == NULL) || (Theme == NULL)) { return EFI_INVALID_PARAMETER; @@ -977,6 +978,25 @@ ModernUiEngineDrawValue ( Rect.Width = MODERN_UI_ROW_VALUE_BOX_WIDTH; } + // + // Paint the shared per-type affordance just left of the value lane so a + // checkbox, drop-down, numeric, date/time, password, string, ordered-list, + // or action control reads the same here as in the in-setup DisplayEngine. + // Plain text values carry no affordance. + // + if ((Value->Type != ModernUiValueText) && (Rect.X > Value->Rect.X)) { + CueSide = MIN ((Rect.Height > 6) ? (Rect.Height - 6) : 0, 14); + if (CueSide >= 6) { + ModernUiEngineDrawControlCue ( + Context, + (MODERN_UI_RECT){ Rect.X - CueSide - 8, Rect.Y + (Rect.Height - CueSide) / 2, CueSide, CueSide }, + Value->Type, + Theme->AccentYellow, + ModernUiBlendColor (Theme->AccentOrange, Theme->BackgroundBlack, 70) + ); + } + } + if ((Value->Type == ModernUiValueOneOf) || (Value->Type == ModernUiValueText)) { return ModernUiDrawValueBox (Context, Rect, Value->Text, Value->Selected, Theme); } @@ -992,6 +1012,126 @@ ModernUiEngineDrawValue ( ); } +EFI_STATUS +EFIAPI +ModernUiEngineDrawControlCue ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT CueRect, + IN MODERN_UI_VALUE_TYPE Type, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL CueColor, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL FillColor + ) +{ + UINTN W; + UINTN H; + UINTN Index; + UINTN DotSide; + EFI_STATUS Status; + + if (Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((CueRect.Width < 6) || (CueRect.Height < 6)) { + return EFI_SUCCESS; + } + + W = CueRect.Width; + H = CueRect.Height; + Status = EFI_SUCCESS; + + switch (Type) { + case ModernUiValueCheckbox: + // + // Toggle box: the stored on/off is still shown as value text; this only + // marks the row as a checkbox. + // + Status = ModernUiStrokeRect (Context, CueRect, CueColor); + if (!EFI_ERROR (Status)) { + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ CueRect.X + 3, CueRect.Y + 3, W - 6, H - 6 }, FillColor); + } + + break; + + case ModernUiValueOneOf: + // + // Drop-down chevron. + // + Status = ModernUiFillTriangle (Context, CueRect, ModernUiTriDown, CueColor); + break; + + case ModernUiValueOrderedList: + // + // Up / down reorder chevrons. + // + Status = ModernUiFillTriangle (Context, (MODERN_UI_RECT){ CueRect.X, CueRect.Y, W, H / 2 - 1 }, ModernUiTriUp, CueColor); + if (!EFI_ERROR (Status)) { + Status = ModernUiFillTriangle (Context, (MODERN_UI_RECT){ CueRect.X, CueRect.Y + H / 2 + 1, W, H / 2 - 1 }, ModernUiTriDown, CueColor); + } + + break; + + case ModernUiValueNumeric: + // + // Adjust indicator: a plus mark. + // + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ CueRect.X + 2, CueRect.Y + H / 2 - 1, W - 4, 2 }, CueColor); + if (!EFI_ERROR (Status)) { + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ CueRect.X + W / 2 - 1, CueRect.Y + 2, 2, H - 4 }, CueColor); + } + + break; + + case ModernUiValueDateTime: + // + // Two segment ticks (three-field value). + // + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ CueRect.X + W / 3, CueRect.Y + 1, 2, H - 2 }, CueColor); + if (!EFI_ERROR (Status)) { + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ CueRect.X + (W * 2) / 3, CueRect.Y + 1, 2, H - 2 }, CueColor); + } + + break; + + case ModernUiValuePassword: + // + // A short row of masked dots. + // + DotSide = MAX (2, W / 5); + for (Index = 0; Index < 3; Index++) { + Status = ModernUiFillRect ( + Context, + (MODERN_UI_RECT){ CueRect.X + 2 + Index * (DotSide + 1), CueRect.Y + H / 2 - DotSide / 2, DotSide, DotSide }, + CueColor + ); + if (EFI_ERROR (Status)) { + break; + } + } + + break; + + case ModernUiValueString: + // + // Text caret. + // + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ CueRect.X + W / 2, CueRect.Y + 1, 2, H - 2 }, CueColor); + break; + + case ModernUiValueAction: + // + // Navigate / activate arrow. + // + Status = ModernUiFillTriangle (Context, CueRect, ModernUiTriRight, CueColor); + break; + + default: + break; + } + + return Status; +} + EFI_STATUS EFIAPI ModernUiEngineDrawPopup ( diff --git a/Library/ModernUiLvglRendererLib/ModernUiRendererLib.c b/Library/ModernUiLvglRendererLib/ModernUiRendererLib.c new file mode 100644 index 0000000..2604f74 --- /dev/null +++ b/Library/ModernUiLvglRendererLib/ModernUiRendererLib.c @@ -0,0 +1,758 @@ +/** @file + LVGL-backed renderer library for ModernSetupPkg (experimental/lvgl-spike). + + This is a drop-in replacement for the hand-rolled GOP rasterizer + (Library/ModernUiRendererLib). It implements the identical + ModernUiRenderer.h API, but instead of issuing GOP fills and bitmap + blits directly, every primitive is composited by LVGL's software draw + pipeline: + + - All geometry (fills, borders, panels, rows, cards, progress, value + boxes, drop-downs) is drawn with lv_draw_rect. + - ASCII text is drawn with lv_draw_label using LVGL's Montserrat font. + - Non-ASCII (CJK) runs fall back to the firmware HII font composited + into the same buffer, because LVGL ships no bundled CJK coverage. + + The bridge is a persistent full-screen XRGB8888 canvas that acts as a + shadow framebuffer. Each primitive renders into the canvas through an + LVGL draw layer and then BLTs only its bounding region to the live GOP + surface. This keeps the imperative (immediate-mode) renderer contract + that ModernDisplayEngineDxe and ModernSetupApp depend on, while routing + all actual pixel generation through LVGL. + + SetupBrowser / FormBrowser / HII ownership is unchanged; this library + only paints. + + Copyright (c) 2026, MarsDoge. All rights reserved.
+ Author: MarsDoge (Dongyan Qian) + Open source: https://github.com/MarsDoge/ModernSetupPkg + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "../ModernUiRendererLib/ModernUiGlyphs.h" +#include "../ModernUiRendererLib/ModernUiRendererInternal.h" + +#define MODERN_UI_TEXT_CELL_HEIGHT MODERN_UI_BUILTIN_GLYPH_HEIGHT + +// +// LVGL bridge state. The library is statically linked into a single +// consuming module (the display engine or the app), so one shadow canvas +// per module instance is sufficient. mLvInitDone guards the one-time +// lv_uefi_init()/lv_init() handshake against repeated ModernUiRendererInit(). +// +STATIC BOOLEAN mLvInitDone = FALSE; +STATIC BOOLEAN mLvglReady = FALSE; +STATIC lv_display_t *mDisplay = NULL; +STATIC lv_obj_t *mCanvas = NULL; +STATIC EFI_GRAPHICS_OUTPUT_BLT_PIXEL *mCanvasBuf = NULL; +STATIC VOID *mDispBuf = NULL; +STATIC UINTN mCanvasW = 0; +STATIC UINTN mCanvasH = 0; +STATIC EFI_GRAPHICS_OUTPUT_PROTOCOL *mGop = NULL; +STATIC EFI_HII_FONT_PROTOCOL *mFont = NULL; + + +/** + Convert a GOP BLT pixel to an LVGL color. + + @param[in] Pixel Source GOP pixel. Reserved is ignored. + + @return Equivalent lv_color_t. +**/ +STATIC +lv_color_t +ToLvColor ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Pixel + ) +{ + return lv_color_make (Pixel.Red, Pixel.Green, Pixel.Blue); +} + + +/** + No-op LVGL display flush callback. + + The shadow canvas is composited and BLT'd by this library directly; the + bridge display is never refreshed through LVGL's timer pipeline, so this + callback only acknowledges completion to satisfy the display contract. + + @param[in] Display LVGL display being flushed. + @param[in] Area Flushed area. Unused. + @param[in] PxMap Pixel data. Unused. +**/ +STATIC +VOID +LvglBridgeFlush ( + IN lv_display_t *Display, + IN const lv_area_t *Area, + IN UINT8 *PxMap + ) +{ + (VOID)Area; + (VOID)PxMap; + lv_display_flush_ready (Display); +} + + +/** + Push a rectangular region of the shadow canvas to the live GOP surface. + + The region is clipped to the canvas extent. NULL/empty regions and a + not-yet-initialized bridge are ignored. + + @param[in] X Left coordinate in canvas/screen pixels. + @param[in] Y Top coordinate in canvas/screen pixels. + @param[in] Width Region width in pixels. + @param[in] Height Region height in pixels. +**/ +STATIC +VOID +BltCanvasRegion ( + IN UINTN X, + IN UINTN Y, + IN UINTN Width, + IN UINTN Height + ) +{ + if (!mLvglReady || (mGop == NULL) || (mCanvasBuf == NULL)) { + return; + } + + if ((X >= mCanvasW) || (Y >= mCanvasH) || (Width == 0) || (Height == 0)) { + return; + } + + if ((X + Width) > mCanvasW) { + Width = mCanvasW - X; + } + + if ((Y + Height) > mCanvasH) { + Height = mCanvasH - Y; + } + + mGop->Blt ( + mGop, + mCanvasBuf, + EfiBltBufferToVideo, + X, + Y, + X, + Y, + Width, + Height, + mCanvasW * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL) + ); +} + + +/** + Initialize a render context and the LVGL shadow-canvas bridge. + + On the first call this performs the one-time LVGL UEFI handshake + (lv_uefi_init() must precede lv_init()), then creates a bridge display + and a full-screen XRGB8888 canvas sized to the active GOP mode. Later + calls reuse the existing bridge and only refresh the cached context. + + @param[out] Context Render context to initialize. Must not be NULL. On + success, Width and Height describe the active GOP mode. + + @retval EFI_SUCCESS Context and bridge were initialized. + @retval EFI_INVALID_PARAMETER Context is NULL. + @retval EFI_NOT_FOUND GOP is unavailable or has no active mode. + @retval EFI_OUT_OF_RESOURCES Canvas allocation failed. +**/ +EFI_STATUS +EFIAPI +ModernUiRendererInit ( + OUT MODERN_UI_RENDER_CONTEXT *Context + ) +{ + EFI_STATUS Status; + UINTN CanvasBytes; + UINTN DispBytes; + + if (Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + ZeroMem (Context, sizeof (*Context)); + + Status = gBS->HandleProtocol ( + gST->ConsoleOutHandle, + &gEfiGraphicsOutputProtocolGuid, + (VOID **)&Context->Gop + ); + if (EFI_ERROR (Status)) { + Status = gBS->LocateProtocol ( + &gEfiGraphicsOutputProtocolGuid, + NULL, + (VOID **)&Context->Gop + ); + } + + if (EFI_ERROR (Status) || (Context->Gop == NULL) || (Context->Gop->Mode == NULL) || (Context->Gop->Mode->Info == NULL)) { + DEBUG ((DEBUG_ERROR, "%a: GOP unavailable: %r\n", __func__, Status)); + return EFI_NOT_FOUND; + } + + ModernUiSelectPreferredGopMode (Context->Gop); + Context->Width = Context->Gop->Mode->Info->HorizontalResolution; + Context->Height = Context->Gop->Mode->Info->VerticalResolution; + + Status = gBS->LocateProtocol ( + &gEfiHiiFontProtocolGuid, + NULL, + (VOID **)&Context->Font + ); + if (EFI_ERROR (Status)) { + Context->Font = NULL; + } + + // + // One-time LVGL handshake. lv_uefi_init() caches the image handle and + // system table for the LVGL UEFI backend and must run before lv_init(). + // + if (!mLvInitDone) { + lv_uefi_init (gImageHandle, gST); + lv_init (); + mLvInitDone = TRUE; + } + + // + // (Re)build the shadow canvas if it is missing or the mode changed. + // + if (!mLvglReady || (mCanvasW != Context->Width) || (mCanvasH != Context->Height)) { + if (mCanvasBuf != NULL) { + FreePool (mCanvasBuf); + mCanvasBuf = NULL; + } + + if (mDispBuf != NULL) { + FreePool (mDispBuf); + mDispBuf = NULL; + } + + CanvasBytes = Context->Width * Context->Height * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + mCanvasBuf = AllocateZeroPool (CanvasBytes); + if (mCanvasBuf == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // The bridge display is never refreshed through LVGL's timer pipeline, + // but a small draw buffer keeps the display contract well-formed. + // + DispBytes = Context->Width * 16 * sizeof (EFI_GRAPHICS_OUTPUT_BLT_PIXEL); + mDispBuf = AllocateZeroPool (DispBytes); + if (mDispBuf == NULL) { + FreePool (mCanvasBuf); + mCanvasBuf = NULL; + return EFI_OUT_OF_RESOURCES; + } + + if (mDisplay == NULL) { + mDisplay = lv_display_create ((int32_t)Context->Width, (int32_t)Context->Height); + if (mDisplay == NULL) { + FreePool (mCanvasBuf); + FreePool (mDispBuf); + mCanvasBuf = NULL; + mDispBuf = NULL; + return EFI_OUT_OF_RESOURCES; + } + + lv_display_set_flush_cb (mDisplay, LvglBridgeFlush); + } + + lv_display_set_buffers ( + mDisplay, + mDispBuf, + NULL, + (uint32_t)DispBytes, + LV_DISPLAY_RENDER_MODE_PARTIAL + ); + + mCanvas = lv_canvas_create (lv_display_get_screen_active (mDisplay)); + if (mCanvas == NULL) { + FreePool (mCanvasBuf); + FreePool (mDispBuf); + mCanvasBuf = NULL; + mDispBuf = NULL; + return EFI_OUT_OF_RESOURCES; + } + + lv_canvas_set_buffer ( + mCanvas, + mCanvasBuf, + (int32_t)Context->Width, + (int32_t)Context->Height, + LV_COLOR_FORMAT_XRGB8888 + ); + } + + mGop = Context->Gop; + mFont = Context->Font; + mCanvasW = Context->Width; + mCanvasH = Context->Height; + mLvglReady = TRUE; + + return EFI_SUCCESS; +} + + +/** + Fill a rectangle by compositing an opaque lv_draw_rect into the shadow + canvas, then BLT the affected region to the GOP surface. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] Rect Pixel rectangle. Zero width or height is invalid. + @param[in] Color Fill color. + + @retval EFI_SUCCESS Rectangle was filled or clipped outside view. + @retval EFI_INVALID_PARAMETER Context is NULL, GOP is unavailable, or Rect is empty. +**/ +EFI_STATUS +EFIAPI +ModernUiFillRect ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT Rect, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color + ) +{ + lv_layer_t Layer; + lv_draw_rect_dsc_t Dsc; + lv_area_t Coords; + + if ((Context == NULL) || (Context->Gop == NULL) || (Rect.Width == 0) || (Rect.Height == 0)) { + return EFI_INVALID_PARAMETER; + } + + if (!mLvglReady || (mCanvas == NULL)) { + return EFI_NOT_READY; + } + + if ((Rect.X >= mCanvasW) || (Rect.Y >= mCanvasH)) { + return EFI_SUCCESS; + } + + if ((Rect.X + Rect.Width) > mCanvasW) { + Rect.Width = mCanvasW - Rect.X; + } + + if ((Rect.Y + Rect.Height) > mCanvasH) { + Rect.Height = mCanvasH - Rect.Y; + } + + lv_canvas_init_layer (mCanvas, &Layer); + + lv_draw_rect_dsc_init (&Dsc); + Dsc.bg_color = ToLvColor (Color); + Dsc.bg_opa = LV_OPA_COVER; + + Coords.x1 = (int32_t)Rect.X; + Coords.y1 = (int32_t)Rect.Y; + Coords.x2 = (int32_t)(Rect.X + Rect.Width - 1); + Coords.y2 = (int32_t)(Rect.Y + Rect.Height - 1); + lv_draw_rect (&Layer, &Dsc, &Coords); + + lv_canvas_finish_layer (mCanvas, &Layer); + + BltCanvasRegion (Rect.X, Rect.Y, Rect.Width, Rect.Height); + return EFI_SUCCESS; +} + + +/** + Composite a UCS-2 run through the firmware HII font into the shadow canvas. + + Used for non-ASCII (CJK) runs, which LVGL's bundled Latin fonts do not + cover. HII renders into the canvas bitmap (not direct-to-screen) so the + run composites with the surrounding LVGL output and is BLT'd in one pass. + + @param[in] X Left coordinate in pixels. + @param[in] Y Top coordinate in pixels. + @param[in] Text Null-terminated UCS-2 run. Must not be NULL. + @param[in] PixelWidth Pre-measured run width used for the BLT region. + @param[in] Color Text foreground color. + @param[in] Background Text background color. + + @retval EFI_SUCCESS Run was rendered. + @retval EFI_UNSUPPORTED HII Font protocol is unavailable. + @retval EFI_OUT_OF_RESOURCES Temporary allocation failed. + @retval others Status from StringToImage(). +**/ +STATIC +EFI_STATUS +DrawHiiCanvasRun ( + IN UINTN X, + IN UINTN Y, + IN CONST CHAR16 *Text, + IN UINTN PixelWidth, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background + ) +{ + EFI_IMAGE_OUTPUT *Blt; + EFI_FONT_DISPLAY_INFO FontInfo; + EFI_STATUS Status; + + if (mFont == NULL) { + return EFI_UNSUPPORTED; + } + + Blt = AllocateZeroPool (sizeof (*Blt)); + if (Blt == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + ZeroMem (&FontInfo, sizeof (FontInfo)); + FontInfo.ForegroundColor = Color; + FontInfo.BackgroundColor = Background; + + // + // Target the shadow canvas bitmap (Image.Bitmap), NOT the live screen, so + // the run blends with the LVGL-drawn pixels already in the canvas. + // + Blt->Width = (UINT16)mCanvasW; + Blt->Height = (UINT16)mCanvasH; + Blt->Image.Bitmap = mCanvasBuf; + + Status = mFont->StringToImage ( + mFont, + EFI_HII_IGNORE_IF_NO_GLYPH | EFI_HII_OUT_FLAG_CLIP | + EFI_HII_OUT_FLAG_CLIP_CLEAN_X | EFI_HII_OUT_FLAG_CLIP_CLEAN_Y | + EFI_HII_IGNORE_LINE_BREAK, + (EFI_STRING)Text, + &FontInfo, + &Blt, + X, + Y, + NULL, + NULL, + NULL + ); + FreePool (Blt); + + BltCanvasRegion (X, Y, PixelWidth, MODERN_UI_TEXT_CELL_HEIGHT); + return Status; +} + + +/** + Composite one embedded bitmap glyph into the shadow canvas. + + ModernUiGlyphs.c carries the package's own CJK/icon glyph bitmaps (the + alpha coverage the app uses for its localized labels). These are rendered + here by alpha-blending each pixel against the canvas-supplied background, + then BLT'd. This keeps the original glyph fidelity while everything else + goes through LVGL; LVGL ships no CJK font, so this is the primary non-ASCII + path and HII is only a fallback for code points without an embedded glyph. + + @param[in] X Left coordinate in pixels. + @param[in] Y Top coordinate in pixels. + @param[in] Glyph Embedded glyph data. Must not be NULL. + @param[in] Color Foreground color. + @param[in] Background Background color blended under the glyph coverage. + + @retval EFI_SUCCESS Glyph was composited. + @retval EFI_INVALID_PARAMETER Glyph is NULL or the bridge is not ready. +**/ +STATIC +EFI_STATUS +DrawBuiltinGlyphCanvas ( + IN UINTN X, + IN UINTN Y, + IN CONST MODERN_UI_BUILTIN_GLYPH *Glyph, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background + ) +{ + UINTN Row; + UINTN Column; + UINTN Alpha; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *Pixel; + + if ((Glyph == NULL) || !mLvglReady || (mCanvasBuf == NULL)) { + return EFI_INVALID_PARAMETER; + } + + for (Row = 0; (Row < Glyph->Height) && ((Y + Row) < mCanvasH); Row++) { + for (Column = 0; (Column < Glyph->Width) && ((X + Column) < mCanvasW); Column++) { + Alpha = Glyph->Bitmap[Row * Glyph->Width + Column]; + Pixel = &mCanvasBuf[(Y + Row) * mCanvasW + (X + Column)]; + Pixel->Red = (UINT8)(((UINTN)Background.Red * (255 - Alpha) + (UINTN)Color.Red * Alpha) / 255); + Pixel->Green = (UINT8)(((UINTN)Background.Green * (255 - Alpha) + (UINTN)Color.Green * Alpha) / 255); + Pixel->Blue = (UINT8)(((UINTN)Background.Blue * (255 - Alpha) + (UINTN)Color.Blue * Alpha) / 255); + Pixel->Reserved = 0; + } + } + + BltCanvasRegion (X, Y, Glyph->Width, Glyph->Height); + return EFI_SUCCESS; +} + + +/** + Draw an ASCII run with lv_draw_label, on a freshly painted background. + + The run background is filled and the label is drawn in a single LVGL + draw layer so both composite into the shadow canvas before one BLT. + + @param[in] X Left coordinate in pixels. + @param[in] Y Top coordinate in pixels. + @param[in] Ascii Null-terminated ASCII (UTF-8 compatible) run. + @param[in] PixelWidth Pre-measured run width used for background and BLT. + @param[in] Color Text foreground color. + @param[in] Background Background color painted behind the run. + + @retval EFI_SUCCESS Run was rendered (or empty width ignored). +**/ +STATIC +EFI_STATUS +DrawLvglAsciiRun ( + IN UINTN X, + IN UINTN Y, + IN CONST CHAR8 *Ascii, + IN UINTN PixelWidth, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background + ) +{ + lv_layer_t Layer; + lv_draw_rect_dsc_t RectDsc; + lv_draw_label_dsc_t LabelDsc; + lv_area_t BgCoords; + lv_area_t TextCoords; + int32_t BottomY; + UINTN BltWidth; + + if ((PixelWidth == 0) || (X >= mCanvasW) || (Y >= mCanvasH)) { + return EFI_SUCCESS; + } + + if ((X + PixelWidth) > mCanvasW) { + PixelWidth = mCanvasW - X; + } + + BottomY = (int32_t)(Y + MODERN_UI_TEXT_CELL_HEIGHT - 1); + if (BottomY >= (int32_t)mCanvasH) { + BottomY = (int32_t)mCanvasH - 1; + } + + // + // The background is painted at the layout-reserved run width (measured on + // the 8 px cell model the callers expect), but the label is clipped to the + // canvas right edge so LVGL's variable-width glyphs are not truncated. Any + // overshoot into a neighbouring column is repainted by the caller's next + // column draw. The BLT covers the wider of the two so glyph overshoot + // reaches the screen. + // + BgCoords.x1 = (int32_t)X; + BgCoords.y1 = (int32_t)Y; + BgCoords.x2 = (int32_t)(X + PixelWidth - 1); + BgCoords.y2 = BottomY; + + TextCoords.x1 = (int32_t)X; + TextCoords.y1 = (int32_t)Y; + TextCoords.x2 = (int32_t)mCanvasW - 1; + TextCoords.y2 = BottomY; + + lv_canvas_init_layer (mCanvas, &Layer); + + lv_draw_rect_dsc_init (&RectDsc); + RectDsc.bg_color = ToLvColor (Background); + RectDsc.bg_opa = LV_OPA_COVER; + lv_draw_rect (&Layer, &RectDsc, &BgCoords); + + lv_draw_label_dsc_init (&LabelDsc); + LabelDsc.text = (const char *)Ascii; + LabelDsc.font = LV_FONT_DEFAULT; + LabelDsc.color = ToLvColor (Color); + LabelDsc.opa = LV_OPA_COVER; + LabelDsc.align = LV_TEXT_ALIGN_LEFT; + lv_draw_label (&Layer, &LabelDsc, &TextCoords); + + lv_canvas_finish_layer (mCanvas, &Layer); + + // + // BLT enough width to carry glyph overshoot past the reserved cell width, + // bounded by the canvas. ~60% slack covers Montserrat's wider caps. + // + BltWidth = PixelWidth + (PixelWidth * 3) / 5 + MODERN_UI_ASCII_CELL_WIDTH; + if ((X + BltWidth) > mCanvasW) { + BltWidth = mCanvasW - X; + } + + BltCanvasRegion (X, Y, BltWidth, MODERN_UI_TEXT_CELL_HEIGHT); + return EFI_SUCCESS; +} + + +/** + Draw UCS-2 text by routing each homogeneous run to the best backend: + text-mode graphic glyphs to LVGL rectangles, ASCII to lv_draw_label, and + non-ASCII (CJK) to the firmware HII font composited into the canvas. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] X Left coordinate in pixels. + @param[in] Y Top coordinate in pixels. + @param[in] Text Null-terminated UCS-2 string. Must not be NULL. + @param[in] Color Text foreground color. + @param[in] Background Background color. + + @retval EFI_SUCCESS Text was rendered. + @retval EFI_INVALID_PARAMETER Context or Text is NULL. + @retval others First error from a run backend. +**/ +EFI_STATUS +EFIAPI +ModernUiDrawText ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN UINTN X, + IN UINTN Y, + IN CONST CHAR16 *Text, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background + ) +{ + CHAR16 Run16[MODERN_UI_TEXT_SEGMENT_MAX + 1]; + CHAR8 Ascii[MODERN_UI_TEXT_SEGMENT_MAX + 1]; + UINTN Index; + UINTN RunLen; + UINTN CurrentX; + UINTN RunWidth; + EFI_STATUS Status; + EFI_STATUS ReturnStatus; + CONST MODERN_UI_BUILTIN_GLYPH *Glyph; + + if ((Context == NULL) || (Context->Gop == NULL) || (Text == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!mLvglReady || (mCanvas == NULL)) { + return EFI_NOT_READY; + } + + CurrentX = X; + ReturnStatus = EFI_SUCCESS; + Index = 0; + while (Text[Index] != L'\0') { + // + // UEFI text-mode graphic glyph: emit as LVGL rectangles, one cell each. + // + if (ModernUiIsTextModeGraphicGlyph (Text[Index])) { + Status = ModernUiDrawTextModeGraphicGlyph (Context, CurrentX, Y, Text[Index], Color, Background); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + + CurrentX += MODERN_UI_GRAPHIC_CELL_WIDTH; + Index++; + continue; + } + + // + // Non-ASCII (CJK / icon): prefer the package's embedded bitmap glyph + // (LVGL ships no CJK font), accumulating code points without an embedded + // glyph into an HII sub-run that is flushed at each glyph boundary. + // + if (Text[Index] > 0x7F) { + RunLen = 0; + while ((Text[Index] != L'\0') && (Text[Index] > 0x7F) && !ModernUiIsTextModeGraphicGlyph (Text[Index])) { + Glyph = ModernUiFindBuiltinGlyph (Text[Index]); + if (Glyph != NULL) { + // + // Flush any pending HII sub-run before the embedded glyph. + // + if (RunLen > 0) { + Run16[RunLen] = L'\0'; + RunWidth = ModernUiMeasureText (Run16); + Status = DrawHiiCanvasRun (CurrentX, Y, Run16, RunWidth, Color, Background); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + + CurrentX += RunWidth; + RunLen = 0; + } + + Status = DrawBuiltinGlyphCanvas (CurrentX, Y, Glyph, Color, Background); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + + CurrentX += Glyph->Advance; + Index++; + continue; + } + + if (RunLen >= MODERN_UI_TEXT_SEGMENT_MAX) { + Run16[RunLen] = L'\0'; + RunWidth = ModernUiMeasureText (Run16); + Status = DrawHiiCanvasRun (CurrentX, Y, Run16, RunWidth, Color, Background); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + + CurrentX += RunWidth; + RunLen = 0; + } + + Run16[RunLen++] = Text[Index++]; + } + + // + // Flush the trailing HII sub-run, if any. + // + if (RunLen > 0) { + Run16[RunLen] = L'\0'; + RunWidth = ModernUiMeasureText (Run16); + Status = DrawHiiCanvasRun (CurrentX, Y, Run16, RunWidth, Color, Background); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + + CurrentX += RunWidth; + } + + continue; + } + + // + // ASCII run: LVGL lv_draw_label. + // + RunLen = 0; + while ((Text[Index] != L'\0') && (Text[Index] <= 0x7F) && + !ModernUiIsTextModeGraphicGlyph (Text[Index]) && (RunLen < MODERN_UI_TEXT_SEGMENT_MAX)) + { + Run16[RunLen] = Text[Index]; + Ascii[RunLen] = (CHAR8)Text[Index]; + RunLen++; + Index++; + } + + Run16[RunLen] = L'\0'; + Ascii[RunLen] = '\0'; + RunWidth = ModernUiMeasureText (Run16); + Status = DrawLvglAsciiRun (CurrentX, Y, Ascii, RunWidth, Color, Background); + if (EFI_ERROR (Status)) { + ReturnStatus = Status; + } + + CurrentX += RunWidth; + } + + return ReturnStatus; +} diff --git a/Library/ModernUiLvglRendererLib/ModernUiRendererLib.inf b/Library/ModernUiLvglRendererLib/ModernUiRendererLib.inf new file mode 100644 index 0000000..206ed8f --- /dev/null +++ b/Library/ModernUiLvglRendererLib/ModernUiRendererLib.inf @@ -0,0 +1,57 @@ +## @file +# LVGL-backed renderer library for ModernSetupPkg (experimental/lvgl-spike). +# +# Drop-in replacement for Library/ModernUiRendererLib: same ModernUiRendererLib +# class and ModernUiRenderer.h API, but every primitive is composited by LVGL's +# software draw pipeline (lv_draw_rect / lv_draw_label) into a shadow canvas and +# BLT'd to GOP. CJK runs fall back to the firmware HII font, which LVGL's bundled +# Latin fonts do not cover. +# +# experimental/lvgl-spike only; selected by MODERN_SETUP_DISPLAY_ENGINE=lvgl. +# Never wired into a default firmware overlay. +# +# Copyright (c) 2026, MarsDoge. All rights reserved.
+# Author: MarsDoge (Dongyan Qian) +# Open source: https://github.com/MarsDoge/ModernSetupPkg +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ModernUiLvglRendererLib + FILE_GUID = 7C1E2A94-3D6B-4F12-9A8E-5B0C7D11AE20 + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 0.1 + LIBRARY_CLASS = ModernUiRendererLib|UEFI_APPLICATION DXE_DRIVER + +[Sources] + ModernUiRendererLib.c + # The backend-agnostic renderer surface and glyph table live in the GOP + # renderer dir (single source of truth) and are compiled into both renderers. + ../ModernUiRendererLib/ModernUiRendererCommon.c + ../ModernUiRendererLib/ModernUiGlyphs.c + +[Packages] + MdePkg/MdePkg.dec + ModernSetupPkg/ModernSetupPkg.dec + LvglSpikePkg/LvglSpikePkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + PrintLib + UefiBootServicesTableLib + LvglCoreLib + +[Protocols] + gEfiGraphicsOutputProtocolGuid + gEfiHiiFontProtocolGuid + +[BuildOptions] + # ModernUiRendererLib.c includes , which pulls lv_conf.h (in + # LvglSpikePkg/Library/LvglLib, on the package include path) via + # LV_CONF_INCLUDE_SIMPLE. + GCC:*_*_*_CC_FLAGS = -DLV_CONF_INCLUDE_SIMPLE diff --git a/Library/ModernUiRendererLib/ModernUiRendererCommon.c b/Library/ModernUiRendererLib/ModernUiRendererCommon.c new file mode 100644 index 0000000..8509fe5 --- /dev/null +++ b/Library/ModernUiRendererLib/ModernUiRendererCommon.c @@ -0,0 +1,1134 @@ +/** @file + Backend-agnostic shared renderer surface for ModernSetupPkg. + + This translation unit is compiled verbatim into BOTH renderer libraries (the + GOP renderer in Library/ModernUiRendererLib and the LVGL renderer in + Library/ModernUiLvglRendererLib). It implements every geometry composition, + text-measurement, and themed-widget function of ModernUiRenderer.h on top of + the three backend primitives each renderer supplies: ModernUiRendererInit, + ModernUiFillRect, and ModernUiDrawText. See ModernUiRendererInternal.h. + + Copyright (c) 2026, MarsDoge. All rights reserved.
+ Author: MarsDoge (Dongyan Qian) + Open source: https://github.com/MarsDoge/ModernSetupPkg + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "ModernUiGlyphs.h" +#include "ModernUiRendererInternal.h" + +/** + Blend two GOP colors by percentage weight. + + @param[in] Base Base color used when Weight is zero. + @param[in] Accent Accent color used when Weight is one hundred. + @param[in] Weight Accent weight in percent. Values above 100 are clamped. + + @return Blended color with Reserved cleared. +**/ +EFI_GRAPHICS_OUTPUT_BLT_PIXEL +EFIAPI +ModernUiBlendColor ( + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Base, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Accent, + IN UINT8 Weight + ) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL Result; + UINTN ClampedWeight; + + ClampedWeight = MIN (Weight, 100); + Result.Red = (UINT8)( + ((UINTN)Base.Red * (100 - ClampedWeight) + + (UINTN)Accent.Red * ClampedWeight) / 100 + ); + Result.Green = (UINT8)( + ((UINTN)Base.Green * (100 - ClampedWeight) + + (UINTN)Accent.Green * ClampedWeight) / 100 + ); + Result.Blue = (UINT8)( + ((UINTN)Base.Blue * (100 - ClampedWeight) + + (UINTN)Accent.Blue * ClampedWeight) / 100 + ); + Result.Reserved = 0; + return Result; +} + + +/** + Select a preferred GOP mode when the active mode is smaller than the target. + + @param[in] Gop Graphics output protocol to inspect. Must not be NULL. + + @retval EFI_SUCCESS Current mode is acceptable or a better mode + was selected. + @retval EFI_INVALID_PARAMETER Gop or mode data is NULL. +**/ +EFI_STATUS +ModernUiSelectPreferredGopMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop + ) +{ + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + UINTN InfoSize; + UINT32 Mode; + UINT32 BestMode; + UINTN BestArea; + UINTN Area; + + if ((Gop == NULL) || (Gop->Mode == NULL) || (Gop->Mode->Info == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Gop->Mode->Info->HorizontalResolution >= MODERN_UI_TARGET_WIDTH) && + (Gop->Mode->Info->VerticalResolution >= MODERN_UI_TARGET_HEIGHT)) + { + return EFI_SUCCESS; + } + + BestMode = Gop->Mode->MaxMode; + BestArea = MAX_UINTN; + for (Mode = 0; Mode < Gop->Mode->MaxMode; Mode++) { + Info = NULL; + InfoSize = 0; + Status = Gop->QueryMode (Gop, Mode, &InfoSize, &Info); + if (EFI_ERROR (Status) || (Info == NULL)) { + continue; + } + + if ((Info->HorizontalResolution >= MODERN_UI_TARGET_WIDTH) && + (Info->VerticalResolution >= MODERN_UI_TARGET_HEIGHT)) + { + Area = (UINTN)Info->HorizontalResolution * (UINTN)Info->VerticalResolution; + if (Area < BestArea) { + BestArea = Area; + BestMode = Mode; + } + } + + FreePool (Info); + } + + if ((BestMode < Gop->Mode->MaxMode) && (BestMode != Gop->Mode->Mode)) { + Gop->SetMode (Gop, BestMode); + } + + return EFI_SUCCESS; +} + + +/** + Fill the full render target with one color. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] Color Fill color. + + @retval EFI_SUCCESS The screen was cleared. + @retval EFI_INVALID_PARAMETER Context is NULL or invalid. + @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. +**/ +EFI_STATUS +EFIAPI +ModernUiClear ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color + ) +{ + if (Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + return ModernUiFillRect (Context, (MODERN_UI_RECT){ 0, 0, Context->Width, Context->Height }, Color); +} + + +/** + Draw a one-pixel rectangle border. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] Rect Pixel rectangle to outline. + @param[in] Color Border color. + + @retval EFI_SUCCESS Border was drawn. + @retval EFI_INVALID_PARAMETER Context is NULL, GOP is unavailable, or Rect is empty. + @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. +**/ +EFI_STATUS +EFIAPI +ModernUiStrokeRect ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT Rect, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color + ) +{ + EFI_STATUS Status; + + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X, Rect.Y, Rect.Width, 1 }, Color); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X, Rect.Y + Rect.Height - 1, Rect.Width, 1 }, Color); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X, Rect.Y, 1, Rect.Height }, Color); + if (EFI_ERROR (Status)) { + return Status; + } + + return ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X + Rect.Width - 1, Rect.Y, 1, Rect.Height }, Color); +} + + +/** + Fill an isosceles triangle inscribed in a rectangle. + + Built from one-pixel renderer fill spans so the shape renders identically + through the GOP and LVGL backends. A failed span aborts and returns its + status, leaving the partially drawn triangle in place. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] Rect Bounding rectangle. Zero width or height is ignored. + @param[in] Dir Apex direction (down / up / right). + @param[in] Color Fill color. + + @retval EFI_SUCCESS Triangle was drawn (or Rect was empty). + @retval EFI_INVALID_PARAMETER Context is NULL. + @retval others Status from the first failing fill span. +**/ +EFI_STATUS +EFIAPI +ModernUiFillTriangle ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT Rect, + IN MODERN_UI_TRI_DIR Dir, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color + ) +{ + UINTN Index; + UINTN Span; + UINTN Step; + EFI_STATUS Status; + + if (Context == NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((Rect.Width == 0) || (Rect.Height == 0)) { + return EFI_SUCCESS; + } + + if (Dir == ModernUiTriRight) { + // + // Horizontal sweep: each column's height tapers toward the right apex. + // + for (Index = 0; Index < Rect.Width; Index++) { + Span = (Rect.Height * (Rect.Width - Index)) / Rect.Width; + if (Span == 0) { + Span = 1; + } + + Status = ModernUiFillRect ( + Context, + (MODERN_UI_RECT){ Rect.X + Index, Rect.Y + (Rect.Height - Span) / 2, 1, Span }, + Color + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; + } + + // + // Vertical sweep: ModernUiTriUp widens downward, ModernUiTriDown widens up. + // + for (Index = 0; Index < Rect.Height; Index++) { + Step = (Dir == ModernUiTriUp) ? (Index + 1) : (Rect.Height - Index); + Span = (Rect.Width * Step) / Rect.Height; + if (Span == 0) { + Span = 1; + } + + Status = ModernUiFillRect ( + Context, + (MODERN_UI_RECT){ Rect.X + (Rect.Width - Span) / 2, Rect.Y + Index, Span, 1 }, + Color + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + + +/** + Return whether a UCS-2 character is a UEFI text-mode graphics character that + should be rendered as a narrow fixed-width shape. + + @param[in] CodePoint UCS-2 code point to classify. + + @retval TRUE CodePoint is a box, arrow, triangle, or checkbox glyph used by + edk2 text-mode setup UI. + @retval FALSE CodePoint should use normal text or built-in glyph rendering. +**/ +BOOLEAN +ModernUiIsTextModeGraphicGlyph ( + IN CHAR16 CodePoint + ) +{ + return (((CodePoint >= BOXDRAW_HORIZONTAL) && (CodePoint <= BOXDRAW_DOUBLE_VERTICAL_HORIZONTAL)) || + ((CodePoint >= ARROW_LEFT) && (CodePoint <= ARROW_DOWN)) || + (CodePoint == 0x25A0) || + (CodePoint == 0x25A1) || + (CodePoint == 0x25B2) || + (CodePoint == 0x25B6) || + (CodePoint == 0x25BA) || + (CodePoint == 0x25BC) || + (CodePoint == 0x25C0) || + (CodePoint == 0x25C4)); +} + + +/** + Draw one UEFI text-mode graphics character as a narrow GOP primitive. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] X Left coordinate in pixels. + @param[in] Y Top coordinate in pixels. + @param[in] CodePoint Box, arrow, triangle, or checkbox glyph to render. + @param[in] Color Foreground color. + @param[in] Background Background fill color. + + @retval EFI_SUCCESS Glyph was rendered. + @retval EFI_INVALID_PARAMETER Context is NULL or GOP is unavailable. + @retval others Status from fill or stroke primitives. +**/ +EFI_STATUS +ModernUiDrawTextModeGraphicGlyph ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN UINTN X, + IN UINTN Y, + IN CHAR16 CodePoint, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background + ) +{ + EFI_STATUS Status; + BOOLEAN Horizontal; + BOOLEAN Vertical; + BOOLEAN Left; + BOOLEAN Right; + BOOLEAN Up; + BOOLEAN Down; + UINTN Index; + + if ((Context == NULL) || (Context->Gop == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ X, Y, MODERN_UI_GRAPHIC_CELL_WIDTH, MODERN_UI_BUILTIN_GLYPH_HEIGHT }, Background); + if (EFI_ERROR (Status)) { + return Status; + } + + Horizontal = FALSE; + Vertical = FALSE; + Left = FALSE; + Right = FALSE; + Up = FALSE; + Down = FALSE; + + switch (CodePoint) { + case BOXDRAW_HORIZONTAL: + case BOXDRAW_DOUBLE_HORIZONTAL: + Left = Right = TRUE; + break; + case BOXDRAW_VERTICAL: + case BOXDRAW_DOUBLE_VERTICAL: + Up = Down = TRUE; + break; + case BOXDRAW_DOWN_RIGHT: + case BOXDRAW_DOWN_RIGHT_DOUBLE: + case BOXDRAW_DOWN_DOUBLE_RIGHT: + case BOXDRAW_DOUBLE_DOWN_RIGHT: + Right = Down = TRUE; + break; + case BOXDRAW_DOWN_LEFT: + case BOXDRAW_DOWN_LEFT_DOUBLE: + case BOXDRAW_DOWN_DOUBLE_LEFT: + case BOXDRAW_DOUBLE_DOWN_LEFT: + Left = Down = TRUE; + break; + case BOXDRAW_UP_RIGHT: + case BOXDRAW_UP_RIGHT_DOUBLE: + case BOXDRAW_UP_DOUBLE_RIGHT: + case BOXDRAW_DOUBLE_UP_RIGHT: + Right = Up = TRUE; + break; + case BOXDRAW_UP_LEFT: + case BOXDRAW_UP_LEFT_DOUBLE: + case BOXDRAW_UP_DOUBLE_LEFT: + case BOXDRAW_DOUBLE_UP_LEFT: + Left = Up = TRUE; + break; + case BOXDRAW_VERTICAL_RIGHT: + case BOXDRAW_VERTICAL_RIGHT_DOUBLE: + case BOXDRAW_VERTICAL_DOUBLE_RIGHT: + case BOXDRAW_DOUBLE_VERTICAL_RIGHT: + Up = Down = Right = TRUE; + break; + case BOXDRAW_VERTICAL_LEFT: + case BOXDRAW_VERTICAL_LEFT_DOUBLE: + case BOXDRAW_VERTICAL_DOUBLE_LEFT: + case BOXDRAW_DOUBLE_VERTICAL_LEFT: + Up = Down = Left = TRUE; + break; + case BOXDRAW_DOWN_HORIZONTAL: + case BOXDRAW_DOWN_HORIZONTAL_DOUBLE: + case BOXDRAW_DOWN_DOUBLE_HORIZONTAL: + case BOXDRAW_DOUBLE_DOWN_HORIZONTAL: + Left = Right = Down = TRUE; + break; + case BOXDRAW_UP_HORIZONTAL: + case BOXDRAW_UP_HORIZONTAL_DOUBLE: + case BOXDRAW_UP_DOUBLE_HORIZONTAL: + case BOXDRAW_DOUBLE_UP_HORIZONTAL: + Left = Right = Up = TRUE; + break; + case BOXDRAW_VERTICAL_HORIZONTAL: + case BOXDRAW_VERTICAL_HORIZONTAL_DOUBLE: + case BOXDRAW_VERTICAL_DOUBLE_HORIZONTAL: + case BOXDRAW_DOUBLE_VERTICAL_HORIZONTAL: + Left = Right = Up = Down = TRUE; + break; + case ARROW_RIGHT: + case 0x25B6: + case 0x25BA: + for (Index = 0; Index < 6; Index++) { + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 2 + Index, Y + 5 + Index, 1, 7 - (Index * 2 > 6 ? 6 : Index * 2) }, Color); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; + case ARROW_LEFT: + case 0x25C0: + case 0x25C4: + for (Index = 0; Index < 6; Index++) { + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 6 - Index, Y + 5 + Index, 1, 7 - (Index * 2 > 6 ? 6 : Index * 2) }, Color); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; + case ARROW_UP: + case 0x25B2: + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 3, Y + 5, 2, 8 }, Color); + if (EFI_ERROR (Status)) { + return Status; + } + + return ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 2, Y + 6, 4, 2 }, Color); + case ARROW_DOWN: + case 0x25BC: + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 3, Y + 5, 2, 8 }, Color); + if (EFI_ERROR (Status)) { + return Status; + } + + return ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 2, Y + 10, 4, 2 }, Color); + case 0x25A0: + return ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 1, Y + 6, 6, 6 }, Color); + case 0x25A1: + return ModernUiStrokeRect (Context, (MODERN_UI_RECT){ X + 1, Y + 6, 6, 6 }, Color); + default: + break; + } + + Horizontal = Left || Right; + Vertical = Up || Down; + if (Horizontal) { + Status = ModernUiFillRect ( + Context, + (MODERN_UI_RECT){ + X + (Left ? 0 : MODERN_UI_GRAPHIC_LINE_X), + Y + MODERN_UI_GRAPHIC_LINE_Y, + (Left && Right) ? MODERN_UI_GRAPHIC_CELL_WIDTH : (MODERN_UI_GRAPHIC_CELL_WIDTH - MODERN_UI_GRAPHIC_LINE_X), + 1 + }, + Color + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (Vertical) { + Status = ModernUiFillRect ( + Context, + (MODERN_UI_RECT){ + X + MODERN_UI_GRAPHIC_LINE_X, + Y + (Up ? 0 : MODERN_UI_GRAPHIC_LINE_Y), + 1, + (Up && Down) ? MODERN_UI_BUILTIN_GLYPH_HEIGHT : (MODERN_UI_BUILTIN_GLYPH_HEIGHT - MODERN_UI_GRAPHIC_LINE_Y) + }, + Color + ); + } + + return Status; +} + + +/** + Return the expected pixel width for a UCS-2 string. + + Built-in CJK glyphs are measured at their bitmap width. Other characters use + the renderer's current ASCII cell width. + + @param[in] Text Null-terminated UCS-2 string. Must not be NULL. + + @return Pixel width. NULL input returns 0. +**/ +UINTN +EFIAPI +ModernUiMeasureText ( + IN CONST CHAR16 *Text + ) +{ + UINTN Index; + UINTN Width; + CONST MODERN_UI_BUILTIN_GLYPH *Glyph; + + if (Text == NULL) { + return 0; + } + + Width = 0; + for (Index = 0; Text[Index] != L'\0'; Index++) { + Glyph = ModernUiFindBuiltinGlyph (Text[Index]); + if (ModernUiIsTextModeGraphicGlyph (Text[Index])) { + Width += MODERN_UI_GRAPHIC_CELL_WIDTH; + } else if (Glyph != NULL) { + Width += Glyph->Advance; + } else if (Text[Index] > 0x7F) { + Width += MODERN_UI_BUILTIN_GLYPH_WIDTH; + } else { + Width += MODERN_UI_ASCII_CELL_WIDTH; + } + } + + return Width; +} + + +/** + Format and draw one UCS-2 text line. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] X Left coordinate in pixels. + @param[in] Y Top coordinate in pixels. + @param[in] Color Text foreground color. + @param[in] Background Text background color. + @param[in] Format PrintLib format string. Must not be NULL. + @param[in] ... Format arguments consumed according to Format. + + @retval EFI_SUCCESS Text was formatted and rendered. + @retval EFI_INVALID_PARAMETER Context or Format is NULL. + @retval others Status returned by ModernUiDrawText(). +**/ +EFI_STATUS +EFIAPI +ModernUiDrawTextFormatted ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN UINTN X, + IN UINTN Y, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background, + IN CONST CHAR16 *Format, + ... + ) +{ + VA_LIST Marker; + CHAR16 Buffer[192]; + + if ((Context == NULL) || (Format == NULL)) { + return EFI_INVALID_PARAMETER; + } + + VA_START (Marker, Format); + UnicodeVSPrint (Buffer, sizeof (Buffer), Format, Marker); + VA_END (Marker); + + return ModernUiDrawText (Context, X, Y, Buffer, Color, Background); +} + + +/** + Draw UCS-2 text constrained to a pixel width. + + The renderer measures mixed ASCII, built-in CJK, and text-mode graphic glyphs + and appends "..." when the string must be truncated. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] X Left coordinate in pixels. + @param[in] Y Top coordinate in pixels. + @param[in] MaxWidth Maximum text width in pixels. + @param[in] Text Null-terminated UCS-2 string. Must not be NULL. + @param[in] Color Text foreground color. + @param[in] Background Text background color. + + @retval EFI_SUCCESS Text was rendered or empty width was ignored. + @retval EFI_INVALID_PARAMETER Context or Text is NULL. + @retval EFI_OUT_OF_RESOURCES Temporary truncation buffer allocation failed. + @retval others Status returned by ModernUiDrawText(). +**/ +EFI_STATUS +EFIAPI +ModernUiDrawTextFit ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN UINTN X, + IN UINTN Y, + IN UINTN MaxWidth, + IN CONST CHAR16 *Text, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background + ) +{ + CHAR16 *Buffer; + CHAR16 Character[2]; + EFI_STATUS Status; + UINTN Index; + UINTN CopyChars; + UINTN CurrentWidth; + UINTN CharacterWidth; + UINTN EllipsisWidth; + UINTN TargetWidth; + UINTN TextLength; + + if ((Context == NULL) || (Text == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (MaxWidth == 0) { + return EFI_SUCCESS; + } + + if (ModernUiMeasureText (Text) <= MaxWidth) { + return ModernUiDrawText (Context, X, Y, Text, Color, Background); + } + + TextLength = StrLen (Text); + Buffer = AllocateZeroPool ((TextLength + 4) * sizeof (CHAR16)); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + EllipsisWidth = ModernUiMeasureText (L"..."); + TargetWidth = (MaxWidth > EllipsisWidth) ? (MaxWidth - EllipsisWidth) : MaxWidth; + CopyChars = 0; + CurrentWidth = 0; + Character[1] = L'\0'; + + for (Index = 0; Text[Index] != L'\0'; Index++) { + Character[0] = Text[Index]; + CharacterWidth = ModernUiMeasureText (Character); + if ((CurrentWidth + CharacterWidth) > TargetWidth) { + break; + } + + Buffer[CopyChars++] = Text[Index]; + CurrentWidth += CharacterWidth; + } + + if ((MaxWidth >= EllipsisWidth) && ((CopyChars + 3) < (TextLength + 4))) { + Buffer[CopyChars++] = L'.'; + Buffer[CopyChars++] = L'.'; + Buffer[CopyChars++] = L'.'; + } + + Buffer[CopyChars] = L'\0'; + Status = ModernUiDrawText (Context, X, Y, Buffer, Color, Background); + FreePool (Buffer); + return Status; +} + + +/** + Draw a themed panel surface and border. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] Rect Panel rectangle. + @param[in] Theme Theme token table. Must not be NULL. + + @retval EFI_SUCCESS Panel was drawn. + @retval EFI_INVALID_PARAMETER Context or Theme is NULL. + @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. +**/ +EFI_STATUS +EFIAPI +ModernUiDrawPanel ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT Rect, + IN CONST MODERN_UI_THEME *Theme + ) +{ + EFI_STATUS Status; + + if ((Context == NULL) || (Theme == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Status = ModernUiFillRect (Context, Rect, Theme->Surface); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((Rect.Width > 2) && (Rect.Height > 2)) { + Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X + 1, Rect.Y + 1, Rect.Width - 2, 1 }, Theme->SurfaceRaised); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return ModernUiStrokeRect (Context, Rect, Theme->Border); +} + + +/** + Draw a focus frame only when requested. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] Rect Rectangle to outline when HasFocus is TRUE. + @param[in] HasFocus TRUE to draw the focus frame. + @param[in] Theme Theme token table. Must not be NULL. + + @retval EFI_SUCCESS Focus frame was drawn or skipped. + @retval EFI_INVALID_PARAMETER Context or Theme is NULL, or Rect is empty + when HasFocus is TRUE. + @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. +**/ +EFI_STATUS +EFIAPI +ModernUiDrawFocusFrame ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT Rect, + IN BOOLEAN HasFocus, + IN CONST MODERN_UI_THEME *Theme + ) +{ + if ((Context == NULL) || (Theme == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!HasFocus) { + return EFI_SUCCESS; + } + + return ModernUiStrokeRect (Context, Rect, Theme->Accent); +} + + +/** + Draw a compact title/value information card. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] Rect Card rectangle. Must be large enough for two text rows. + @param[in] Title Card title text. Must not be NULL. + @param[in] Value Card value text. Must not be NULL. + @param[in] Theme Theme token table. Must not be NULL. + + @retval EFI_SUCCESS Card was drawn. + @retval EFI_INVALID_PARAMETER Context, Title, Value, or Theme is NULL, or + Rect is empty. + @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. + @retval others Status returned by text rendering. +**/ +EFI_STATUS +EFIAPI +ModernUiDrawInfoCard ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT Rect, + IN CONST CHAR16 *Title, + IN CONST CHAR16 *Value, + IN CONST MODERN_UI_THEME *Theme + ) +{ + EFI_STATUS Status; + + if ((Context == NULL) || (Title == NULL) || (Value == NULL) || (Theme == NULL) || (Rect.Width == 0) || (Rect.Height == 0)) { + return EFI_INVALID_PARAMETER; + } + + Status = ModernUiDrawPanel (Context, Rect, Theme); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = ModernUiDrawTextFit (Context, Rect.X + 18, Rect.Y + 16, Rect.Width - 36, Title, Theme->MutedText, Theme->Surface); + if (EFI_ERROR (Status)) { + return Status; + } + + return ModernUiDrawTextFit (Context, Rect.X + 18, Rect.Y + 48, Rect.Width - 36, Value, Theme->Text, Theme->Surface); +} + + +/** + Return the themed background color used by a selectable row. + + @param[in] Selected TRUE when the row is selected. + @param[in] Disabled TRUE when the row is visible but disabled or grayed. + @param[in] Action TRUE when the row represents an action-like command. + @param[in] Subtitle TRUE when the row is a section subtitle. + @param[in] Theme Theme token table. Must not be NULL. + + @return Row background color. NULL Theme returns zero. +**/ +EFI_GRAPHICS_OUTPUT_BLT_PIXEL +EFIAPI +ModernUiGetSelectableRowBackground ( + IN BOOLEAN Selected, + IN BOOLEAN Disabled, + IN BOOLEAN Action, + IN BOOLEAN Subtitle, + IN CONST MODERN_UI_THEME *Theme + ) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL RowColor; + + ZeroMem (&RowColor, sizeof (RowColor)); + if (Theme == NULL) { + return RowColor; + } + + if (Selected) { + RowColor = ModernUiBlendColor (Theme->BackgroundBlack, Theme->SelectedBand, 74); + } else if (Action || Subtitle) { + RowColor = Theme->SurfaceRaised; + } else { + RowColor = Theme->Surface; + } + + if (Disabled && !Selected) { + RowColor = ModernUiBlendColor (Theme->Surface, Theme->Background, 35); + } + + return RowColor; +} + + +/** + Draw a themed selectable row surface. + + This is a shared visual primitive for DisplayEngine statement rows and + experimental app list rows. It does not own FormBrowser or HII semantics. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] Rect Pixel rectangle for the row surface. + @param[in] Selected TRUE when the row is selected. + @param[in] Disabled TRUE when the row is visible but disabled or grayed. + @param[in] Action TRUE when the row represents an action-like command. + @param[in] Subtitle TRUE when the row is a section subtitle. + @param[in] Theme Theme token table. Must not be NULL. + + @retval EFI_SUCCESS Row was drawn. + @retval EFI_INVALID_PARAMETER Context or Theme is NULL, or Rect is empty. + @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. +**/ +EFI_STATUS +EFIAPI +ModernUiDrawSelectableRow ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT Rect, + IN BOOLEAN Selected, + IN BOOLEAN Disabled, + IN BOOLEAN Action, + IN BOOLEAN Subtitle, + IN CONST MODERN_UI_THEME *Theme + ) +{ + EFI_GRAPHICS_OUTPUT_BLT_PIXEL RowColor; + EFI_STATUS Status; + + if ((Context == NULL) || (Theme == NULL) || (Rect.Width == 0) || (Rect.Height == 0)) { + return EFI_INVALID_PARAMETER; + } + + RowColor = ModernUiGetSelectableRowBackground (Selected, Disabled, Action, Subtitle, Theme); + Status = ModernUiFillRect (Context, Rect, RowColor); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Selected && (Rect.Width > 6) && (Rect.Height > 4)) { + if ((Rect.Width > 10) && (Rect.Height > 8)) { + Status = ModernUiFillRect ( + Context, + (MODERN_UI_RECT){ Rect.X + 6, Rect.Y + 3, Rect.Width - 6, Rect.Height - 6 }, + ModernUiBlendColor (RowColor, Theme->AccentOrange, 18) + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + Status = ModernUiFillRect ( + Context, + (MODERN_UI_RECT){ Rect.X, Rect.Y, Rect.Width, 2 }, + ModernUiBlendColor (Theme->AccentYellow, Theme->AccentOrange, 45) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = ModernUiFillRect ( + Context, + (MODERN_UI_RECT){ Rect.X, Rect.Y + Rect.Height - 3, Rect.Width, 2 }, + ModernUiBlendColor (Theme->AccentOrange, Theme->SelectedBand, 35) + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = ModernUiFillRect ( + Context, + (MODERN_UI_RECT){ Rect.X, Rect.Y + 2, 7, Rect.Height - 4 }, + Theme->AccentYellow + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Rect.Width > 20) { + Status = ModernUiStrokeRect (Context, Rect, Theme->PopupBorder); + if (EFI_ERROR (Status)) { + return Status; + } + + return ModernUiFillRect ( + Context, + (MODERN_UI_RECT){ Rect.X + 8, Rect.Y + 2, Rect.Width - 8, 1 }, + ModernUiBlendColor (Theme->AccentYellow, Theme->AccentOrange, 45) + ); + } + + return EFI_SUCCESS; + } + + if (Subtitle && (Rect.Width > 6)) { + return ModernUiFillRect ( + Context, + (MODERN_UI_RECT){ Rect.X, Rect.Y + Rect.Height - 1, Rect.Width, 1 }, + Theme->Border + ); + } + + return EFI_SUCCESS; +} + + +/** + Draw the standard border used around an action/selectable row. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] Rect Row rectangle. + @param[in] Selected TRUE when the row is selected. + @param[in] Theme Theme token table. Must not be NULL. + + @retval EFI_SUCCESS Row border was drawn. + @retval EFI_INVALID_PARAMETER Context or Theme is NULL, or Rect is empty. + @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. +**/ +EFI_STATUS +EFIAPI +ModernUiDrawSelectableRowBorder ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT Rect, + IN BOOLEAN Selected, + IN CONST MODERN_UI_THEME *Theme + ) +{ + if ((Context == NULL) || (Theme == NULL) || (Rect.Width == 0) || (Rect.Height == 0)) { + return EFI_INVALID_PARAMETER; + } + + return ModernUiStrokeRect ( + Context, + Rect, + Selected ? ModernUiBlendColor (Theme->AccentOrange, Theme->Border, 30) : Theme->Border + ); +} + + +/** + Draw a value selector box with a trailing drop-down marker. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] Rect Selector rectangle. + @param[in] Value Selected value text. Must not be NULL. + @param[in] Selected TRUE when the owning row is selected. + @param[in] Theme Theme token table. Must not be NULL. + + @retval EFI_SUCCESS Selector was drawn. + @retval EFI_INVALID_PARAMETER Context, Value, or Theme is NULL, or Rect is + empty. + @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. + @retval others Status returned by text rendering. +**/ +EFI_STATUS +EFIAPI +ModernUiDrawValueBox ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT Rect, + IN CONST CHAR16 *Value, + IN BOOLEAN Selected, + IN CONST MODERN_UI_THEME *Theme + ) +{ + EFI_STATUS Status; + + if ((Context == NULL) || (Value == NULL) || (Theme == NULL) || (Rect.Width == 0) || (Rect.Height == 0)) { + return EFI_INVALID_PARAMETER; + } + + Status = ModernUiFillRect (Context, Rect, Selected ? Theme->SelectedBand : Theme->Surface); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = ModernUiStrokeRect (Context, Rect, Selected ? Theme->PopupBorder : Theme->Border); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = ModernUiDrawTextFit ( + Context, + Rect.X + 16, + Rect.Y + ((Rect.Height > 18) ? ((Rect.Height - 18) / 2) : 0), + (Rect.Width > 48) ? (Rect.Width - 48) : Rect.Width, + Value, + Theme->Text, + Selected ? Theme->SelectedBand : Theme->Surface + ); + if (EFI_ERROR (Status)) { + return Status; + } + + return ModernUiDrawText ( + Context, + Rect.X + Rect.Width - 26, + Rect.Y + ((Rect.Height > 18) ? ((Rect.Height - 18) / 2) : 0), + L"v", + Theme->AccentYellow, + Selected ? Theme->SelectedBand : Theme->Surface + ); +} + + +/** + Draw a drop-down list frame. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] Rect Drop-down rectangle. + @param[in] Theme Theme token table. Must not be NULL. + + @retval EFI_SUCCESS Drop-down frame was drawn. + @retval EFI_INVALID_PARAMETER Context or Theme is NULL, or Rect is empty. + @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. +**/ +EFI_STATUS +EFIAPI +ModernUiDrawDropdownFrame ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT Rect, + IN CONST MODERN_UI_THEME *Theme + ) +{ + EFI_STATUS Status; + + if ((Context == NULL) || (Theme == NULL) || (Rect.Width == 0) || (Rect.Height == 0)) { + return EFI_INVALID_PARAMETER; + } + + Status = ModernUiFillRect (Context, Rect, Theme->BackgroundBlack); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = ModernUiStrokeRect (Context, Rect, Theme->PopupBorder); + if (EFI_ERROR (Status)) { + return Status; + } + + if ((Rect.Width <= 2) || (Rect.Height <= 2)) { + return EFI_SUCCESS; + } + + return ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X + 1, Rect.Y + 1, Rect.Width - 2, 1 }, Theme->GlowOrange); +} + + +/** + Draw a horizontal progress bar. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] Rect Progress track rectangle. + @param[in] Percent Completion percentage. Values above 100 are clamped. + @param[in] Track Track color. + @param[in] Fill Fill color. + + @retval EFI_SUCCESS Progress bar was drawn. + @retval EFI_INVALID_PARAMETER Context is NULL, GOP is unavailable, or Rect is empty. + @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. +**/ +EFI_STATUS +EFIAPI +ModernUiDrawProgress ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN MODERN_UI_RECT Rect, + IN UINTN Percent, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Track, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Fill + ) +{ + EFI_STATUS Status; + UINTN FillWidth; + + if (Percent > 100) { + Percent = 100; + } + + Status = ModernUiFillRect (Context, Rect, Track); + if (EFI_ERROR (Status)) { + return Status; + } + + FillWidth = (Rect.Width * Percent) / 100; + if (FillWidth == 0) { + return EFI_SUCCESS; + } + + return ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X, Rect.Y, FillWidth, Rect.Height }, Fill); +} diff --git a/Library/ModernUiRendererLib/ModernUiRendererInternal.h b/Library/ModernUiRendererLib/ModernUiRendererInternal.h new file mode 100644 index 0000000..2ef7492 --- /dev/null +++ b/Library/ModernUiRendererLib/ModernUiRendererInternal.h @@ -0,0 +1,95 @@ +/** @file + Private shared declarations for the ModernSetupPkg renderer libraries. + + ModernUiRendererCommon.c implements the backend-agnostic renderer surface + (geometry compositions, text measurement, themed widgets) on top of three + backend primitives that each renderer provides: ModernUiRendererInit, + ModernUiFillRect, and ModernUiDrawText. The GOP renderer + (Library/ModernUiRendererLib) and the LVGL renderer + (Library/ModernUiLvglRendererLib) share ModernUiRendererCommon.c and + ModernUiGlyphs.c verbatim and supply only those three primitives. + + Copyright (c) 2026, MarsDoge. All rights reserved.
+ Author: MarsDoge (Dongyan Qian) + Open source: https://github.com/MarsDoge/ModernSetupPkg + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef MODERN_UI_RENDERER_INTERNAL_H_ +#define MODERN_UI_RENDERER_INTERNAL_H_ + +#include +#include +#include + +// +// Fixed text-grid metrics shared by both renderers. The renderer measures and +// lays out on an 8 px ASCII cell / fixed-width CJK cell model so that callers +// computing positions from ModernUiMeasureText() are backend-stable. +// +#define MODERN_UI_ASCII_CELL_WIDTH 8 +#define MODERN_UI_GRAPHIC_CELL_WIDTH MODERN_UI_ASCII_CELL_WIDTH +#define MODERN_UI_GRAPHIC_LINE_Y 9 +#define MODERN_UI_GRAPHIC_LINE_X 4 +#define MODERN_UI_TEXT_SEGMENT_MAX 96 +#define MODERN_UI_TARGET_WIDTH 1024 +#define MODERN_UI_TARGET_HEIGHT 768 + +/** + Select a preferred GOP mode when the active mode is smaller than the target. + + @param[in] Gop Graphics output protocol to inspect. Must not be NULL. + + @retval EFI_SUCCESS Current mode is acceptable or a better mode + was selected. + @retval EFI_INVALID_PARAMETER Gop or mode data is NULL. +**/ +EFI_STATUS +ModernUiSelectPreferredGopMode ( + IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop + ); + +/** + Return whether a UCS-2 character is a UEFI text-mode graphics character that + should be rendered as a narrow fixed-width shape. + + @param[in] CodePoint UCS-2 code point to classify. + + @retval TRUE CodePoint is a box, arrow, triangle, or checkbox glyph used by + edk2 text-mode setup UI. + @retval FALSE CodePoint should use normal text or built-in glyph rendering. +**/ +BOOLEAN +ModernUiIsTextModeGraphicGlyph ( + IN CHAR16 CodePoint + ); + +/** + Draw one UEFI text-mode graphics character as narrow renderer primitives. + + The glyph is composed entirely from ModernUiFillRect/ModernUiStrokeRect, so it + renders identically through either backend. + + @param[in] Context Initialized render context. Must not be NULL. + @param[in] X Left coordinate in pixels. + @param[in] Y Top coordinate in pixels. + @param[in] CodePoint Box, arrow, triangle, or checkbox glyph to render. + @param[in] Color Foreground color. + @param[in] Background Background fill color. + + @retval EFI_SUCCESS Glyph was rendered. + @retval EFI_INVALID_PARAMETER Context is NULL or GOP is unavailable. + @retval others Status from fill or stroke primitives. +**/ +EFI_STATUS +ModernUiDrawTextModeGraphicGlyph ( + IN MODERN_UI_RENDER_CONTEXT *Context, + IN UINTN X, + IN UINTN Y, + IN CHAR16 CodePoint, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color, + IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background + ); + +#endif // MODERN_UI_RENDERER_INTERNAL_H_ diff --git a/Library/ModernUiRendererLib/ModernUiRendererLib.c b/Library/ModernUiRendererLib/ModernUiRendererLib.c index 0641372..5f5b4a5 100644 --- a/Library/ModernUiRendererLib/ModernUiRendererLib.c +++ b/Library/ModernUiRendererLib/ModernUiRendererLib.c @@ -1,5 +1,10 @@ /** @file - GOP-backed renderer library for ModernSetupPkg. + GOP-backed renderer primitives for ModernSetupPkg. + + Provides the three backend primitives consumed by the shared + ModernUiRendererCommon.c: ModernUiRendererInit, ModernUiFillRect, and + ModernUiDrawText. All higher-level geometry/text/widget functions live in the + shared translation unit. Copyright (c) 2026, MarsDoge. All rights reserved.
Author: MarsDoge (Dongyan Qian) @@ -19,114 +24,8 @@ #include #include "ModernUiGlyphs.h" +#include "ModernUiRendererInternal.h" -#define MODERN_UI_ASCII_CELL_WIDTH 8 -#define MODERN_UI_GRAPHIC_CELL_WIDTH MODERN_UI_ASCII_CELL_WIDTH -#define MODERN_UI_GRAPHIC_LINE_Y 9 -#define MODERN_UI_GRAPHIC_LINE_X 4 -#define MODERN_UI_TEXT_SEGMENT_MAX 96 -#define MODERN_UI_TARGET_WIDTH 1024 -#define MODERN_UI_TARGET_HEIGHT 768 - -/** - Blend two GOP colors by percentage weight. - - @param[in] Base Base color used when Weight is zero. - @param[in] Accent Accent color used when Weight is one hundred. - @param[in] Weight Accent weight in percent. Values above 100 are clamped. - - @return Blended color with Reserved cleared. -**/ -EFI_GRAPHICS_OUTPUT_BLT_PIXEL -EFIAPI -ModernUiBlendColor ( - IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Base, - IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Accent, - IN UINT8 Weight - ) -{ - EFI_GRAPHICS_OUTPUT_BLT_PIXEL Result; - UINTN ClampedWeight; - - ClampedWeight = MIN (Weight, 100); - Result.Red = (UINT8)( - ((UINTN)Base.Red * (100 - ClampedWeight) + - (UINTN)Accent.Red * ClampedWeight) / 100 - ); - Result.Green = (UINT8)( - ((UINTN)Base.Green * (100 - ClampedWeight) + - (UINTN)Accent.Green * ClampedWeight) / 100 - ); - Result.Blue = (UINT8)( - ((UINTN)Base.Blue * (100 - ClampedWeight) + - (UINTN)Accent.Blue * ClampedWeight) / 100 - ); - Result.Reserved = 0; - return Result; -} - -/** - Select a preferred GOP mode when the active mode is smaller than the target. - - @param[in] Gop Graphics output protocol to inspect. Must not be NULL. - - @retval EFI_SUCCESS Current mode is acceptable or a better mode - was selected. - @retval EFI_INVALID_PARAMETER Gop or mode data is NULL. -**/ -STATIC -EFI_STATUS -SelectPreferredGopMode ( - IN EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop - ) -{ - EFI_STATUS Status; - EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; - UINTN InfoSize; - UINT32 Mode; - UINT32 BestMode; - UINTN BestArea; - UINTN Area; - - if ((Gop == NULL) || (Gop->Mode == NULL) || (Gop->Mode->Info == NULL)) { - return EFI_INVALID_PARAMETER; - } - - if ((Gop->Mode->Info->HorizontalResolution >= MODERN_UI_TARGET_WIDTH) && - (Gop->Mode->Info->VerticalResolution >= MODERN_UI_TARGET_HEIGHT)) - { - return EFI_SUCCESS; - } - - BestMode = Gop->Mode->MaxMode; - BestArea = MAX_UINTN; - for (Mode = 0; Mode < Gop->Mode->MaxMode; Mode++) { - Info = NULL; - InfoSize = 0; - Status = Gop->QueryMode (Gop, Mode, &InfoSize, &Info); - if (EFI_ERROR (Status) || (Info == NULL)) { - continue; - } - - if ((Info->HorizontalResolution >= MODERN_UI_TARGET_WIDTH) && - (Info->VerticalResolution >= MODERN_UI_TARGET_HEIGHT)) - { - Area = (UINTN)Info->HorizontalResolution * (UINTN)Info->VerticalResolution; - if (Area < BestArea) { - BestArea = Area; - BestMode = Mode; - } - } - - FreePool (Info); - } - - if ((BestMode < Gop->Mode->MaxMode) && (BestMode != Gop->Mode->Mode)) { - Gop->SetMode (Gop, BestMode); - } - - return EFI_SUCCESS; -} /** Initialize a render context from firmware graphics services. @@ -170,7 +69,7 @@ ModernUiRendererInit ( return EFI_NOT_FOUND; } - SelectPreferredGopMode (Context->Gop); + ModernUiSelectPreferredGopMode (Context->Gop); Context->Width = Context->Gop->Mode->Info->HorizontalResolution; Context->Height = Context->Gop->Mode->Info->VerticalResolution; @@ -186,6 +85,7 @@ ModernUiRendererInit ( return EFI_SUCCESS; } + /** Fill a rectangle in the active render target. @@ -235,68 +135,6 @@ ModernUiFillRect ( ); } -/** - Fill the full render target with one color. - - @param[in] Context Initialized render context. Must not be NULL. - @param[in] Color Fill color. - - @retval EFI_SUCCESS The screen was cleared. - @retval EFI_INVALID_PARAMETER Context is NULL or invalid. - @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. -**/ -EFI_STATUS -EFIAPI -ModernUiClear ( - IN MODERN_UI_RENDER_CONTEXT *Context, - IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color - ) -{ - if (Context == NULL) { - return EFI_INVALID_PARAMETER; - } - - return ModernUiFillRect (Context, (MODERN_UI_RECT){ 0, 0, Context->Width, Context->Height }, Color); -} - -/** - Draw a one-pixel rectangle border. - - @param[in] Context Initialized render context. Must not be NULL. - @param[in] Rect Pixel rectangle to outline. - @param[in] Color Border color. - - @retval EFI_SUCCESS Border was drawn. - @retval EFI_INVALID_PARAMETER Context is NULL, GOP is unavailable, or Rect is empty. - @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. -**/ -EFI_STATUS -EFIAPI -ModernUiStrokeRect ( - IN MODERN_UI_RENDER_CONTEXT *Context, - IN MODERN_UI_RECT Rect, - IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color - ) -{ - EFI_STATUS Status; - - Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X, Rect.Y, Rect.Width, 1 }, Color); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X, Rect.Y + Rect.Height - 1, Rect.Width, 1 }, Color); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X, Rect.Y, 1, Rect.Height }, Color); - if (EFI_ERROR (Status)) { - return Status; - } - - return ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X + Rect.Width - 1, Rect.Y, 1, Rect.Height }, Color); -} /** Draw one UCS-2 text run through HII Font. @@ -367,6 +205,7 @@ DrawHiiText ( return Status; } + /** Draw one built-in bitmap glyph. @@ -426,6 +265,7 @@ DrawBuiltinGlyph ( ); } + /** Draw a visible placeholder for a missing non-ASCII glyph. @@ -458,267 +298,6 @@ DrawMissingGlyph ( return ModernUiStrokeRect (Context, (MODERN_UI_RECT){ X + 2, Y + 2, MODERN_UI_BUILTIN_GLYPH_WIDTH - 4, MODERN_UI_BUILTIN_GLYPH_HEIGHT - 4 }, Color); } -/** - Return whether a UCS-2 character is a UEFI text-mode graphics character that - should be rendered as a narrow fixed-width shape. - - @param[in] CodePoint UCS-2 code point to classify. - - @retval TRUE CodePoint is a box, arrow, triangle, or checkbox glyph used by - edk2 text-mode setup UI. - @retval FALSE CodePoint should use normal text or built-in glyph rendering. -**/ -STATIC -BOOLEAN -IsTextModeGraphicGlyph ( - IN CHAR16 CodePoint - ) -{ - return (((CodePoint >= BOXDRAW_HORIZONTAL) && (CodePoint <= BOXDRAW_DOUBLE_VERTICAL_HORIZONTAL)) || - ((CodePoint >= ARROW_LEFT) && (CodePoint <= ARROW_DOWN)) || - (CodePoint == 0x25A0) || - (CodePoint == 0x25A1) || - (CodePoint == 0x25B2) || - (CodePoint == 0x25B6) || - (CodePoint == 0x25BA) || - (CodePoint == 0x25BC) || - (CodePoint == 0x25C0) || - (CodePoint == 0x25C4)); -} - -/** - Draw one UEFI text-mode graphics character as a narrow GOP primitive. - - @param[in] Context Initialized render context. Must not be NULL. - @param[in] X Left coordinate in pixels. - @param[in] Y Top coordinate in pixels. - @param[in] CodePoint Box, arrow, triangle, or checkbox glyph to render. - @param[in] Color Foreground color. - @param[in] Background Background fill color. - - @retval EFI_SUCCESS Glyph was rendered. - @retval EFI_INVALID_PARAMETER Context is NULL or GOP is unavailable. - @retval others Status from fill or stroke primitives. -**/ -STATIC -EFI_STATUS -DrawTextModeGraphicGlyph ( - IN MODERN_UI_RENDER_CONTEXT *Context, - IN UINTN X, - IN UINTN Y, - IN CHAR16 CodePoint, - IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color, - IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background - ) -{ - EFI_STATUS Status; - BOOLEAN Horizontal; - BOOLEAN Vertical; - BOOLEAN Left; - BOOLEAN Right; - BOOLEAN Up; - BOOLEAN Down; - UINTN Index; - - if ((Context == NULL) || (Context->Gop == NULL)) { - return EFI_INVALID_PARAMETER; - } - - Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ X, Y, MODERN_UI_GRAPHIC_CELL_WIDTH, MODERN_UI_BUILTIN_GLYPH_HEIGHT }, Background); - if (EFI_ERROR (Status)) { - return Status; - } - - Horizontal = FALSE; - Vertical = FALSE; - Left = FALSE; - Right = FALSE; - Up = FALSE; - Down = FALSE; - - switch (CodePoint) { - case BOXDRAW_HORIZONTAL: - case BOXDRAW_DOUBLE_HORIZONTAL: - Left = Right = TRUE; - break; - case BOXDRAW_VERTICAL: - case BOXDRAW_DOUBLE_VERTICAL: - Up = Down = TRUE; - break; - case BOXDRAW_DOWN_RIGHT: - case BOXDRAW_DOWN_RIGHT_DOUBLE: - case BOXDRAW_DOWN_DOUBLE_RIGHT: - case BOXDRAW_DOUBLE_DOWN_RIGHT: - Right = Down = TRUE; - break; - case BOXDRAW_DOWN_LEFT: - case BOXDRAW_DOWN_LEFT_DOUBLE: - case BOXDRAW_DOWN_DOUBLE_LEFT: - case BOXDRAW_DOUBLE_DOWN_LEFT: - Left = Down = TRUE; - break; - case BOXDRAW_UP_RIGHT: - case BOXDRAW_UP_RIGHT_DOUBLE: - case BOXDRAW_UP_DOUBLE_RIGHT: - case BOXDRAW_DOUBLE_UP_RIGHT: - Right = Up = TRUE; - break; - case BOXDRAW_UP_LEFT: - case BOXDRAW_UP_LEFT_DOUBLE: - case BOXDRAW_UP_DOUBLE_LEFT: - case BOXDRAW_DOUBLE_UP_LEFT: - Left = Up = TRUE; - break; - case BOXDRAW_VERTICAL_RIGHT: - case BOXDRAW_VERTICAL_RIGHT_DOUBLE: - case BOXDRAW_VERTICAL_DOUBLE_RIGHT: - case BOXDRAW_DOUBLE_VERTICAL_RIGHT: - Up = Down = Right = TRUE; - break; - case BOXDRAW_VERTICAL_LEFT: - case BOXDRAW_VERTICAL_LEFT_DOUBLE: - case BOXDRAW_VERTICAL_DOUBLE_LEFT: - case BOXDRAW_DOUBLE_VERTICAL_LEFT: - Up = Down = Left = TRUE; - break; - case BOXDRAW_DOWN_HORIZONTAL: - case BOXDRAW_DOWN_HORIZONTAL_DOUBLE: - case BOXDRAW_DOWN_DOUBLE_HORIZONTAL: - case BOXDRAW_DOUBLE_DOWN_HORIZONTAL: - Left = Right = Down = TRUE; - break; - case BOXDRAW_UP_HORIZONTAL: - case BOXDRAW_UP_HORIZONTAL_DOUBLE: - case BOXDRAW_UP_DOUBLE_HORIZONTAL: - case BOXDRAW_DOUBLE_UP_HORIZONTAL: - Left = Right = Up = TRUE; - break; - case BOXDRAW_VERTICAL_HORIZONTAL: - case BOXDRAW_VERTICAL_HORIZONTAL_DOUBLE: - case BOXDRAW_VERTICAL_DOUBLE_HORIZONTAL: - case BOXDRAW_DOUBLE_VERTICAL_HORIZONTAL: - Left = Right = Up = Down = TRUE; - break; - case ARROW_RIGHT: - case 0x25B6: - case 0x25BA: - for (Index = 0; Index < 6; Index++) { - Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 2 + Index, Y + 5 + Index, 1, 7 - (Index * 2 > 6 ? 6 : Index * 2) }, Color); - if (EFI_ERROR (Status)) { - return Status; - } - } - - return EFI_SUCCESS; - case ARROW_LEFT: - case 0x25C0: - case 0x25C4: - for (Index = 0; Index < 6; Index++) { - Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 6 - Index, Y + 5 + Index, 1, 7 - (Index * 2 > 6 ? 6 : Index * 2) }, Color); - if (EFI_ERROR (Status)) { - return Status; - } - } - - return EFI_SUCCESS; - case ARROW_UP: - case 0x25B2: - Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 3, Y + 5, 2, 8 }, Color); - if (EFI_ERROR (Status)) { - return Status; - } - - return ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 2, Y + 6, 4, 2 }, Color); - case ARROW_DOWN: - case 0x25BC: - Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 3, Y + 5, 2, 8 }, Color); - if (EFI_ERROR (Status)) { - return Status; - } - - return ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 2, Y + 10, 4, 2 }, Color); - case 0x25A0: - return ModernUiFillRect (Context, (MODERN_UI_RECT){ X + 1, Y + 6, 6, 6 }, Color); - case 0x25A1: - return ModernUiStrokeRect (Context, (MODERN_UI_RECT){ X + 1, Y + 6, 6, 6 }, Color); - default: - break; - } - - Horizontal = Left || Right; - Vertical = Up || Down; - if (Horizontal) { - Status = ModernUiFillRect ( - Context, - (MODERN_UI_RECT){ - X + (Left ? 0 : MODERN_UI_GRAPHIC_LINE_X), - Y + MODERN_UI_GRAPHIC_LINE_Y, - (Left && Right) ? MODERN_UI_GRAPHIC_CELL_WIDTH : (MODERN_UI_GRAPHIC_CELL_WIDTH - MODERN_UI_GRAPHIC_LINE_X), - 1 - }, - Color - ); - if (EFI_ERROR (Status)) { - return Status; - } - } - - if (Vertical) { - Status = ModernUiFillRect ( - Context, - (MODERN_UI_RECT){ - X + MODERN_UI_GRAPHIC_LINE_X, - Y + (Up ? 0 : MODERN_UI_GRAPHIC_LINE_Y), - 1, - (Up && Down) ? MODERN_UI_BUILTIN_GLYPH_HEIGHT : (MODERN_UI_BUILTIN_GLYPH_HEIGHT - MODERN_UI_GRAPHIC_LINE_Y) - }, - Color - ); - } - - return Status; -} - -/** - Return the expected pixel width for a UCS-2 string. - - Built-in CJK glyphs are measured at their bitmap width. Other characters use - the renderer's current ASCII cell width. - - @param[in] Text Null-terminated UCS-2 string. Must not be NULL. - - @return Pixel width. NULL input returns 0. -**/ -UINTN -EFIAPI -ModernUiMeasureText ( - IN CONST CHAR16 *Text - ) -{ - UINTN Index; - UINTN Width; - CONST MODERN_UI_BUILTIN_GLYPH *Glyph; - - if (Text == NULL) { - return 0; - } - - Width = 0; - for (Index = 0; Text[Index] != L'\0'; Index++) { - Glyph = ModernUiFindBuiltinGlyph (Text[Index]); - if (IsTextModeGraphicGlyph (Text[Index])) { - Width += MODERN_UI_GRAPHIC_CELL_WIDTH; - } else if (Glyph != NULL) { - Width += Glyph->Advance; - } else if (Text[Index] > 0x7F) { - Width += MODERN_UI_BUILTIN_GLYPH_WIDTH; - } else { - Width += MODERN_UI_ASCII_CELL_WIDTH; - } - } - - return Width; -} /** Draw UCS-2 text using HII Font and built-in bitmap glyph fallback. @@ -763,9 +342,9 @@ ModernUiDrawText ( ReturnStatus = EFI_SUCCESS; for (Index = 0; Text[Index] != L'\0'; ) { Glyph = ModernUiFindBuiltinGlyph (Text[Index]); - if ((Glyph != NULL) || IsTextModeGraphicGlyph (Text[Index]) || (Text[Index] > 0x7F)) { - if (IsTextModeGraphicGlyph (Text[Index])) { - Status = DrawTextModeGraphicGlyph (Context, CurrentX, Y, Text[Index], Color, Background); + if ((Glyph != NULL) || ModernUiIsTextModeGraphicGlyph (Text[Index]) || (Text[Index] > 0x7F)) { + if (ModernUiIsTextModeGraphicGlyph (Text[Index])) { + Status = ModernUiDrawTextModeGraphicGlyph (Context, CurrentX, Y, Text[Index], Color, Background); CurrentX += MODERN_UI_GRAPHIC_CELL_WIDTH; } else if (Glyph != NULL) { Status = DrawBuiltinGlyph (Context, CurrentX, Y, Glyph, Color, Background); @@ -800,583 +379,3 @@ ModernUiDrawText ( return ReturnStatus; } - -/** - Format and draw one UCS-2 text line. - - @param[in] Context Initialized render context. Must not be NULL. - @param[in] X Left coordinate in pixels. - @param[in] Y Top coordinate in pixels. - @param[in] Color Text foreground color. - @param[in] Background Text background color. - @param[in] Format PrintLib format string. Must not be NULL. - @param[in] ... Format arguments consumed according to Format. - - @retval EFI_SUCCESS Text was formatted and rendered. - @retval EFI_INVALID_PARAMETER Context or Format is NULL. - @retval others Status returned by ModernUiDrawText(). -**/ -EFI_STATUS -EFIAPI -ModernUiDrawTextFormatted ( - IN MODERN_UI_RENDER_CONTEXT *Context, - IN UINTN X, - IN UINTN Y, - IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color, - IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background, - IN CONST CHAR16 *Format, - ... - ) -{ - VA_LIST Marker; - CHAR16 Buffer[192]; - - if ((Context == NULL) || (Format == NULL)) { - return EFI_INVALID_PARAMETER; - } - - VA_START (Marker, Format); - UnicodeVSPrint (Buffer, sizeof (Buffer), Format, Marker); - VA_END (Marker); - - return ModernUiDrawText (Context, X, Y, Buffer, Color, Background); -} - -/** - Draw UCS-2 text constrained to a pixel width. - - The renderer measures mixed ASCII, built-in CJK, and text-mode graphic glyphs - and appends "..." when the string must be truncated. - - @param[in] Context Initialized render context. Must not be NULL. - @param[in] X Left coordinate in pixels. - @param[in] Y Top coordinate in pixels. - @param[in] MaxWidth Maximum text width in pixels. - @param[in] Text Null-terminated UCS-2 string. Must not be NULL. - @param[in] Color Text foreground color. - @param[in] Background Text background color. - - @retval EFI_SUCCESS Text was rendered or empty width was ignored. - @retval EFI_INVALID_PARAMETER Context or Text is NULL. - @retval EFI_OUT_OF_RESOURCES Temporary truncation buffer allocation failed. - @retval others Status returned by ModernUiDrawText(). -**/ -EFI_STATUS -EFIAPI -ModernUiDrawTextFit ( - IN MODERN_UI_RENDER_CONTEXT *Context, - IN UINTN X, - IN UINTN Y, - IN UINTN MaxWidth, - IN CONST CHAR16 *Text, - IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Color, - IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Background - ) -{ - CHAR16 *Buffer; - CHAR16 Character[2]; - EFI_STATUS Status; - UINTN Index; - UINTN CopyChars; - UINTN CurrentWidth; - UINTN CharacterWidth; - UINTN EllipsisWidth; - UINTN TargetWidth; - UINTN TextLength; - - if ((Context == NULL) || (Text == NULL)) { - return EFI_INVALID_PARAMETER; - } - - if (MaxWidth == 0) { - return EFI_SUCCESS; - } - - if (ModernUiMeasureText (Text) <= MaxWidth) { - return ModernUiDrawText (Context, X, Y, Text, Color, Background); - } - - TextLength = StrLen (Text); - Buffer = AllocateZeroPool ((TextLength + 4) * sizeof (CHAR16)); - if (Buffer == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - EllipsisWidth = ModernUiMeasureText (L"..."); - TargetWidth = (MaxWidth > EllipsisWidth) ? (MaxWidth - EllipsisWidth) : MaxWidth; - CopyChars = 0; - CurrentWidth = 0; - Character[1] = L'\0'; - - for (Index = 0; Text[Index] != L'\0'; Index++) { - Character[0] = Text[Index]; - CharacterWidth = ModernUiMeasureText (Character); - if ((CurrentWidth + CharacterWidth) > TargetWidth) { - break; - } - - Buffer[CopyChars++] = Text[Index]; - CurrentWidth += CharacterWidth; - } - - if ((MaxWidth >= EllipsisWidth) && ((CopyChars + 3) < (TextLength + 4))) { - Buffer[CopyChars++] = L'.'; - Buffer[CopyChars++] = L'.'; - Buffer[CopyChars++] = L'.'; - } - - Buffer[CopyChars] = L'\0'; - Status = ModernUiDrawText (Context, X, Y, Buffer, Color, Background); - FreePool (Buffer); - return Status; -} - -/** - Draw a themed panel surface and border. - - @param[in] Context Initialized render context. Must not be NULL. - @param[in] Rect Panel rectangle. - @param[in] Theme Theme token table. Must not be NULL. - - @retval EFI_SUCCESS Panel was drawn. - @retval EFI_INVALID_PARAMETER Context or Theme is NULL. - @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. -**/ -EFI_STATUS -EFIAPI -ModernUiDrawPanel ( - IN MODERN_UI_RENDER_CONTEXT *Context, - IN MODERN_UI_RECT Rect, - IN CONST MODERN_UI_THEME *Theme - ) -{ - EFI_STATUS Status; - - if ((Context == NULL) || (Theme == NULL)) { - return EFI_INVALID_PARAMETER; - } - - Status = ModernUiFillRect (Context, Rect, Theme->Surface); - if (EFI_ERROR (Status)) { - return Status; - } - - if ((Rect.Width > 2) && (Rect.Height > 2)) { - Status = ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X + 1, Rect.Y + 1, Rect.Width - 2, 1 }, Theme->SurfaceRaised); - if (EFI_ERROR (Status)) { - return Status; - } - } - - return ModernUiStrokeRect (Context, Rect, Theme->Border); -} - -/** - Draw a focus frame only when requested. - - @param[in] Context Initialized render context. Must not be NULL. - @param[in] Rect Rectangle to outline when HasFocus is TRUE. - @param[in] HasFocus TRUE to draw the focus frame. - @param[in] Theme Theme token table. Must not be NULL. - - @retval EFI_SUCCESS Focus frame was drawn or skipped. - @retval EFI_INVALID_PARAMETER Context or Theme is NULL, or Rect is empty - when HasFocus is TRUE. - @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. -**/ -EFI_STATUS -EFIAPI -ModernUiDrawFocusFrame ( - IN MODERN_UI_RENDER_CONTEXT *Context, - IN MODERN_UI_RECT Rect, - IN BOOLEAN HasFocus, - IN CONST MODERN_UI_THEME *Theme - ) -{ - if ((Context == NULL) || (Theme == NULL)) { - return EFI_INVALID_PARAMETER; - } - - if (!HasFocus) { - return EFI_SUCCESS; - } - - return ModernUiStrokeRect (Context, Rect, Theme->Accent); -} - -/** - Draw a compact title/value information card. - - @param[in] Context Initialized render context. Must not be NULL. - @param[in] Rect Card rectangle. Must be large enough for two text rows. - @param[in] Title Card title text. Must not be NULL. - @param[in] Value Card value text. Must not be NULL. - @param[in] Theme Theme token table. Must not be NULL. - - @retval EFI_SUCCESS Card was drawn. - @retval EFI_INVALID_PARAMETER Context, Title, Value, or Theme is NULL, or - Rect is empty. - @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. - @retval others Status returned by text rendering. -**/ -EFI_STATUS -EFIAPI -ModernUiDrawInfoCard ( - IN MODERN_UI_RENDER_CONTEXT *Context, - IN MODERN_UI_RECT Rect, - IN CONST CHAR16 *Title, - IN CONST CHAR16 *Value, - IN CONST MODERN_UI_THEME *Theme - ) -{ - EFI_STATUS Status; - - if ((Context == NULL) || (Title == NULL) || (Value == NULL) || (Theme == NULL) || (Rect.Width == 0) || (Rect.Height == 0)) { - return EFI_INVALID_PARAMETER; - } - - Status = ModernUiDrawPanel (Context, Rect, Theme); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = ModernUiDrawTextFit (Context, Rect.X + 18, Rect.Y + 16, Rect.Width - 36, Title, Theme->MutedText, Theme->Surface); - if (EFI_ERROR (Status)) { - return Status; - } - - return ModernUiDrawTextFit (Context, Rect.X + 18, Rect.Y + 48, Rect.Width - 36, Value, Theme->Text, Theme->Surface); -} - -/** - Return the themed background color used by a selectable row. - - @param[in] Selected TRUE when the row is selected. - @param[in] Disabled TRUE when the row is visible but disabled or grayed. - @param[in] Action TRUE when the row represents an action-like command. - @param[in] Subtitle TRUE when the row is a section subtitle. - @param[in] Theme Theme token table. Must not be NULL. - - @return Row background color. NULL Theme returns zero. -**/ -EFI_GRAPHICS_OUTPUT_BLT_PIXEL -EFIAPI -ModernUiGetSelectableRowBackground ( - IN BOOLEAN Selected, - IN BOOLEAN Disabled, - IN BOOLEAN Action, - IN BOOLEAN Subtitle, - IN CONST MODERN_UI_THEME *Theme - ) -{ - EFI_GRAPHICS_OUTPUT_BLT_PIXEL RowColor; - - ZeroMem (&RowColor, sizeof (RowColor)); - if (Theme == NULL) { - return RowColor; - } - - if (Selected) { - RowColor = ModernUiBlendColor (Theme->BackgroundBlack, Theme->SelectedBand, 74); - } else if (Action || Subtitle) { - RowColor = Theme->SurfaceRaised; - } else { - RowColor = Theme->Surface; - } - - if (Disabled && !Selected) { - RowColor = ModernUiBlendColor (Theme->Surface, Theme->Background, 35); - } - - return RowColor; -} - -/** - Draw a themed selectable row surface. - - This is a shared visual primitive for DisplayEngine statement rows and - experimental app list rows. It does not own FormBrowser or HII semantics. - - @param[in] Context Initialized render context. Must not be NULL. - @param[in] Rect Pixel rectangle for the row surface. - @param[in] Selected TRUE when the row is selected. - @param[in] Disabled TRUE when the row is visible but disabled or grayed. - @param[in] Action TRUE when the row represents an action-like command. - @param[in] Subtitle TRUE when the row is a section subtitle. - @param[in] Theme Theme token table. Must not be NULL. - - @retval EFI_SUCCESS Row was drawn. - @retval EFI_INVALID_PARAMETER Context or Theme is NULL, or Rect is empty. - @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. -**/ -EFI_STATUS -EFIAPI -ModernUiDrawSelectableRow ( - IN MODERN_UI_RENDER_CONTEXT *Context, - IN MODERN_UI_RECT Rect, - IN BOOLEAN Selected, - IN BOOLEAN Disabled, - IN BOOLEAN Action, - IN BOOLEAN Subtitle, - IN CONST MODERN_UI_THEME *Theme - ) -{ - EFI_GRAPHICS_OUTPUT_BLT_PIXEL RowColor; - EFI_STATUS Status; - - if ((Context == NULL) || (Theme == NULL) || (Rect.Width == 0) || (Rect.Height == 0)) { - return EFI_INVALID_PARAMETER; - } - - RowColor = ModernUiGetSelectableRowBackground (Selected, Disabled, Action, Subtitle, Theme); - Status = ModernUiFillRect (Context, Rect, RowColor); - if (EFI_ERROR (Status)) { - return Status; - } - - if (Selected && (Rect.Width > 6) && (Rect.Height > 4)) { - if ((Rect.Width > 10) && (Rect.Height > 8)) { - Status = ModernUiFillRect ( - Context, - (MODERN_UI_RECT){ Rect.X + 6, Rect.Y + 3, Rect.Width - 6, Rect.Height - 6 }, - ModernUiBlendColor (RowColor, Theme->AccentOrange, 18) - ); - if (EFI_ERROR (Status)) { - return Status; - } - } - - Status = ModernUiFillRect ( - Context, - (MODERN_UI_RECT){ Rect.X, Rect.Y, Rect.Width, 2 }, - ModernUiBlendColor (Theme->AccentYellow, Theme->AccentOrange, 45) - ); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = ModernUiFillRect ( - Context, - (MODERN_UI_RECT){ Rect.X, Rect.Y + Rect.Height - 3, Rect.Width, 2 }, - ModernUiBlendColor (Theme->AccentOrange, Theme->SelectedBand, 35) - ); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = ModernUiFillRect ( - Context, - (MODERN_UI_RECT){ Rect.X, Rect.Y + 2, 7, Rect.Height - 4 }, - Theme->AccentYellow - ); - if (EFI_ERROR (Status)) { - return Status; - } - - if (Rect.Width > 20) { - Status = ModernUiStrokeRect (Context, Rect, Theme->PopupBorder); - if (EFI_ERROR (Status)) { - return Status; - } - - return ModernUiFillRect ( - Context, - (MODERN_UI_RECT){ Rect.X + 8, Rect.Y + 2, Rect.Width - 8, 1 }, - ModernUiBlendColor (Theme->AccentYellow, Theme->AccentOrange, 45) - ); - } - - return EFI_SUCCESS; - } - - if (Subtitle && (Rect.Width > 6)) { - return ModernUiFillRect ( - Context, - (MODERN_UI_RECT){ Rect.X, Rect.Y + Rect.Height - 1, Rect.Width, 1 }, - Theme->Border - ); - } - - return EFI_SUCCESS; -} - -/** - Draw the standard border used around an action/selectable row. - - @param[in] Context Initialized render context. Must not be NULL. - @param[in] Rect Row rectangle. - @param[in] Selected TRUE when the row is selected. - @param[in] Theme Theme token table. Must not be NULL. - - @retval EFI_SUCCESS Row border was drawn. - @retval EFI_INVALID_PARAMETER Context or Theme is NULL, or Rect is empty. - @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. -**/ -EFI_STATUS -EFIAPI -ModernUiDrawSelectableRowBorder ( - IN MODERN_UI_RENDER_CONTEXT *Context, - IN MODERN_UI_RECT Rect, - IN BOOLEAN Selected, - IN CONST MODERN_UI_THEME *Theme - ) -{ - if ((Context == NULL) || (Theme == NULL) || (Rect.Width == 0) || (Rect.Height == 0)) { - return EFI_INVALID_PARAMETER; - } - - return ModernUiStrokeRect ( - Context, - Rect, - Selected ? ModernUiBlendColor (Theme->AccentOrange, Theme->Border, 30) : Theme->Border - ); -} - -/** - Draw a value selector box with a trailing drop-down marker. - - @param[in] Context Initialized render context. Must not be NULL. - @param[in] Rect Selector rectangle. - @param[in] Value Selected value text. Must not be NULL. - @param[in] Selected TRUE when the owning row is selected. - @param[in] Theme Theme token table. Must not be NULL. - - @retval EFI_SUCCESS Selector was drawn. - @retval EFI_INVALID_PARAMETER Context, Value, or Theme is NULL, or Rect is - empty. - @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. - @retval others Status returned by text rendering. -**/ -EFI_STATUS -EFIAPI -ModernUiDrawValueBox ( - IN MODERN_UI_RENDER_CONTEXT *Context, - IN MODERN_UI_RECT Rect, - IN CONST CHAR16 *Value, - IN BOOLEAN Selected, - IN CONST MODERN_UI_THEME *Theme - ) -{ - EFI_STATUS Status; - - if ((Context == NULL) || (Value == NULL) || (Theme == NULL) || (Rect.Width == 0) || (Rect.Height == 0)) { - return EFI_INVALID_PARAMETER; - } - - Status = ModernUiFillRect (Context, Rect, Selected ? Theme->SelectedBand : Theme->Surface); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = ModernUiStrokeRect (Context, Rect, Selected ? Theme->PopupBorder : Theme->Border); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = ModernUiDrawTextFit ( - Context, - Rect.X + 16, - Rect.Y + ((Rect.Height > 18) ? ((Rect.Height - 18) / 2) : 0), - (Rect.Width > 48) ? (Rect.Width - 48) : Rect.Width, - Value, - Theme->Text, - Selected ? Theme->SelectedBand : Theme->Surface - ); - if (EFI_ERROR (Status)) { - return Status; - } - - return ModernUiDrawText ( - Context, - Rect.X + Rect.Width - 26, - Rect.Y + ((Rect.Height > 18) ? ((Rect.Height - 18) / 2) : 0), - L"v", - Theme->AccentYellow, - Selected ? Theme->SelectedBand : Theme->Surface - ); -} - -/** - Draw a drop-down list frame. - - @param[in] Context Initialized render context. Must not be NULL. - @param[in] Rect Drop-down rectangle. - @param[in] Theme Theme token table. Must not be NULL. - - @retval EFI_SUCCESS Drop-down frame was drawn. - @retval EFI_INVALID_PARAMETER Context or Theme is NULL, or Rect is empty. - @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. -**/ -EFI_STATUS -EFIAPI -ModernUiDrawDropdownFrame ( - IN MODERN_UI_RENDER_CONTEXT *Context, - IN MODERN_UI_RECT Rect, - IN CONST MODERN_UI_THEME *Theme - ) -{ - EFI_STATUS Status; - - if ((Context == NULL) || (Theme == NULL) || (Rect.Width == 0) || (Rect.Height == 0)) { - return EFI_INVALID_PARAMETER; - } - - Status = ModernUiFillRect (Context, Rect, Theme->BackgroundBlack); - if (EFI_ERROR (Status)) { - return Status; - } - - Status = ModernUiStrokeRect (Context, Rect, Theme->PopupBorder); - if (EFI_ERROR (Status)) { - return Status; - } - - if ((Rect.Width <= 2) || (Rect.Height <= 2)) { - return EFI_SUCCESS; - } - - return ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X + 1, Rect.Y + 1, Rect.Width - 2, 1 }, Theme->GlowOrange); -} - -/** - Draw a horizontal progress bar. - - @param[in] Context Initialized render context. Must not be NULL. - @param[in] Rect Progress track rectangle. - @param[in] Percent Completion percentage. Values above 100 are clamped. - @param[in] Track Track color. - @param[in] Fill Fill color. - - @retval EFI_SUCCESS Progress bar was drawn. - @retval EFI_INVALID_PARAMETER Context is NULL, GOP is unavailable, or Rect is empty. - @retval EFI_OUT_OF_RESOURCES Temporary BLT allocation failed. -**/ -EFI_STATUS -EFIAPI -ModernUiDrawProgress ( - IN MODERN_UI_RENDER_CONTEXT *Context, - IN MODERN_UI_RECT Rect, - IN UINTN Percent, - IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Track, - IN EFI_GRAPHICS_OUTPUT_BLT_PIXEL Fill - ) -{ - EFI_STATUS Status; - UINTN FillWidth; - - if (Percent > 100) { - Percent = 100; - } - - Status = ModernUiFillRect (Context, Rect, Track); - if (EFI_ERROR (Status)) { - return Status; - } - - FillWidth = (Rect.Width * Percent) / 100; - if (FillWidth == 0) { - return EFI_SUCCESS; - } - - return ModernUiFillRect (Context, (MODERN_UI_RECT){ Rect.X, Rect.Y, FillWidth, Rect.Height }, Fill); -} diff --git a/Library/ModernUiRendererLib/ModernUiRendererLib.inf b/Library/ModernUiRendererLib/ModernUiRendererLib.inf index 469f815..9af3fd2 100644 --- a/Library/ModernUiRendererLib/ModernUiRendererLib.inf +++ b/Library/ModernUiRendererLib/ModernUiRendererLib.inf @@ -18,6 +18,7 @@ [Sources] ModernUiRendererLib.c + ModernUiRendererCommon.c ModernUiGlyphs.c [Packages] diff --git a/Scripts/build-loongarchvirt.sh b/Scripts/build-loongarchvirt.sh index a6c38ce..f6a13e0 100755 --- a/Scripts/build-loongarchvirt.sh +++ b/Scripts/build-loongarchvirt.sh @@ -36,6 +36,13 @@ export PATH="/opt/homebrew/bin:${PATH}" export WORKSPACE export GCC_LOONGARCH64_PREFIX ConfigureModernSetupPackagePath +# Experimental LvglSpikePkg is only consumed by MODERN_SETUP_DISPLAY_ENGINE=lvgl; +# making it resolvable here is harmless for native/modern. +AppendPackagePath "${PKG_DIR}/Experimental" +export PACKAGES_PATH + +# LoongArch64/RISC-V64 UEFI support is upstream in the pinned External/lvgl +# baseline; the submodule is consumed pristine with no local patching. if [[ ! -d "${WORKSPACE}/MdePkg" || ! -d "${WORKSPACE}/OvmfPkg/LoongArchVirt" ]]; then echo "WORKSPACE does not look like an edk2 checkout with LoongArchVirt: ${WORKSPACE}" >&2 @@ -71,9 +78,9 @@ if theme_pcd is None: f"Unsupported MODERN_SETUP_THEME={theme_name!r}; " "use orange, amber, dark-orange, red, accent-red, dark-red, graphite, or graphite-gold" ) -if display_engine not in {"modern", "native"}: +if display_engine not in {"modern", "native", "lvgl"}: raise SystemExit( - f"Unsupported MODERN_SETUP_DISPLAY_ENGINE={display_engine!r}; use modern or native" + f"Unsupported MODERN_SETUP_DISPLAY_ENGINE={display_engine!r}; use modern, native, or lvgl" ) if include_app_flag not in {"0", "1", "false", "true", "no", "yes"}: raise SystemExit( @@ -86,23 +93,59 @@ if replace_uiapp_flag not in {"0", "1", "false", "true", "no", "yes"}: replace_uiapp = replace_uiapp_flag in {"1", "true", "yes"} include_modern_setup_app = (include_app_flag in {"1", "true", "yes"}) or replace_uiapp -modern_setup_app_component = " ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf" -modern_setup_app_component_boot_manager_fallback = """ ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf { - - GCC:*_*_*_CC_FLAGS = -DMODERN_SETUP_NATIVE_FALLBACK_BOOT_MANAGER_MENU=1 - }""" +# In lvgl mode the ModernSetupApp shell and the ModernDisplayEngine both pull the +# LVGL-backed renderer, so they force-link compiler intrinsics (memcpy/memset). +_app_lvgl_intrinsics = ( + " \n" + " NULL|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf\n" + if display_engine == "lvgl" + else "" +) +modern_setup_app_component = ( + " ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf" + if display_engine != "lvgl" + else ( + " ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf {\n" + f"{_app_lvgl_intrinsics}" + " }" + ) +) +modern_setup_app_component_boot_manager_fallback = ( + " ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf {\n" + " \n" + " GCC:*_*_*_CC_FLAGS = -DMODERN_SETUP_NATIVE_FALLBACK_BOOT_MANAGER_MENU=1\n" + f"{_app_lvgl_intrinsics}" + " }" +) modern_setup_app_fdf_inf = "INF ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf" modern_setup_app_uiapp_fdf_inf = "INF RuleOverride = MODERN_SETUP_UIAPP ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf" modern_display_component = " ModernSetupPkg/Universal/ModernDisplayEngineDxe/ModernDisplayEngineDxe.inf" +modern_display_component_lvgl = ( + " ModernSetupPkg/Universal/ModernDisplayEngineDxe/ModernDisplayEngineDxe.inf {\n" + " \n" + " NULL|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf\n" + " }" +) modern_display_fdf_inf = "INF ModernSetupPkg/Universal/ModernDisplayEngineDxe/ModernDisplayEngineDxe.inf" boot_manager_menu_component = " MdeModulePkg/Application/BootManagerMenuApp/BootManagerMenuApp.inf" boot_manager_menu_fdf_inf = "INF MdeModulePkg/Application/BootManagerMenuApp/BootManagerMenuApp.inf" driver_sample_component = " MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf" driver_sample_fdf_inf = "INF MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf" -library_block = """ ModernUiEngineLib|ModernSetupPkg/Library/ModernUiEngineLib/ModernUiEngineLib.inf - ModernUiRendererLib|ModernSetupPkg/Library/ModernUiRendererLib/ModernUiRendererLib.inf - ModernUiThemeLib|ModernSetupPkg/Library/ModernUiThemeLib/ModernUiThemeLib.inf -""" +# LVGL core (upstream lvgl sources as a BASE library); resolved only in lvgl mode +# and consumed transitively through the LVGL renderer library. +lvgl_library_block = " LvglCoreLib|LvglSpikePkg/Library/LvglLib/LvglCoreLib.inf\n" +# The renderer library class resolves to the LVGL-backed implementation in lvgl +# mode and to the hand-rolled GOP rasterizer otherwise (identical API). +renderer_inf = ( + "ModernSetupPkg/Library/ModernUiLvglRendererLib/ModernUiRendererLib.inf" + if display_engine == "lvgl" + else "ModernSetupPkg/Library/ModernUiRendererLib/ModernUiRendererLib.inf" +) +library_block = ( + " ModernUiEngineLib|ModernSetupPkg/Library/ModernUiEngineLib/ModernUiEngineLib.inf\n" + f" ModernUiRendererLib|{renderer_inf}\n" + " ModernUiThemeLib|ModernSetupPkg/Library/ModernUiThemeLib/ModernUiThemeLib.inf\n" +) app_library_block = """ ModernUiPlatformDataLib|ModernSetupPkg/Library/ModernUiPlatformDataLib/ModernUiPlatformDataLib.inf ModernUiBootDataLib|ModernSetupPkg/Library/ModernUiBootDataLib/ModernUiBootDataLib.inf ModernUiDeviceDataLib|ModernSetupPkg/Library/ModernUiDeviceDataLib/ModernUiDeviceDataLib.inf @@ -133,8 +176,10 @@ dsc = dsc.replace( 1, ) if "ModernUiEngineLib|ModernSetupPkg" not in dsc: - if display_engine == "modern" or include_modern_setup_app: + if (display_engine == "modern" or display_engine == "lvgl") or include_modern_setup_app: dsc = dsc.replace("[LibraryClasses.common]\n", "[LibraryClasses.common]\n" + library_block, 1) +if display_engine == "lvgl" and "LvglCoreLib|LvglSpikePkg" not in dsc: + dsc = dsc.replace("[LibraryClasses.common]\n", "[LibraryClasses.common]\n" + lvgl_library_block, 1) if include_modern_setup_app and "ModernUiPlatformDataLib|ModernSetupPkg" not in dsc: dsc = dsc.replace("[LibraryClasses.common]\n", "[LibraryClasses.common]\n" + app_library_block, 1) if include_modern_setup_app and modern_setup_app_component not in dsc: @@ -166,7 +211,7 @@ if replace_uiapp: driver_sample_component + "\n OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf {", 1, ) -if display_engine == "modern": +if (display_engine == "modern" or display_engine == "lvgl"): dsc = dsc.replace( " CustomizedDisplayLib | MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf", " CustomizedDisplayLib | ModernSetupPkg/Library/ModernUiCustomizedDisplayLib/ModernUiCustomizedDisplayLib.inf", @@ -174,7 +219,7 @@ if display_engine == "modern": ) dsc = dsc.replace( " MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf", - modern_display_component, + modern_display_component_lvgl if display_engine == "lvgl" else modern_display_component, 1, ) if enable_driver_sample and driver_sample_component not in dsc: @@ -183,7 +228,7 @@ if enable_driver_sample and driver_sample_component not in dsc: driver_sample_component + "\n MdeModulePkg/Application/UiApp/UiApp.inf {", 1, ) -if display_engine == "modern": +if (display_engine == "modern" or display_engine == "lvgl"): dsc += ( "\n[PcdsFixedAtBuild]\n" f" gModernSetupPkgTokenSpaceGuid.PcdModernSetupTheme|{theme_pcd}\n" @@ -202,7 +247,7 @@ fdf = fdf.replace( "!include OvmfPkg/LoongArchVirt/VarStore.fdf.inc", 1, ) -if display_engine == "modern": +if (display_engine == "modern" or display_engine == "lvgl"): fdf = fdf.replace( "INF MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf", modern_display_fdf_inf, diff --git a/Scripts/build-lvgl-spike-loongarch.sh b/Scripts/build-lvgl-spike-loongarch.sh new file mode 100755 index 0000000..ea72ea5 --- /dev/null +++ b/Scripts/build-lvgl-spike-loongarch.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# Copyright (c) 2026, MarsDoge. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# experimental/lvgl-spike build validation: +# Compile + link the LVGL core + software renderer + upstream UEFI port (with the +# LoongArch64/RISC-V64 arch-gate patch in External/lvgl) for LOONGARCH64 under the +# real edk2 LoongArch GCC toolchain. Builds a single UEFI_APPLICATION module +# (LvglSpikeProbe) against the stock LoongArchVirtQemu platform DSC -- no FD, no +# default overlay touched. Answers: does the patch let LVGL build on LoongArch? +set -euo pipefail + +PKG_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# shellcheck disable=SC1091 +source "${PKG_DIR}/Scripts/edk2-workspace.sh" +WORKSPACE="$(DetectWorkspace)" +TARGET="${TARGET:-DEBUG}" +JOBS="${JOBS:-$(nproc 2>/dev/null || echo 4)}" + +GCC_LOONGARCH64_PREFIX="${GCC_LOONGARCH64_PREFIX:-}" +if [[ -z "${GCC_LOONGARCH64_PREFIX}" ]]; then + if command -v loongarch64-unknown-linux-gnu-gcc >/dev/null 2>&1; then + GCC_LOONGARCH64_PREFIX="loongarch64-unknown-linux-gnu-" + elif command -v loongarch64-linux-gnu-gcc >/dev/null 2>&1; then + GCC_LOONGARCH64_PREFIX="loongarch64-linux-gnu-" + else + GCC_LOONGARCH64_PREFIX="loongarch64-unknown-linux-gnu-" + fi +fi + +export WORKSPACE +export GCC_LOONGARCH64_PREFIX +ConfigureModernSetupPackagePath +# Make the experimental LvglSpikePkg resolvable as a package. +AppendPackagePath "${PKG_DIR}/Experimental" +export PACKAGES_PATH + +MODULE="${PKG_DIR}/Experimental/LvglSpikePkg/Library/LvglLib/LvglSpikeProbe.inf" +PLATFORM_DSC="${PKG_DIR}/Experimental/LvglSpikePkg/LvglSpikeLoongArch.dsc" + +# LoongArch64/RISC-V64 UEFI support is upstream in the pinned External/lvgl +# baseline; the submodule is consumed pristine with no local patching. + +if ! command -v "${GCC_LOONGARCH64_PREFIX}gcc" >/dev/null 2>&1; then + echo "Missing LoongArch GCC: ${GCC_LOONGARCH64_PREFIX}gcc" >&2 + exit 1 +fi +if [[ ! -f "${WORKSPACE}/OvmfPkg/LoongArchVirt/LoongArchVirtQemu.dsc" ]]; then + echo "WORKSPACE missing LoongArchVirtQemu platform: ${WORKSPACE}" >&2 + exit 1 +fi + +cd "${WORKSPACE}" +if [[ ! -x BaseTools/Source/C/bin/VfrCompile ]]; then + make -C BaseTools -j"${JOBS}" +fi + +set +u +# shellcheck disable=SC1091 +source edksetup.sh +set -u + +echo "== Building LvglSpikeProbe (LVGL+UEFI port) for LOONGARCH64, TARGET=${TARGET} ==" +build \ + -a LOONGARCH64 \ + -t GCC \ + -p "${PLATFORM_DSC}" \ + -b "${TARGET}" \ + -n "${JOBS}" + +echo "" +echo "OK: LVGL built for LOONGARCH64." +echo "EFI: ${WORKSPACE}/Build/*/${TARGET}_GCC/LOONGARCH64/LvglSpikeProbe.efi" diff --git a/Scripts/build-lvgl-spike-riscv.sh b/Scripts/build-lvgl-spike-riscv.sh new file mode 100755 index 0000000..1d6e1e1 --- /dev/null +++ b/Scripts/build-lvgl-spike-riscv.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +# Copyright (c) 2026, MarsDoge. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# experimental/lvgl-spike build validation (RISC-V): +# Compile + link the LVGL core + software renderer + upstream UEFI port (with the +# LoongArch64/RISC-V64 arch-gate patch) for RISCV64 under the edk2 RISC-V GCC +# toolchain. Per repo convention RISC-V is build-only (no run/capture helper), so +# this proves compile+link, not render. Builds one UEFI_APPLICATION; no FD. +set -euo pipefail + +PKG_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +# shellcheck disable=SC1091 +source "${PKG_DIR}/Scripts/edk2-workspace.sh" +WORKSPACE="$(DetectWorkspace)" +TARGET="${TARGET:-DEBUG}" +JOBS="${JOBS:-$(nproc 2>/dev/null || echo 4)}" + +GCC_RISCV64_PREFIX="${GCC_RISCV64_PREFIX:-}" +if [[ -z "${GCC_RISCV64_PREFIX}" ]]; then + if command -v riscv64-unknown-elf-gcc >/dev/null 2>&1; then + GCC_RISCV64_PREFIX="riscv64-unknown-elf-" + elif command -v riscv64-linux-gnu-gcc >/dev/null 2>&1; then + GCC_RISCV64_PREFIX="riscv64-linux-gnu-" + else + GCC_RISCV64_PREFIX="riscv64-unknown-elf-" + fi +fi + +export WORKSPACE +export GCC_RISCV64_PREFIX +ConfigureModernSetupPackagePath +AppendPackagePath "${PKG_DIR}/Experimental" +export PACKAGES_PATH + +PLATFORM_DSC="${PKG_DIR}/Experimental/LvglSpikePkg/LvglSpikeRiscV.dsc" + +# LoongArch64/RISC-V64 UEFI support is upstream in the pinned External/lvgl +# baseline; the submodule is consumed pristine with no local patching. + +if ! command -v "${GCC_RISCV64_PREFIX}gcc" >/dev/null 2>&1; then + echo "Missing RISC-V GCC: ${GCC_RISCV64_PREFIX}gcc" >&2 + exit 1 +fi + +cd "${WORKSPACE}" +if [[ ! -x BaseTools/Source/C/bin/VfrCompile ]]; then + make -C BaseTools -j"${JOBS}" +fi + +set +u +# shellcheck disable=SC1091 +source edksetup.sh +set -u + +echo "== Building LvglSpikeProbe (LVGL+UEFI port) for RISCV64, TARGET=${TARGET} ==" +build \ + -a RISCV64 \ + -t GCC \ + -p "${PLATFORM_DSC}" \ + -b "${TARGET}" \ + -n "${JOBS}" + +echo "" +echo "OK: LVGL built for RISCV64." +echo "EFI: ${WORKSPACE}/Build/LvglSpikeRiscV/${TARGET}_GCC/RISCV64/LvglSpikeProbe.efi" diff --git a/Scripts/build-ovmf-x64.sh b/Scripts/build-ovmf-x64.sh index 12b765f..eac2849 100755 --- a/Scripts/build-ovmf-x64.sh +++ b/Scripts/build-ovmf-x64.sh @@ -17,11 +17,22 @@ JOBS="${JOBS:-$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)}" MODERN_SETUP_THEME="${MODERN_SETUP_THEME:-graphite-gold}" MODERN_SETUP_DISPLAY_ENGINE="${MODERN_SETUP_DISPLAY_ENGINE:-modern}" MODERN_SETUP_REPLACE_UIAPP="${MODERN_SETUP_REPLACE_UIAPP:-0}" +# Opt-in control-rich VFR test driver (checkbox/numeric/date/time/string/ +# password/ordered-list), reachable via Device Manager. Used to exercise the +# DisplayEngine control affordances; off by default. +MODERN_SETUP_DEMO_DRIVER_SAMPLE="${MODERN_SETUP_DEMO_DRIVER_SAMPLE:-0}" GENERATE_ONLY="${GENERATE_ONLY:-0}" OVERLAY_DIR="${WORKSPACE}/Build/ModernSetupPkgOverlay" export WORKSPACE ConfigureModernSetupPackagePath +# Experimental LvglSpikePkg is only consumed by MODERN_SETUP_DISPLAY_ENGINE=lvgl; +# making it resolvable here is harmless for native/modern. +AppendPackagePath "${PKG_DIR}/Experimental" +export PACKAGES_PATH + +# LoongArch64/RISC-V64 UEFI support is upstream in the pinned External/lvgl +# baseline; the submodule is consumed pristine with no local patching. if [[ ! -d "${WORKSPACE}/MdePkg" || ! -d "${WORKSPACE}/OvmfPkg" ]]; then echo "WORKSPACE does not look like an edk2 checkout with MdePkg and OvmfPkg: ${WORKSPACE}" >&2 @@ -36,7 +47,7 @@ fi mkdir -p "${OVERLAY_DIR}" -python3 - <<'PY' "${WORKSPACE}" "${OVERLAY_DIR}" "${MODERN_SETUP_THEME}" "${MODERN_SETUP_DISPLAY_ENGINE}" "${MODERN_SETUP_REPLACE_UIAPP}" +python3 - <<'PY' "${WORKSPACE}" "${OVERLAY_DIR}" "${MODERN_SETUP_THEME}" "${MODERN_SETUP_DISPLAY_ENGINE}" "${MODERN_SETUP_REPLACE_UIAPP}" "${MODERN_SETUP_DEMO_DRIVER_SAMPLE}" from pathlib import Path import re import sys @@ -46,6 +57,7 @@ overlay = Path(sys.argv[2]) theme_name = sys.argv[3].strip().lower() display_engine = sys.argv[4].strip().lower() replace_uiapp_flag = sys.argv[5].strip().lower() +enable_driver_sample = sys.argv[6].strip().lower() in {"1", "true", "yes"} theme_pcd = { "orange": "0x00", "amber": "0x00", @@ -61,9 +73,9 @@ if theme_pcd is None: f"Unsupported MODERN_SETUP_THEME={theme_name!r}; " "use orange, amber, dark-orange, red, accent-red, dark-red, graphite, or graphite-gold" ) -if display_engine not in {"modern", "native"}: +if display_engine not in {"modern", "native", "lvgl"}: raise SystemExit( - f"Unsupported MODERN_SETUP_DISPLAY_ENGINE={display_engine!r}; use modern or native" + f"Unsupported MODERN_SETUP_DISPLAY_ENGINE={display_engine!r}; use modern, native, or lvgl" ) if replace_uiapp_flag not in {"0", "1", "false", "true", "no", "yes"}: raise SystemExit( @@ -71,19 +83,56 @@ if replace_uiapp_flag not in {"0", "1", "false", "true", "no", "yes"}: ) replace_uiapp = replace_uiapp_flag in {"1", "true", "yes"} -modern_setup_app_component_boot_manager_fallback = """ ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf { - - GCC:*_*_*_CC_FLAGS = -DMODERN_SETUP_NATIVE_FALLBACK_BOOT_MANAGER_MENU=1 - }""" +# When ModernSetupApp replaces UiApp, in lvgl mode the app shell also renders +# through the LVGL-backed renderer (resolved via the ModernUiRendererLib class +# below), so it must force-link IntrinsicLib like the lvgl-mode display engine. +_app_lvgl_intrinsics = ( + " \n" + " NULL|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf\n" + if display_engine == "lvgl" + else "" +) +modern_setup_app_component_boot_manager_fallback = ( + " ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf {\n" + " \n" + " GCC:*_*_*_CC_FLAGS = -DMODERN_SETUP_NATIVE_FALLBACK_BOOT_MANAGER_MENU=1\n" + f"{_app_lvgl_intrinsics}" + " }" +) modern_setup_app_uiapp_fdf_inf = "INF RuleOverride = MODERN_SETUP_UIAPP ModernSetupPkg/Application/ModernSetupApp/ModernSetupApp.inf" +# In lvgl mode the SAME ModernDisplayEngineDxe interaction backend is used as in +# modern mode; only the renderer library class is swapped to the LVGL-backed +# implementation (below), and IntrinsicLib is force-linked because the renderer +# now pulls LVGL's software draw pipeline. modern_display_component = " ModernSetupPkg/Universal/ModernDisplayEngineDxe/ModernDisplayEngineDxe.inf" +modern_display_component_lvgl = ( + " ModernSetupPkg/Universal/ModernDisplayEngineDxe/ModernDisplayEngineDxe.inf {\n" + " \n" + " NULL|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf\n" + " }" +) modern_display_fdf_inf = "INF ModernSetupPkg/Universal/ModernDisplayEngineDxe/ModernDisplayEngineDxe.inf" +# LVGL core (the upstream lvgl sources, built as a BASE library) is resolved only +# in lvgl mode and consumed transitively through the LVGL renderer library. +lvgl_library_block = " LvglCoreLib|LvglSpikePkg/Library/LvglLib/LvglCoreLib.inf\n" boot_manager_menu_component = " MdeModulePkg/Application/BootManagerMenuApp/BootManagerMenuApp.inf" boot_manager_menu_fdf_inf = "INF MdeModulePkg/Application/BootManagerMenuApp/BootManagerMenuApp.inf" -library_block = """ ModernUiEngineLib|ModernSetupPkg/Library/ModernUiEngineLib/ModernUiEngineLib.inf - ModernUiRendererLib|ModernSetupPkg/Library/ModernUiRendererLib/ModernUiRendererLib.inf - ModernUiThemeLib|ModernSetupPkg/Library/ModernUiThemeLib/ModernUiThemeLib.inf -""" +# Opt-in control-rich VFR test driver (reachable via Device Manager). +driver_sample_component = " MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf" +driver_sample_fdf_inf = "INF MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf" +# The renderer library class resolves to the LVGL-backed implementation in lvgl +# mode and to the hand-rolled GOP rasterizer otherwise. Both expose the identical +# ModernUiRenderer.h API, so ModernUiEngineLib and its consumers are unchanged. +renderer_inf = ( + "ModernSetupPkg/Library/ModernUiLvglRendererLib/ModernUiRendererLib.inf" + if display_engine == "lvgl" + else "ModernSetupPkg/Library/ModernUiRendererLib/ModernUiRendererLib.inf" +) +library_block = ( + " ModernUiEngineLib|ModernSetupPkg/Library/ModernUiEngineLib/ModernUiEngineLib.inf\n" + f" ModernUiRendererLib|{renderer_inf}\n" + " ModernUiThemeLib|ModernSetupPkg/Library/ModernUiThemeLib/ModernUiThemeLib.inf\n" +) app_library_block = """ ModernUiPlatformDataLib|ModernSetupPkg/Library/ModernUiPlatformDataLib/ModernUiPlatformDataLib.inf ModernUiBootDataLib|ModernSetupPkg/Library/ModernUiBootDataLib/ModernUiBootDataLib.inf ModernUiDeviceDataLib|ModernSetupPkg/Library/ModernUiDeviceDataLib/ModernUiDeviceDataLib.inf @@ -121,7 +170,7 @@ dsc = replace_regex_once( r"\1Build/ModernSetupPkgOverlay/OvmfX64ModernSetup.fdf", "FLASH_DEFINITION", ) -if display_engine == "modern" or replace_uiapp: +if (display_engine == "modern" or display_engine == "lvgl") or replace_uiapp: if "ModernUiEngineLib|ModernSetupPkg" not in dsc: if "[LibraryClasses.common]\n" in dsc: dsc = replace_once(dsc, "[LibraryClasses.common]\n", "[LibraryClasses.common]\n" + library_block, "LibraryClasses.common") @@ -150,7 +199,16 @@ if replace_uiapp: boot_manager_menu_component + r"\n\1", "QemuKernelLoaderFsDxe component", ) -if display_engine == "modern": +if (display_engine == "modern" or display_engine == "lvgl"): + # lvgl mode additionally resolves the LVGL core library, consumed + # transitively by the LVGL renderer. + if display_engine == "lvgl" and "LvglCoreLib|LvglSpikePkg" not in dsc: + if "[LibraryClasses.common]\n" in dsc: + dsc = replace_once(dsc, "[LibraryClasses.common]\n", "[LibraryClasses.common]\n" + lvgl_library_block, "LibraryClasses.common for lvgl") + elif "[LibraryClasses]\n" in dsc: + dsc = replace_once(dsc, "[LibraryClasses]\n", "[LibraryClasses]\n" + lvgl_library_block, "LibraryClasses for lvgl") + else: + raise SystemExit("Expected LibraryClasses anchor for lvgl") dsc = replace_regex_once( dsc, r"^\s*CustomizedDisplayLib\s*\|\s*MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib\.inf\s*$", @@ -160,18 +218,25 @@ if display_engine == "modern": dsc = replace_regex_once( dsc, r"^\s*MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe\.inf\s*$", - modern_display_component, + modern_display_component_lvgl if display_engine == "lvgl" else modern_display_component, "DisplayEngineDxe component", ) dsc += ( "\n[PcdsFixedAtBuild]\n" f" gModernSetupPkgTokenSpaceGuid.PcdModernSetupTheme|{theme_pcd}\n" ) +if enable_driver_sample and driver_sample_component not in dsc: + dsc = replace_regex_once( + dsc, + r"^( MdeModulePkg/Application/UiApp/UiApp\.inf)", + driver_sample_component + r"\n\1", + "UiApp DSC component anchor for DriverSample", + ) (overlay / "OvmfX64ModernSetup.dsc").write_text(dsc) fdf_path = workspace / "OvmfPkg/OvmfPkgX64.fdf" fdf = fdf_path.read_text() -if display_engine == "modern": +if (display_engine == "modern" or display_engine == "lvgl"): fdf = replace_regex_once( fdf, r"^\s*INF\s+MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe\.inf\s*$", @@ -200,6 +265,13 @@ if replace_uiapp: " UI STRING=\"ModernSetupApp\" Optional\n" " }\n" ) +if enable_driver_sample and driver_sample_fdf_inf not in fdf: + fdf = replace_regex_once( + fdf, + r"^(\s*INF\s+MdeModulePkg/Application/UiApp/UiApp\.inf\s*)$", + driver_sample_fdf_inf + r"\n\1", + "UiApp FDF INF anchor for DriverSample", + ) (overlay / "OvmfX64ModernSetup.fdf").write_text(fdf) PY diff --git a/Tests/Smoke/smoke_validate.py b/Tests/Smoke/smoke_validate.py index bab30d2..882f601 100755 --- a/Tests/Smoke/smoke_validate.py +++ b/Tests/Smoke/smoke_validate.py @@ -67,6 +67,17 @@ "ModernUiHiiBridge.h", "ModernUiPageAdapter.h", ) +# The experimental LVGL rendering backend (Experimental/LvglSpikePkg and the +# LVGL-backed renderer) is selected only by MODERN_SETUP_DISPLAY_ENGINE=lvgl and +# must never appear in a default (native/modern) generated overlay. These tokens +# are checked against the *generated* overlays only -- the build scripts +# themselves legitimately reference them inside the lvgl branch. +PROHIBITED_DEFAULT_OVERLAY_LVGL_TOKENS = ( + "LvglCoreLib", + "ModernUiLvglRendererLib", + "LvglSpikePkg", + "LvglDisplayEngineDxe", +) PROHIBITED_THEME_ALIAS_TOKENS = ( "\x61\x6f\x72\x75\x73", "\x61\x73\x75\x73", @@ -2263,7 +2274,6 @@ def check_phase33_display_form_view_model_boundary(root: Path) -> list[str]: "ModernDisplayFormRowGetVisualRole", "MODERN_DISPLAY_FORM_ROW", "ModernDisplayDrawStatementRowAccents", - "ModernDisplayDrawStatementValueLane", ): if token not in row_surface_body: raise SmokeFailure(f"DisplayEngine row surface must consume the Phase34/36 row model/helper: {token}") @@ -2280,8 +2290,8 @@ def check_phase33_display_form_view_model_boundary(root: Path) -> list[str]: "ModernDisplayStatementTextInset", "ModernDisplayDrawRightRailDivider", "ModernDisplayPageStatusText", - "ModernDisplayDrawStatementValueLane", - "ModernDisplayDrawStatementValueLaneCue", + "ModernDisplayKindToValueType", + "ModernDisplayDrawStatementRowCue", "ModernDisplayFooterStatusReservedColumns", "ModernDisplayPageState", "ModernDisplayPageStateRebootRequired", @@ -2310,32 +2320,42 @@ def check_phase33_display_form_view_model_boundary(root: Path) -> list[str]: if token not in page_chrome_body: raise SmokeFailure(f"Phase39/40 page chrome must render existing FormBrowser presentation context: {token}") - value_lane_body = extract_c_function_body(internal_text, "ModernDisplayDrawStatementValueLane") + # Per-opcode control affordance: the DisplayEngine reuses the SHARED engine + # cue vocabulary (ModernUiEngineDrawControlCue) rather than carrying its own + # shape switch. The adapter only maps its row kind to the shared value type; + # the affordance shapes themselves live once in ModernUiEngineLib so the App + # value lane and the in-setup row read identically. + kind_cue_body = extract_c_function_body(internal_text, "ModernDisplayKindToValueType") for token in ( - "ModernDisplayDrawStatementValueLaneCue", + "ModernDisplayFormRowCheckbox", + "ModernUiValueCheckbox", + "ModernDisplayFormRowNumeric", + "ModernUiValueNumeric", + "ModernDisplayFormRowOrderedList", + "ModernUiValueOrderedList", + ): + if token not in kind_cue_body: + raise SmokeFailure(f"Phase41 control cue kind mapper missing mapping token: {token}") + + # The post-text overlay draws the affordance only on editable rows and only + # after native text has been printed; it must classify already-materialized + # statement data, delegate to the shared engine cue, and never read/write + # HII or storage. + row_cue_body = extract_c_function_body(internal_text, "ModernDisplayDrawStatementRowCue") + for token in ( + "ModernDisplayClassifyStatementForForm", "ModernDisplayFormRowIsTextOnly", - "ModernDisplayFormRowStateHighlighted", "ModernDisplayFormRowStateDisabled", "ModernDisplayFormRowStateReadOnly", + "ModernUiEngineDrawControlCue", + "ModernDisplayKindToValueType", ): - if token not in value_lane_body: - raise SmokeFailure(f"Phase41 value lane polish missing guarded draw token: {token}") - - value_lane_cue_body = extract_c_function_body(internal_text, "ModernDisplayDrawStatementValueLaneCue") - for token in ( - "ModernUiStrokeRect", - "ModernUiFillRect", - "ModernUiBlendColor", - "ModernDisplayFormRowAccentColor", - "ModernDisplayFormRowStateSelected", - "Theme->AccentYellow", - "Theme->Border", - ): - if token not in value_lane_cue_body: - raise SmokeFailure(f"Phase41 value lane cue helper missing presentation token: {token}") - for token in ("ConfigAccess", "RouteConfig", "ExtractConfig", "SetVariable", "HiiSetBrowserData", "HiiGetString"): - if token in value_lane_cue_body: - raise SmokeFailure(f"Phase41 value lane cue helper contains prohibited semantic/storage token: {token}") + if token not in row_cue_body: + raise SmokeFailure(f"Phase41 control cue overlay missing guarded draw token: {token}") + for body in (kind_cue_body, row_cue_body): + for token in ("ConfigAccess", "RouteConfig", "ExtractConfig", "SetVariable", "HiiSetBrowserData", "HiiGetString"): + if token in body: + raise SmokeFailure(f"Phase41 control cue helper contains prohibited semantic/storage token: {token}") right_help_body = extract_c_function_body(internal_text, "ModernDisplayDrawRightHelpRailContext") for token in ( @@ -2369,6 +2389,34 @@ def check_phase33_display_form_view_model_boundary(root: Path) -> list[str]: if token not in engine_text: raise SmokeFailure(f"Phase36 DisplayEngine footer status chip missing token: {token}") + # The shared control-cue vocabulary is the single home for per-type + # affordance shapes; both the App value lane and the DisplayEngine row cue + # delegate to it, so the shapes are defined exactly once here. It is built + # only from renderer primitives and keys on the engine value type. + control_cue_body = extract_c_function_body(engine_text, "ModernUiEngineDrawControlCue") + for token in ( + "ModernUiValueCheckbox", + "ModernUiValueOneOf", + "ModernUiValueOrderedList", + "ModernUiValueNumeric", + "ModernUiValueDateTime", + "ModernUiValuePassword", + "ModernUiValueString", + "ModernUiValueAction", + "ModernUiFillTriangle", + "ModernUiStrokeRect", + "ModernUiFillRect", + ): + if token not in control_cue_body: + raise SmokeFailure(f"Phase41 shared control cue vocabulary missing affordance token: {token}") + # The App value lane must route through the shared cue, not invent its own. + draw_value_body = extract_c_function_body(engine_text, "ModernUiEngineDrawValue") + if "ModernUiEngineDrawControlCue" not in draw_value_body: + raise SmokeFailure("Phase41 App value lane must delegate control affordances to ModernUiEngineDrawControlCue") + for token in ("ConfigAccess", "RouteConfig", "ExtractConfig", "SetVariable", "HiiSetBrowserData", "HiiGetString"): + if token in control_cue_body: + raise SmokeFailure(f"Phase41 shared control cue contains prohibited semantic/storage token: {token}") + form_display = root / "Universal" / "ModernDisplayEngineDxe" / "FormDisplay.c" form_display_text = strip_c_comments(form_display.read_text(encoding="utf-8")) display_one_menu = extract_c_function_body(form_display_text, "DisplayOneMenu") @@ -2532,6 +2580,9 @@ def check_overlay_generation(root: Path) -> list[str]: if not generated_path.exists(): raise SmokeFailure(f"missing generated overlay file: {generated_path}") assert_not_contains_any(generated_path, PROHIBITED_DEFAULT_OVERLAY_TOKENS) + # The LVGL backend is experimental and lvgl-mode only; it must + # never leak into a default native/modern overlay. + assert_not_contains_any(generated_path, PROHIBITED_DEFAULT_OVERLAY_LVGL_TOKENS) dsc = workspace / "Build" / "ModernSetupPkgOverlay" / dsc_name if engine == "modern": diff --git a/Universal/ModernDisplayEngineDxe/FormDisplay.c b/Universal/ModernDisplayEngineDxe/FormDisplay.c index d589880..8fafe54 100644 --- a/Universal/ModernDisplayEngineDxe/FormDisplay.c +++ b/Universal/ModernDisplayEngineDxe/FormDisplay.c @@ -2637,6 +2637,21 @@ DisplayOneMenu ( } } + // + // Overlay the per-opcode control affordance AFTER all row text (and its + // highlight background) has been printed, so the cue is composited on top + // instead of being overpainted by the native text background. + // + ModernDisplayDrawStatementRowCue ( + gFormData, + Statement, + BeginCol, + MenuOption->Row, + RowWidth, + Highlight, + (BOOLEAN)(gUserInput->SelectedStatement == Statement) + ); + return EFI_SUCCESS; } diff --git a/Universal/ModernDisplayEngineDxe/FormDisplay.h b/Universal/ModernDisplayEngineDxe/FormDisplay.h index 4024025..317b52a 100644 --- a/Universal/ModernDisplayEngineDxe/FormDisplay.h +++ b/Universal/ModernDisplayEngineDxe/FormDisplay.h @@ -101,6 +101,34 @@ ModernDisplayDrawStatementRow ( IN BOOLEAN Selected ); +/** + Draw a per-opcode control affordance over an already-painted statement row. + + Call this AFTER native FormBrowser has printed the row's prompt/value text and + highlight background, so the affordance is composited on top instead of being + overpainted. It paints only a small non-semantic cue glyph at the row's right + edge and never reads, writes, or owns any HII/FormBrowser value or semantics. + + @param[in] FormData DisplayEngine form that owns Statement. May be NULL. + @param[in] Statement Statement to classify. May be NULL (then no cue). + @param[in] Column Text-grid column where the row starts. + @param[in] Row Text-grid row of the statement. + @param[in] Width Text-grid column count of the row. + @param[in] Highlight TRUE when the row currently has keyboard highlight. + @param[in] Selected TRUE when the row is in edit/selection mode. +**/ +VOID +EFIAPI +ModernDisplayDrawStatementRowCue ( + IN FORM_DISPLAY_ENGINE_FORM *FormData OPTIONAL, + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement OPTIONAL, + IN UINTN Column, + IN UINTN Row, + IN UINTN Width, + IN BOOLEAN Highlight, + IN BOOLEAN Selected + ); + // // Screen definitions //