X Window研究笔记(11)

本文详细探讨了XWindow系统中的扩展机制,特别是如何利用类似装饰者模式的方法来为现有对象如屏幕(ScreenRec)、窗口(WindowRec)等动态添加职责。通过具体示例,介绍了装饰者模式在XServer中的应用原理及其带来的灵活性。
X Window研究笔记(11)

转载时请注明出处和作者联系方式
作者联系方式:李先静 <xianjimli at hotmail dot com>

11.X Window扩展机制--对象装饰

Decorator模式是一个非常重要的模式,它在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。X Server是用C开发的,不方便使用正统的装饰模式,但大量使用了类似装饰模式的扩展方式。可以被装饰的对象有:

  1. 屏幕: ScreenRec
  2. 窗口:WindowRec
  3. 图片: PixmapRec
  4. 绘图上下文: GC
  5. 颜色映射: Colormap
  6. 客户端:ClientRec

其实现过程可以这样理解:
  1. 初始化时,把原始对象的部分函数指针保存下来,并用自己的函数去替代它。
  2. 调用时,装饰之后的函数被调用,在该函数中,完成一些装饰性功能,并适当的位置调用原始的函数。

其中,用得最多的是对ScreenRec的装饰,下面我们以sprite模块中对ScreenRec的装饰为例,分析一下它的实现原理:

sprite指像鼠标指针一类的屏幕精灵,它的特点是,形状可能不规则,可以在屏幕上移动,会覆盖当前位置的图像,当它移动到新的位置时,自动恢复先前位置的图像。

sprite的初始化是在miSpriteInitialize中完成的:

Bool
miSpriteInitialize(pScreen,cursorFuncs,screenFuncs)
ScreenPtrpScreen;
miSpriteCursorFuncPtrcursorFuncs;
miPointerScreenFuncPtrscreenFuncs;
...{
miSpriteScreenPtrpPriv;
VisualPtrpVisual;
#ifdefRENDER
PictureScreenPtrps
=GetPictureScreenIfSet(pScreen);
#endif

if(miSpriteGeneration!=serverGeneration)
...{
miSpriteScreenIndex
=AllocateScreenPrivateIndex();
if(miSpriteScreenIndex<0)
returnFALSE;
miSpriteGeneration
=serverGeneration;
miSpriteGCIndex
=AllocateGCPrivateIndex();
}

if(!AllocateGCPrivate(pScreen,miSpriteGCIndex,sizeof(miSpriteGCRec)))
returnFALSE;
pPriv
=(miSpriteScreenPtr)xalloc(sizeof(miSpriteScreenRec));
if(!pPriv)
returnFALSE;
if(!miPointerInitialize(pScreen,&miSpritePointerFuncs,screenFuncs,TRUE))
...{
xfree((pointer)pPriv);
returnFALSE;
}

for(pVisual=pScreen->visuals;
pVisual
->vid!=pScreen->rootVisual;
pVisual
++)
;
pPriv
->pVisual=pVisual;
pPriv
->CloseScreen=pScreen->CloseScreen;
pPriv
->GetImage=pScreen->GetImage;
pPriv
->GetSpans=pScreen->GetSpans;
pPriv
->SourceValidate=pScreen->SourceValidate;
pPriv
->CreateGC=pScreen->CreateGC;
pPriv
->BlockHandler=pScreen->BlockHandler;
pPriv
->InstallColormap=pScreen->InstallColormap;
pPriv
->StoreColors=pScreen->StoreColors;

pPriv
->PaintWindowBackground=pScreen->PaintWindowBackground;
pPriv
->PaintWindowBorder=pScreen->PaintWindowBorder;
pPriv
->CopyWindow=pScreen->CopyWindow;
pPriv
->ClearToBackground=pScreen->ClearToBackground;

pPriv
->SaveDoomedAreas=pScreen->SaveDoomedAreas;
pPriv
->RestoreAreas=pScreen->RestoreAreas;
#ifdefRENDER
if(ps)
...{
pPriv
->Composite=ps->Composite;
pPriv
->Glyphs=ps->Glyphs;
}

#endif

pPriv
->pCursor=NULL;
pPriv
->x=0;
pPriv
->y=0;
pPriv
->isUp=FALSE;
pPriv
->shouldBeUp=FALSE;
pPriv
->pCacheWin=NullWindow;
pPriv
->isInCacheWin=FALSE;
pPriv
->checkPixels=TRUE;
pPriv
->pInstalledMap=NULL;
pPriv
->pColormap=NULL;
pPriv
->funcs=cursorFuncs;
pPriv
->colors[SOURCE_COLOR].red=0;
pPriv
->colors[SOURCE_COLOR].green=0;
pPriv
->colors[SOURCE_COLOR].blue=0;
pPriv
->colors[MASK_COLOR].red=0;
pPriv
->colors[MASK_COLOR].green=0;
pPriv
->colors[MASK_COLOR].blue=0;
pScreen
->devPrivates[miSpriteScreenIndex].ptr=(pointer)pPriv;
pScreen
->CloseScreen=miSpriteCloseScreen;
pScreen
->GetImage=miSpriteGetImage;
pScreen
->GetSpans=miSpriteGetSpans;
pScreen
->SourceValidate=miSpriteSourceValidate;
pScreen
->CreateGC=miSpriteCreateGC;
pScreen
->BlockHandler=miSpriteBlockHandler;
pScreen
->InstallColormap=miSpriteInstallColormap;
pScreen
->StoreColors=miSpriteStoreColors;

pScreen
->PaintWindowBackground=miSpritePaintWindowBackground;
pScreen
->PaintWindowBorder=miSpritePaintWindowBorder;
pScreen
->CopyWindow=miSpriteCopyWindow;
pScreen
->ClearToBackground=miSpriteClearToBackground;

pScreen
->SaveDoomedAreas=miSpriteSaveDoomedAreas;
pScreen
->RestoreAreas=miSpriteRestoreAreas;
#ifdefRENDER
if(ps)
...{
ps
->Composite=miSpriteComposite;
ps
->Glyphs=miSpriteGlyphs;
}

#endif

returnTRUE;
}


