DirectFB 代码导读
转载时请注明出处和作者联系方式
作者联系方式:李先静 <xianjimli at hotmail dot com>
DirectFB 是一个庞大的系统,对它进行彻底分析要花不少时间。幸好多数情况下,只要弄清楚它的基本架构,再重点读一些关键的代码,也就差不多了。前几个月为了完善 DFB 的窗口管理器,我花了一些时间去研究 DFB 的架构。把其中一些经验写到这里,供有兴趣的朋友参考。
总的说来, DFB 由以下几部分组成:
1. 基本库函数。 这部分代码在 lib 目录下,它分为三个部分:
- direct : 里面是一些公共函数,其中包括哈希表、链表、线程、调试信息、 signal 处理、优化过的 memcpy 和平台相关的一些函数。
-
- fusion :它有两个版本,一个是针对单进程的,要求所有应用程序在一个进程中运行,这相对来说比较简单。另外一个是针对多进程的,应用程序可以在多个进程中运行。它实现了一些进程间通信机制,其中包括互斥、共享内存、共享内存中的 vector 实现、带引用计数的内核对象和 reactor 等。多进程版本还需要一个内核模块 linux-fusion 的支持。
-
- voodoo : 不清楚(若那位高手知道,请补充一下,谢谢)。
2. 对第三方组件库的包装。 这部分代码在 interfaces 目录下。 Interfaces 可能会引起别人的误解,因为它并不是 DFB 对外提供的接口,而是把第三方组件纳入 DFB 的接口。它包括三类:
-
- 字体 。字体有点阵字体和矢量字体之分,矢量字体又有诸如 truetype 之类几种格式。前者可能比较简单,而后者的处理相当复杂,要借助如 freetype 等第三方程序库来实现。 DFB 定义了 IDirectFBFont 接口来处理字体,在第三方字体程序库上加上一个 adapter 就可以在 DFB 中使用了。
-
- 图片 。图片格式的种类很多,像 BMP 之类的位图处理可能比较简单,而像 JPG 和 PNG 等的图片,采用了高级的压缩技术,解压算法比较复杂,通常需要第三方程序库的支持。 DFB 定义了 IDirectFBImageProvider 接口来处理图片,在第三方图片程序库上加上一个 adapter 就可以在 DFB 中使用了。
-
- 视频 。视频格式更多,解压算法也更复杂,自然也要借助第三方库来实现。 DFB 定义了 IDirectFBVideoProvider 接口来处理视频,在第三方视频程序库上加上一个 adapter 就可以在 DFB 中使用了。
3. 核心代码。 这部分代码在 src 目录下。它可以分为两大类:
-
- 核心组件 。 DFB 的 core 由多个部分组成,每个部分称为一个 core_part ,都实现同一个接口 CorePart 。这个接口并不描述它们的功能,而是用于管理的。初看这些函数时,可能会感到有些奇怪。最好要先了解 DFB 采用的 master/slave 模型:第一个运行应用程序是 master 进程,后来运行的应用程序是 slave 进程。 master 进程负责初始化和 ~ 初始化 arena ,它只能在所有 slave 退出之后才能退出。而 slave 进程则可以随时加入 arena ,也可以随时退出 arena 。
-
- CoreInitialize : master 进程初始化 arena 。
-
- CoreJoin : slave 进程进入 arena 时的初始化。
-
- CoreShutdown : master 进程 ~ 初始化 arena 。
-
- CoreLeave : slave 进程离开 arena 时的 ~ 初始化。
-
- CoreSuspend :休眠,主要用于省电功能。
-
- CoreResume :唤醒,主要用于省电功能。
核心组件包括下面几个组件:
-
- dfb_core_clipboard: 剪切板。
-
- dfb_core_colorhash :调色板。
-
- dfb_core_gfxcard :图形卡,主要完成基本的绘图功能,如绘直线、填充等等。
-
- dfb_core_input :输入设备。
-
- dfb_core_layers :分层功能,好像要硬件支持,通常都只有一个层。
-
- dfb_core_screens :逻辑屏幕 ( 可能像 X 一样支持多个屏幕吧,不太清楚,有时间再研究 ) 。
-
- dfb_core_system :显示输出,把 gfxcard 绘制后的图形数据输出到屏幕上,即可以通过 fbdev 输出到本机屏幕上,也可以通过 sdl/x11/vnc 输出到远程主机的屏幕上。对于像 sdl/x11 等,也包括对输入事件的处理。
-
- dfb_core_wm :窗口管理器。
以上这些 core_part ,有的是直接实现的,比如 clipboard 。有的只是一层包装,具体的实现在一个独立的共享库中,在运行时通过参数来控制加载具体的实现,如 system 。
-
- 对外接口 。这主要是给上层应用程序使用的。其中包括:
-
- IDirectFBInputDevice: 输入设备
-
- IDirectFBScreen: 屏幕。
-
- IDirectFBSurface: 绘图表面。
-
- IDirectFBPalette: 调色板。
-
- IDirectFBFont: 字体
-
- IDirectFBImageProvider :图片
-
- IDirectFBVideoProvider :视频
-
- IDirectFBWindow :窗口
-
- DirectFBEventBuffer: 事件缓冲
4. 窗口管理器。 这部分代码在 wm 目录下。 DFB 实现了两个窗口管理器。
-
- default :实现了基本的窗口管理功能,支持一些快捷键。
-
- unique :功能也很弱,不过架构还可以,加入自己的功能很方便。
5. 输入设备。 这部分代码在 inputdrivers 目录下。其实这些代码并不是真正的驱动,只是一个 adapter 层,它把从 linux 设备文件读到的事件,转换成 DFB 自己的事件格式,然后调用 dfb_input_dispatch 把事件分发出去。
6. 输出设备。 这部分代码在 system 目录下。这也是一个 adapter 层,主要对显示设备的抽象,有的也包括对输入事件的处理。其中包括:
-
- fbdev: 输出到 frame buffer 。
-
- osx: 输出到 mac os 上。
-
- vnc : 输出到 Virtual Network Computing (类似于微软远程桌面的一个协议)。
-
- x11 : 输出到 X Window 上,在 0.9.24 仍然有问题,建议使用 SDL 。
-
- sdl : 输出到 Simple DirectMedia Layer 。
- sdl : 输出到 Simple DirectMedia Layer 。
7. 值得注意的几个问题:
-
- master/slave 模型 。 master/slave 是 DFB 的基本模型,一定要先了解它,否则很难了解 DFB 的基本架构。
-
- reactor 模式 。 DFB 中的消息,无论是进程内的,还是进程间的,都是通过 reactor 来传递的。这是一种简单的发布 - 订阅机制,谁关心谁就注册。不先弄清楚 reactor 的机制,很容易就被消息的流向搞糊涂了。
-
- 加锁术语 。加锁 / 解锁动作常用 lock/unlook 、 acquire/release 、 wait/release 等术语,而 DFB 里使用 skirmish_prevail/skirmish_dismiss 。
-
- 引用计数术语 。增加 / 减少引用计数常用 ref/unref 、 addref/release 等术语。 DFB 里使用了 ref/unref ,同时增加了几个动作: link/unlink 用于增加和减少全局引用计数, ref 增加的计数,只要应用程序退出, fusion 自动释放这个引用计数。而 link 增加的引用计数非要用 unlink 减少才行,应用程序退出时不会自动减少。 inherit: 继承另外一个对象的引用计数,即把被继承的对象的引用计数加到继承者身来,不但如此,当被继承的对象的引用计数增减时,自动增减继承者的引用计数。
-
- 注册 / 注销术语 。注册 / 注销常用 register/unregister 等术语, DFB 使用了 attach/detatch 。
-
- 内核对象的宏 。 DFB 用宏 FUSION_OBJECT_METHODS 去实现一个 fusion object 的子类, FUSION_OBJECT_METHODS 是在 object.h 里定义的。像 CoreWindow 和 CoreSurface 等内核对象,都调用这个宏去实现自己的方法。
-
- CorePart 的宏 。 DFB 用宏 DFB_CORE_PART 去实现一个 core part , DFB_CORE_PART 是在 core_parts.h 定义的。
-
- 动态模块的加载 。 DFB 并不要求动态加载模块实现特定的接口函数,而当模块被加载时 (dlopen 时 ) ,把自己安装到框架中。模块使用了 gcc 的 __attribute__((constructor)) 扩展,模块被加载时,该函数自动执行,然后调用 direct_modules_register 注册自己。
-
- cardstate :我开始被 blit 函数弄糊涂了,并不像 WIN32 下那样要求指明源和目标。后来才知道它是调用另外的函数去设置源和目标,而不是通过参数来指定。
在研究 DFB 时,我的主要精力放在 DFB 的架构、窗口管理器、输入设备驱动和 fusion 上,其它地方花的时间比较少,特别是对 gfx 只有一个模糊的概念,抱歉不能在这些方面提供更多信息。我对图形算法不太熟悉,大家可以问我某个流程是怎么走的,某个对象与另外一个对象是怎么交互的,但不要问我某个算法是怎么实现的。
欢迎交流。