文档章节

AWTK WEB版移植笔记-基础

lixianjing
 lixianjing
发布于 09/12 07:16
字数 1606
阅读 19
收藏 0

AWTK WEB版移植笔记-基础

将一个C语言写的hello world编译成web版本是很简单的,网上有大量这样的例子。写这样的例子是必要的,让我们能够快速入门,但是实际项目则要复杂的多,这里会介绍一些emscripten的基础知识,同时强调移植AWTK过程中遇到的问题,希望能让大家少走弯路。

一、命令行参数

  • 1.EXPORTED_FUNCTIONS 用于导出应用程序中C的函数供JS调用。如:
-s EXPORTED_FUNCTIONS="['_awtk_web_init']"

函数名前面要加下划线,比如函数名为awtk_web_init,导出的名称则为_awtk_web_init。

对于小的项目,导出的函数很少,直接写在命令行也是可以的。对于大的项目,导出的函数很多,应该把内容写到文件中,通过@符合告诉emcc从文件中读取导出的函数,这样维护起来会方便很多。如:

-s EXPORTED_FUNCTIONS=@configs/export_app_funcs.json

configs/export_app_funcs.json的内容:

[
    "_awtk_web_init",
    "_awtk_web_deinit",
    "_awtk_web_main_loop_step",
    "_awtk_web_on_key_down",
    "_awtk_web_on_key_up",
    "_awtk_web_on_wheel",
    "_awtk_web_on_pointer_down",
    "_awtk_web_on_pointer_move",
    "_awtk_web_on_im_commit",
    "_awtk_web_on_pointer_up"
]
  • 2.EXTRA_EXPORTED_RUNTIME_METHODS 用于导出runtime中的函数供JS调用。如:
-s EXTRA_EXPORTED_RUNTIME_METHODS ="['cwrap']"

同理,将它的内容放在文件中,也是更可取的方法。如:

-s EXTRA_EXPORTED_RUNTIME_METHODS=@configs/export_runtime_funcs.json

configs/export_runtime_funcs.json的内容:

[
    "ccall",
    "cwrap",
    "addFunction",
    "removeFunction",
    "addOnPostRun",
    "addOnInit",
    "addOnExit",
    "addOnPreMain",
    "UTF8ToString"
]
  • 3.调试和优化

对于大的项目,调试版本最好不要加-g标志,产生的代码实在太大了,可能让浏览器处于假死状态,根本没法调试。用缺省生成的代码调试基本上就OK了。

发布版本建议加-Os,代码体积会大大减小,而且它会把数据独立出来,提高加载的速度。

  • 4.调试的宏。

emcc生成的常量数据的首地址并不会按32bit/64bit对齐,AWTK就踩到这个坑里了,SAFE_HEAP宏有助于发现这个问题。建议定义下列这些宏:

-DSAFE_HEAP=1 -DASSERTIONS=1 -DSTACK_OVERFLOW_CHECK=1

后来我给AWTK的常量全加上了对齐的属性:

#ifdef _MSC_VER
#define TK_CONST_DATA_ALIGN(v) __declspec(align(8)) v
#else
#define TK_CONST_DATA_ALIGN(v) v __attribute__((aligned(8)))
#endif /*_MSC_VER*/
TK_CONST_DATA_ALIGN(const unsigned char data_a_b_c_any[]) = { 
0x08,0x00,0x00,0x01,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x2d,0x62,0x2d,0x63,0x2e,0x61,0x6e,
0x79,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x61,0x62,0x63,0x0a,0x00,0x00,0x00,0x00,};/*52*/
  • 5.命令行参数过长的问题

对于一个大型项目,在Windows平台下,命令行参数很容易超长。最简单的办法就是将emcc的参数写入文件中,通过@符合告诉emcc从文件读取。如:

emcc -v @args.txt

二、C语言调用JS的函数

  • 1.头文件

包含emscripten.h头文件,它提供了一些方法,让C语言调用JS的函数。

#include <emscripten.h>
  • 2.通过emscripten_run_script调用。

如:

emscripten_run_script("alert('hi')");
  • 3.通过EM_JS/EM_ASM调用。

如:

#include <emscripten.h>

EM_JS(void, call_alert, (), {
  alert('hello world!');
  throw 'all done';
});

int main() {
  call_alert();
  return 0;
}
#include <emscripten.h>

int main() {
  EM_ASM(
    alert('hello world!');
    throw 'all done';
  );
  return 0;
}
    1. 通过EM_ASM_INT之类的宏调用。

这是最快也是最简单的调用方式,AWTK里基本上都是采用这种方式调用的。如:

C端代码:

static ret_t vgcanvas_web_save(vgcanvas_t *vgcanvas) {
  int32_t ret = EM_ASM_INT({ return VGCanvas.save(); }, 0); 

  return ret ? RET_OK : RET_FAIL;
}

JS端代码:

VGCanvas.save = function () {
  VGCanvas.ctx.save();

  return true;
}

**传递数值参数也非常简单。**如:

C端代码:

static ret_t vgcanvas_web_move_to(vgcanvas_t *vgcanvas, float_t x, float_t y) {
  EM_ASM_INT({ return VGCanvas.moveTo($0, $1); }, x, y);
    
  return RET_OK;
}   

JS端代码:

VGCanvas.moveTo = function (x, y) {
  VGCanvas.ctx.moveTo(x, y);
  
  return true;
} 

传递字符串参数就麻烦一点了。如:

C端代码:

static ret_t vgcanvas_web_set_text_align(vgcanvas_t *vgcanvas,
                                         const char *text_align) {
  EM_ASM_INT({ return VGCanvas.setTextAlign($0); }, text_align);
  return RET_OK;
} 

JS端代码:

VGCanvas.setTextAlign = function (value) {
  VGCanvas.ctx.textAlign = pointerToString(value);

  return true;
}

字符串参数传递到JS函数里时,JS函数拿到的内存地址的偏移量,需要把它解码出来,生成JS的字符串对象。pointerToString函数是这样实现的:

function pointerToString(pointer) {
  return pointer && Module.UTF8ToString(pointer, 1024) || null;
}

Module.UTF8ToString函数需要在前面介绍的EXTRA_EXPORTED_RUNTIME_METHODS中导出才能使用。

AWTK中还用了二进制数据作为参数,网上没有见到相关的例子,只好自己去看代码研究了。AWTK里需要把位图数据(rgba颜色值),传递到JS中,再设置到画布里。具体做法是这样的:

VGCanvas.updateMutableImage = function (id) {
  let mutableImage = ImageCache.get(id);

  let w = mutableImage.width;        
  let h = mutableImage.height;       
  let size = mutableImage.width * mutableImage.height;
  let start = mutableImage.addr >> 2;
  let end = start + size; 
  let array = Module.HEAP32.subarray(start, end);
  let ctx = mutableImage.getContext('2d');
  let imageData = ctx.getImageData(0, 0, w, h);
  let data = new Int32Array(imageData.data.buffer);

  for(let i = 0; i < size; i++) {
    data[i] = array[i];
  }                                         
  ctx.putImageData(imageData, 0, 0, 0, 0, w, h);

  return true;
}

mutableImage.addr是rgba数据的地址,它是用malloc分配出来的,我看了malloc函数的实现,它就相对于Module.HEAP32的字节数偏移量。由于HEAP32是4字节数据,在作为偏移量使用时,需要右移2位:

  let start = mutableImage.addr >> 2;

再通过subarray从HEAP32中获取这段数据:

  let array = Module.HEAP32.subarray(start, end);

另外这里值得一提的是,imageData.data是Int8Array,要转换成Int32Array,可以用下列方式:

  let imageData = ctx.getImageData(0, 0, w, h);
  let data = new Int32Array(imageData.data.buffer);

按下面这种方式,则是把每一个元素从8bit扩展成32bit了。

  let imageData = ctx.getImageData(0, 0, w, h);
  let data = new Int32Array(imageData.data);

三、JS调用C的函数

要在JS里调用C的函数,一般用Module.cwrap包装一下,它需要提供以下参数:

  • 函数名
  • 返回值
  • 参数列表

参数和返回值的类型有:

  • number
  • string
  • array

如:

Awtk._onImCommit = Module.cwrap('awtk_web_on_im_commit', 'number', ['string', 'number']);

Awtk.onImCommit = function (text, timestamp) {
  return Awtk._onImCommit(text, timestamp);
}

常见的用法的在文档中都有清楚的说明,这里不再赘述。如果参数是一个回调函数,就稍微麻烦一点。

  • 1.要导出addFunction/removeFunction(参考前面)

  • 2.要指定参数RESERVED_FUNCTION_POINTERS。

如:

-s RESERVED_FUNCTION_POINTERS=1000
  • 3.调用addFunction把函数转成一个number,再作为参数传入。

如:

widget_on(this.nativeObj, type, Module.addFunction(wrap_on_event(on_event)), ctx);