这个函数有点长,但我们只需要理解关键几点:
  1. AllocateScreenPrivateIndex分配私有数据空间,用于保存原始函数指针等信息。
  2. pPriv->PaintWindowBackground = pScreen->PaintWindowBackground; 之类的语句用于保存原始的函数指针。
  3. pScreen->PaintWindowBackground = miSpritePaintWindowBackground; 之类的语句用于把原始的函数指针替换为装饰之后的函数。

这里要特别说明的是,所谓的原始函数指针,并非一定是原装正品,可能已经是被别的模块装饰之后的函数。

下面我们继续看函数调用的实现:

staticvoid
miSpritePaintWindowBackground(pWin,pRegion,what)
WindowPtrpWin;
RegionPtrpRegion;
intwhat;
...{
ScreenPtrpScreen;
miSpriteScreenPtrpScreenPriv;

pScreen
=pWin->drawable.pScreen;

SCREEN_PROLOGUE(pScreen,PaintWindowBackground);

pScreenPriv
=(miSpriteScreenPtr)pScreen->devPrivates[miSpriteScreenIndex].ptr;
if(pScreenPriv->isUp)
...{
/**//*
*Ifthecursorisonthesamescreenasthewindow,checkthe
*regiontopaintforthecursorandremoveitasnecessary
*/

if(RECT_IN_REGION(pScreen,pRegion,&pScreenPriv->saved)!=rgnOUT)
miSpriteRemoveCursor(pScreen);
}


(
*pScreen->PaintWindowBackground)(pWin,pRegion,what);

SCREEN_EPILOGUE(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)

弄清楚了这两个宏,上面的程序不难理解了。这种扩展方式的好处在于,运行时动态为对象添加功能,同时又避免了扩展功能与框架的耦合,这是通过子类继承父类,然后重载部分虚函数无法实现的。

(待续)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值