#include #include #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 #include #include #include #include #include #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; }