DirectFB运行机制

  • 格式:doc
  • 大小:26.00 KB
  • 文档页数:4

事实上我对DirectFB的理解是比较肤浅的,几年前的确花了一些时间研究去DirectFB的实现,但只是集中在一部分功能的代码上。

后来GUI(DirectFB/GTK)由一位同事接手了,我就没有花太多时间在上面了。

可能是因为前面写过几篇关于DirectFB的文章,结果还真有不少网友把我当专家了。

答应过一位网友写篇介绍DirectFB运行机制的文章,这个周末花了点时间写了这篇短文,放在这里供有需要的朋友参考。

文中若有不当之处,望大家不吝指正。

DirectFBInit 这个函数其实并没有做什么特殊的初始化工作,只是加载了配置文件而已。

加载的顺序如下,重复的配置内容以后加载的为优先:系统全局配置文件,老版本文件为/etc/directfbrc,新版本文件为SYSCONFDIR"/directfbrc"用户全局配置文件,文件为~/.directfbrc(系统)应用程序特定的配置文件SYSCONFDIR"/directfbrc.应用程序名"(用户)应用程序特定的配置文件~/.directfbrc.应用程序名命令行参数DirectFBCreate 真正的初始化是在这里面进行的,严格的说这也不叫初始化了,因为这是创建DirectFB对象,对象当然是可以创建/销毁多次,而初始化通常只能做一次了。

考虑到在一个进程中DirectFBCreate通常只会调一次,进程退出时才销毁,所以我们还是把它看作初始化。

现在我们来看看DirectFBCreate做了什么: direct_initialize direct是一个基础库,这个函数主要做了两件事情:初始化log。

direct实现了三种LOG方法,第一种是输出到stderr上,第二种是输出到指定的文件里,第三种是通过UDP输出到网络上。

这种方法对嵌入式环境是特别有用的,因为很多时候没有机会看终端上的信息。

安装信号处理函数。

主要是针对像SIGTERM这类会导致程序退出的SIGNAL,DirectFB希望在程序退出之前做些善后处理。

值得学习的是direct是一个基础库,它不能直接调用上层组件的函数,所以提供了direct_signal_handler_add之类的函数让上层组件注册/注销回调函数,避免了下层函数与上层函数的耦合。

dfb_core_create 核心组件初始化。

核心组件都是以共享库形式存在的,这里主要是加载这些共享库,并调用相应的初始化函数。

为了避免框架与组件之间的耦合,DirectFB 是通过遍历目录去加载的,删除不必要插件可以提高加载速度。

dfb_system_lookup 其实system在这里并没有表示出它真正的意义,这里的system 实际上是指显示方式,比如基于framebuffer的fbdev和基于XWindow的x11等等。

它先调用direct_modules_explore_directory在找出目录中所有的system实现,然后与配置中指定的名称匹配,找到指定的system后调用GetSystemInfo得到一些system信息。

direct_find_best_memcpy 因为GUI里面memcpy的效率对系统整体的效率影响很大,所以DirectFB针对不同的系统实现了一些优化过的memcpy函数。

fusion_enter 初始化fusion,fusion是用于进程间通信的机制,/dev/fusion/n是用于进程间消息传递的文件,每个进程都有这样一个文件。

在本函数中会打开这个文件,并用ioctrl发送FUSION_ENTER命令。

随后映射/初始共享内存。

最后创建消息分发线程,其线程入口函数为fusion_dispatch_loop。

fusion_arena_enter 我没有直接用过arena,它的主要功能似乎只是对共享数据的管理,通过一个名称与一块数据关联起来,放到hash表中,每个进程都可以通过名字取相应的数据。

IDirectFB_Construct构造一个IDirectFB对象。

通过dfb_layer_at_translated获取primary layer.通过dfb_layer_get_primary_context获取primary layer, CoreLayerContext里面最重要的东西可能就是CoreWindowStack,CoreWindowStack管理着所有窗口,对应用程序产生直接的影响。

通过dfb_layer_context_windowstack获取窗口栈。

初始化IDirectFB接口的虚函数,让函数指针指到具体的函数上。

随便说一句,最近还有一些冒充专家的外行说C语言只能按面向过程的方法开发软件,甚至说C语言只适合开发小项目,真是无语了。

建议他们看看DirectFB的代码,update一下他们陈旧的观念。

如果是master进程,还要调用InitLayers, 它的主要功能是根据配置初始化layer,包括显示区域大小,调色板,透明色,窗口栈的显示区域和绘制背景(背景图片或者颜色填充)等等。

dfb_wm_post_init如果是master进程,还要执行窗口栈的PostInit,我看了几个窗口管理器的实现,这个函数都是空的。

