zoukankan      html  css  js  c++  java
  • X Window研究笔记(11)

    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)
        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
    ->= 0;
        pPriv
    ->= 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)

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

    (待续)

     
  • 相关阅读:
    vue关于样式的绑定
    vue插口
    vue组件 父子 通讯
    vue实现翻页功能加高阶函数加购物车
    MySQL 索引面试总结
    phpstorm配置git并解决Terminal 中文乱码(Unicode 编码)的方法
    php 下载图片并打包成Zip格式压缩包
    MySQL5.6升级到5.7详细教程
    windows下安装vue教程
    一致性hash算法详解
  • 原文地址:https://www.cnblogs.com/zhangyunlin/p/6167678.html
Copyright © 2011-2022 走看看