最麻烦的是函数用完之后,要调用removeFunction把函数从表里移出,对于同步调用的回调函数这没有什么问题,但是对异步调用函数,特别是多次调用的异步函数,什么时候可以移出只有C代码里才知道,所以需要在C代码里添加处理。如:

#ifdef AWTK_WEB_JS
#include <emscripten.h>
#endif /*AWTK_WEB_JS*/

static ret_t emitter_item_destroy(emitter_item_t* iter) {
  if (iter->on_destroy) {
    iter->on_destroy(iter);
  }

#ifdef AWTK_WEB_JS
  EM_ASM_INT({ return TBrowser.releaseFunction($0); }, iter->handler);
#endif /*AWTK_WEB_JS*/

  memset(iter, 0x00, sizeof(emitter_item_t));
  TKMEM_FREE(iter);

  return RET_OK;
}

© 著作权归作者所有

lixianjing
粉丝 10
博文 9
码字总数 10611
作品 2
深圳
私信 提问
国产开源 GUI 引擎 AWTK 1.1 发布

一、介绍 AWTK全称Toolkit AnyWhere,是ZLG开发的开源GUI引擎,旨在为嵌入式系统、WEB、各种小程序、手机和PC打造的通用GUI引擎,为用户提供一个功能强大、高效可靠、简单易用、可轻松做出炫...

lixianjing
04/25
5.7K
16
AWTK 1.2 发布,国产开源 GUI 引擎

AWTK全称为Toolkit AnyWhere,是ZLG倾心打造的一套基于C语言开发的GUI框架。旨在为用户提供一个功能强大、高效可靠、简单易用、可轻松做出炫酷效果的GUI引擎,并支持跨平台同步开发,一次编程...

lixianjing
07/30
4.1K
24
开源 GUI 引擎 - AWTK

AWTK = Toolkit AnyWhere 随着手机、智能手表等便携式设备的普及,用户对 GUI 的要求越来越高,嵌入式系统对高性能、高可靠性、低功耗、美观炫酷的 GUI 的需求也越来越迫切,ZLG开源 GUI 引擎...

lixianjing
02/20
8.8K
16
物联网 GUI 开发套件 - AWTK-IOTJS

一、介绍 关于 iotjs iotjs 是三星开源的 javascript 物联网开发平台。它为 javascript 应用程序提供了访问硬件、网络、文件系统和异步化的能力,功能类似于 nodejs,但无论是代码体积还是内...

lixianjing
06/11
4.5K
2
物联网 GUI 开发套件 AWTK-IOTJS 发布

一、介绍 awtk-iotjs 将 awtk 和 iotjs 有机结合起来,用 javascript 打通 GUI、网络和硬件,完全采用 javascript 开发物联网应用程序。 关于 iotjs iotjs 是三星开源的 javascript 物联网开...

lixianjing
06/11
3K
6

没有更多内容

加载失败,请刷新页面

加载更多

还为PDF转Word抓狂?以下神器让你在职场倍受欢迎!

身在职场的你,是否一直在琢磨:如何能让自己在公司更受欢迎?如何才能在办公室里混个好人缘?如何在同事圈里留个好印象?其实,想要让自己成为受欢迎的人,只要让自己成为大家需要的人不就行...

foxit2world
17分钟前
6
0
AndServer+Service打造Android服务器实现so文件调用

so 文件调用 随着 Android 移动安全的高速发展,不管是为了执行效率还是程序的安全性等,关键代码下沉 native 层已成为基本操作。 native 层的开发就是通指的 JNI/NDK 开发,通过 JNI 可以实...

夜幕NightTeam
18分钟前
3
0
Docker下kafka学习三部曲之二:本地环境搭建

在上一章《 Docker下kafka学习,三部曲之一:极速体验kafka》中我们快速体验了kafka的消息分发和订阅功能,但是对环境搭建的印象仅仅是执行了几个命令和脚本,本章我们通过实战来学习如何编写...

程序员欣宸
19分钟前
4
0
萌新推荐!不再为Excel转换PDF发愁,Aspose.Cells for .NET一步到位!

Aspose.Cells for .NET(点击下载)是Excel电子表格编程API,可加快电子表格管理和处理任务,支持构建具有生成,修改,转换,呈现和打印电子表格功能的跨平台应用程序。 将Excel工作簿转换为...

mnrssj
19分钟前
6
0
对于绘画小白怎么画制服?该注意什么?

怎样制作学生服装?想必绘画初学者们常常会想的问题吧,不知道怎样才能画好人物的衣服,别着急,今日就在这儿讲一些关于如何绘画学生衣服校服的教程给我们!期望能够帮到你们! 轻便西装是不...

热爱画画的我
25分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部