move event handling to native

This commit is contained in:
Siyuan Miao 2025-06-26 19:43:21 +00:00
parent b3243b2742
commit b707596f89
26 changed files with 58227 additions and 809 deletions

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"os"
"strconv"
"sync"
"github.com/jetkvm/kvm/internal/logging"
@ -102,6 +103,25 @@ type Config struct {
DefaultLogLevel string `json:"default_log_level"`
}
func (c *Config) GetDisplayRotation() uint16 {
rotationInt, err := strconv.ParseUint(c.DisplayRotation, 10, 16)
if err != nil {
logger.Warn().Err(err).Msg("invalid display rotation, using default")
return 270
}
return uint16(rotationInt)
}
func (c *Config) SetDisplayRotation(rotation string) error {
_, err := strconv.ParseUint(rotation, 10, 16)
if err != nil {
logger.Warn().Err(err).Msg("invalid display rotation, using default")
return err
}
c.DisplayRotation = rotation
return nil
}
const configPath = "/userdata/kvm_config.json"
var defaultConfig = &Config{

View File

@ -153,13 +153,6 @@ func waitCtrlAndRequestDisplayUpdate(shouldWakeDisplay bool) {
func updateStaticContents() {
//contents that never change
nativeInstance.UpdateLabelIfChanged("home_info_mac_addr", networkState.MACString())
systemVersion, appVersion, err := GetLocalVersion()
if err == nil {
nativeInstance.UpdateLabelIfChanged("boot_screen_version", systemVersion.String())
nativeInstance.UpdateLabelIfChanged("boot_screen_app_version", appVersion.String())
nativeInstance.UpdateLabelAndChangeVisibility("system_version", systemVersion.String())
nativeInstance.UpdateLabelAndChangeVisibility("app_version", appVersion.String())
}
// get cpu info
cpuInfo, err := os.ReadFile("/proc/cpuinfo")
@ -266,34 +259,6 @@ func wakeDisplay(force bool) {
backlightState = 0
}
// watchTsEvents monitors the touchscreen for events and simply calls wakeDisplay() to ensure the
// touchscreen interface still works even with LCD dimming/off.
// TODO: This is quite a hack, really we should be getting an event from jetkvm_native, or the whole display backlight
// control should be hoisted up to jetkvm_native.
func watchTsEvents() {
ts, err := os.OpenFile(touchscreenDevice, os.O_RDONLY, 0666)
if err != nil {
displayLogger.Warn().Err(err).Msg("failed to open touchscreen device")
return
}
defer ts.Close()
// This buffer is set to 24 bytes as that's the normal size of events on /dev/input
// Reference: https://www.kernel.org/doc/Documentation/input/input.txt
// This could potentially be set higher, to require multiple events to wake the display.
buf := make([]byte, 24)
for {
_, err := ts.Read(buf)
if err != nil {
displayLogger.Warn().Err(err).Msg("failed to read from touchscreen device")
return
}
wakeDisplay(false)
}
}
// startBacklightTickers starts the two tickers for dimming and switching off the display
// if they're not already set. This is done separately to the init routine as the "never dim"
// option has the value set to zero, but time.NewTicker only accept positive values.
@ -347,7 +312,6 @@ func initDisplay() {
go func() {
displayLogger.Info().Msg("setting initial display contents")
time.Sleep(500 * time.Millisecond)
_, _ = nativeInstance.DisplaySetRotation(config.DisplayRotation)
updateStaticContents()
displayInited = true
displayLogger.Info().Msg("display inited")
@ -355,6 +319,4 @@ func initDisplay() {
wakeDisplay(true)
requestDisplayUpdate(true)
}()
go watchTsEvents()
}

View File

@ -1,2 +1,3 @@
build
deps
ui_index.c

File diff suppressed because it is too large Load Diff

View File

@ -1,53 +1,43 @@
cmake_minimum_required(VERSION 3.14)
include(FetchContent)
include(ExternalProject)
project(jknative LANGUAGES C CXX)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Specify path to own LVGL config header
set(
LV_CONF_PATH
${CMAKE_CURRENT_SOURCE_DIR}/lv_conf.h
CACHE STRING "" FORCE
)
set(
LV_DRIVERS_PUBLIC_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/lv_drv_conf.h
CACHE STRING "" FORCE
)
# Rockchip SDK paths
set(RK_SDK_BASE "/opt/jetkvm-native-buildkit")
set(RK_MEDIA_OUTPUT "${RK_SDK_BASE}/media/out")
set(RK_MEDIA_INCLUDE_PATH "${RK_MEDIA_OUTPUT}/include")
set(RK_APP_MEDIA_LIBS_PATH "${RK_MEDIA_OUTPUT}/lib")
set(LV_BUILD_EXAMPLES 0 CACHE BOOL "" FORCE)
set(LV_USE_KCONFIG ON CACHE BOOL "" FORCE)
set(LV_BUILD_DEFCONFIG_PATH ${CMAKE_CURRENT_SOURCE_DIR}/lvgl_defconfig CACHE PATH "" FORCE)
# # libgpiod
# ExternalProject_Add(libgpiod-project
# URL https://mirrors.edge.kernel.org/pub/software/libs/libgpiod/libgpiod-2.2.tar.gz
# URL_HASH SHA256=f89c2176250f1a9563265479eb8ad5f22a63f42db6a1f438effc570f0254d2f5
# SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/libgpiod
# BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/deps/libgpiod
# CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env CPPFLAGS=-fPIC ${CMAKE_CURRENT_SOURCE_DIR}/deps/libgpiod/configure --enable-tools=no CC=${CMAKE_C_COMPILER} --host=${CMAKE_HOST_SYSTEM_PROCESSOR}
# BUILD_COMMAND make && make install
# BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/deps/libgpiod/lib/libgpiod.a
# )
# Fetch LVGL from GitHub
FetchContent_Declare(
lvgl
GIT_REPOSITORY https://github.com/lvgl/lvgl.git
GIT_TAG v8.3.11
GIT_TAG v9.3.0
GIT_SHALLOW 1
UPDATE_DISCONNECTED 1
PATCH_COMMAND
git apply ${CMAKE_CURRENT_SOURCE_DIR}/lvgl-no-examples.patch
COMMAND rm -rf demos examples
)
)
FetchContent_MakeAvailable(lvgl)
# Fetch LVGL drivers from GitHub
FetchContent_Declare(
lv_drivers
GIT_REPOSITORY https://github.com/lvgl/lv_drivers.git
GIT_TAG v8.3.0
UPDATE_DISCONNECTED 1
)
FetchContent_MakeAvailable(lv_drivers)
# Get source files, excluding CMake generated files
file(GLOB_RECURSE sources CONFIGURE_DEPENDS "*.c" "ui/*.c")
list(FILTER sources EXCLUDE REGEX "CMakeFiles.*CompilerId.*\\.c$")
@ -60,18 +50,21 @@ target_include_directories(jknative PRIVATE
${RK_MEDIA_INCLUDE_PATH}/libdrm
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/ui
${CMAKE_CURRENT_BINARY_DIR}/deps/libgpiod/include
)
# Set library search path
target_link_directories(jknative PRIVATE ${RK_APP_MEDIA_LIBS_PATH})
# target_link_directories(jknative PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/deps/libgpiod/lib)
target_link_libraries(jknative PRIVATE
lvgl::lvgl
lvgl::drivers
pthread
rockit
rockchip_mpp
rga
m
)
# libgpiod
)
install(TARGETS jknative DESTINATION lib)

55746
internal/native/cgo/Makefile Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,53 @@
#!/bin/bash
set -e
C_RST="$(tput sgr0)"
C_ERR="$(tput setaf 1)"
C_OK="$(tput setaf 2)"
C_WARN="$(tput setaf 3)"
C_INFO="$(tput setaf 5)"
msg() { printf '%s%s%s\n' $2 "$1" $C_RST; }
msg_info() { msg "$1" $C_INFO; }
msg_ok() { msg "$1" $C_OK; }
msg_err() { msg "$1" $C_ERR; }
msg_warn() { msg "$1" $C_WARN; }
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
BUILD_DIR=${SCRIPT_DIR}/build
CMAKE_TOOLCHAIN_FILE=/opt/jetkvm-native-buildkit/rv1106-jetkvm-v2.cmake
CLEAN_ALL=${CLEAN_ALL:-0}
if [ "$CLEAN_ALL" -eq 1 ]; then
find . -name build -exec rm -r {} +
rm -rf "${BUILD_DIR}"
fi
set -x
VERBOSE=1 cmake -B build \
TMP_DIR=$(mktemp -d)
pushd "${SCRIPT_DIR}" > /dev/null
msg_info "▶ Generating UI index"
./ui_index.gen.sh
msg_info "▶ Building native library"
VERBOSE=1 cmake -B "${BUILD_DIR}" \
-DCMAKE_SYSTEM_PROCESSOR=armv7l \
-DCMAKE_SYSTEM_NAME=Linux \
-DCMAKE_CROSSCOMPILING=1 \
-DCMAKE_TOOLCHAIN_FILE=$CMAKE_TOOLCHAIN_FILE \
-DLV_BUILD_USE_KCONFIG=ON \
-DLV_BUILD_DEFCONFIG_PATH=${SCRIPT_DIR}/lvgl_defconfig \
-DCONFIG_LV_BUILD_EXAMPLES=OFF \
-DCONFIG_LV_BUILD_DEMOS=OFF \
-DSKIP_GLIBC_NAMES=ON \
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="${TMP_DIR}"
cmake --build build
msg_info "▶ Copying built library and header files"
cmake --build "${BUILD_DIR}" --target install
cp -r "${TMP_DIR}/include" ../
cp -r "${TMP_DIR}/lib" ../
rm -rf "${TMP_DIR}"
popd > /dev/null

View File

@ -30,6 +30,15 @@ void jetkvm_set_video_handler(jetkvm_video_handler_t *handler) {
video_handler = handler;
}
void jetkvm_set_indev_handler(jetkvm_indev_handler_t *handler) {
lvgl_set_indev_handler(handler);
}
const char *jetkvm_ui_event_code_to_name(int code) {
lv_event_code_t cCode = (lv_event_code_t)code;
return lv_event_code_get_name(code);
}
void video_report_format(bool ready, const char *error, u_int16_t width, u_int16_t height, double frame_per_second)
{
state.ready = ready;
@ -174,8 +183,8 @@ const char *jetkvm_ui_get_var(const char *name) {
return NULL;
}
void jetkvm_ui_init() {
lvgl_init();
void jetkvm_ui_init(u_int16_t rotation) {
lvgl_init(rotation);
}
void jetkvm_ui_tick() {
@ -186,19 +195,9 @@ void jetkvm_set_video_state_handler(jetkvm_video_state_handler_t *handler) {
video_state_handler = handler;
}
void jetkvm_ui_set_rotation(u_int8_t rotation)
void jetkvm_ui_set_rotation(u_int16_t rotation)
{
printf("setting rotation to %d\n", rotation);
if (rotation == 90)
{
lv_disp_set_rotation(NULL, LV_DISP_ROT_90);
} else if (rotation == 180) {
lv_disp_set_rotation(NULL, LV_DISP_ROT_180);
} else if (rotation == 270) {
lv_disp_set_rotation(NULL, LV_DISP_ROT_270);
} else {
lv_disp_set_rotation(NULL, LV_DISP_ROT_NONE);
}
lvgl_set_rotation(NULL, rotation);
}
const char *jetkvm_ui_get_current_screen() {

View File

@ -16,21 +16,22 @@ typedef struct
typedef void (jetkvm_video_state_handler_t)(jetkvm_video_state_t *state);
typedef void (jetkvm_log_handler_t)(int level, const char *filename, const char *funcname, int line, const char *message);
typedef void (jetkvm_video_handler_t)(const uint8_t *frame, ssize_t len);
typedef void (jetkvm_indev_handler_t)(int code);
void jetkvm_set_log_handler(jetkvm_log_handler_t *handler);
void jetkvm_set_video_handler(jetkvm_video_handler_t *handler);
void jetkvm_set_indev_handler(jetkvm_indev_handler_t *handler);
void jetkvm_set_video_state_handler(jetkvm_video_state_handler_t *handler);
void jetkvm_ui_set_var(const char *name, const char *value);
const char *jetkvm_ui_get_var(const char *name);
void jetkvm_ui_init();
void jetkvm_ui_init(u_int16_t rotation);
void jetkvm_ui_tick();
void jetkvm_set_video_state_handler(jetkvm_video_state_handler_t *handler);
void jetkvm_ui_set_rotation(u_int8_t rotation);
void jetkvm_ui_set_rotation(u_int16_t rotation);
const char *jetkvm_ui_get_current_screen();
void jetkvm_ui_load_screen(const char *obj_name);
int jetkvm_ui_set_text(const char *obj_name, const char *text);
@ -42,6 +43,8 @@ void jetkvm_ui_set_opacity(const char *obj_name, u_int8_t opacity);
int jetkvm_ui_add_flag(const char *obj_name, const char *flag_name);
int jetkvm_ui_clear_flag(const char *obj_name, const char *flag_name);
const char *jetkvm_ui_event_code_to_name(int code);
int jetkvm_video_init();
void jetkvm_video_shutdown();
void jetkvm_video_start();
@ -55,4 +58,6 @@ jetkvm_video_state_t *jetkvm_video_get_status();
void video_report_format(bool ready, const char *error, u_int16_t width, u_int16_t height, double frame_per_second);
int video_send_frame(const uint8_t *frame, ssize_t len);
#endif //VIDEO_DAEMON_CTRL_H

View File

@ -1,608 +0,0 @@
/**
* @file lv_conf.h
* Configuration file for v8.1.0
*/
/*
* Copy this file as `lv_conf.h`
* 1. simply next to the `lvgl` folder
* 2. or any other places and
* - define `LV_CONF_INCLUDE_SIMPLE`
* - add the path as include path
*/
/* clang-format off */
#if 1 /*Set it to "1" to enable content*/
#ifndef LV_CONF_H
#define LV_CONF_H
#include <stdint.h>
/*====================
COLOR SETTINGS
*====================*/
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#define LV_COLOR_DEPTH 16
/*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/
#define LV_COLOR_16_SWAP 0
/*Enable more complex drawing routines to manage screens transparency.
*Can be used if the UI is above another layer, e.g. an OSD menu or video player.
*Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to non LV_OPA_COVER value*/
#define LV_COLOR_SCREEN_TRANSP 0
/* 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 (LV_COLOR_DEPTH == 32 ? 0: 128)
/*Images pixels with this color will not be drawn if they are chroma keyed)*/
#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00) /*pure green*/
/*=========================
MEMORY SETTINGS
*=========================*/
/*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/
#define LV_MEM_CUSTOM 1
#if LV_MEM_CUSTOM == 0
/*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/
# define LV_MEM_SIZE (32U * 1024U) /*[bytes]*/
/*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
//#define LV_MEM_POOL_INCLUDE your_alloc_library /* Uncomment if using an external allocator*/
//#define LV_MEM_POOL_ALLOC your_alloc /* Uncomment if using an external allocator*/
#endif
#else /*LV_MEM_CUSTOM*/
# define LV_MEM_CUSTOM_INCLUDE <stdlib.h> /*Header for the dynamic memory function*/
# define LV_MEM_CUSTOM_ALLOC malloc
# define LV_MEM_CUSTOM_FREE free
# define LV_MEM_CUSTOM_REALLOC realloc
#endif /*LV_MEM_CUSTOM*/
/*Number of the intermediate memory buffer used during rendering and other internal processing mechanisms.
*You will see an error log message if there wasn't enough buffers. */
#define LV_MEM_BUF_MAX_NUM 16
/*Use the standard `memcpy` and `memset` instead of LVGL's own functions. (Might or might not be faster).*/
#define LV_MEMCPY_MEMSET_STD 0
/*====================
HAL SETTINGS
*====================*/
/*Default display refresh period. LVG will redraw changed areas with this period time*/
#define LV_DISP_DEF_REFR_PERIOD 10 /*[ms]*/
/*Input device read period in milliseconds*/
#define LV_INDEV_DEF_READ_PERIOD 10 /*[ms]*/
/*Use a custom tick source that tells the elapsed time in milliseconds.
*It removes the need to manually update the tick with `lv_tick_inc()`)*/
// #define LV_TICK_CUSTOM 0
// #if LV_TICK_CUSTOM
// #define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/
// #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/
// #endif /*LV_TICK_CUSTOM*/
#define LV_TICK_CUSTOM 1
#if LV_TICK_CUSTOM
#define LV_TICK_CUSTOM_INCLUDE <stdint.h> /*Header for the system time function*/
#define LV_TICK_CUSTOM_SYS_TIME_EXPR (custom_tick_get()) /*Expression evaluating to current system time in ms*/
#endif /*LV_TICK_CUSTOM*/
/*Default Dot 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]*/
/*=======================
* FEATURE CONFIGURATION
*=======================*/
/*-------------
* Drawing
*-----------*/
/*Enable complex draw engine.
*Required to draw shadow, gradient, rounded corners, circles, arc, skew lines, image transformations or any masks*/
#define LV_DRAW_COMPLEX 1
#if LV_DRAW_COMPLEX != 0
/*Allow buffering some shadow calculation.
*LV_SHADOW_CACHE_SIZE is the max. shadow size to buffer, where shadow size is `shadow_width + radius`
*Caching has LV_SHADOW_CACHE_SIZE^2 RAM cost*/
#define LV_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: to disable caching */
#define LV_CIRCLE_CACHE_SIZE 4
#endif /*LV_DRAW_COMPLEX*/
/*Default image cache size. Image caching keeps the images opened.
*If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added)
*With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images.
*However the opened images might consume additional RAM.
*0: to disable caching*/
#define LV_IMG_CACHE_DEF_SIZE 0
/*Maximum buffer size to allocate for rotation. Only used if software rotation is enabled in the display driver.*/
#define LV_DISP_ROT_MAX_BUF (10*1024)
/*-------------
* GPU
*-----------*/
/*Use STM32's DMA2D (aka Chrom Art) GPU*/
#define LV_USE_GPU_STM32_DMA2D 0
#if LV_USE_GPU_STM32_DMA2D
/*Must be defined to include path of CMSIS header of target processor
e.g. "stm32f769xx.h" or "stm32f429xx.h"*/
#define LV_GPU_DMA2D_CMSIS_INCLUDE
#endif
/*Use NXP's PXP GPU iMX RTxxx platforms*/
#define LV_USE_GPU_NXP_PXP 0
#if LV_USE_GPU_NXP_PXP
/*1: Add default bare metal and FreeRTOS interrupt handling routines for PXP (lv_gpu_nxp_pxp_osa.c)
* and call lv_gpu_nxp_pxp_init() automatically during lv_init(). Note that symbol SDK_OS_FREE_RTOS
* has to be defined in order to use FreeRTOS OSA, otherwise bare-metal implementation is selected.
*0: lv_gpu_nxp_pxp_init() has to be called manually before lv_init()
*/
#define LV_USE_GPU_NXP_PXP_AUTO_INIT 0
#endif
/*Use NXP's VG-Lite GPU iMX RTxxx platforms*/
#define LV_USE_GPU_NXP_VG_LITE 0
/*Use exnternal renderer*/
#define LV_USE_EXTERNAL_RENDERER 0
/*Use SDL renderer API. Requires LV_USE_EXTERNAL_RENDERER*/
#define LV_USE_GPU_SDL 0
#if LV_USE_GPU_SDL
# define LV_GPU_SDL_INCLUDE_PATH <SDL2/SDL.h>
#endif
/*-------------
* Logging
*-----------*/
/*Enable the log module*/
#define LV_USE_LOG 0
#if LV_USE_LOG
/*How important log should be added:
*LV_LOG_LEVEL_TRACE A lot of logs to give 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 Only critical issue, when the system may fail
*LV_LOG_LEVEL_USER Only logs added by the user
*LV_LOG_LEVEL_NONE Do not log anything*/
# define LV_LOG_LEVEL LV_LOG_LEVEL_WARN
/*1: Print the log with 'printf';
*0: User need to register a callback with `lv_log_register_print_cb()`*/
# define LV_LOG_PRINTF 0
/*Enable/disable LV_LOG_TRACE in modules that produces a huge number of logs*/
# define LV_LOG_TRACE_MEM 1
# define LV_LOG_TRACE_TIMER 1
# define LV_LOG_TRACE_INDEV 1
# define LV_LOG_TRACE_DISP_REFR 1
# define LV_LOG_TRACE_EVENT 1
# define LV_LOG_TRACE_OBJ_CREATE 1
# define LV_LOG_TRACE_LAYOUT 1
# define LV_LOG_TRACE_ANIM 1
#endif /*LV_USE_LOG*/
/*-------------
* Asserts
*-----------*/
/*Enable asserts if an operation is failed or an 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 the MCU*/
#define LV_ASSERT_HANDLER_INCLUDE <stdint.h>
#define LV_ASSERT_HANDLER while(1); /*Halt by default*/
/*-------------
* Others
*-----------*/
/*1: Show CPU usage and FPS count in the right bottom corner*/
#define LV_USE_PERF_MONITOR 0
#if LV_USE_PERF_MONITOR
#define LV_USE_PERF_MONITOR_POS LV_ALIGN_BOTTOM_RIGHT
#endif
/*1: Show the used memory and the memory fragmentation in the left bottom corner
* Requires LV_MEM_CUSTOM = 0*/
#define LV_USE_MEM_MONITOR 0
#if LV_USE_PERF_MONITOR
#define LV_USE_MEM_MONITOR_POS LV_ALIGN_BOTTOM_LEFT
#endif
/*1: Draw random colored rectangles over the redrawn areas*/
#define LV_USE_REFR_DEBUG 0
/*Change the built in (v)snprintf functions*/
#define LV_SPRINTF_CUSTOM 0
#if LV_SPRINTF_CUSTOM
# define LV_SPRINTF_INCLUDE <stdio.h>
# define lv_snprintf snprintf
# define lv_vsnprintf vsnprintf
#else /*LV_SPRINTF_CUSTOM*/
# define LV_SPRINTF_USE_FLOAT 0
#endif /*LV_SPRINTF_CUSTOM*/
#define LV_USE_USER_DATA 1
/*Garbage Collector settings
*Used if lvgl is bound to higher level language and the memory is managed by that language*/
#define LV_ENABLE_GC 0
#if LV_ENABLE_GC != 0
# define LV_GC_INCLUDE "gc.h" /*Include Garbage Collector related things*/
#endif /*LV_ENABLE_GC*/
/*=====================
* COMPILER SETTINGS
*====================*/
/*For big endian systems set to 1*/
#define LV_BIG_ENDIAN_SYSTEM 0
/*Define a custom attribute to `lv_tick_inc` function*/
#define LV_ATTRIBUTE_TICK_INC
/*Define a custom attribute to `lv_timer_handler` function*/
#define LV_ATTRIBUTE_TIMER_HANDLER
/*Define a custom attribute to `lv_disp_flush_ready` function*/
#define LV_ATTRIBUTE_FLUSH_READY
/*Required alignment size for buffers*/
#define LV_ATTRIBUTE_MEM_ALIGN_SIZE 1
/*Will be added where memories 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 font's bitmaps*/
#define LV_ATTRIBUTE_LARGE_CONST
/*Complier prefix for a big 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
/*Prefix variables that are used in GPU accelerated operations, often these need to be placed in RAM sections that are DMA accessible*/
#define LV_ATTRIBUTE_DMA
/*Export integer constant to binding. This macro is used with constants in the form of LV_<CONST> 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*/
/*Extend the default -32k..32k coordinate range to -4M..4M by using int32_t for coordinates instead of int16_t*/
#define LV_USE_LARGE_COORD 0
/*==================
* 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 0
#define LV_FONT_MONTSERRAT_18 0
#define LV_FONT_MONTSERRAT_20 0
#define LV_FONT_MONTSERRAT_22 0
#define LV_FONT_MONTSERRAT_24 0
#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_12_SUBPX 0
#define LV_FONT_MONTSERRAT_28_COMPRESSED 0 /*bpp = 3*/
#define LV_FONT_DEJAVU_16_PERSIAN_HEBREW 0 /*Hebrew, Arabic, Perisan letters and all their forms*/
#define LV_FONT_SIMSUN_16_CJK 0 /*1000 most common CJK radicals*/
/*Pixel perfect monospace fonts*/
#define LV_FONT_UNSCII_8 0
#define LV_FONT_UNSCII_16 0
/*Optionally declare custom fonts here.
*You can use these fonts as default font too and they will be available globally.
*E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) LV_FONT_DECLARE(my_font_2)*/
#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.
*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 subpixel rendering*/
#define LV_USE_FONT_SUBPX 0
#if LV_USE_FONT_SUBPX
/*Set the pixel order of the display. Physical order of RGB channels. Doesn't matter with "normal" fonts.*/
#define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/
#endif
/*=================
* 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
/*Can break (wrap) texts 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
/*The control character to use for signalling text recoloring.*/
#define LV_TXT_COLOR_CMD "#"
/*Support bidirectional texts. Allows mixing Left-to-Right and Right-to-Left texts.
*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 texts 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 an other form based on their position in the text*/
#define LV_USE_ARABIC_PERSIAN_CHARS 0
/*==================
* WIDGET USAGE
*================*/
/*Documentation of the widgets: https://docs.lvgl.io/latest/en/html/widgets/index.html*/
#define LV_USE_ARC 1
#define LV_USE_ANIMIMG 1
#define LV_USE_BAR 1
#define LV_USE_BTN 1
#define LV_USE_BTNMATRIX 1
#define LV_USE_CANVAS 1
#define LV_USE_CHECKBOX 1
#define LV_USE_DROPDOWN 0 /*Requires: lv_label*/
#define LV_USE_IMG 1 /*Requires: lv_label*/
#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 texts*/
#endif
#define LV_USE_LINE 1
#define LV_USE_ROLLER 0 /*Requires: lv_label*/
#if LV_USE_ROLLER
# define LV_ROLLER_INF_PAGES 7 /*Number of extra "pages" when the roller is infinite*/
#endif
#define LV_USE_SLIDER 1 /*Requires: lv_bar*/
#define LV_USE_SWITCH 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_TABLE 0
/*==================
* EXTRA COMPONENTS
*==================*/
/*-----------
* Widgets
*----------*/
#define LV_USE_CALENDAR 0
#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
#endif /*LV_USE_CALENDAR*/
#define LV_USE_CHART 0
#define LV_USE_COLORWHEEL 0
#define LV_USE_IMGBTN 1
#define LV_USE_KEYBOARD 1
#define LV_USE_LED 0
#define LV_USE_LIST 1
#define LV_USE_METER 0
#define LV_USE_MSGBOX 0
#define LV_USE_SPINBOX 0
#define LV_USE_SPINNER 0
#define LV_USE_TABVIEW 0
#define LV_USE_TILEVIEW 0
#define LV_USE_WIN 0
#define LV_USE_SPAN 1
#if LV_USE_SPAN
/*A line text can contain maximum num of span descriptor */
# define LV_SPAN_SNIPPET_STACK_SIZE 64
#endif
/*-----------
* 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_BASIC 1
/*A theme designed for monochrome displays*/
#define LV_USE_THEME_MONO 0
/*-----------
* Layouts
*----------*/
/*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 party libraries
*--------------------*/
/*File system interfaces for common APIs
*To enable set a driver letter for that API*/
#define LV_USE_FS_STDIO '\0' /*Uses fopen, fread, etc*/
//#define LV_FS_STDIO_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */
#define LV_USE_FS_POSIX '\0' /*Uses open, read, etc*/
//#define LV_FS_POSIX_PATH "/home/john/" /*Set the working directory. If commented it will be "./" */
#define LV_USE_FS_WIN32 '\0' /*Uses CreateFile, ReadFile, etc*/
//#define LV_FS_WIN32_PATH "C:\\Users\\john\\" /*Set the working directory. If commented it will be ".\\" */
#define LV_USE_FS_FATFS '\0' /*Uses f_open, f_read, etc*/
/*PNG decoder library*/
#define LV_USE_PNG 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_SJPG 0
/*GIF decoder library*/
#define LV_USE_GIF 0
/*QR code library*/
#define LV_USE_QRCODE 0
/*FreeType library*/
#define LV_USE_FREETYPE 0
#if LV_USE_FREETYPE
/*Memory used by FreeType to cache characters [bytes] (-1: no caching)*/
# define LV_FREETYPE_CACHE_SIZE (16 * 1024)
#endif
/*Rlottie library*/
#define LV_USE_RLOTTIE 0
/*-----------
* Others
*----------*/
/*1: Enable API to take snapshot for object*/
#define LV_USE_SNAPSHOT 1
/*==================
* EXAMPLES
*==================*/
/*Enable the examples to be built with the library*/
#define LV_BUILD_EXAMPLES 0
/*--END OF LV_CONF_H--*/
#endif /*LV_CONF_H*/
#endif /*End of "Content enable"*/

View File

@ -0,0 +1,23 @@
CONFIG_LV_OS_PTHREAD=y
CONFIG_LV_USE_OBJ_ID=y
CONFIG_LV_USE_OBJ_NAME=y
CONFIG_LV_USE_OBJ_ID_BUILTIN=y
CONFIG_LV_USE_OBJ_PROPERTY=y
CONFIG_LV_USE_OBJ_PROPERTY_NAME=y
CONFIG_LV_USE_PRIVATE_API=y
# CONFIG_LV_USE_CALENDAR is not set
# CONFIG_LV_USE_CHART is not set
# CONFIG_LV_USE_CHECKBOX is not set
# CONFIG_LV_USE_MSGBOX is not set
# CONFIG_LV_USE_ROLLER is not set
# CONFIG_LV_USE_SCALE is not set
# CONFIG_LV_USE_SLIDER is not set
# CONFIG_LV_USE_TABLE is not set
# CONFIG_LV_USE_TABVIEW is not set
# CONFIG_LV_USE_TILEVIEW is not set
CONFIG_LV_USE_QRCODE=y
CONFIG_LV_USE_LINUX_FBDEV=y
CONFIG_LV_USE_EVDEV=y
CONFIG_LV_USE_ST7789=y
CONFIG_LV_BUILD_EXAMPLES=n
CONFIG_LV_BUILD_DEMOS=n

View File

@ -6,45 +6,74 @@
#include "log.h"
#include "screen.h"
#include <lvgl.h>
#include <display/fbdev.h>
#include <indev/evdev.h>
// #include "st7789/lcd.h"
#include "ui/ui.h"
#include "ui_index.h"
#define DISP_BUF_SIZE (300 * 240 * 2)
static lv_color_t buf[DISP_BUF_SIZE];
static lv_disp_draw_buf_t disp_buf;
static lv_disp_drv_t disp_drv;
static lv_indev_drv_t indev_drv;
void lvgl_init(void) {
indev_handler_t *indev_handler = NULL;
void lvgl_set_indev_handler(indev_handler_t *handler) {
indev_handler = handler;
}
void handle_indev_event(lv_event_t *e) {
if (indev_handler == NULL) {
return;
}
indev_handler(lv_event_get_code(e));
}
void lvgl_init(u_int16_t rotation) {
log_trace("initalizing lvgl");
/*LittlevGL init*/
lv_init();
log_trace("initalizing fbdev");
fbdev_init();
lv_disp_draw_buf_init(&disp_buf, buf, NULL, DISP_BUF_SIZE);
lv_disp_drv_init(&disp_drv);
disp_drv.draw_buf = &disp_buf;
disp_drv.flush_cb = fbdev_flush;
disp_drv.hor_res = 240;
disp_drv.ver_res = 300;
disp_drv.rotated = LV_DISP_ROT_270;
disp_drv.sw_rotate = true;
// disp_drv.full_refresh = true;
/*Linux frame buffer device init*/
lv_disp_drv_register(&disp_drv);
/*Linux frame buffer device init*/
lv_display_t *disp = lv_linux_fbdev_create();
// lv_display_set_physical_resolution(disp, 240, 300);
lv_display_set_resolution(disp, 240, 300);
lv_linux_fbdev_set_file(disp, "/dev/fb0");
log_trace("initalizing evdev");
evdev_init();
evdev_set_file("/dev/input/event1");
lvgl_set_rotation(disp, rotation);
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = evdev_read;
lv_indev_drv_register(&indev_drv);
// lv_display_t *disp = lv_st7789_create(LCD_H_RES, LCD_V_RES, LV_LCD_FLAG_NONE, lcd_send_cmd, lcd_send_color);
// lv_display_set_resolution(disp, 240, 300);
// lv_display_set_rotation(disp, LV_DISP_ROTATION_270);
// lv_color_t * buf1 = NULL;
// lv_color_t * buf2 = NULL;
// uint32_t buf_size = LCD_H_RES * LCD_V_RES / 10 * lv_color_format_get_size(lv_display_get_color_format(disp));
// buf1 = lv_malloc(buf_size);
// if(buf1 == NULL) {
// log_error("display draw buffer malloc failed");
// return;
// }
// buf2 = lv_malloc(buf_size);
// if(buf2 == NULL) {
// log_error("display buffer malloc failed");
// lv_free(buf1);
// return;
// }
// lv_display_set_buffers(disp, buf1, buf2, buf_size, LV_DISPLAY_RENDER_MODE_PARTIAL);
/* Linux input device init */
lv_indev_t *mouse = lv_evdev_create(LV_INDEV_TYPE_POINTER, "/dev/input/event1");
lv_indev_set_group(mouse, lv_group_get_default());
lv_indev_set_display(mouse, disp);
lv_indev_add_event_cb(mouse, handle_indev_event, LV_EVENT_ALL, NULL);
log_trace("initalizing ui");
ui_init();
log_info("ui initalized");
@ -58,6 +87,45 @@ void lvgl_tick(void) {
ui_tick();
}
void lvgl_set_rotation(lv_display_t *disp, u_int16_t rotation) {
log_info("setting rotation to %d", rotation);
if (rotation == 0) {
lv_display_set_rotation(disp, LV_DISP_ROTATION_0);
} else if (rotation == 90) {
lv_display_set_rotation(disp, LV_DISP_ROTATION_90);
} else if (rotation == 180) {
lv_display_set_rotation(disp, LV_DISP_ROTATION_180);
} else if (rotation == 270) {
lv_display_set_rotation(disp, LV_DISP_ROTATION_270);
} else {
log_error("invalid rotation %d", rotation);
}
lv_style_t *flex_screen_style = ui_get_style("flex_screen");
if (flex_screen_style == NULL) {
log_error("flex_screen style not found");
return;
}
lv_style_t *flex_screen_menu_style = ui_get_style("flex_screen_menu");
if (flex_screen_menu_style == NULL) {
log_error("flex_screen_menu style not found");
return;
}
if (rotation == 90) {
lv_style_set_pad_left(flex_screen_style, 24);
lv_style_set_pad_right(flex_screen_style, 44);
} else if (rotation == 270) {
lv_style_set_pad_left(flex_screen_style, 44);
lv_style_set_pad_right(flex_screen_style, 24);
}
log_info("refreshing objects");
lv_obj_report_style_change(&flex_screen_style);
lv_obj_report_style_change(&flex_screen_menu_style);
}
uint32_t custom_tick_get(void)
{
static uint64_t start_ms = 0;
@ -85,6 +153,16 @@ lv_obj_t *ui_get_obj(const char *name) {
return NULL;
}
lv_style_t *ui_get_style(const char *name) {
for (size_t i = 0; i < ui_styles_size; i++) {
if (strcmp(ui_styles[i].name, name) == 0) {
return ui_styles[i].getter();
}
}
return NULL;
}
const char *ui_get_current_screen() {
lv_obj_t *scr = lv_scr_act();
if (scr == NULL) {

View File

@ -3,11 +3,19 @@
#include <lvgl.h>
void lvgl_init(void);
typedef void (indev_handler_t)(lv_event_code_t code);
void lvgl_set_indev_handler(indev_handler_t *handler);
void lvgl_init(u_int16_t rotation);
void lvgl_tick(void);
void lvgl_set_rotation(lv_display_t *disp, u_int16_t rotation);
void ui_set_text(const char *name, const char *text);
lv_obj_t *ui_get_obj(const char *name);
lv_style_t *ui_get_style(const char *name);
lv_img_dsc_t *ui_get_image(const char *name);
#endif // SCREEN_H

View File

@ -12,6 +12,14 @@ done)
const int ui_objects_size = sizeof(ui_objects) / sizeof(ui_objects[0]);
ui_style_map ui_styles[] = {
$(grep 'lv_style_t \*get_style_' ui/styles.h | sed 's/lv_style_t \*get_style_//g' | sed 's/_MAIN_DEFAULT();//g' | sed 's/\r//' | while read -r line; do
echo " {\"$line\", &get_style_${line}_MAIN_DEFAULT},"
done)
};
const int ui_styles_size = sizeof(ui_styles) / sizeof(ui_styles[0]);
ui_img_map ui_images[] = {
$(grep "extern const lv_img_dsc_t " ui/images.h | sed 's/extern const lv_img_dsc_t //g' | sed 's/;//g' | while read -r line; do
echo " {\"$line\", &$line},"

View File

@ -3,6 +3,7 @@
#include "ui/ui.h"
#include "ui/screens.h"
#include "ui/styles.h"
#include "ui/images.h"
#include "ui/vars.h"
@ -14,6 +15,14 @@ typedef struct {
extern ui_obj_map ui_objects[];
extern const int ui_objects_size;
typedef struct {
const char *name;
lv_style_t *(*getter)();
} ui_style_map;
extern ui_style_map ui_styles[];
extern const int ui_styles_size;
typedef struct {
const char *name;
const lv_img_dsc_t *img; // Pointer to the image descriptor const

View File

@ -0,0 +1,276 @@
//go:build linux
package native
import (
"fmt"
"unsafe"
"github.com/rs/zerolog"
)
/*
#cgo LDFLAGS: -Llib -ljknative -llvgl
#cgo CFLAGS: -Iinclude
#include "ctrl.h"
#include <stdlib.h>
typedef const char cchar_t;
typedef const uint8_t cuint8_t;
extern void jetkvm_go_log_handler(int level, cchar_t *filename, cchar_t *funcname, int line, cchar_t *message);
static inline void jetkvm_cgo_setup_log_handler() {
jetkvm_set_log_handler(&jetkvm_go_log_handler);
}
extern void jetkvm_go_video_state_handler(jetkvm_video_state_t *state);
static inline void jetkvm_cgo_setup_video_state_handler() {
jetkvm_set_video_state_handler(&jetkvm_go_video_state_handler);
}
extern void jetkvm_go_video_handler(cuint8_t *frame, ssize_t len);
static inline void jetkvm_cgo_setup_video_handler() {
jetkvm_set_video_handler(&jetkvm_go_video_handler);
}
extern void jetkvm_go_indev_handler(int code);
static inline void jetkvm_cgo_setup_indev_handler() {
jetkvm_set_indev_handler(&jetkvm_go_indev_handler);
}
*/
import "C"
//export jetkvm_go_video_state_handler
func jetkvm_go_video_state_handler(state *C.jetkvm_video_state_t) {
videoState := VideoState{
Ready: bool(state.ready),
Error: C.GoString(state.error),
Width: int(state.width),
Height: int(state.height),
FramePerSecond: float64(state.frame_per_second),
}
videoStateChan <- videoState
}
//export jetkvm_go_log_handler
func jetkvm_go_log_handler(level C.int, filename *C.cchar_t, funcname *C.cchar_t, line C.int, message *C.cchar_t) {
logMessage := nativeLogMessage{
Level: zerolog.Level(level),
Message: C.GoString(message),
File: C.GoString(filename),
FuncName: C.GoString(funcname),
Line: int(line),
}
logChan <- logMessage
}
//export jetkvm_go_video_handler
func jetkvm_go_video_handler(frame *C.cuint8_t, len C.ssize_t) {
videoFrameChan <- C.GoBytes(unsafe.Pointer(frame), C.int(len))
}
//export jetkvm_go_indev_handler
func jetkvm_go_indev_handler(code C.int) {
indevEventChan <- int(code)
}
var eventCodeToNameMap = map[int]string{}
func uiEventCodeToName(code int) string {
name, ok := eventCodeToNameMap[code]
if !ok {
cCode := C.int(code)
cName := C.jetkvm_ui_event_code_to_name(cCode)
name = C.GoString(cName)
eventCodeToNameMap[code] = name
}
return name
}
func setUpNativeHandlers() {
C.jetkvm_cgo_setup_log_handler()
C.jetkvm_cgo_setup_video_state_handler()
C.jetkvm_cgo_setup_video_handler()
C.jetkvm_cgo_setup_indev_handler()
}
func uiInit(rotation uint16) {
cRotation := C.u_int16_t(rotation)
defer C.free(unsafe.Pointer(&cRotation))
C.jetkvm_ui_init(cRotation)
}
func uiTick() {
C.jetkvm_ui_tick()
}
func videoInit() error {
ret := C.jetkvm_video_init()
if ret != 0 {
return fmt.Errorf("failed to initialize video: %d", ret)
}
return nil
}
func videoShutdown() {
C.jetkvm_video_shutdown()
}
func videoStart() {
C.jetkvm_video_start()
}
func videoStop() {
C.jetkvm_video_stop()
}
func uiSetVar(name string, value string) {
nameCStr := C.CString(name)
defer C.free(unsafe.Pointer(nameCStr))
valueCStr := C.CString(value)
defer C.free(unsafe.Pointer(valueCStr))
C.jetkvm_ui_set_var(nameCStr, valueCStr)
}
func uiGetVar(name string) string {
nameCStr := C.CString(name)
defer C.free(unsafe.Pointer(nameCStr))
return C.GoString(C.jetkvm_ui_get_var(nameCStr))
}
func uiSwitchToScreen(screen string) {
screenCStr := C.CString(screen)
defer C.free(unsafe.Pointer(screenCStr))
C.jetkvm_ui_load_screen(screenCStr)
}
func uiGetCurrentScreen() string {
screenCStr := C.jetkvm_ui_get_current_screen()
return C.GoString(screenCStr)
}
func uiObjSetState(objName string, state string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
stateCStr := C.CString(state)
defer C.free(unsafe.Pointer(stateCStr))
C.jetkvm_ui_set_state(objNameCStr, stateCStr)
return true, nil
}
// TODO: use Enum instead of string but it's not a hot path and performance is not a concern now
func uiObjAddFlag(objName string, flag string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
flagCStr := C.CString(flag)
defer C.free(unsafe.Pointer(flagCStr))
C.jetkvm_ui_add_flag(objNameCStr, flagCStr)
return true, nil
}
func uiObjClearFlag(objName string, flag string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
flagCStr := C.CString(flag)
defer C.free(unsafe.Pointer(flagCStr))
C.jetkvm_ui_clear_flag(objNameCStr, flagCStr)
return true, nil
}
func uiObjHide(objName string) (bool, error) {
return uiObjAddFlag(objName, "LV_OBJ_FLAG_HIDDEN")
}
func uiObjShow(objName string) (bool, error) {
return uiObjClearFlag(objName, "LV_OBJ_FLAG_HIDDEN")
}
func uiObjSetOpacity(objName string, opacity int) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
C.jetkvm_ui_set_opacity(objNameCStr, C.u_int8_t(opacity))
return true, nil
}
func uiObjFadeIn(objName string, duration uint32) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
C.jetkvm_ui_fade_in(objNameCStr, C.u_int32_t(duration))
return true, nil
}
func uiObjFadeOut(objName string, duration uint32) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
C.jetkvm_ui_fade_out(objNameCStr, C.u_int32_t(duration))
return true, nil
}
func uiLabelSetText(objName string, text string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
textCStr := C.CString(text)
defer C.free(unsafe.Pointer(textCStr))
ret := C.jetkvm_ui_set_text(objNameCStr, textCStr)
if ret < 0 {
return false, fmt.Errorf("failed to set text: %d", ret)
}
return ret == 0, nil
}
func uiImgSetSrc(objName string, src string) (bool, error) {
objNameCStr := C.CString(objName)
defer C.free(unsafe.Pointer(objNameCStr))
srcCStr := C.CString(src)
defer C.free(unsafe.Pointer(srcCStr))
C.jetkvm_ui_set_image(objNameCStr, srcCStr)
return true, nil
}
func uiDispSetRotation(rotation uint16) (bool, error) {
nativeLogger.Info().Uint16("rotation", rotation).Msg("setting rotation")
cRotation := C.u_int16_t(rotation)
defer C.free(unsafe.Pointer(&cRotation))
C.jetkvm_ui_set_rotation(cRotation)
return true, nil
}
func videoGetStreamQualityFactor() (float64, error) {
factor := C.jetkvm_video_get_quality_factor()
return float64(factor), nil
}
func videoSetStreamQualityFactor(factor float64) error {
C.jetkvm_video_set_quality_factor(C.float(factor))
return nil
}
func videoGetEDID() (string, error) {
edidCStr := C.jetkvm_video_get_edid_hex()
return C.GoString(edidCStr), nil
}
func videoSetEDID(edid string) error {
edidCStr := C.CString(edid)
defer C.free(unsafe.Pointer(edidCStr))
C.jetkvm_video_set_edid(edidCStr)
return nil
}

View File

@ -0,0 +1,109 @@
//go:build !linux
package native
func panicPlatformNotSupported() {
panic("platform not supported")
}
func setUpNativeHandlers() {
panicPlatformNotSupported()
}
func uiSetVar(name string, value string) {
panicPlatformNotSupported()
}
func uiGetVar(name string) string {
panicPlatformNotSupported()
return ""
}
func uiSwitchToScreen(screen string) {
panicPlatformNotSupported()
}
func uiGetCurrentScreen() string {
panicPlatformNotSupported()
return ""
}
func uiObjSetState(objName string, state string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjAddFlag(objName string, flag string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjClearFlag(objName string, flag string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjHide(objName string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjShow(objName string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjSetOpacity(objName string, opacity int) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjFadeIn(objName string, duration uint32) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiObjFadeOut(objName string, duration uint32) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiLabelSetText(objName string, text string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiImgSetSrc(objName string, src string) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiDispSetRotation(rotation uint16) (bool, error) {
panicPlatformNotSupported()
return false, nil
}
func uiEventCodeToName(code int) string {
panicPlatformNotSupported()
return ""
}
func videoGetStreamQualityFactor() (float64, error) {
panicPlatformNotSupported()
return 0, nil
}
func videoSetStreamQualityFactor(factor float64) error {
panicPlatformNotSupported()
return nil
}
func videoGetEDID() (string, error) {
panicPlatformNotSupported()
return "", nil
}
func videoSetEDID(edid string) error {
panicPlatformNotSupported()
return nil
}

72
internal/native/chan.go Normal file
View File

@ -0,0 +1,72 @@
package native
import (
"time"
"github.com/rs/zerolog"
)
var (
videoFrameChan chan []byte = make(chan []byte)
videoStateChan chan VideoState = make(chan VideoState)
logChan chan nativeLogMessage = make(chan nativeLogMessage)
indevEventChan chan int = make(chan int)
)
func (n *Native) handleVideoFrameChan() {
lastFrame := time.Now()
for {
frame := <-videoFrameChan
now := time.Now()
sinceLastFrame := now.Sub(lastFrame)
lastFrame = now
n.onVideoFrameReceived(frame, sinceLastFrame)
}
}
func (n *Native) handleVideoStateChan() {
for {
state := <-videoStateChan
n.onVideoStateChange(state)
}
}
func (n *Native) handleLogChan() {
for {
entry := <-logChan
l := n.l.With().
Str("file", entry.File).
Str("func", entry.FuncName).
Int("line", entry.Line).
Logger()
switch entry.Level {
case zerolog.DebugLevel:
l.Debug().Msg(entry.Message)
case zerolog.InfoLevel:
l.Info().Msg(entry.Message)
case zerolog.WarnLevel:
l.Warn().Msg(entry.Message)
case zerolog.ErrorLevel:
l.Error().Msg(entry.Message)
case zerolog.PanicLevel:
l.Panic().Msg(entry.Message)
case zerolog.FatalLevel:
l.Fatal().Msg(entry.Message)
case zerolog.TraceLevel:
l.Trace().Msg(entry.Message)
case zerolog.NoLevel:
l.Info().Msg(entry.Message)
default:
l.Info().Msg(entry.Message)
}
}
}
func (n *Native) handleIndevEventChan() {
for {
event := <-indevEventChan
name := uiEventCodeToName(event)
n.onIndevEvent(name)
}
}

View File

@ -7,10 +7,11 @@ import (
func (n *Native) setUIVars() {
uiSetVar("app_version", n.appVersion.String())
uiSetVar("system_version", n.systemVersion.String())
}
func (n *Native) initUI() {
uiInit()
uiInit(n.displayRotation)
n.setUIVars()
}
@ -61,7 +62,7 @@ func (n *Native) UIObjSetImageSrc(objName string, image string) (bool, error) {
return uiImgSetSrc(objName, image)
}
func (n *Native) DisplaySetRotation(rotation string) (bool, error) {
func (n *Native) DisplaySetRotation(rotation uint16) (bool, error) {
return uiDispSetRotation(rotation)
}

File diff suppressed because one or more lines are too long

View File

@ -1,12 +1,12 @@
{
"navigation": {
"selectedUserPageObject": "[jetkvm.eez-project]:/userPages/0",
"selectedUserWidgetObject": "[jetkvm.eez-project]:/userWidgets/-1",
"selectedActionObject": "[jetkvm.eez-project]:/actions/7",
"selectedGlobalVariableObject": "[jetkvm.eez-project]:/variables/globalVariables/0",
"selectedUserPageObject": "[jetkvm.eez-project]:/userPages/5",
"selectedActionObject": "[jetkvm.eez-project]:/actions/0",
"selectedGlobalVariableObject": "[jetkvm.eez-project]:/variables/globalVariables/1",
"selectedStyleObject": "[jetkvm.eez-project]:/lvglStyles/styles/0",
"selectedThemeObject": "[jetkvm.eez-project]:/themes/0",
"selectedFontObject": "[jetkvm.eez-project]:/fonts/1",
"selectedFontObject": "[jetkvm.eez-project]:/fonts/4",
"selectedBitmapObject": "[jetkvm.eez-project]:/bitmaps/11",
"subnavigationSelectedItems": {
"variables-tab/sub-navigation/selected-item": "Global"
}
@ -29,7 +29,7 @@
},
{
"type": "border",
"selected": 4,
"selected": 2,
"size": 113.5,
"location": "right",
"children": [
@ -77,7 +77,7 @@
},
{
"type": "border",
"selected": 0,
"selected": 1,
"location": "bottom",
"children": [
{
@ -130,7 +130,6 @@
"type": "tabset",
"id": "#2e3d9a08-c69c-42b5-b434-525109f2a5a7",
"weight": 1,
"selected": 1,
"enableClose": false,
"children": [
{
@ -189,7 +188,8 @@
"enableClose": false,
"icon": "svg:variable"
}
]
],
"active": true
}
]
},
@ -197,46 +197,46 @@
"type": "tabset",
"id": "EDITORS",
"weight": 49.31058517127421,
"selected": 1,
"selected": 4,
"enableDeleteWhenEmpty": false,
"enableClose": false,
"children": [
{
"type": "tab",
"id": "#467ae6a9-0f08-4392-a580-25f8916524e4",
"name": "BootScreen",
"id": "#aec56ae8-5b75-4a2f-ad81-f0d7d683c77a",
"name": "FontBook24",
"component": "editor",
"config": {
"objectPath": "[jetkvm.eez-project]:/userPages/0",
"objectPath": "[jetkvm.eez-project]:/fonts/4",
"permanent": false
},
"icon": "material:font_download"
},
{
"type": "tab",
"id": "#c8dece00-e490-46b8-8a14-5dcfa8bbce36",
"name": "AboutScreen",
"component": "editor",
"config": {
"objectPath": "[jetkvm.eez-project]:/userPages/5",
"permanent": false
},
"icon": "svg:page"
},
{
"type": "tab",
"id": "#d1704ae1-993d-4a7d-bade-0903bb61e9c6",
"name": "AboutScreen",
"id": "#e9afee88-d3be-4069-aa77-22930d1efcf3",
"name": "HomeScreen",
"component": "editor",
"config": {
"objectPath": "[jetkvm.eez-project]:/userPages/5",
"objectPath": "[jetkvm.eez-project]:/userPages/1",
"permanent": true
},
"icon": "svg:page"
},
{
"type": "tab",
"id": "#b444e818-4663-4332-ab40-a34acb670c8c",
"name": "MenuAdvancedScreen",
"component": "editor",
"config": {
"objectPath": "[jetkvm.eez-project]:/userPages/4",
"permanent": true
},
"icon": "svg:page"
},
{
"type": "tab",
"id": "#f8258cf3-e12a-4be9-8021-33199674dbe6",
"id": "#f5a057a5-977c-46be-8702-5447d603a34f",
"name": "MenuScreen",
"component": "editor",
"config": {
@ -247,7 +247,7 @@
},
{
"type": "tab",
"id": "#5dcfc8a4-36a7-4dea-a0cc-8dbeb4aab914",
"id": "#8af97439-79ee-4705-be34-53ca8814f3a0",
"name": "Settings",
"component": "editor",
"config": {
@ -257,8 +257,7 @@
},
"icon": "material:settings"
}
],
"active": true
]
},
{
"type": "row",
@ -1066,7 +1065,8 @@
"0": {
"0": {
"0": {
"1": {}
"1": {},
"$selected": true
}
}
}
@ -1090,6 +1090,7 @@
"0": {
"0": {
"0": {
"0": {},
"1": {
"0": {}
}
@ -1101,7 +1102,9 @@
"3": {
"0": {
"0": {},
"1": {}
"1": {
"$selected": true
}
},
"1": {
"0": {},
@ -1154,8 +1157,8 @@
},
"transform": {
"translate": {
"x": -194,
"y": -454
"x": -230,
"y": -94
},
"scale": 1
},
@ -1171,9 +1174,8 @@
"0": {
"0": {
"0": {
"0": {
"$selected": true
}
"0": {},
"$selected": true
},
"1": {
"0": {
@ -1201,11 +1203,28 @@
}
},
"[jetkvm.eez-project]:/userPages/4[flow-state]": {
"selection": {},
"selection": {
"0": {
"0": {
"0": {
"0": {
"$selected": true
}
},
"1": {
"0": {
"0": {},
"1": {},
"2": {}
}
}
}
}
},
"transform": {
"translate": {
"x": -150,
"y": -120
"x": -176,
"y": -127
},
"scale": 1
},
@ -1226,12 +1245,19 @@
"1": {
"0": {
"0": {},
"1": {},
"1": {
"1": {}
},
"2": {},
"3": {},
"4": {},
"5": {},
"6": {}
"5": {
"1": {
"$selected": true
}
},
"6": {},
"7": {}
}
}
}
@ -1266,6 +1292,38 @@
"secondToPx": 200,
"scrollLeft": 0
}
},
"[jetkvm.eez-project]:/userPages/6[flow-state]": {
"selection": {
"0": {
"0": {
"0": {},
"1": {
"0": {
"0": {},
"2": {
"1": {
"$selected": true
}
}
}
}
}
}
},
"transform": {
"translate": {
"x": -138,
"y": -122
},
"scale": 1
},
"timeline": {
"isEditorActive": false,
"position": 0,
"secondToPx": 200,
"scrollLeft": 0
}
}
},
"activeOutputSection": 0,
@ -1281,13 +1339,10 @@
"logsPanelFilter": "all",
"selectedStylePropertyName": "",
"lvglPart": "MAIN",
"lvglState": "DEFAULT",
"lvglState": "DISABLED",
"lvglExpandedPropertiesGroup": [
"POSITION AND SIZE",
"PADDING",
"MARGIN",
"TEXT",
"BACKGROUND"
"TEXT"
],
"showInactiveFlowsInDebugger": true,
"globalFlowZoom": true,

View File

@ -13,15 +13,19 @@ type Native struct {
lD *zerolog.Logger
systemVersion *semver.Version
appVersion *semver.Version
displayRotation uint16
onVideoStateChange func(state VideoState)
onVideoFrameReceived func(frame []byte, duration time.Duration)
onIndevEvent func(event string)
}
type NativeOptions struct {
SystemVersion *semver.Version
AppVersion *semver.Version
DisplayRotation uint16
OnVideoStateChange func(state VideoState)
OnVideoFrameReceived func(frame []byte, duration time.Duration)
OnIndevEvent func(event string)
}
func NewNative(opts NativeOptions) *Native {
@ -39,14 +43,23 @@ func NewNative(opts NativeOptions) *Native {
}
}
onIndevEvent := opts.OnIndevEvent
if onIndevEvent == nil {
onIndevEvent = func(event string) {
nativeLogger.Info().Str("event", event).Msg("indev event")
}
}
return &Native{
ready: make(chan struct{}),
l: nativeLogger,
lD: displayLogger,
systemVersion: opts.SystemVersion,
appVersion: opts.AppVersion,
displayRotation: opts.DisplayRotation,
onVideoStateChange: opts.OnVideoStateChange,
onVideoFrameReceived: opts.OnVideoFrameReceived,
onIndevEvent: opts.OnIndevEvent,
}
}
@ -59,6 +72,7 @@ func (n *Native) Start() {
go n.handleLogChan()
go n.handleVideoStateChan()
go n.handleVideoFrameChan()
go n.handleIndevEventChan()
n.initUI()
go n.tickUI()

21
internal/native/single.go Normal file
View File

@ -0,0 +1,21 @@
package native
import "sync"
var (
instance *Native
instanceLock sync.RWMutex
)
func setInstance(n *Native) {
instanceLock.Lock()
defer instanceLock.Unlock()
if instance == nil {
instance = n
}
if instance != n {
panic("instance is already set")
}
}

View File

@ -286,14 +286,25 @@ func rpcTryUpdate() error {
}
func rpcSetDisplayRotation(params DisplayRotationSettings) error {
var err error
_, err = nativeInstance.DisplaySetRotation(params.Rotation)
if err == nil {
config.DisplayRotation = params.Rotation
if err := SaveConfig(); err != nil {
return fmt.Errorf("failed to save config: %w", err)
}
currentRotation := config.DisplayRotation
if currentRotation == params.Rotation {
return nil
}
err := config.SetDisplayRotation(params.Rotation)
if err != nil {
return err
}
_, err = nativeInstance.DisplaySetRotation(config.GetDisplayRotation())
if err != nil {
return err
}
if err := SaveConfig(); err != nil {
return fmt.Errorf("failed to save config: %w", err)
}
return err
}

View File

@ -11,14 +11,20 @@ import (
var nativeInstance *native.Native
func initNative(systemVersion *semver.Version, appVersion *semver.Version) {
nativeInstance = native.NewNative(native.NativeOptions{
SystemVersion: systemVersion,
AppVersion: appVersion,
SystemVersion: systemVersion,
AppVersion: appVersion,
DisplayRotation: config.GetDisplayRotation(),
OnVideoStateChange: func(state native.VideoState) {
lastVideoState = state
triggerVideoStateUpdate()
requestDisplayUpdate(true)
},
OnIndevEvent: func(event string) {
nativeLogger.Trace().Str("event", event).Msg("indev event received")
wakeDisplay(false)
},
OnVideoFrameReceived: func(frame []byte, duration time.Duration) {
if currentSession != nil {
err := currentSession.VideoTrack.WriteSample(media.Sample{Data: frame, Duration: duration})