dfb_core_activate初始化完成后激活dfb_core。

这也是很重要的,可以避免初始化完成之前dfb_core被其它线程使用。

IDirectFB_CreateInputEventBuffer这个函数创建一个EventBuffer,通过EventBuffer可以获取输入事件,不过通常应该通过Window去创建EventBuffer,那样可以收到更高层的事件。

InputEventBuffer实际上主要是对InputDevice的包装,不要小看这个包装,包装后有三个重要变化:Event被缓冲,不用担心事件丢失或者阻塞驱动程序。

包装前的InputDevice事件是来一个处理一个,包装后则是先放到缓冲中,然后一个一个的处理。

由被动变为主动,更符合GUI的事件处理的惯例。

包装前是上层设置回调函数,在事件到来时回调函数被下层调用,包装后是上层主动调用GetEvevnt 获取事件。

由多线程变为单线程,简化了处理。

包装前是由输入线程回调上层设置的回调函数,每个输入设备都是一个独立的线程,所以回调函数要考虑多线程并发执行的问题,包装后多线程问题由EventBuffer处理掉了,上层通过GetEvent获取事件并处理,处理函数始终是在主线程中执行的,所以不用考虑与输入线程的并发问题了。

IDirectFB_SetCooperativeLevel设置指定的IDirectFB对象与其它IDirectFB对象的协作级别,DFSCL_FULLSCREEN和DFSCL_EXCLUSIVE的处理方式是一样的,与DFSCL_NORMAL的差别是它们有自己独立的Context,也就是说设置为DFSCL_FULLSCREEN的DirectFB对象自成一个体系,不会与其它DirectFB对象干扰。

在多进程情况下,一般是设置为DFSCL_NORMAL的,它保证所有窗口由同一个窗口器管理。

(题外话,这个函数里调用了drop_window函数,drop_window的实现是有点问题的,它始终调用dfb_windowstack_cursor_enable去打开光标,这是不对的,应该根据据情况而定,在IDirectFB_Destruct里调用时就不应该打开光标,因为这可能会造成程序不正常退出。

) 输入设备事件所有GUI系统都是基于事件驱动模型的,搞清楚DirectFB事件处理过程对理解DirectFB是很有帮助的。

这里介绍一下DirectFB的输入事件处理。

输入设备驱动程序输入设备驱动程序的代码是放在inputdrivers目录中的,编译之后安装到$(PREFIX)/lib/directfb-1.1-0/inputdrivers中。

运行时每个驱动程序都会创建一个线程,它们通常都是挂在设备文件上,当有输入事件时就会被唤醒。

唤醒后先读取输入事件,然后调用dfb_input_dispatch把事件分发给上层组件。

要注意的是,输入设备线程是在driver_open_device里创建的,而driver_open_device是在dfb_core_create函数里调用的,也就是说线程起来的时候dfb_core可能还没有完成初始化,如果在这个时候有输入事件上来,可以会造成程序死掉。

为了避免这个问题,应该在dfb_input_dispatch里检查一下dfb_core初始化是否完毕。

dfb_input_dispatch先调用fixup_xxx函数对输入事件做些转换和映射,再调用core_input_filter对一些特殊键处理,比如屏幕截图等,再调用fusion_reactor_dispatch分发事件。

分发时先通过函数_dfb_windowstack_inputdevice_listener把事件分发给窗口管理,然后再分发给其它全局回调函数,最后通过reactor把分发给用户注册的回调函数。

_dfb_windowstack_inputdevice_listener通过dfb_wm_process_input调窗口管理器的wm_process_input函数。

default窗口管理器是在这个函数里对输入事件处理的,它的实现比较简单,我们这里不讨论了。

unique窗口管理里却不是以这种方式实现的,它调用dfb_input_add_global把_unique_device_listener注册为全局的输入事件的回调函数。

其实unique也完全可以用dfb_wm_process_input来实现的,可能是实现者的想法不一样而已。

_unique_device_listener则按输入设备类别把事件分发给相应的处理函数,这些函数是在devices中实现的。

我们broncho平台的手写就是在pointer.c中拦截笔点事件的,现在想来,手写完全可以独立出来的。

这些函数把DFBInputEvent事件转换为UniqueInputEvent事件,再调用unique_device_dispatch分发事件。

unique_device_dispatch这里消息分为两路,一路经_unique_cursor_device_listener去更新当前光标的位置,一路经_unique_input_switch_device_listener把消息投递到焦点窗口。

_unique_input_switch_device_listener消息经unique_input_channel_dispatch到_unique_window_input_channel_listener到dfb_window_post_event到IDirectFBEventBuffer_WindowReact到IDirectFBEventBuffer_AddItem,最后放入EventBuffer中。