LVGL with SDL2
LVGL ships an SDL2 display and input driver, so a UI can be built and iterated
on the desktop before it ever runs on hardware. The same lv_conf.h and
application code then compile for the target.
CMake project
cmake_minimum_required(VERSION 3.10)
project(lvgl C)
set(CMAKE_C_STANDARD 11) # C11
set(CMAKE_C_STANDARD_REQUIRED ON)
include_directories(${PROJECT_SOURCE_DIR})
file(GLOB_RECURSE INCLUDES "lv_drivers/*.h" "lv_demos/*.h" "lvgl/*.h" "./*.h")
file(GLOB_RECURSE SOURCES "lv_drivers/*.c" "lv_demos/*.c" "lvgl/*.c")
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
set(SDL2_DIR "C:/SDL2-2.30.1/x86_64-w64-mingw32")
include_directories(${SDL2_DIR}/include)
link_directories(${SDL2_DIR}/lib)
add_executable(main main.c mouse_cursor_icon.c ${SOURCES} ${INCLUDES})
target_compile_definitions(main PRIVATE LV_CONF_INCLUDE_SIMPLE)
target_link_libraries(main PRIVATE SDL2)
add_custom_target(run COMMAND ${EXECUTABLE_OUTPUT_PATH}/main)
Details worth getting right:
link_directoriespoints atlib, notbin. The MinGW SDL2 package puts the import librarylibSDL2.dll.ainliband the runtimeSDL2.dllinbin. Pointing the linker atbinfails to resolve SDL symbols; the DLL inbinis what has to be onPATHor beside the executable at run time.- Optimisation flags follow the language. This is a C project, so
CMAKE_C_FLAGSapplies; settingCMAKE_CXX_FLAGShas no effect. Better still, leave it alone and let-DCMAKE_BUILD_TYPE=Releasechoose. GLOB_RECURSEdoes not re-run on its own. Adding a source file does not trigger a reconfigure, so the build silently omits it. Either re-run CMake by hand after adding files, or addCONFIGURE_DEPENDS(CMake 3.12+), or list sources explicitly.LV_CONF_INCLUDE_SIMPLEmakes LVGL includelv_conf.hby plain name rather than by relative path, which is what lets the file sit at the project root. Defining it on the target rather than directory-wide keeps it scoped.
Finding SDL2 with find_package
The alternative to hard-coded paths. It requires SDL2's CMake config files, which ship in the development package:
find_package(SDL2 REQUIRED)
message(STATUS "SDL2 include dirs: ${SDL2_INCLUDE_DIRS}")
message(STATUS "SDL2 libraries: ${SDL2_LIBRARIES}")
find_package(SDL2 REQUIRED SDL2) — with the package name repeated as a
component — is not valid; the second argument is read as a component name that
SDL2 does not define.
When SDL2 is found this way, link against the imported target rather than the bare name so that include directories and interface flags come along automatically:
target_link_libraries(main PRIVATE SDL2::SDL2)
Point CMake at the config directory if it is not on the default search path:
-DSDL2_DIR="C:\\SDL2\\cmake"
or, when only the import library is available and there is no CMake config:
-DSDL2_DIR="C:\\SDL2\\cmake" -DSDL2_LIBRARY="C:\\SDL2\\x86_64-w64-mingw32\\lib\\libSDL2.dll.a"
Running
SDL2.dll must be findable at run time — copy it next to the executable, or add
its directory to PATH. A missing DLL shows as the program exiting immediately
with no output, which looks like a crash rather than a load failure.
The MSYS2 UCRT64 toolchain described on the C++ page builds this
project as-is; use its pacman SDL2 package
(mingw-w64-ucrt-x86_64-SDL2) instead of a downloaded SDK to avoid the path
juggling above entirely.