《基于 GTK+ 和 X-window 的 GUI 在嵌入式 Linux 中的应用》
嵌入式 Linux 下 GUI 的选择,对大多数开发人员来说是一个需要权衡对比的过程。
选择 GTK+ 运行在 X 系统上,然后 X 系统运行在嵌入系统的 framebuffer 上,这会是一个很好的选择。
当然,GTK+ 与 X 一般都是被大家考虑为体积较大的桌面系统的好搭配,但实际上对于嵌入系统来说,它也有着诸多的优点:
1、 X-window 系统与 GTK+ 都非常稳定可靠,X-window 系统是经历了长期的开发及应用实践的,GTK+ 也是一个比较成熟的开放源代码项目;
2、 X-window 系统是一个灵活的 client/server 的模型结构,一个应用客户端的崩溃不会影响到图形系统的其他部分,这是一个很重要的特性,它有利于支持第三方应用的扩展开发,而不影响到主体部分;
3、 GTK+有两个重要的库:GDK和GLIB。
(1)GDK抽象了底层的窗口管理,要移植 GTK+ 到另一个不同的窗口系统的话,我们只需要移植 GDK 就可以了。
(2)GLIB 是一个工具集合,它包括了数据类型,各种宏定义,类型转化,字符串处理,任何应用程序都可以链接这个 GLIB 库,使用其中的各种数据类型、方法,来避免重复代码,或者说避免开发人员重新发明轮子,这样有利于减少整个系统的尺寸;
4、 对 GTK+/X 的裁剪是很容易的,它们有着很好的可配置的选项,有着清晰的代码结构,可以保证安全正确地去掉大段的不需要的代码;
5、 GTK+ 有着大量的应用,GTK+ 已经被用在了很多重要的应用系统中;
6、 GTK+ 的授权是 LGPL 方式的,X 是 non-copyleft free license 的,第三方开发的系统都能与它们进行链接;
7、 GTK+/X 二者都是基于 C 代码的,而不是C++;
8、 GTK+ 使用 C 来实现了面向对象的架构;
其他:
Microwindows 和 X-Window 相比也是一个不错的选择,它占用大约 100KB-600KB 大小的内存,和文件存储空间,虽然已经有了一个其上的 GTK+ 移植,但还是不够成熟;
对于X-window系统,广大的网络开发者已经做了大量的工作来减小其的尺寸,最知名的有TinyX。可以通过对不需要的代码的裁剪及去除XLIB中静态数据来减少总体的尺寸,如:color管理系统,弧形,粗线条等。
在大多数开发人员的印象里,X 系统很庞大,但实际上,你听到的,是那些对 X 不够了解的人的一种误解。在经过裁剪后的情况下,GTK+/X
要比 GTK+/FB 与 Qt/E 还要来得有效,且 XLIB 对一般的应用程序有着更好的支持作用,应用程序的开发会变得更高效。
我们可以从标准的 GTK+ 发行版本来裁剪,裁剪掉其中的不需要的,修改已经有的代码,并加入新的特性所需要的代码。裁剪的范围包括小的改动,也包括一些大的结构性的、核心的改变。
最开始,我们把不需要的 Widgets 去除掉,比如:GtkGamma、GtkHRuler、过时了的 GtkList(被 GtkCList所替代了)、和我们不需要的 GtkFrame 边框。
接着,修改Widgets的大小与绘制方法,GTK+提供了一个主题引擎机制,来控制窗口的外观与效果。它允许在运行中设置字体,设置行间隔,设置
绘制特性。这样的机制很不错,但不够灵活,代码中很多设置的地方都是使用硬编码的方式;另外,一种主题,就是一堆额外的代码段和参数,这样会增加整体的尺
寸。
需要找出影响到窗口系统整体尺寸的内容,再来修改它。比如,一个按钮的大小与绘制,包括这样的参数:边框的宽度,x/y的位置(主题引擎需要的参
数),缺省的间隔(常量),缺省的左上角的位置(常量),获得焦点。这些在嵌入系统中并不需要那么完整,我们可以根据实际的需求来简化代码,来避免
GTK+的复杂性。
另外,使用面向对象的方法,来继承窗口Widgets的特性,作为子类也是一个有效的方法。
GTK+总是假定一个窗口里面包含了另一个窗口,它们就是嵌套关系。但对于我们经常会碰到的有软键盘的应用时,就不完全正确了。软键盘虽然是属于一个窗口的,但却会超出那个窗口。所以为了突破这个假定,需要对GtkWindow增加一些特性,将软键盘处理成一种特殊的子窗口。
软键盘所在的窗口,需要处理软键盘的按键事件,并将按键转发给软键盘工具条。当软键盘按下,软键盘的回调函数就被注册到原始窗口上,这样软键盘就会响应按键事件。在GtkWindow上增加接口,可以创建,响应按键。
在小屏幕的嵌入系统中,可以将滚动条做得更简化些,去掉边框,使用单个滚动条。这些都更适合嵌入系统。
在嵌入系统GUI中,还需要建立一个窗口管理器。我们可以选择一个开放代码的,轻量级的X管理器,Aewm。在嵌入系统中,我们会将最上层的窗口设置为获得焦点,并且只有对话框能移动,能显示其标题栏。
窗口管理器是一个交互端,它可以管理内部与外部的应用程序的窗口。每一
个应用程序的窗口,都会建立一个 socket
连接,并取一个名字。一个应用可以把请求将自己放在窗口堆栈的最下面,或者将一个命名的应用往上移。如果一个对话框要在最上层的窗口上打开,那么窗口管理
器就将告诉这个最上层的窗口它将不再获得焦点,而新对话框将获得焦点。
经过一系列的改进后,我们就得到了一个稳定的,功能和性能都能满足嵌入系统要求的GUI了。在ARM系统下,得到的尺寸大小为:
其中 GTK+ 里面仍然还有不需要的代码,可以将其再去除。如果再简化一下的话,GTK+ 可以做到850KB,总体大小可以到 2.8M。
将修改过的 GUI 运行在一个 ARM7 的系统上,CPU 为 100MHZ,运行时的效果还不错,窗口响应用户操作的速度很迅速,新的画面创建与显示的速度都能接受。
但是,启动时的时间却有些问题,比较慢。在这个 CPU 上,应用程序画面加载与显示的时间需要 2.4秒,其中 1.5 秒是花在了建立与显示 UI 上。
在较慢的 CPU 上,这样的启动速度是 GTK+ 运行在 X,X 再写到 framebuffer
上导致的。我们需要分析具体的瓶颈在什么地方。在深入的调试中,当使用PC机来运行我们的应用,而在ARM设备上显示时,初始化和显示的时间几乎可以忽略
不计。同样,将应用运行在ARM设备上,而在PC机上显示时,性能也很好。所以数据包的传输,framebuffer上的显示及GTK+的运算速度都不是
问题所在。速度慢的原因可能是这几个因素的先后顺序引起的。而且内存的占用上也存在问题。在初始阶段,GTK+构造了大量的对象,GTK+和X是使用共享内存来通讯的,X写到framebuffer,framebuffer也是不变地写到显存上。这些都是发生在一个较窄总线上相同的内存空间里,这个就会引起速度慢。
现在知道了X在启动时比较花费时间。在客户模式下的GTK+的应用,需要连接到X server,通过了认证,得到位深及其他资源。当然,使用X系统的好处要远大于它的不足。X系统提供了一个灵活的client/server模型,这样更有利于应用的灵活加入。你可以在同一时间显示不用的应用窗口,而像GTK+/Fb等其他的GUI方式无法做到这一点,当然QPE是个例外。
2.4秒的延时,也并不能完全否定一切。在一个700MHZ的windows系统的PC上,Microsoft Word, Excel and
IE几乎都需要2秒的时间来启动。KEdit, 一个KDE的应用程序,在500MHZ的PIII上,加载的时间也需要1.37秒。
对于启动时间,需要采用其他的办法来加以改善。一个策略就是在用户等待的时候,显示一些东西来表示系统正在工作,这样会使用户忽略掉启动时间的缓慢。另一个策略就是可以预先在背后启动一些通用的程序,来有效减少集中启动的时间。这也是通常嵌入系统所惯用的做法。
在ARM7的系统上,由于没有浮点运算FPU,所以GTK+中的浮点运算部分最好是去掉,否则会大大影响性能。GTK+使用到的浮点变量只分布在少数的几个窗口中,并且去掉它们会带来3%到12%的性能提高。
高像素的应用会导致速度较慢,这大多是由于GTK+与X中对高像素的效率低下的处理有关。如涉及到的XPM,XPM (X pixmap)格式是被设计来做到较好的兼容性,而不是更加快速。X系统是一个像素一个像素地画到server的pixmap的。GTK+的像素处理也很低效,它是使用fgetc()来读取XPM文件的,这就会带来大量的上下文切换开销。
X窗口系统的结构也导致了像素的加载变慢。GTK+客户端需要加载,分析XPM文件,将像素值通过传输协议发送给server,然后server才将像素值放入framebuffer。如果客户端直接将数据写到framebuffer server那将会有效很多。
处理的GTK+像素的办法就是,写一个临时的中间过程,取得render过的像素,使用这个原始数据来替换XPM数据,这个原始数据就可以直接强制写到X server上。从结构上来看,这虽然不是一个很好的处理办法,但在效率上却要比使用XPM要快上80%。