quickjs利用libuv实现setTimeout函数
零、前言
默认quickjs 是有setTimeout函数的实现的。但是由于js的进程与ui进程会有冲突,因此,需要通过重写quickjs的setTimeout,改用系统进程实现才行。
这里以大部分基于quickjs开源的框架中使用最多的libuv作为底层库。
一、编译libuv库
通过github源码编译libuv还是有点麻烦,官方没有怎么提供libuv编译脚本,这里我使用微软的vcpkg工具来编译。
1 git clone https://github.com/microsoft/vcpkg #下载vcpkg 2 bootstrap-vcpkg.bat #下周vcpkg.exe 3 vcpkg.exe search libuv 4 vcpkg.exe install libuv #下载libuv源码,编译、并安装到~/installed目录下
后面经过测试,发现从vcpkg编译的lib,有个 GetSystemTimePreciseAsFileTime 函数在win7上未找到。因此需要修改libuv的源代码,改成兼容win7版本,具体修改 src/win/util.c:uv_clock_gettime:449 把 GetSystemTimePreciseAsFileTime(&ft); 改成 GetSystemTimeAsFileTime(&ft); 换成低版本。反正windows上只是仿真,精度误差无所谓。改完之后,也可以用cmake进行编译。然后用Visul Studio进行编译。
cmake -G "Visual Studio 16 2019" -B build
简单代码,测试libuv库是否链接成功
1 int count = 0; 2 void wait_for_a_while(uv_idle_t *handle) 3 { 4 count ++; 5 printf("wait for a while %d\n", count); 6 if(count > 10){ 7 uv_idle_stop(handle); 8 } 9 } 10 void test_uv() 11 { 12 uv_idle_t idler; 13 uv_idle_init(uv_default_loop(), &idler); 14 uv_idle_start(&idler, wait_for_a_while); 15 uv_run(uv_default_loop(), UV_RUN_DEFAULT); 16 uv_loop_close(uv_default_loop()); 17 }
二、修改原来lvgl-simulator
原先的lvgl主循环
1 lv_ui guider_ui; 2 3 int main(int argc, char **argv) 4 { 5 (void)argc; /*Unused*/ 6 (void)argv; /*Unused*/ 7 8 /*Initialize LVGL*/ 9 lv_init(); 10 11 /*Initialize the HAL (display, input devices, tick) for LVGL*/ 12 hal_init(); 13 14 setup_ui(&guider_ui); 15 events_init(&guider_ui); 16 custom_init(&guider_ui); 17 18 while(1) { 19 /* Periodically call the lv_task handler. 20 * It could be done in a timer interrupt or an OS task too.*/ 21 lv_timer_handler(); 22 usleep(5 * 1000); 23 } 24 25 return 0; 26 }
之前测试,直接在custom_init里运行js脚本时,会因为阻塞问题,导致主界面会卡死。因此需要对这个main主循环进行改造。在主循环里,创建两个线程,一个gui线程,一个event事件线程。
修改后主循环代码如下
1 uv_loop_t *event_loop; 2 uv_loop_t *gui_loop; 3 4 uv_timer_t job_req; 5 uv_timer_t job_event; 6 7 void gui_loop_cb(uv_timer_t *handle){ 8 lv_timer_handler(); 9 } 10 void event_loop_cb(uv_timer_t *handle){ 11 printf("event_loop_cb\n"); 12 } 13 14 int main(int argc, char **argv) 15 { 16 (void)argc; /*Unused*/ 17 (void)argv; /*Unused*/ 18 19 /*Initialize LVGL*/ 20 lv_init(); 21 22 /*Initialize the HAL (display, input devices, tick) for LVGL*/ 23 hal_init(); 24 25 gui_loop = uv_default_loop(); 26 event_loop = uv_default_loop(); 27 uv_timer_init(gui_loop, &job_req); 28 uv_timer_start(&job_req, gui_loop_cb, 100, 5); 29 30 uv_timer_init(event_loop, &job_event); 31 uv_timer_start(&job_event, event_loop_cb, 100, 500); 32 33 setup_ui(&guider_ui); 34 events_init(&guider_ui); 35 custom_init(&guider_ui); 36 37 while(uv_run(event_loop, UV_RUN_NOWAIT) || uv_run(gui_loop, UV_RUN_NOWAIT) ) 38 { 39 } 40 return 0; 41 }
三、导出setTimeout函数
为了验证功能,部分对象没有进行释放,setTimeout没有做多实例处理。同时还实现绑定click事件。
1 extern uv_loop_t *event_loop; 2 uv_timer_t lvgl_job_req; 3 typedef struct js_timer_data_s { 4 JSContext *ctx; 5 JSValue func; 6 } js_timer_data_t; 7 8 static void uv__timeout_cb(uv_timer_t *handle) { 9 // printf("timeout! uv\n"); 10 js_timer_data_t *data = (js_timer_data_t *)handle->data; 11 JSContext *ctx = data->ctx; 12 JSValue func = JS_DupValue(ctx, data->func); 13 JSValue ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL); 14 JS_FreeValue(ctx, func); 15 if(JS_IsException(ret)){ 16 js_std_dump_error(ctx); 17 } 18 JS_FreeValue(ctx, ret); 19 return ; 20 } 21 22 23 static JSValue lvgl_setTimeout(JSContext *ctx, JSValueConst this_val, 24 int argc, JSValueConst *argv) 25 { 26 int delay; 27 JSValueConst func; 28 func = argv[0]; 29 if(!JS_IsFunction(ctx, func)){ 30 printf("Not a function\n"); 31 return JS_EXCEPTION; 32 } 33 JS_ToInt32(ctx, &delay, argv[1]); 34 35 uv_timer_init(event_loop, &lvgl_job_req); 36 37 js_timer_data_t *data = calloc(1, sizeof(js_timer_data_t)); 38 data->ctx = ctx; 39 data->func = JS_DupValue(ctx, func); 40 lvgl_job_req.data = data; 41 uv_timer_start(&lvgl_job_req, uv__timeout_cb, delay, 0); 42 // uv_run(loop, UV_RUN_ONCE); 43 return JS_NULL; 44 } 45 46 void click_event_cb(lv_event_t * e){ 47 js_timer_data_t *data = (js_timer_data_t *)e->user_data; 48 JSContext *ctx = data->ctx; 49 JSValue func = JS_DupValue(ctx, data->func); 50 JSValue ret = JS_Call(ctx, func, JS_UNDEFINED, 0, NULL); 51 JS_FreeValue(ctx, func); 52 if(JS_IsException(ret)){ 53 js_std_dump_error(ctx); 54 } 55 JS_FreeValue(ctx, ret); 56 } 57 static JSValue lvgl_bind_click(JSContext *ctx, JSValueConst this_val, 58 int argc, JSValueConst *argv) 59 { 60 lv_obj_ptr_t p; 61 JS_ToPointer(ctx, &p, argv[0]); 62 lv_obj_t * obj = (lv_obj_t *)p; 63 64 JSValueConst func = argv[1]; 65 if(!JS_IsFunction(ctx, func)){ 66 printf("Not a function\n"); 67 return JS_EXCEPTION; 68 } 69 js_timer_data_t *data = calloc(1, sizeof(js_timer_data_t)); 70 data->ctx = ctx; 71 data->func = JS_DupValue(ctx, func); 72 73 lv_obj_add_event_cb(obj, click_event_cb, LV_EVENT_CLICKED, data); 74 } 75 76 static JSValue lvgl_clearTimeout(JSContext *ctx, JSValueConst this_val, 77 int argc, JSValueConst *argv) 78 { 79 uv_timer_stop(&lvgl_job_req); 80 return JS_NULL; 81 }
四、js脚本测试
1 lvgl.lvgl_set_clickable(lbl); //设置可点击 2 //绑定点击事件 3 lvgl.lvgl_bind_click(lbl, function(){ 4 console.log("onclick"); 5 console.log(this); 6 lvgl.lvgl_obj_set_style_bg_color(lbl, 0xff0000); 7 clearTimeout(); 8 }); 9 10 lvgl.lvgl_scr_load(screen); 11 12 function changeState(){ 13 var x = Math.random()*400; 14 var y = Math.random()*300; 15 console.log(x + " " + y); 16 lvgl.lvgl_obj_set_pos(lbl, x, y); 17 setTimeout(changeState, 1000); //定时执行 18 } 19 changeState();
参考资料:
https://zhuanlan.zhihu.com/c_1094553212761309184
https://stackoverflow.com/questions/17100883/how-does-the-uv-run-nowait-mode-work-in-libuv
本文地址:https://www.cnblogs.com/wunaozai/p/17853717.html
系列目录:https://www.cnblogs.com/wunaozai/p/17853962.html
作者:无脑仔的小明 出处:http://www.cnblogs.com/wunaozai/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 如果文中有什么错误,欢迎指出。以免更多的人被误导。有需要沟通的,可以站内私信,文章留言,或者关注“无脑仔的小明”公众号私信我。一定尽力回答。 |