文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>X Window研究笔记(11)

X Window研究笔记(11)

时间:2010-03-31  来源:erlongabc

转载时请注明出处和作者联系方式
作者联系方式:李先静 <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)
    ScreenPtr               pScreen;
    miSpriteCursorFuncPtr   cursorFuncs;
    miPointerScreenFuncPtr  screenFuncs;
...{
    miSpriteScreenPtr   pPriv;
    VisualPtr           pVisual;
#ifdef RENDER
    PictureScreenPtr    ps = GetPictureScreenIfSet(pScreen);
#endif
   
    if (miSpriteGeneration != serverGeneration)
    ...{
        miSpriteScreenIndex = AllocateScreenPrivateIndex ();
        if (miSpriteScreenIndex < 0)
            return FALSE;
        miSpriteGeneration = serverGeneration;
        miSpriteGCIndex = AllocateGCPrivateIndex ();
    }
    if (!AllocateGCPrivate(pScreen, miSpriteGCIndex, sizeof(miSpriteGCRec)))
        return FALSE;
    pPriv = (miSpriteScreenPtr) xalloc (sizeof (miSpriteScreenRec));
    if (!pPriv)
        return FALSE;
    if (!miPointerInitialize (pScreen, &miSpritePointerFuncs, screenFuncs,TRUE))
    ...{
        xfree ((pointer) pPriv);
        return FALSE;
}
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;
#ifdef RENDER
    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;
#ifdef RENDER
    if (ps)
    ...{
        ps->Composite = miSpriteComposite;
        ps->Glyphs = miSpriteGlyphs;
    }
#endif

    return TRUE;
}


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

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

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

static void
miSpritePaintWindowBackground (pWin, pRegion, what)
    WindowPtr   pWin;
    RegionPtr   pRegion;
    int         what;
...{
    ScreenPtr       pScreen;
    miSpriteScreenPtr    pScreenPriv;

    pScreen = pWin->drawable.pScreen;

    SCREEN_PROLOGUE (pScreen, PaintWindowBackground);

    pScreenPriv = (miSpriteScreenPtr) pScreen->devPrivates[miSpriteScreenIndex].ptr;
    if (pScreenPriv->isUp)
    ...{
        /**//*
         * If the cursor is on the same screen as the window, check the
         * region to paint for the cursor and remove it as necessary
         */
        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)

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

(待续)
排行榜 更多 +
合合合军团

合合合军团

策略塔防 下载
街头滑板

街头滑板

体育竞技 下载
武者生存

武者生存

体育竞技 下载