X Window研究笔记(11)
转载时请注明出处和作者联系方式
作者联系方式:李先静 <xianjimli at hotmail dot com>
11.X Window扩展机制--对象装饰
Decorator模式是一个非常重要的模式,它在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。X Server是用C开发的,不方便使用正统的装饰模式,但大量使用了类似装饰模式的扩展方式。可以被装饰的对象有:
- 屏幕: ScreenRec
- 窗口:WindowRec
- 图片: PixmapRec
- 绘图上下文: GC
- 颜色映射: Colormap
- 客户端:ClientRec
其实现过程可以这样理解:
- 初始化时,把原始对象的部分函数指针保存下来,并用自己的函数去替代它。
- 调用时,装饰之后的函数被调用,在该函数中,完成一些装饰性功能,并适当的位置调用原始的函数。
其中,用得最多的是对ScreenRec的装饰,下面我们以sprite模块中对ScreenRec的装饰为例,分析一下它的实现原理:
sprite指像鼠标指针一类的屏幕精灵,它的特点是,形状可能不规则,可以在屏幕上移动,会覆盖当前位置的图像,当它移动到新的位置时,自动恢复先前位置的图像。
sprite的初始化是在miSpriteInitialize中完成的:







































































































这个函数有点长,但我们只需要理解关键几点:
- AllocateScreenPrivateIndex分配私有数据空间,用于保存原始函数指针等信息。
- pPriv->PaintWindowBackground = pScreen->PaintWindowBackground; 之类的语句用于保存原始的函数指针。
- pScreen->PaintWindowBackground = miSpritePaintWindowBackground; 之类的语句用于把原始的函数指针替换为装饰之后的函数。
这里要特别说明的是,所谓的原始函数指针,并非一定是原装正品,可能已经是被别的模块装饰之后的函数。
下面我们继续看函数调用的实现:




























为了看明白这段程序,先得弄清楚两个宏:
SCREEN_PROLOGUE: 用于取出原始的函数指针,后面可以调用原始函数。
#define SCREEN_PROLOGUE(pScreen, field)/
((pScreen)->field = /
((miSpriteScreenPtr) (pScreen)->devPrivates[miSpriteScreenIndex].ptr)->field)
SCREEN_EPILOGUE:重新把装饰过的放回去,以便于下次再调。
#define SCREEN_EPILOGUE(pScreen, field, wrapper)/
((pScreen)->field = wrapper)
弄清楚了这两个宏,上面的程序不难理解了。这种扩展方式的好处在于,运行时动态为对象添加功能,同时又避免了扩展功能与框架的耦合,这是通过子类继承父类,然后重载部分虚函数无法实现的。
(待续)