转载时请注明出处和作者联系方式
文章出处:http://blog.youkuaiyun.com/jack0106
作者联系方式:冯牮 fengjian0106@yahoo.com.cn
clutter是一个GUI库,使用opengl作为底层的绘图引擎。整个库使用c语言开发,基于glib和gobject对象系统。通常情况,main函数的形式如下:
熟悉gtk的朋友可以看出,main函数中的这种代码结构,和gtk程序中的main函数结构很类似,这是因为clutter和gtk中的主事件循环,都是基于GMainLoop以及GSource,而且他们的设计上,本身就有某种相似性。
最近把clutter-0.2,clutter-0.4和clutter-1.0版本的源代码翻出来看了看,主要是看其中事件循环这一部分的代码,学习一下其中的代码实现方法。
gui程序,从原理上来说,就是接收用户的输入(比如键盘输入,鼠标输入等),然后针对这些不同的输入事件,做出不同的处理。在GMainLoop这个框架中,就需要继承GSource,并且重新实现prepare/check/dispatch这三个虚函数,然后把这个子类GSource添加到主事件循环中。clutter-0.2的代码量最小,所以以此作为分析学习的切入点。下面是从clutter-0.2中取出的代码片段,我们重点看一下clutter中实现的一个GSource子类--ClutterXEventSource
这个版本的clutter,是运行在X11环境上的。Xserver管理了所有的输入设备,将所有的输入事件转换成Xevent,然后分发给对应的Xclient。Xclietn和Xserver之间,则是通过一个socket进行协议通信。所以ClutterXEventSource中,定义了一个GPollFD event_poll_fd,这个变量中,保存的就是cultter和Xserver建立的socket连接的文件描述符。
prepare/check/dispatch这三个函数的代码,也比较简单,原理上看,就是等待XServer发送过来的用户输入事件,然后就进入dispatch函数,在dispatch函数,获取Xserver发送过来的XEvent,紧接着就调用clutter_dispatch_x_event()。
到这一步为止,用户通过键盘或鼠标的输入,就已经传递到了clutter内部,ClutterXEventSource的工作,就算是完成了。剩下的操作,是clutter对这个XEvent的转换(数据类型转换等等),以及在内部的分发,传递和处理(将event分发到对应的ClutterActor,然后对应的ClutterActor处理这个event),这个分发过程的细节,不在此次的讨论范围内。当分发和处理结束后,就将进入GMainLoop的下一次循环调度,当用户再次输入的时候,dispatch函数,将被再次调用。这样的话,整个GUI图形环境就运行起来了,ClutterXEventSource接收用户的输入,然后clutter处理这个输入事件,使用opengl重新绘制图形,然后这一次事件处理结束,clutter等待下一次用户输入。
clutter这个库的一个优势,就是可以方便的制作出动画效果。动画的原理,就是在固定的时间间隔上,重新绘制视图,只要这个时间间隔选取的合适,就可以产生动画效果。要在GMainLoop中实现动画,就需要用到另外一个GSource的子类--GTimeoutSource(GTimeoutSource是一个内部对象,作为使用者,我们只需要使用g_timeout_*这一簇函数就行了),设置好合适的时间间隔后,类似的,在GTimeoutSource的dispatch函数中,调用clutter的绘图引擎重新渲染视图,就可以产生动画的效果。
clutter中事件循环的基本原理就是这样,因为参考的是clutter-0.2版本的代码,代码量比较小,所以分析起来也比较容易。熟悉了这个版本的代码后,就可以看更高版本的代码实现细节了,我选的是0.4和1.0版本的源代码,看完这两版代码后发现,从0.4版本开始,event相关的代码部分,就已经成型并且基本稳定,考虑到1.0才是clutter的正式版,因此选取clutter-1.0来进行说明。
clutter-0.2只能运行在X11环境中,因此在构造ClutterXEventSource的时候,代码没有设计更多的层次,在初始化代码中,直接就初始化了ClutterXEventSource。到了clutter-1.0中,采用面向对象思想进行了更好的设计,并且匹配了多种后端,包括X11/win32/sdl/egl等等。在clutter的初始化阶段,直接调用后端的初始化代码,然后再由每个后端各自初始化自己的事件循环。因此,在clutter中定义了一个纯虚类,ClutterBackend,这个纯虚类有如下的一系列函数接口,不同的后端,只要继承这个ClutterBackend并且实现如下的函数就可以了。
ClutterBackendX11结构体,就是ClutterBackend的一个子类,clutter_backend_x11_init_events函数,就是这个子类重新实现的init_events虚函数。
ClutterEventSource以及它的prepare/check/dispatch这3个函数,和clutter-0.2版本中的区别不大,结构都是一致的,clutter_event_dispatch函数,是用户输入事件对应的代码的入口点,在这个函数中,clutter获取Xevent,然后转换event并且分发处理,事件处理结束后,函数返回,进入poll下一次循环调度。
需要特别说明的一点是,从结构上来看,由于事件循环的初始化,是包含在后端的初始化过程中的,所以ClutterEventSource可以设计成ClutterBackendX11的一个成员变量,ClutterBackendX11中的event_source指针,就是指向这个成员变量。但是,从代码中可以看到,在ClutterEventSource中,也有一个指针backend,指向包含该source的ClutterBackend,这是因为clutter_event_dispatch这个函数的第一个参数是GSource,在dispatch函数中,如果要使用backend,就只能通过ClutterEventSource->backend这个指针来获取。
写到这里,还想起一件事,顺便一起记录下来,clutter整体的框架,和qt中的Graphics View Framework很类似。clutter中的ClutterStage,相当于qt中的QGraphicsScene,clutter中的ClutterActor,相当于qt中的QGraphicsItem。这两套框架的接口设计,差不多都是一样的,如果熟悉其中的一套框架,那么另外一套框架上手起来也是比较容易的。