-
Notifications
You must be signed in to change notification settings - Fork 0
Control
control就是相当与安卓中的View,对android的自定义控件的有了解都知道,自定义view最主要是重写onDraw、onMeasure、onLayout方法,其实还有一个onTouch方法。这里先简单看下一个control的结构体表示。
struct _LIBAROMA_CONTROL{
word id;
voidp internal;
LIBAROMA_WINDOWP window;
LIBAROMA_COLORSETP colorset;
/* px measured */
int x;
int y;
int w;
int h;
/* requested */
int rx;
int ry;
int rw;
int rh;
/* measured size */
int left;
int top;
int width;
int height;
/* minimum control size */
int minw;
int minh;
/* callbacks */
LIBAROMA_CONTROL_HANDLERP handler;
};这里可以看出control和window的数据结构比较类似多了一个最小的宽高。还有一个回调函数结构体LIBAROMA_CONTROL_HANDLERP handler
typedef struct {
dword (*message)(LIBAROMA_CONTROLP, LIBAROMA_MSGP);
void (*draw)(LIBAROMA_CONTROLP, LIBAROMA_CANVASP);
byte (*focus)(LIBAROMA_CONTROLP, byte);
void (*destroy)(LIBAROMA_CONTROLP);
byte (*thread)(LIBAROMA_CONTROLP);
} LIBAROMA_CONTROL_HANDLER, * LIBAROMA_CONTROL_HANDLERP;- draw回调和android的onDraw回调类似,这里有两个参数(LIBAROMA_CONTROLP, LIBAROMA_CANVASP第一个是控件本身,第二个是一张画布。
- thread回调相当于android view里面的draw方法,只不过这里如果是激活window,每16ms会调用一次,其作用是更新控件的一些状进状态,并进一步决定是否重绘control
- message 这个回调有点类似android view中的onTouch方法,是在一个事件处理线程中被调用,改函数的参数LIBAROMA_MSGP,是读取来自改control依附的window的事件队列。
- focus 这个回调暂时没发现用的场景,暂不分析。
- destroy control附属于的window销毁的时候调用,用来释放control对应的内存,和android的onDetachFromWindow有点类似
这里需要特别关注几个成员word id、 voidp internal、 LIBAROMA_WINDOWP window
- id和android 中view对应一个id类似。通过id可以遍历window中的control 树找到对应的控件。
- internal 改成员是voidp,用的时候一般强转成对应的结构体,该成员一般表示控件的属性和状态等(颜色,文字、样式等)。
- window control依赖的window一般在构造一个control的时候会传进一个window。
在windowmanager分析章节中,直到windowmanger有个线程,相当于渲染线程,改线程每16ms调用window的ui_thread回调和,wm的ui_thread回调,其中在window的ui_thread会遍历window里面的control,首先回调control的thread方法,根据thread回调决是否调用libaroma_control_draw,也就是在这个方法中绘制control,根据libaroma_control_draw,返回值,进一步决定是否要更新wm里面更新区域(现阶段只是更新windowmanger里面的几个变量)所有control中如果没有一个要更新界面的,windowmanger就不需要从新上屏,只要有一个需要更新,就需要更新windowmanger中几个变量决定更新区域,最后就是刷新上屏,上屏区域,就是之上几个变量决定的区域。
说这么多其实我们最关心control是怎么绘制出来的。就要详细分析下libaroma_control_draw方法。改方法有两个参数,第一个参数当前control,第二个参数决定是否立即上屏。
byte libaroma_control_draw(LIBAROMA_CONTROLP ctl, byte sync) {
LIBAROMA_CANVASP c = libaroma_control_draw_begin(ctl);
if (c != NULL) {
if (ctl->handler->draw != NULL) {
ctl->handler->draw(ctl, c);
}
libaroma_control_draw_end(ctl, c, sync);
return 1;
}
return 0;
}其实看起来是很简介,其实里面暗含好多玄机,大致看做了以下几件事
- 拿到一个Canvas(没错 Canvas,这里和android很类似)
- 回调control的回调draw方法,把刚才的Canvas传进去
- 最后调用libaroma_control_draw_end
写自己的控件一般是实现自己的draw方法,在画布上作图。画布其实就是控件依赖的window对应区域的存储空间。所以这里要先分析下画布拿到的过程。
安卓有Canvas,当时对这个Canvas其实不太理解,现在研究Libaroma之后,其实Canvas对应一片内存,先简单看下Canvas对应的数据结构。
struct _LIBAROMA_CANVAS{
int w; /* width */
int h; /* height */
int l; /* line size */
int s; /* width x height */
wordp data; /* color data */
bytep alpha; /* alpha data */
bytep hicolor; /* hicolor data */
byte flags; /* flags */
};这里简单解释下 w、h就是canvas的宽高 l现在暂时返现是window的line size ,s=w*h,这里有几个指针这里才是重点,这几个指针决定了绘制区域对应的存储空间。
LIBAROMA_CANVASP libaroma_control_draw_begin(LIBAROMA_CONTROLP ctl) {
if (!libaroma_window_isactive(ctl->window) || !libaroma_control_isvisible(
ctl)) {
return NULL;
}
LIBAROMA_WINDOWP win = ctl->window;
if (win->handler != NULL) {
if (win->handler->control_draw_begin != NULL) {
return win->handler->control_draw_begin(win, ctl);
}
}
if (win->dc == NULL) {
return NULL;
}
LIBAROMA_CANVASP c = libaroma_canvas_area(win->dc, ctl->x, ctl->y, ctl->w,
ctl->h);
return c;
}看看这个libaroma_control_draw_begin方法其实做的事情真的不少,大致分为以下几方面
- 看当前control在所依赖的window是否激活,或者是或否可见,不可见直接返回NULL,control就不绘制了,这里其实就是过滤下,因为渲染线程是遍历window上的所有control,这里就是对于不激活不可见的window进行过滤,减轻渲染线程的负担。
- 这里又有一个比较好玩的hook,如果window设置了control_draw_begin 回调就有window的回调分配对应的canvas,这里相当交给开发者自己实现自己的window,加大的可定制性。
- 这里看下默认获取Canvas指针的过程libaroma_canvas_area
LIBAROMA_CANVASP libaroma_canvas_area(
LIBAROMA_CANVASP parent,
int x,
int y,
int w,
int h) {
if (!parent) {
ALOGW("canvas_area parent is null");
return NULL;
}
/* initializing canvas memory */
LIBAROMA_CANVASP c = (LIBAROMA_CANVASP) calloc(sizeof(LIBAROMA_CANVAS),1);
if (!c) {
ALOGW("canvas_area malloc(LIBAROMA_CANVASP) Error");
return NULL;
}
if (!libaroma_canvas_area_update(c,parent,x,y,w,h)){
free(c);
return NULL;
}
return c;
}做了以下几件事
- 分配canvas结构体存储空间
- 更新canvas树结构体的成员变量
其实最重要的就是libaroma_canvas_area_update 函数
byte libaroma_canvas_area_update(
LIBAROMA_CANVASP c,
LIBAROMA_CANVASP parent,
int x,
int y,
int w,
int h) {
if (!parent) {
ALOGW("canvas_area_update parent is null");
return 0;
}
if (!c) {
ALOGW("canvas_area_update canvas is null");
return 0;
}
/* Set Target Positions */
int x2 = x + w;
int y2 = y + h;
/* Fix Positions */
if (x2 > parent->w) {
x2 = parent->w;
}
if (y2 > parent->h) {
y2 = parent->h;
}
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
/* Set Fixed Size */
w = x2 - x;
h = y2 - y;
if ((w < 1) || (h < 1)) {
ALOGW("canvas_area_update calculated width or height < 1");
return 0;
}
c->w = w;
c->h = h;
c->s = w * h;
c->flags = LIBAROMA_CANVAS_CHILD;
c->l = parent->l;
c->data = parent->data + (y * parent->l) + x;
if (parent->alpha != NULL) {
c->alpha = parent->alpha + (y * parent->l) + x;
}
else {
c->alpha = NULL;
}
if (parent->hicolor != NULL) {
c->hicolor = parent->hicolor + (y * parent->l) + x;
}
else {
c->hicolor = NULL;
}
return 1;
}其实就是无非就是做几件事情。
- 修正canvas 宽高,因为canvas对应的区域有可能超过window的区域。
- 把control在window对应位置的指针付给canvas成员,其实核心就在这里(找到对应的存储空间)
- 这里canvas有个flags比较有趣(这里应该是canvas结构体在释放的时候有用)
这里我才搞懂为啥canvas的l 为parent的l,这个可能在绘制的时候找到,对应的存储空间,因为这里我们得到的只是头指针,其实一个方形区域对应的window里面的内存是不连续的。window分配的空间应该是连续的。
绘制控件完毕,无非就是是否直接上屏。这里就贴出下代码。
void libaroma_control_draw_end(LIBAROMA_CONTROLP ctl, LIBAROMA_CANVASP c,
byte sync) {
if (sync) {
libaroma_control_draw_flush(ctl, c, sync);
}其实就是根据sync决定是否上屏,继续跟进去看。
byte libaroma_control_draw_flush(LIBAROMA_CONTROLP ctl,
LIBAROMA_CANVASP canvas, byte sync) {
if (ctl == NULL) {
ALOGW("window_control_draw ctl is null");
return 0;
}
if (ctl->window == NULL) {
ALOGW("window_control_draw ctl doesn't have window");
return 0;
}
LIBAROMA_WINDOWP win = ctl->window;
if (win->handler != NULL) {
if (win->handler->control_draw_flush != NULL) {
return win->handler->control_draw_flush(win, ctl, canvas, sync);
}
}
if (win->dc == NULL) {
ALOGW("window_control_draw window dc uninitialized");
return 0;
}
if (sync) {
int sx = ctl->x;
int sy = ctl->y;
libaroma_window_sync(win, sx, sy, ctl->w, ctl->h);
}
return 1;
}这里面也有hook-control_draw_flush 这里也加大了自定义程度,一般在写复杂控件的时候用,这里看下默认的流程,其中核心就在libaroma_window_sync.这里先不深入分析,里面也有一个hook(sync)之后分析从头理一般window 对应的Canvas的存储空间的分配,获取子Canvas,上屏的流程。
其实这里上屏过程会从先从window到windowmanger,再到fb回调过程。,这里先埋个引子(window对应的dc 是在哪里获取的?)
control绘制的流程其实是特定的主要入后就是在ui渲染线程
- windowmamger的ui渲染线程遍历window里面的控件,调用控件的thread回调
- 根据thread回调决定是否绘制控件(thread回调一定程度上减轻了渲染线程的负担)
- 获取控件对应的canvas
- 控件是否激活
- 控件在所依附window是否可见其中激活可以个自定义hook回调control_isvisible
- 不激活,不可见则不绘制控件,这里一定程度也减轻了渲染线程的负担
- 调用控件的draw方法 绘制控件
- 决定是否同步上屏,这里面会从window 到 windowmanger 到fb 一层层的call下去
再总结下window对应回调调用的时机,从上到依次回调
- control_isvisible hook判断control是否可见,可见之后才找对应的canvas。
- control_draw_begin hook分配control依赖window对应位置的canvas。
- control_draw_flush hook上屏过程,如果没有hook就是默认上屏过程
- sync 默认上屏过程hook。