From 980675e87c80237f8cfd3a48dbe7dc65e119bd3a Mon Sep 17 00:00:00 2001 From: MarsDoge Date: Wed, 3 Jun 2026 11:18:37 +0800 Subject: [PATCH 1/5] spike(lvgl): add LVGL v9.5.0 as External/lvgl submodule (experimental) Open-source-rendering-backend evaluation spike. Pins lvgl/lvgl at the v9.5.0 release tag (85aa60d) under External/lvgl, alongside the existing External/edk2 baseline. Scope/discipline (experimental tier, like ModernUiHiiBridgeLib): - Lives only on experimental/lvgl-spike; not for default overlays, not for ModernSetupApp, not wired into any shipped firmware. - Bootstrap does not pull it by default (separate opt-in init). - Goal of the spike: answer the one question that actually gates a backend switch -- does LVGL build + render under edk2 on a hard arch (LoongArch), not just X64 where LvglPkg already works. License: LVGL is MIT (compatible). Aptio-styled chrome/wallpaper from downstream LVGL-UEFI ports is intentionally NOT used (AMI brand asset). --- .gitmodules | 3 +++ External/lvgl | 1 + 2 files changed, 4 insertions(+) create mode 160000 External/lvgl 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/External/lvgl b/External/lvgl new file mode 160000 index 0000000..85aa60d --- /dev/null +++ b/External/lvgl @@ -0,0 +1 @@ +Subproject commit 85aa60d18b3d5e5588d7b247abf90198f07c8a63 From f5a0e6b46f6f52434a11413f87733fc5414eec09 Mon Sep 17 00:00:00 2001 From: MarsDoge Date: Fri, 5 Jun 2026 10:33:55 +0800 Subject: [PATCH 2/5] refactor(renderer): extract shared backend-agnostic surface + triangle primitive Split ModernUiRendererLib into a thin GOP backend plus a shared, backend-agnostic surface (ModernUiRendererCommon.c / ModernUiRendererInternal.h) that both the GOP and the experimental LVGL backend compile verbatim. The shared surface owns geometry compositions, text measurement, themed widgets, and the new ModernUiFillTriangle shape primitive (down/up/right apex) so chevron/arrow cues are a single renderer primitive rather than per-consumer magic-pixel shapes. A backend now provides only the three irreducible primitives: ModernUiRendererInit, ModernUiFillRect, ModernUiDrawText. Co-Authored-By: Claude Opus 4.8 --- Include/ModernUi/ModernUiRenderer.h | 31 + .../ModernUiRendererCommon.c | 1134 +++++++++++++++++ .../ModernUiRendererInternal.h | 95 ++ .../ModernUiRendererLib/ModernUiRendererLib.c | 1029 +-------------- .../ModernUiRendererLib.inf | 1 + 5 files changed, 1275 insertions(+), 1015 deletions(-) create mode 100644 Library/ModernUiRendererLib/ModernUiRendererCommon.c create mode 100644 Library/ModernUiRendererLib/ModernUiRendererInternal.h 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/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] From 8ba7cb38c54f38d8c66cd61d5dfc7931a4d552f1 Mon Sep 17 00:00:00 2001 From: MarsDoge Date: Fri, 5 Jun 2026 10:34:32 +0800 Subject: [PATCH 3/5] feat(renderer): experimental LVGL backend + LvglSpikePkg Add ModernUiLvglRendererLib, a second ModernUiRendererLib implementation (selected by MODERN_SETUP_DISPLAY_ENGINE=lvgl) that composites every primitive through LVGL's software renderer into a persistent XRGB8888 shadow canvas, then BLTs only the touched region to GOP. ASCII text uses lv_draw_label (Montserrat); CJK runs fall back to the package's embedded bitmap glyphs. It shares the backend-agnostic surface and triangle primitive verbatim with the GOP backend. Bump External/lvgl v9.5.0 -> v9.5.0-1-g4cdddfe4a to pick up the pristine upstream "feat(uefi): support loongarch64 and riscv64 building" commit (no local patching of the submodule). Add LvglSpikePkg plus build-lvgl-spike-{loongarch, riscv}.sh as the experimental harness, and teach build-ovmf-x64.sh / build-loongarchvirt.sh the lvgl display-engine mode (force-linking compiler intrinsics for the LVGL-backed app shell and display engine). All experimental pieces stay out of the default modern/native overlays. Co-Authored-By: Claude Opus 4.8 --- .../Include/Library/LvglCoreLib.h | 18 + .../LvglSpikePkg/Include/Library/LvglLib.h | 69 + Experimental/LvglSpikePkg/Include/LvglTheme.h | 157 ++ .../Library/LvglLib/EscExitHandler.c | 155 ++ .../Library/LvglLib/LvglCoreLib.inf | 499 ++++++ .../LvglSpikePkg/Library/LvglLib/LvglLib.c | 176 ++ .../LvglSpikePkg/Library/LvglLib/LvglLib.inf | 543 ++++++ .../Library/LvglLib/LvglLibCommon.h | 45 + .../Library/LvglLib/LvglSpikeProbe.c | 133 ++ .../Library/LvglLib/LvglSpikeProbe.inf | 58 + .../Library/LvglLib/LvglUefiPort.c | 212 +++ .../Library/LvglLib/LvglUefiPort.h | 164 ++ .../Library/LvglLib/MouseCursorIcon.c | 43 + .../LvglSpikePkg/Library/LvglLib/lv_conf.h | 1526 +++++++++++++++++ .../Library/LvglLib/lv_port_indev.c | 567 ++++++ .../Library/LvglLib/lv_port_indev.h | 71 + .../Library/LvglLib/lv_uefi_display.c | 83 + .../LvglSpikePkg/Library/LvglLib/lvgl | 1 + .../LvglDisplayEngineDxe.c | 76 + .../LvglDisplayEngineDxe.h | 60 + .../LvglDisplayEngineDxe.inf | 64 + .../LvglDisplayEngineDxe/LvglFormRenderer.c | 229 +++ .../LvglSpikePkg/LvglSpikeLoongArch.dsc | 53 + Experimental/LvglSpikePkg/LvglSpikePkg.dec | 30 + Experimental/LvglSpikePkg/LvglSpikeRiscV.dsc | 44 + Experimental/LvglSpikePkg/README.md | 103 ++ Experimental/LvglSpikePkg/REFERENCES.md | 46 + External/lvgl | 2 +- .../ModernUiRendererLib.c | 758 ++++++++ .../ModernUiRendererLib.inf | 57 + Scripts/build-loongarchvirt.sh | 77 +- Scripts/build-lvgl-spike-loongarch.sh | 73 + Scripts/build-lvgl-spike-riscv.sh | 66 + Scripts/build-ovmf-x64.sh | 102 +- 34 files changed, 6328 insertions(+), 32 deletions(-) create mode 100644 Experimental/LvglSpikePkg/Include/Library/LvglCoreLib.h create mode 100644 Experimental/LvglSpikePkg/Include/Library/LvglLib.h create mode 100644 Experimental/LvglSpikePkg/Include/LvglTheme.h create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/EscExitHandler.c create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/LvglCoreLib.inf create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/LvglLib.c create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/LvglLib.inf create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/LvglLibCommon.h create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/LvglSpikeProbe.c create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/LvglSpikeProbe.inf create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/LvglUefiPort.c create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/LvglUefiPort.h create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/MouseCursorIcon.c create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/lv_conf.h create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/lv_port_indev.c create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/lv_port_indev.h create mode 100644 Experimental/LvglSpikePkg/Library/LvglLib/lv_uefi_display.c create mode 120000 Experimental/LvglSpikePkg/Library/LvglLib/lvgl create mode 100644 Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglDisplayEngineDxe.c create mode 100644 Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglDisplayEngineDxe.h create mode 100644 Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglDisplayEngineDxe.inf create mode 100644 Experimental/LvglSpikePkg/LvglDisplayEngineDxe/LvglFormRenderer.c create mode 100644 Experimental/LvglSpikePkg/LvglSpikeLoongArch.dsc create mode 100644 Experimental/LvglSpikePkg/LvglSpikePkg.dec create mode 100644 Experimental/LvglSpikePkg/LvglSpikeRiscV.dsc create mode 100644 Experimental/LvglSpikePkg/README.md create mode 100644 Experimental/LvglSpikePkg/REFERENCES.md create mode 100644 Library/ModernUiLvglRendererLib/ModernUiRendererLib.c create mode 100644 Library/ModernUiLvglRendererLib/ModernUiRendererLib.inf create mode 100755 Scripts/build-lvgl-spike-loongarch.sh create mode 100755 Scripts/build-lvgl-spike-riscv.sh 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 index 85aa60d..4cdddfe 160000 --- a/External/lvgl +++ b/External/lvgl @@ -1 +1 @@ -Subproject commit 85aa60d18b3d5e5588d7b247abf90198f07c8a63 +Subproject commit 4cdddfe4ae6aaf08c713d86673f5675875415998 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/Scripts/build-loongarchvirt.sh b/Scripts/build-loongarchvirt.sh index e319c1c..04825d9 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 @@ -69,9 +76,9 @@ if theme_pcd is None: f"Unsupported MODERN_SETUP_THEME={theme_name!r}; " "use orange, amber, dark-orange, red, accent-red, or dark-red" ) -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( @@ -84,23 +91,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 @@ -131,8 +174,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: @@ -164,7 +209,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", @@ -172,7 +217,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: @@ -181,7 +226,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" @@ -200,7 +245,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 b55df10..1a89391 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:-orange}" 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", @@ -59,9 +71,9 @@ if theme_pcd is None: f"Unsupported MODERN_SETUP_THEME={theme_name!r}; " "use orange, amber, dark-orange, red, accent-red, or dark-red" ) -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( @@ -69,19 +81,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 @@ -119,7 +168,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") @@ -148,7 +197,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*$", @@ -158,18 +216,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*$", @@ -198,6 +263,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 From 4295e8acc11aba2d7e82e0acc2cf94471492a4da Mon Sep 17 00:00:00 2001 From: MarsDoge Date: Fri, 5 Jun 2026 10:34:44 +0800 Subject: [PATCH 4/5] feat(ui): unify App/DisplayEngine control-affordance vocabulary Define the per-control cue shapes (checkbox box, one-of down chevron, ordered-list up/down, numeric plus, date/time ticks, password dots, string caret, action right arrow) once in ModernUiEngineDrawControlCue, keyed on MODERN_UI_VALUE_TYPE and built only from renderer primitives. Both surfaces now 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 via ModernDisplayKindToValueType and overlays the cue at the row's right edge AFTER native FormBrowser prints the row text, so it composites on top instead of being overpainted. Removes the DisplayEngine's private duplicate shape code (ModernDisplayCueTriangle / ModernDisplayDrawKindCue). The cue path classifies already-materialized statement data only and never touches HII/config/storage. Co-Authored-By: Claude Opus 4.8 --- Include/ModernUi/ModernUiEngine.h | 32 +++ .../CustomizedDisplayLibInternal.c | 231 +++++++++--------- Library/ModernUiEngineLib/ModernUiEngineLib.c | 140 +++++++++++ .../ModernDisplayEngineDxe/FormDisplay.c | 15 ++ .../ModernDisplayEngineDxe/FormDisplay.h | 28 +++ 5 files changed, 335 insertions(+), 111 deletions(-) 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/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 f81b704..ad5beb5 100644 --- a/Library/ModernUiEngineLib/ModernUiEngineLib.c +++ b/Library/ModernUiEngineLib/ModernUiEngineLib.c @@ -725,6 +725,7 @@ ModernUiEngineDrawValue ( ) { MODERN_UI_RECT Rect; + UINTN CueSide; if ((Context == NULL) || (Value == NULL) || (Theme == NULL)) { return EFI_INVALID_PARAMETER; @@ -740,6 +741,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); } @@ -755,6 +775,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/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 // From 3e843e01d73985f701565a3a1a6e57a856029f69 Mon Sep 17 00:00:00 2001 From: MarsDoge Date: Fri, 5 Jun 2026 10:35:07 +0800 Subject: [PATCH 5/5] docs+smoke: document renderer/control-cue contracts and update rules Document the renderer-backend split and the single control-affordance vocabulary in MODULE_BOUNDARIES.md (new "Control-affordance vocabulary" section, "Do not add a second copy" rule), record the user-visible changes in CHANGELOG, and update smoke_validate.py to assert the new contract: ModernUiEngineDrawValue delegates to ModernUiEngineDrawControlCue, the DisplayEngine row cue uses ModernDisplayKindToValueType, the kind->value-type mapping is present, and the shared cue helper carries no config/storage tokens. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 61 +++++++++++++++++++++ Docs/MODULE_BOUNDARIES.md | 47 +++++++++++++++++ Tests/Smoke/smoke_validate.py | 99 ++++++++++++++++++++++++++--------- 3 files changed, 183 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bbb731..b0b7dba 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`. - Phase49 ModernSetupApp: the Boot page now ends with a native boot-tools entry row; pressing Enter on it opens the native Boot Manager / Boot Maintenance via `SendForm()` instead of launching a boot option, and the per-entry BootNext / 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/Tests/Smoke/smoke_validate.py b/Tests/Smoke/smoke_validate.py index c3295a1..2e7b322 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":