mirror of
https://gitee.com/zlgopen/awtk.git
synced 2024-11-30 02:58:26 +08:00
update docs
This commit is contained in:
parent
7da0296ade
commit
c32416ca0d
BIN
.sconsign.dblite
BIN
.sconsign.dblite
Binary file not shown.
@ -75,3 +75,6 @@ demos\demo1
|
||||
|
||||
[stm32f103ze](https://github.com/xianjimli/lftk-stm32f103ze-raw.git)
|
||||
|
||||
## 文档
|
||||
|
||||
* [LFTK脚本绑定的实现原理 - lua绑定](docs/binding_lua.md)
|
||||
|
11
doc_todo.md
11
doc_todo.md
@ -1,11 +0,0 @@
|
||||
* lcd
|
||||
* canvas
|
||||
* font
|
||||
* theme
|
||||
* image
|
||||
|
||||
* main_loop
|
||||
* window manager
|
||||
* widget
|
||||
* controls
|
||||
* lua/python
|
@ -1,26 +1,26 @@
|
||||
# LFTK脚本绑定实现原理 - lua
|
||||
# LFTK脚本绑定的实现原理 - lua绑定
|
||||
|
||||
脚本化是[LFTK](https://github.com/xianjimli/lftk)的一个基本功能。[LFTK](https://github.com/xianjimli/lftk)计划支持嵌入式系统中常用的脚本,如lua、python和jerryscript。脚本绑定说简单也简单,说复杂也复杂,采用FFI(calling external C functions)和SWIG绑定一些简单的函数是很方便的,但绑定一个复杂的GUI系统还是很有挑战的。之所以不采用通用的工具,而是实现自己的代码产生器,主要有以下考虑:
|
||||
|
||||
* 我使用FFI和SWIG的经验很有限,觉得有些功能不太好实现,至少目前我还不知道实现这些功能的方法和思路。
|
||||
|
||||
* 担心FFI和SWIG的运行环境的可移植性。在Windows、Linux和Macos上肯定是没有问题的,但是在嵌入式系统,特别是RTOS和裸系统中,支持像动态库,甚至文件系统可能都是困难的。
|
||||
* 担心FFI和SWIG的运行环境的可移植性。在Windows、Linux和Macos上肯定是没有问题的,但是在嵌入式系统,特别是RTOS和裸系统中,支持像动态库等功能可能有些困难的。
|
||||
|
||||
* 对jerryscript没有支持。jerryscript是三星开源的javascript实现,在嵌入式系统中用得比较多,通用的绑定机制对jerryscript没有支持。
|
||||
|
||||
* 代码风格。不同语言有不同的代码风格,特别是命名风格,直接把C言语的风格映射过去,对于开发者不太友好。FFI和SWIG都需要做额外的工作才能实现这个功能。
|
||||
|
||||
* LFTK是完全采用面向对象设计,并用C实现面向对象编程的。而通用的绑定机制对此并不友好,实现起来非常困难。
|
||||
* LFTK采用面向对象设计,并用C实现面向对象编程的。而通用的绑定机制对此并不友好,实现起来非常困难。
|
||||
|
||||
* 自定义的代码产生器并不复杂,而且具有更大的灵活性。
|
||||
|
||||
基于以上这些原因,我决定自己实现[LFTK](https://github.com/xianjimli/lftk)的脚本绑定机制。它的实现原理如下:用特定格式的API注释来描述要脚本化的API,用一个名为gen_idl的工具把注释提取出来生成JSON的接口描述文件,然后用不同的代码产生器生成各种语言的绑定:
|
||||
基于以上这些原因,我决定自己实现[LFTK](https://github.com/xianjimli/lftk)的脚本绑定机制。它的实现原理如下:用特定格式的API注释来描述要脚本化的API,用一个名为gen_idl的工具把注释提取出来生成JSON的接口描述文件,然后用不同的代码产生器生成对应语言的绑定:
|
||||
|
||||
![1](images/lftk_binding.png)
|
||||
|
||||
## 注释格式
|
||||
|
||||
我采用了类型[jsduck](https://github.com/senchalabs/jsduck)的API注释格式,但是jsduck并不支持C语言的数据类型,完全要兼容jsduck也不可能。
|
||||
我采用了类似于[jsduck](https://github.com/senchalabs/jsduck)的API注释格式,但是jsduck并不支持C语言的数据类型,所以没有必要去兼容jsduck。
|
||||
|
||||
### 一、类的注释
|
||||
|
||||
@ -49,7 +49,7 @@
|
||||
*/
|
||||
```
|
||||
|
||||
里面说明了成员变量的类型、名称和只读性等信息。
|
||||
里面说明了成员变量的类型、名称和是否只读等信息。
|
||||
|
||||
### 三、函数的注释
|
||||
|
||||
@ -102,7 +102,7 @@ typedef enum _align_v_t {
|
||||
}align_v_t;
|
||||
```
|
||||
|
||||
里面定义了枚举的名称和各个值。
|
||||
里面定义了枚举的名称和各个枚举值。
|
||||
|
||||
|
||||
## 代码产生器
|
||||
@ -115,11 +115,12 @@ typedef enum _align_v_t {
|
||||
|
||||
## lua绑定
|
||||
|
||||
对lua绑定也是花了一些,由于对lua不熟悉,还特意买了两本书,翻了一下有些帮助,后来还是靠阅读lua的代码解决了问题。这里做个笔记,方便有需要的朋友参考:
|
||||
|
||||
对lua绑定花了一些时间,由于对lua不熟悉,还特意买了两本书,阅读lua的源码也有很大帮助。这里做个笔记,方便有需要的朋友参考:
|
||||
|
||||
### 一、全局函数的绑定
|
||||
|
||||
这个很多资料里都有介绍。
|
||||
|
||||
示例:
|
||||
|
||||
* 1.实现wrap函数
|
||||
@ -144,7 +145,7 @@ static int wrap_lftk_quit(lua_State* L) {
|
||||
|
||||
### 二、构造函数的绑定
|
||||
|
||||
* 1.实现wrap函数。构造函数的wrap函数并无多大差别,只是最后要调用lftk\_newuserdata创建一个userdata对象,并关联metatable。我开始用的lua\_pushlightuserdata函数,后来发现lua里所用的ligthuserdata都是用的一个metatable,修改一个对象的metatable,其它类型的对象的metatable也被修改了,我感觉这种做法并不合理。
|
||||
* 1.实现wrap函数。构造函数的wrap函数和普通函数的wrap差不多,只是最后要调用lftk\_newuserdata创建一个userdata对象,并关联metatable。我开始用的lua\_pushlightuserdata函数,后来发现lua里全部的ligthuserdata用的是一个metatable,修改一个对象的metatable,其它类型的对象的metatable也被修改了(我感觉这种做法并不合理)。
|
||||
|
||||
```
|
||||
static int wrap_button_create(lua_State* L) {
|
||||
@ -161,6 +162,7 @@ static int wrap_button_create(lua_State* L) {
|
||||
```
|
||||
|
||||
* 2.注册
|
||||
|
||||
```
|
||||
static void button_t_init(lua_State* L) {
|
||||
static const struct luaL_Reg static_funcs[] = {
|
||||
@ -172,18 +174,170 @@ static void button_t_init(lua_State* L) {
|
||||
}
|
||||
```
|
||||
|
||||
### 三、成员函数的绑定
|
||||
### 三、成员函数和获取成员变量的绑定
|
||||
|
||||
### 四、设置属性的函数的绑定
|
||||
* 1.实现wrap函数。成员函数的wrap函数和普通函数的wrap差不多。
|
||||
|
||||
### 五、获取属性的函数的绑定
|
||||
```
|
||||
static int wrap_check_button_set_value(lua_State* L) {
|
||||
ret_t ret = 0;
|
||||
widget_t* widget = (widget_t*)lftk_checkudata(L, 1, "widget_t");
|
||||
uint32_t value = (uint32_t)luaL_checkinteger(L, 2);
|
||||
ret = (ret_t)check_button_set_value(widget, value);
|
||||
|
||||
### 六、枚举的绑定
|
||||
lua_pushnumber(L,(lua_Number)(ret));
|
||||
|
||||
### 七、回调函数的处理
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
* 2.注册。为了让成员函数能够一级一级的调到父类中去,我使用了lua的__index函数。
|
||||
|
||||
先把类的成员函数放到一张表中,方便后面查找。
|
||||
|
||||
```
|
||||
static const struct luaL_Reg check_button_t_member_funcs[] = {
|
||||
{"set_text", wrap_check_button_set_text},
|
||||
{"set_value", wrap_check_button_set_value},
|
||||
{NULL, NULL}
|
||||
};
|
||||
```
|
||||
|
||||
通过find_member到上表中查找成员函数,如果找到就直接返回该函数。如果没找到,再看是不是成员变量,是则返回成员变量的值。最后再到父类中去查找,重复这个过程。
|
||||
|
||||
```
|
||||
static int wrap_check_button_t_get_prop(lua_State* L) {
|
||||
check_button_t* obj = (check_button_t*)lftk_checkudata(L, 1, "check_button_t");
|
||||
const char* name = (const char*)luaL_checkstring(L, 2);
|
||||
const luaL_Reg* ret = find_member(check_button_t_member_funcs, name);
|
||||
|
||||
(void)obj;
|
||||
(void)name;
|
||||
if(ret) {
|
||||
lua_pushcfunction(L, ret->func);
|
||||
return 1;
|
||||
}
|
||||
if(strcmp(name, "value") == 0) {
|
||||
lua_pushboolean(L,(lua_Integer)(obj->value));
|
||||
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return wrap_widget_t_get_prop(L);
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
注册metatable。
|
||||
|
||||
```
|
||||
static const struct luaL_Reg index_funcs[] = {
|
||||
{"__index", wrap_check_button_t_get_prop},
|
||||
{"__newindex", wrap_check_button_t_set_prop},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
luaL_newmetatable(L, "lftk.check_button_t");
|
||||
lua_pushstring(L, "__index");
|
||||
lua_pushvalue(L, -2);
|
||||
lua_settable(L, -3);
|
||||
```
|
||||
|
||||
### 四、设置成员变量的绑定
|
||||
|
||||
这个我是使用__newindex函数来实现的。如果修改readonly的成员会打印警告,否则就直接修改。对于不存在的成员变量,到父类中去查找,重复这个过程。
|
||||
|
||||
```
|
||||
static int wrap_check_button_t_set_prop(lua_State* L) {
|
||||
check_button_t* obj = (check_button_t*)lftk_checkudata(L, 1, "check_button_t");
|
||||
const char* name = (const char*)luaL_checkstring(L, 2);
|
||||
(void)obj;
|
||||
(void)name;
|
||||
if(strcmp(name, "value") == 0) {
|
||||
printf("value is readonly\n");
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return wrap_widget_t_set_prop(L);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### 五、枚举的绑定
|
||||
|
||||
枚举则是直接创建了一张表,把枚举的值放到表中即可。
|
||||
|
||||
```
|
||||
static void value_type_t_init(lua_State* L) {
|
||||
lua_newtable(L);
|
||||
lua_setglobal(L, "ValueType");
|
||||
lua_getglobal(L, "ValueType");
|
||||
|
||||
lua_pushstring(L, "INVALID");
|
||||
lua_pushinteger(L, VALUE_TYPE_INVALID);
|
||||
lua_settable(L, -3);
|
||||
```
|
||||
|
||||
### 六、回调函数的处理
|
||||
|
||||
回调函数的处理麻烦一点,而且书里没有讲过,所以花了一些功夫。后来发现有回调函数的函数,很难自动产生代码,所幸这样的函数没几个,干脆手写了这部代码。
|
||||
|
||||
```
|
||||
static ret_t call_on_event(void* ctx, event_t* e) {
|
||||
lua_State* L = (lua_State*)s_current_L;
|
||||
int func_id = (char*)ctx - (char*)NULL;
|
||||
|
||||
lua_settop(L, 0);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, func_id);
|
||||
lftk_newuserdata(L, e, "event_t", NULL);
|
||||
|
||||
lua_pcall(L,1,1,0);
|
||||
|
||||
return RET_OK;
|
||||
}
|
||||
|
||||
static int wrap_widget_on(lua_State* L) {
|
||||
ret_t ret = 0;
|
||||
widget_t* widget = (widget_t*)lftk_checkudata(L, 1, "widget_t");
|
||||
event_type_t type = (event_type_t)luaL_checkinteger(L, 2);
|
||||
|
||||
if(lua_isfunction(L, 3)) {
|
||||
int func_id = 0;
|
||||
lua_pushvalue(L, 3);
|
||||
func_id = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
ret = (ret_t)widget_on(widget, type, call_on_event, (char*)NULL + func_id);
|
||||
lua_pushnumber(L,(lua_Number)ret);
|
||||
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int wrap_widget_off(lua_State* L) {
|
||||
ret_t ret = 0;
|
||||
widget_t* widget = (widget_t*)lftk_checkudata(L, 1, "widget_t");
|
||||
uint32_t id = (uint32_t)luaL_checkinteger(L, 2);
|
||||
emitter_item_t* item = emitter_find(widget->emitter, id);
|
||||
|
||||
if(item) {
|
||||
uint32_t func_id = (char*)(item->ctx) - (char*)NULL;
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, func_id);
|
||||
ret = (ret_t)widget_off(widget, id);
|
||||
}
|
||||
|
||||
lua_pushnumber(L,(lua_Number)(ret));
|
||||
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
## lua示例
|
||||
|
||||
本例中创建了两个按钮和一个进度条,可以通过按钮来控制进度条的值。
|
||||
|
||||
```
|
||||
function application_init()
|
||||
local win = Window.create(nil, 0, 0, 0, 0)
|
||||
@ -211,3 +365,9 @@ end
|
||||
|
||||
application_init()
|
||||
```
|
||||
|
||||
## 参考资料
|
||||
|
||||
* http://book.luaer.cn
|
||||
* https://www.lua.org/manual/5.2/
|
||||
* http://www.cnblogs.com/luweimy/p/3972353.html
|
||||
|
@ -1,12 +1,5 @@
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* Ref:
|
||||
* https://www.codingnow.com/2000/download/lua_manual.html
|
||||
* http://book.luaer.cn/_3.htm
|
||||
* http://www.cnblogs.com/luweimy/p/3972353.html
|
||||
*/
|
||||
|
||||
const builtin = `#include "custom.c"\n\n`;
|
||||
|
||||
function genAll(json) {
|
||||
|
Loading…
Reference in New Issue
Block a user