You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
340 lines
8.8 KiB
340 lines
8.8 KiB
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#define SDL_MAIN_HANDLED /*To fix SDL's "undefined reference to WinMain" \
|
|
issue*/
|
|
#include "demos/lv_demos.h"
|
|
#include "examples/lv_examples.h"
|
|
#include "sdl/sdl.h"
|
|
#include <SDL2/SDL.h>
|
|
#include <lvgl.h>
|
|
|
|
#include <lua.h>
|
|
|
|
#include <lauxlib.h>
|
|
#include <lualib.h>
|
|
|
|
#include <luavgl.h>
|
|
#include "widgets/widgets.h"
|
|
|
|
typedef struct {
|
|
lua_State *L;
|
|
lv_obj_t *root;
|
|
} lua_context_t;
|
|
|
|
typedef struct {
|
|
lv_obj_t *root;
|
|
make_font_cb make_font;
|
|
delete_font_cb delete_font;
|
|
} luavgl_args_t;
|
|
|
|
/**
|
|
* Initialize the Hardware Abstraction Layer (HAL) for the LVGL graphics
|
|
* library
|
|
*/
|
|
static void hal_init(void)
|
|
{
|
|
/* Use the 'monitor' driver which creates window on PC's monitor to simulate a
|
|
* display*/
|
|
sdl_init();
|
|
|
|
/*Create a display buffer*/
|
|
static lv_disp_draw_buf_t disp_buf1;
|
|
static lv_color_t buf1_1[SDL_HOR_RES * SDL_VER_RES];
|
|
lv_disp_draw_buf_init(&disp_buf1, buf1_1, NULL, SDL_HOR_RES * SDL_VER_RES);
|
|
|
|
/*Create a display*/
|
|
static lv_disp_drv_t disp_drv;
|
|
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
|
|
disp_drv.draw_buf = &disp_buf1;
|
|
disp_drv.flush_cb = sdl_display_flush;
|
|
disp_drv.hor_res = SDL_HOR_RES;
|
|
disp_drv.ver_res = SDL_VER_RES;
|
|
|
|
lv_disp_t *disp = lv_disp_drv_register(&disp_drv);
|
|
|
|
lv_theme_t *th = lv_theme_default_init(
|
|
disp, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED),
|
|
LV_THEME_DEFAULT_DARK, LV_FONT_DEFAULT);
|
|
lv_disp_set_theme(disp, th);
|
|
|
|
lv_group_t *g = lv_group_create();
|
|
lv_group_set_default(g);
|
|
|
|
/* Add the mouse as input device
|
|
* Use the 'mouse' driver which reads the PC's mouse*/
|
|
static lv_indev_drv_t indev_drv_1;
|
|
lv_indev_drv_init(&indev_drv_1); /*Basic initialization*/
|
|
indev_drv_1.type = LV_INDEV_TYPE_POINTER;
|
|
|
|
/*This function will be called periodically (by the library) to get the mouse
|
|
* position and state*/
|
|
indev_drv_1.read_cb = sdl_mouse_read;
|
|
lv_indev_t *mouse_indev = lv_indev_drv_register(&indev_drv_1);
|
|
|
|
static lv_indev_drv_t indev_drv_2;
|
|
lv_indev_drv_init(&indev_drv_2); /*Basic initialization*/
|
|
indev_drv_2.type = LV_INDEV_TYPE_KEYPAD;
|
|
indev_drv_2.read_cb = sdl_keyboard_read;
|
|
lv_indev_t *kb_indev = lv_indev_drv_register(&indev_drv_2);
|
|
lv_indev_set_group(kb_indev, g);
|
|
|
|
static lv_indev_drv_t indev_drv_3;
|
|
lv_indev_drv_init(&indev_drv_3); /*Basic initialization*/
|
|
indev_drv_3.type = LV_INDEV_TYPE_ENCODER;
|
|
indev_drv_3.read_cb = sdl_mousewheel_read;
|
|
lv_indev_t *enc_indev = lv_indev_drv_register(&indev_drv_3);
|
|
lv_indev_set_group(enc_indev, g);
|
|
|
|
/*Set a cursor for the mouse*/
|
|
LV_IMG_DECLARE(mouse_cursor_icon); /*Declare the image file.*/
|
|
lv_obj_t *cursor_obj =
|
|
lv_img_create(lv_scr_act()); /*Create an image object for the cursor */
|
|
lv_img_set_src(cursor_obj, &mouse_cursor_icon); /*Set the image source*/
|
|
lv_indev_set_cursor(mouse_indev,
|
|
cursor_obj); /*Connect the image object to the driver*/
|
|
}
|
|
|
|
/*
|
|
** Prints an error message, adding the program name in front of it
|
|
** (if present)
|
|
*/
|
|
static void l_message(const char *pname, const char *msg)
|
|
{
|
|
printf("%s: %s\n", pname ? pname : " ", msg);
|
|
}
|
|
|
|
/*
|
|
** Check whether 'status' is not OK and, if so, prints the error
|
|
** message on the top of the stack. It assumes that the error object
|
|
** is a string, as it was either generated by Lua or by 'msghandler'.
|
|
*/
|
|
static int report(lua_State *L, int status)
|
|
{
|
|
if (status != LUA_OK) {
|
|
const char *msg = lua_tostring(L, -1);
|
|
l_message("luactx", msg);
|
|
lua_pop(L, 1); /* remove message */
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
** Message handler used to run all chunks
|
|
*/
|
|
static int msghandler(lua_State *L)
|
|
{
|
|
const char *msg = lua_tostring(L, 1);
|
|
if (msg == NULL) { /* is error object not a string? */
|
|
if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */
|
|
lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */
|
|
return 1; /* that is the message */
|
|
else
|
|
msg = lua_pushfstring(L, "(error object is a %s value)",
|
|
luaL_typename(L, 1));
|
|
}
|
|
|
|
/* append a standard traceback */
|
|
luaL_traceback(L, L, msg, 1);
|
|
|
|
msg = lua_tostring(L, -1);
|
|
lua_pop(L, 1);
|
|
|
|
lv_obj_t *root = NULL;
|
|
luavgl_ctx_t *ctx = luavgl_context(L);
|
|
root = ctx->root ? ctx->root : lv_scr_act();
|
|
lv_obj_t *label = lv_label_create(root);
|
|
lv_label_set_text(label, msg);
|
|
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP);
|
|
lv_obj_set_style_text_font(label, LV_FONT_DEFAULT, 0);
|
|
lv_obj_set_width(label, LV_PCT(80));
|
|
lv_obj_center(label);
|
|
|
|
printf("trace back: \n%s\n", msg);
|
|
return 0; /* return no trace, since we already processed it. */
|
|
}
|
|
|
|
static int lua_panic(lua_State *L)
|
|
{
|
|
printf("LUA panic:\n%s\n", lua_tostring(L, -1));
|
|
return 0; /* return to Lua to abort */
|
|
}
|
|
|
|
/*
|
|
** protected main entry
|
|
*/
|
|
static int pmain(lua_State *L)
|
|
{
|
|
int status;
|
|
const char *script = lua_tostring(L, 1);
|
|
|
|
luavgl_args_t *args = lua_touserdata(L, 2);
|
|
if (args == NULL || args->root == NULL) {
|
|
printf("Null root object.\n");
|
|
return 0;
|
|
}
|
|
|
|
luavgl_set_root(L, args->root);
|
|
luavgl_set_font_extension(L, args->make_font, args->delete_font);
|
|
|
|
/**
|
|
* Set global variable SCRIPT_PATH, to make image src path easier.
|
|
*/
|
|
char *path = strdup(script);
|
|
if (path == NULL) {
|
|
printf("no memory.\n");
|
|
return 0;
|
|
}
|
|
|
|
int i = strlen(path);
|
|
for (; i; i--) {
|
|
if (path[i] == '/') {
|
|
path[i + 1] = '\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
printf("script path: %s\n", path);
|
|
lua_pushstring(L, path);
|
|
lua_setglobal(L, "SCRIPT_PATH");
|
|
luaL_openlibs(L);
|
|
|
|
lua_getglobal(L, "package");
|
|
lua_getfield(L, -1, "path");
|
|
|
|
const char *pkg_path = lua_tostring(L, -1);
|
|
char *new_path = malloc(strlen(pkg_path) + strlen(script) + 2);
|
|
strcpy(new_path, pkg_path);
|
|
strcat(new_path, ";"), strcat(new_path, path), strcat(new_path, "?.lua");
|
|
lua_pop(L, 1);
|
|
lua_pushstring(L, new_path);
|
|
lua_setfield(L, -2, "path");
|
|
lua_pop(L, 1);
|
|
free(path);
|
|
free(new_path);
|
|
|
|
lua_atpanic(L, &lua_panic);
|
|
|
|
luaL_requiref(L, "lvgl", luaopen_lvgl, 1);
|
|
lua_pop(L, 1);
|
|
luavgl_widgets_init(L);
|
|
|
|
lua_pushcfunction(L, msghandler); /* push message handler */
|
|
int base = lua_gettop(L);
|
|
status = luaL_loadfile(L, script);
|
|
if (status != LUA_OK) {
|
|
lua_pushfstring(L, "failed to load: %s\n", script);
|
|
/* manually show the error to screen. */
|
|
lua_insert(L, 1);
|
|
msghandler(L);
|
|
return 0;
|
|
}
|
|
|
|
status = lua_pcall(L, 0, 0, base);
|
|
lua_remove(L, base); /* remove message handler from the stack */
|
|
report(L, status);
|
|
lua_pushboolean(L, 1); /* signal no errors */
|
|
return 1;
|
|
}
|
|
|
|
lua_context_t *lua_load_script(const char *script, luavgl_args_t *args)
|
|
{
|
|
int ret, status;
|
|
/* create the thread to run script. */
|
|
if (script == NULL) {
|
|
printf("args error.\n");
|
|
return NULL;
|
|
}
|
|
|
|
printf("run script: %s\n", script);
|
|
lua_State *L = luaL_newstate(); /* create state */
|
|
if (L == NULL) {
|
|
printf("no mem for lua state.\n");
|
|
return NULL;
|
|
}
|
|
|
|
lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
|
|
lua_pushstring(L, script);
|
|
lua_pushlightuserdata(L, args);
|
|
status = lua_pcall(L, 2, 1, 0); /* do the call */
|
|
ret = lua_toboolean(L, -1);
|
|
report(L, status);
|
|
if (!ret || status != LUA_OK) {
|
|
/* This should never happen */
|
|
printf("pcall failed.\n");
|
|
lua_close(L);
|
|
return NULL;
|
|
}
|
|
|
|
/* script may fail, but we continue until page destoried. */
|
|
lua_context_t *luactx = calloc(sizeof(*luactx), 1);
|
|
if (luactx == NULL) {
|
|
printf("no memory.\n");
|
|
goto lua_exit;
|
|
}
|
|
|
|
luactx->L = L;
|
|
luactx->root = args->root;
|
|
return luactx;
|
|
|
|
lua_exit:
|
|
lua_close(L);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int lua_terminate(lua_context_t *luactx)
|
|
{
|
|
lua_State *L = luactx->L;
|
|
|
|
lua_close(L);
|
|
free(luactx);
|
|
return 0;
|
|
}
|
|
|
|
static lua_context_t *lua_ctx;
|
|
static luavgl_args_t args;
|
|
|
|
static void reload_cb(lv_event_t *e)
|
|
{
|
|
(void)e;
|
|
if (lua_ctx != NULL) {
|
|
lua_terminate(lua_ctx);
|
|
}
|
|
|
|
lua_ctx = lua_load_script(LUAVGL_EXAMPLE_DIR "/examples.lua", &args);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
(void)argc; /*Unused*/
|
|
(void)argv; /*Unused*/
|
|
|
|
/*Initialize LVGL*/
|
|
lv_init();
|
|
|
|
/*Initialize the HAL (display, input devices, tick) for LVGL*/
|
|
hal_init();
|
|
|
|
args.root = lv_scr_act();
|
|
|
|
lua_ctx = lua_load_script(LUAVGL_EXAMPLE_DIR "/examples.lua", &args);
|
|
|
|
lv_obj_t *btn = lv_btn_create(lv_layer_sys());
|
|
lv_obj_align(btn, LV_ALIGN_BOTTOM_RIGHT, 0, -50);
|
|
lv_obj_set_size(btn, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
|
|
lv_obj_set_style_pad_all(btn, 5, 0);
|
|
lv_obj_add_event_cb(btn, reload_cb, LV_EVENT_CLICKED, NULL);
|
|
lv_obj_t* label = lv_label_create(btn);
|
|
lv_label_set_text(label, "RELOAD");
|
|
lv_obj_center(label);
|
|
|
|
while (1) {
|
|
/* Periodically call the lv_task handler.
|
|
* It could be done in a timer interrupt or an OS task too.*/
|
|
lv_timer_handler();
|
|
usleep(5 * 1000);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|