From 85ae941c198eaaad1c2dbb434cb77a4dffaef4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Ko=C5=82ucki?= Date: Mon, 25 May 2026 20:38:45 +0200 Subject: [PATCH] Initial commit --- .gitignore | 1 + .gitmodules | 3 + CMakeLists.txt | 82 ++++++++++++++++++++ CMakePresets.json | 13 ++++ build.sh | 3 + include/lv_ctr_disp.h | 63 ++++++++++++++++ libraries/lvgl | 1 + lv_conf.h | 19 +++++ source/lv_ctr_disp.c | 169 ++++++++++++++++++++++++++++++++++++++++++ source/main.c | 52 +++++++++++++ 10 files changed, 406 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 CMakePresets.json create mode 100755 build.sh create mode 100644 include/lv_ctr_disp.h create mode 160000 libraries/lvgl create mode 100644 lv_conf.h create mode 100644 source/lv_ctr_disp.c create mode 100644 source/main.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..112f7b9 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libraries/lvgl"] + path = libraries/lvgl + url = https://github.com/lvgl/lvgl.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..1a3e170 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,82 @@ +cmake_minimum_required(VERSION 3.16) + +# ----------------------------------------------------------------------------- +# Project +# ----------------------------------------------------------------------------- +project(Lomo C CXX ASM) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 20) + +# ----------------------------------------------------------------------------- +# devkitPro / libctru setup +# ----------------------------------------------------------------------------- +if(NOT DEFINED ENV{DEVKITPRO}) + message(FATAL_ERROR "Please set DEVKITPRO in your environment.") +endif() + +if(NOT DEFINED ENV{DEVKITARM}) + message(FATAL_ERROR "Please set DEVKITARM in your environment.") +endif() + +set(DEVKITPRO $ENV{DEVKITPRO}) +set(DEVKITARM $ENV{DEVKITARM}) + +# Load devkitPro CMake support +include("${DEVKITPRO}/cmake/3DS.cmake") + +# ----------------------------------------------------------------------------- +# LVGL +# ----------------------------------------------------------------------------- + +cmake_policy(SET CMP0077 NEW) +#set(LV_CONF_INCLUDE_SIMPLE ON) +#set(LV_BUILD_CONF_PATH ./include/lv_conf.h) +set(CONFIG_LV_BUILD_EXAMPLES OFF) +set(CONFIG_LV_BUILD_DEMOS OFF) +add_subdirectory(libraries/lvgl) + +# ----------------------------------------------------------------------------- +# Target +# ----------------------------------------------------------------------------- +add_executable(${PROJECT_NAME} + source/main.c + source/lv_ctr_disp.c +) + +target_include_directories(${PROJECT_NAME} PRIVATE + include +) + +# ----------------------------------------------------------------------------- +# Libraries +# ----------------------------------------------------------------------------- +target_link_libraries(${PROJECT_NAME} + lvgl + ctru + citro2d + citro3d + m +) + +# ----------------------------------------------------------------------------- +# 3DS metadata +# ----------------------------------------------------------------------------- +set_target_properties(${PROJECT_NAME} PROPERTIES + OUTPUT_NAME "lomo" +) + +# Generates: +# .3dsx +# .smdh +# .elf +ctr_generate_smdh( + OUTPUT "${PROJECT_NAME}.smdh" + NAME "Lomo" + DESCRIPTION "Camera app for the rest of us" + AUTHOR "BluRaf" +) + +ctr_create_3dsx(${PROJECT_NAME} + SMDH "${PROJECT_NAME}.smdh" +) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..d2a3e6d --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,13 @@ +{ + "version": 3, + "configurePresets": [ + { + "name": "3ds", + "generator": "Unix Makefiles", + "binaryDir": "build", + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "$env{DEVKITPRO}/cmake/3DS.cmake" + } + } + ] +} diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..782323d --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cmake --preset 3ds +cmake --build build --parallel $(nproc) diff --git a/include/lv_ctr_disp.h b/include/lv_ctr_disp.h new file mode 100644 index 0000000..4f28920 --- /dev/null +++ b/include/lv_ctr_disp.h @@ -0,0 +1,63 @@ +/** + * @file lv_ctr_disp.h + * + */ + +#if 1 + +#ifndef LV_CTR_DISP_H +#define LV_CTR_DISP_H + +#ifdef __cplusplus +extern "C" { +#endif + +/********************* + * INCLUDES + *********************/ +#if defined(LV_LVGL_H_INCLUDE_SIMPLE) +#include "lvgl.h" +#else +#include "lvgl/lvgl.h" +#endif + +/********************* + * DEFINES + *********************/ + +// TODO: make it configurable for RGB888/RGB565 +// (currently hardcoded to ARGB888, otherwise I was getting only white screen) +#define CTR_LV_COLOR_FORMAT LV_COLOR_FORMAT_ARGB8888 +#define CTR_TEX_COLOR_FORMAT GPU_RGBA8 +#define CTR_TR_IN_COLOR_FORMAT GX_TRANSFER_FMT_RGBA8 +#define CTR_TR_OUT_COLOR_FORMAT GX_TRANSFER_FMT_RGBA8 + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * GLOBAL PROTOTYPES + **********************/ +/* Initialize low level display driver */ +void lv_ctr_disp_init(void); + +/* Enable updating the screen (the flushing process) when disp_flush() is called by LVGL + */ +void disp_enable_update(void); + +/* Disable updating the screen (the flushing process) when disp_flush() is called by LVGL + */ +void disp_disable_update(void); + +/********************** + * MACROS + **********************/ + +#ifdef __cplusplus +} /*extern "C"*/ +#endif + +#endif /*LV_CTR_DISP_H*/ + +#endif /*Disable/Enable content*/ diff --git a/libraries/lvgl b/libraries/lvgl new file mode 160000 index 0000000..fe12120 --- /dev/null +++ b/libraries/lvgl @@ -0,0 +1 @@ +Subproject commit fe121205addd990871d350f252c7d40dab9789da diff --git a/lv_conf.h b/lv_conf.h new file mode 100644 index 0000000..c0d1df8 --- /dev/null +++ b/lv_conf.h @@ -0,0 +1,19 @@ +#ifndef LV_CONF_H +#define LV_CONF_H + +#define LV_COLOR_DEPTH 32 +#define LV_USE_OS 0 + +#define LV_MEM_SIZE (256U * 1024U) + +#define LV_USE_LOG 1 +//#define LV_LOG_LEVEL LV_LOG_LEVEL_TRACE +#define LV_LOG_PRINTF 1 + +#define LV_USE_ASSERT_NULL 1 + +#define LV_USE_STDLIB_MALLOC LV_STDLIB_BUILTIN +#define LV_USE_STDLIB_STRING LV_STDLIB_BUILTIN +#define LV_USE_STDLIB_SPRINTF LV_STDLIB_BUILTIN + +#endif diff --git a/source/lv_ctr_disp.c b/source/lv_ctr_disp.c new file mode 100644 index 0000000..77533d7 --- /dev/null +++ b/source/lv_ctr_disp.c @@ -0,0 +1,169 @@ +/** + * @file lv_ctr_disp.c + * + */ + +#if 1 + +/********************* + * INCLUDES + *********************/ +#include "lv_ctr_disp.h" +#include +#include <3ds.h> +#include +#include + +/********************* + * DEFINES + *********************/ +#define BOTTOM_DISP_HOR_RES 320 +#define BOTTOM_DISP_VER_RES 240 +#define BOTTOM_TEX_HOR_RES 512 +#define BOTTOM_TEX_VER_RES 256 + +#define BYTE_PER_PIXEL (LV_COLOR_FORMAT_GET_SIZE(CTR_LV_COLOR_FORMAT)) +#define BOTTOM_DISP_STRIDE (BOTTOM_TEX_HOR_RES * BYTE_PER_PIXEL) +#define BOTTOM_DISP_BUF_SIZE BOTTOM_DISP_STRIDE * BOTTOM_DISP_VER_RES + +/********************** + * TYPEDEFS + **********************/ + +/********************** + * STATIC PROTOTYPES + **********************/ +static void disp_init(void); + +static void disp_flush(lv_display_t * disp, const lv_area_t * area, uint8_t * px_map); + +/* Calculate nearest power of 2 for a given size */ +__attribute__ ((const)) static inline u32 tex_pot(u32 x) { + if (x < 32) return 32; + return 1 << (32 - __builtin_clz(x - 1)); +} + +/********************** + * STATIC VARIABLES + **********************/ + +C3D_RenderTarget *bottom_screen; +C3D_Tex tex; +Tex3DS_SubTexture subt3x; +LV_ATTRIBUTE_MEM_ALIGN static uint8_t * buf_1 = NULL; +LV_ATTRIBUTE_MEM_ALIGN static uint8_t * buf_2 = NULL; + +/********************** + * MACROS + **********************/ + +/********************** + * GLOBAL FUNCTIONS + **********************/ + +void lv_ctr_disp_init(void) +{ + /*------------------------- + * Initialize your display + * -----------------------*/ + disp_init(); + + /*------------------------------------ + * Create a display and set a flush_cb + * -----------------------------------*/ + lv_display_t * bottom_disp = lv_display_create(BOTTOM_DISP_HOR_RES, BOTTOM_DISP_VER_RES); + lv_display_set_color_format(bottom_disp, CTR_LV_COLOR_FORMAT); + lv_display_set_flush_cb(bottom_disp, disp_flush); + lv_display_set_default(bottom_disp); + + /* Two buffers for double buffering. */ + buf_1 = linearAlloc(BOTTOM_DISP_BUF_SIZE); + buf_2 = linearAlloc(BOTTOM_DISP_BUF_SIZE); + lv_display_set_buffers_with_stride(bottom_disp, buf_1, buf_2, BOTTOM_DISP_BUF_SIZE, BOTTOM_DISP_STRIDE, LV_DISPLAY_RENDER_MODE_FULL); + +} + +/********************** + * STATIC FUNCTIONS + **********************/ + +/*Initialize your display and the required peripherals.*/ +static void disp_init(void) +{ + C3D_Init(C3D_DEFAULT_CMDBUF_SIZE); + C2D_Init(C2D_DEFAULT_MAX_OBJECTS); + C2D_Prepare(); + bottom_screen = C2D_CreateScreenTarget(GFX_BOTTOM, GFX_LEFT); + + if(0 == C3D_TexInit(&tex, BOTTOM_TEX_HOR_RES, BOTTOM_TEX_VER_RES, CTR_TEX_COLOR_FORMAT)) puts("Could not create main texture."); + memset(tex.data, 0xFF, tex.size); + C3D_TexFlush(&tex); + tex.border = 0xFFFFFFFF; + C3D_TexSetWrap(&tex, GPU_CLAMP_TO_BORDER, GPU_CLAMP_TO_BORDER); + + subt3x.width = BOTTOM_DISP_HOR_RES; + subt3x.height = BOTTOM_DISP_VER_RES; + subt3x.left = 0.0f; + subt3x.top = 1.0f; + subt3x.right = (BOTTOM_DISP_HOR_RES/(float)tex.width); + subt3x.bottom = (1.0f - BOTTOM_DISP_VER_RES/(float)tex.height); + + C3D_TexSetFilter(&tex, GPU_NEAREST, GPU_NEAREST); +} + +volatile bool disp_flush_enabled = true; + +/* Enable updating the screen (the flushing process) when disp_flush() is called by LVGL + */ +void disp_enable_update(void) +{ + disp_flush_enabled = true; +} + +/* Disable updating the screen (the flushing process) when disp_flush() is called by LVGL + */ +void disp_disable_update(void) +{ + disp_flush_enabled = false; +} + +/*Flush the content of the internal buffer the specific area on the display. + *`px_map` contains the rendered image as raw pixel map and it should be copied to `area` on the display. + *You can use DMA or any hardware acceleration to do this operation in the background but + *'lv_display_flush_ready()' has to be called when it's finished.*/ +static void disp_flush(lv_display_t * disp_drv, const lv_area_t * area, uint8_t * px_map) +{ + if (lv_display_flush_is_last(disp_drv)) { + GSPGPU_FlushDataCache(px_map, tex.width * tex.height * 4); + + u32 dim = GX_BUFFER_DIM(tex.width, tex.height); + C3D_SyncDisplayTransfer((u32 *)px_map, dim, (u32*)tex.data, dim, + (GX_TRANSFER_FLIP_VERT(0) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | + GX_TRANSFER_IN_FORMAT(CTR_TR_IN_COLOR_FORMAT) | GX_TRANSFER_OUT_FORMAT(CTR_TR_OUT_COLOR_FORMAT) | + GX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO)) + ); + + // start frame, clear targets + C3D_FrameBegin(C3D_FRAME_SYNCDRAW); + C2D_TargetClear(bottom_screen, C2D_Color32f(0, 0, 0, 1)); + C2D_ImageTint tint; + C2D_PlainImageTint(&tint, C2D_Color32(255,255,255,255), 1.0f); + + gspWaitForVBlank(); + + C2D_SceneBegin(bottom_screen); + + C2D_Image img = { &tex, &subt3x }; + C2D_DrawImageAt(img, 0, 0, 0.6f, &tint, 1.f, 1.f); + + C3D_FrameEnd(0); + } + lv_display_flush_ready(disp_drv); +} + +#else /*Enable this file at the top*/ + +/*This dummy typedef exists purely to silence -Wpedantic.*/ +typedef int keep_pedantic_happy; +#endif + diff --git a/source/main.c b/source/main.c new file mode 100644 index 0000000..1c2a0e7 --- /dev/null +++ b/source/main.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include <3ds.h> +#include "lvgl.h" +#include "lv_ctr_disp.h" + + +long unsigned int sys_tick(void) +{ + long unsigned int tick = (long unsigned int)osGetTime(); + return tick; +} + +void sys_init(void) +{ + gfxInitDefault(); + consoleInit(GFX_TOP, NULL); +} + +void sys_free(void) +{ + gfxExit(); +} + +int main(int argc, char* argv[]) +{ + sys_init(); + lv_init(); + lv_ctr_disp_init(); + lv_tick_set_cb(sys_tick); + + lv_obj_t * label = lv_label_create(lv_screen_active()); + lv_label_set_text(label, "Hello world"); + lv_obj_center(label); + lv_obj_invalidate(lv_screen_active()); + + // Main loop + while (aptMainLoop()) + { + lv_timer_handler(); + + hidScanInput(); + + u32 kDown = hidKeysDown(); + if (kDown & KEY_START) + break; // break in order to return to hbmenu + } + + sys_free(); + return 0; +}