转至元数据结尾
转至元数据起始

正在查看旧版本。 查看 当前版本.

与当前比较 恢复该版本 查看页面历史

版本 1 当前的 »

client通过发送ConfigureWindow请求配置窗口,配置窗口的内容包括窗口位置、窗口尺寸、窗口栈的调整、窗口边框大小等。X11 Server通过ProcConfigureWindow请求,调用ConfigureWindow实现。

  1. 根据掩码获取窗口的新坐标,宽高尺寸(CWX | CWY | CWWidth | CWHeight)等。涉及窗口移动和缩放

  2. 根据掩码获取窗口边框大小,窗口栈调整位置等

    1. CWBorderWidth:调整窗口边框

    2. CWStackMode:调整窗口在栈中的位置,包括:TopIf、BottomIf、Opposite、Above、Below等;其中CWSibling可指定相对哪个兄弟窗口的栈位置变化

    3. 重定向ConfigureWindow请求至管理客户端,如:窗口管理器等,发送ConfigureRequest事件

    4. 重定向窗口缩放至管理客户端,发送ResizeRequest事件

    5. 通过回调ConfigNotify函数告知服务端的X11扩展协议窗口配置变化,如:composite等

    6. 通过发送ConfigureNotify事件,通知感兴趣的client窗口配置变化

    7. 回调ChangeBorderWidth处理边框

    8. 回调MoveWindow处理窗口移动

    9. 回调ResizeWindow处理窗口缩放

    10. 调用ReflectStackChange处理窗口在窗口栈中的位置变化

    11. 调用CheckCursorConfinement处理在窗口以及子窗口上的光标

ReflectStackChange

对pWin窗口进行重排,新暴露的窗口的可见区域需要进行重绘。

// 将pWin重排在pSib之上,若pSib为空,则将pWin重排在栈的底部
static void ReflectStackChange(WindowPtr pWin, WindowPtr pSib, VTKind kind){
    Bool WasViewable = (Bool) pWin->viewable;
    Bool anyMarked;
    WindowPtr pFirstChange;
    WindowPtr pLayerWin;
    ScreenPtr pScreen = pWin->drawable.pScreen;
    if (!pWin->parent)
        return;
    // 更新pWin在窗口栈的位置,并计算第一个在栈中重新暴露的窗口:
    // pWin在pNextSib之上(窗口下移),则为pWin->nextSib
    // pWin在pNextSib之下(窗口上移),则为pWin
    pFirstChange = MoveWindowInStack(pWin, pSib);
    if (WasViewable) {
        // 调用miMarkOverlappedWindows,从pFirstChange开始向后遍历其兄弟窗口,标记被pWin重叠的窗口,若存在返回true
        anyMarked = (*pScreen->MarkOverlappedWindows) (pWin, pFirstChange, &pLayerWin);
        if (pLayerWin != pWin)
            pFirstChange = pLayerWin;
        if (anyMarked) {
            // 调用miValidateTree重新计算pWin父窗口及其子窗口的可见区域,并计算待重新绘制区域(需重新显示的区域)
            (*pScreen->ValidateTree) (pLayerWin->parent, pFirstChange, kind);
            // 调用miHandleValidateExposures,深度优先,先绘制边框,再绘制背景,然后发送Expose事件给客户端绘制
            (*pScreen->HandleExposures) (pLayerWin->parent);
            if (pWin->drawable.pScreen->PostValidateTree)
                (*pScreen->PostValidateTree) (pLayerWin->parent, pFirstChange, kind);
        }
    }
    if (pWin->realized)
        WindowsRestructured();
}

MarkOverlappedWindows

miMarkOverlappedWindows实现了MarkOverlappedWindows回调功能。该函数主要时过滤一批确定不需要重绘的窗口,识别有可能会需重新绘制的窗口,从而减少后续的计算量。在pWin窗口之上兄弟窗口不存在重绘的需求,可以过滤;在pWin窗口之下的窗口有可能会要求重新绘制,如:pWin窗口缩小了,pWin窗口的边框改变了,pWin窗口移动了等。

// pWin为需重排的窗口,pFirst为第一个改变显示的窗口,pWin和pFirst为同一层窗口
// pWin窗口向下移,pFirst为窗口原堆叠层的pWin的下一个相邻窗口(pWin->nextSib),标记与pWin重叠的窗口
// pWin窗口向上移,pFirst为pWin本身窗口,标记被pWin遮挡的窗口
Bool miMarkOverlappedWindows(WindowPtr pWin, WindowPtr pFirst, WindowPtr *ppLayerWin){
    BoxPtr box;
    WindowPtr pChild, pLast;
    Bool anyMarked = FALSE;
    MarkWindowProcPtr MarkWindow = pWin->drawable.pScreen->MarkWindow;
    if (ppLayerWin)
        *ppLayerWin = pWin;

    if (pWin == pFirst) {
        // Blindly mark pWin and all of its inferiors.   This is a slight overkill 
        // if there are mapped windows that outside pWin's border, 
        // but it's better than wasting time on RectIn checks.
        pChild = pWin;
       // 深度优先遍历,初始化该窗口及其子窗口的窗口和边框区域
       // 子窗口的区域限制在pWin的窗口(winSize)区域内
        while (1) {
            if (pChild->viewable) {
                 // 根据窗口的drawable构建winSize区域,该区域限制在父窗口的winSize范围内
                if (RegionBroken(&pChild->winSize))
                    SetWinSize(pChild);
                // 包括窗口边框的区域,该区域限制在父窗口的winSize范围内
                if (RegionBroken(&pChild->borderSize))
                    SetBorderSize(pChild);
                // miMarkWindow, 构建Validate对象,记录起始坐标
                (*MarkWindow) (pChild);
                if (pChild->firstChild) {
                    pChild = pChild->firstChild;
                    continue;
                }
            }
            while (!pChild->nextSib && (pChild != pWin))
                pChild = pChild->parent;
            // pWin的子窗口遍历完成,退出循环
            if (pChild == pWin)
                break;
            pChild = pChild->nextSib;
        }
        // pWin窗口向上移,pWin为第一个暴露的窗口,可能需要重绘,因此需标记
        anyMarked = TRUE;
       // 下一个相邻窗口
        pFirst = pFirst->nextSib;
    }
    if ((pChild = pFirst)) {
       // pWin区间,窗口向上移,该区域会遮挡相邻窗口,向下移,则会暴露相邻窗口
        box = RegionExtents(&pWin->borderSize);
        pLast = pChild->parent->lastChild;
        while (1) {
            if (pChild->viewable) {
                if (RegionBroken(&pChild->winSize))
                    SetWinSize(pChild);
                if (RegionBroken(&pChild->borderSize))
                    SetBorderSize(pChild);
                // 区域有重叠,这标记该窗口,可能被遮挡,也可能会被暴露
                if (RegionContainsRect(&pChild->borderSize, box)) {
                    // 调用miMarkWindow
                    (*MarkWindow) (pChild);
                    anyMarked = TRUE;
                    if (pChild->firstChild) {
                        pChild = pChild->firstChild;
                        continue;
                    }
                }
            }
            while (!pChild->nextSib && (pChild != pLast))
                pChild = pChild->parent;
            if (pChild == pLast)
                break;
            pChild = pChild->nextSib;
        }
    }
    if (anyMarked)
        (*MarkWindow) (pWin->parent);
    return anyMarked;
}

ValidateTree

计算窗口及其子窗口的clipList,boardList,exposed区域。

  1. 计算可能变化的可见区域(totalClip),为减少重绘开销。有两种方式:

    1. 父窗口的borderClip区域减去pChild之上的子窗口区域(boarderSize),这些子窗口区域的显示内容不会改变

    2. 所有与移动窗口有重叠的子窗口的可见区域之和,为父窗口的最大可见区域,这些区域的内容会发生变化

  2. 过滤优化:在可见区域(totalClip)内,计算子窗口间是否重叠,若无,则不需重绘

  3. 子窗口的clipList,boardList,exposed区域,按照深度优先递归计算窗口的可见区域

    1. 窗口当可见性发生变化时(Unobscured, PartiallyObscured, FullyObscured),可能发送VisibilityNotify事件

    2. 当可见区域发生变化时,回调ClipNotify函数(present_clip_notify/compClipNotify/xf86XVClipNotify)

  4. 计算父窗口的可见区域,需重绘区域(exposed)等

int miValidateTree(WindowPtr pParent,  WindowPtr pChild,  VTKind kind) {
    // Total clipping region available to the marked children. 
    // pParent's clipList  merged with the borderClips of all the marked children.
    RegionRec totalClip;  
    RegionRec childClip;        /* The new borderClip for the current child */
    RegionRec childUnion;       /* the space covered by borderSize for all marked children */
    RegionRec exposed;          /* For intermediate calculations */
    WindowPtr pWin;
    Bool overlap;
    int viewvals;
    Bool forward;

    ScreenPtr pScreen = pParent->drawable.pScreen;
    if (pChild == NullWindow)
        pChild = pParent->firstChild;
    RegionNull(&childClip);
    RegionNull(&exposed);

    // compute the area of the parent window occupied by the marked children + the parent itself.  
    // This is the area which can be divied up among the marked children in their new configuration.
    RegionNull(&totalClip);
    viewvals = 0;
    // 计算可能变化的可见区域(totalClip),有两种方法:
    // 1. 父窗口的borderClip区域减去pChild之上的子窗口区域(boarderSize),这些子窗口区域的显示内容不会改变
    // 2. 所有与移动窗口有重叠的子窗口的可见区域之和,为父窗口的最大可见区域,这些区域的内容会发生变化
    if (RegionBroken(&pParent->clipList) && !RegionBroken(&pParent->borderClip)) {
        // RegionBroken表示数据空间为空,一般来说根窗口的clipList置为broken,因为根窗口时常变化。
        kind = VTBroken;
        // When rebuilding clip lists after out of memory, assume everything is busted.
        forward = TRUE;
        RegionCopy(&totalClip, &pParent->borderClip);
        RegionIntersect(&totalClip, &totalClip, &pParent->winSize);
        // 在pChild之上的相邻窗口,遮挡了pParent,减去pWin的窗口大小(加上边框)
        // 如果pWin为compositor client管理,可能需要透明化,因此不能将其区域去掉
        for (pWin = pParent->firstChild; pWin != pChild; pWin = pWin->nextSib) {
            if (pWin->viewable && !TreatAsTransparent(pWin))
                RegionSubtract(&totalClip, &totalClip, &pWin->borderSize);
        }
        for (pWin = pChild; pWin; pWin = pWin->nextSib)
            if (pWin->valdata && pWin->viewable)
                viewvals++;
        RegionEmpty(&pParent->clipList);
    }
    else {
        // pChild后续相邻窗口的可见区域之和,即为pWin最大可见区域
        if ((pChild->drawable.y < pParent->lastChild->drawable.y) ||
            ((pChild->drawable.y == pParent->lastChild->drawable.y) &&
             (pChild->drawable.x < pParent->lastChild->drawable.x))) {
            forward = TRUE;
            for (pWin = pChild; pWin; pWin = pWin->nextSib) {
                if (pWin->valdata) { // 与移动窗口有重叠
                    RegionAppend(&totalClip, getBorderClip(pWin));
                    if (pWin->viewable)
                        viewvals++;
                }
            }
        }
        else {
            forward = FALSE;
            pWin = pParent->lastChild;
            while (1) {
                if (pWin->valdata) { // 与移动窗口有重叠
                    RegionAppend(&totalClip, getBorderClip(pWin));
                    if (pWin->viewable)
                        viewvals++;
                }
                if (pWin == pChild)
                    break;
                pWin = pWin->prevSib;
            }
        }
        RegionValidate(&totalClip, &overlap);
    }
    // Now go through the children of the root and figure their new borderClips from the totalClip, 
    // passing that off to miComputeClips to handle recursively. 
    // Once that's done, we remove the child from the totalClip to clip any siblings below it.
    overlap = TRUE;
    // 在可见区域(totalClip)内,计算子窗口间是否重叠,若无,则不需重绘
    if (kind != VTStack) {
        // 子窗口的可见区域加上父窗口的可见区域,为父窗口整个可见区域
        RegionUnion(&totalClip, &totalClip, &pParent->clipList);
        if (viewvals > 1) {
            // precompute childUnion to discover whether any of them overlap. 
            // This seems redundant, but performance studies have demonstrated 
            // that the cost of this loop is lower than the cost of multiple Subtracts in the loop below.
            RegionNull(&childUnion);
            if (forward) {
                for (pWin = pChild; pWin; pWin = pWin->nextSib)
                    if (pWin->valdata && pWin->viewable && !TreatAsTransparent(pWin))
                        RegionAppend(&childUnion, &pWin->borderSize);
            }
            else {
                pWin = pParent->lastChild;
                while (1) {
                    if (pWin->valdata && pWin->viewable && !TreatAsTransparent(pWin))
                        RegionAppend(&childUnion, &pWin->borderSize);
                    if (pWin == pChild)
                        break;
                    pWin = pWin->prevSib;
                }
            }
            // 窗口之间是否有重叠,若无重叠,不需要重绘。
            RegionValidate(&childUnion, &overlap);
            if (overlap)
                RegionUninit(&childUnion);
        }
    }
    for (pWin = pChild; pWin != NullWindow; pWin = pWin->nextSib) {
        if (pWin->viewable) {
            if (pWin->valdata) {
                RegionIntersect(&childClip, &totalClip, &pWin->borderSize);
                // 计算pWin及其子窗口的clipList,boardList,exposed区域
                miComputeClips(pWin, pScreen, &childClip, kind, &exposed);
                if (overlap && !TreatAsTransparent(pWin)) {
                    // pWin遮挡了下层的窗口,下层窗口对boarderSize区域不可见,因此可以去掉该区域
                    RegionSubtract(&totalClip, &totalClip, &pWin->borderSize);
                }
            }
            else if (pWin->visibility == VisibilityNotViewable) {
                miTreeObscured(pWin);
            }
        }
        else { // 不可显示,则清空clipList,删除临时数据
            if (pWin->valdata) {
                RegionEmpty(&pWin->clipList);
                if (pScreen->ClipNotify)
                    (*pScreen->ClipNotify) (pWin, 0, 0);
                RegionEmpty(&pWin->borderClip);
                pWin->valdata = NULL;
            }
        }
    }
    RegionUninit(&childClip);
    // 计算父窗口的可剪接区域,需重绘区域等
    if (!overlap) {
        // 没重叠,减去子窗口的可见区域,即为父窗口的可见区域
        RegionSubtract(&totalClip, &totalClip, &childUnion);
        RegionUninit(&childUnion);
    }
    RegionNull(&pParent->valdata->after.exposed);
    RegionNull(&pParent->valdata->after.borderExposed);

    // each case below is responsible for updating the clipList and serial number for the parent window
    switch (kind) {
    case VTStack:
        // 子窗口间的堆叠层次变化,不影响父窗口的clipList区域
        break;
    default:
        // totalClip contains the new clipList for the parent. 
        // Figure out exposures and obscures as per miComputeClips and reset the parent's clipList.
        // 只重绘未绘制区域。clipList中的区域已绘制。边框不需要重绘,因为子窗口从未覆盖边框
        RegionSubtract(&pParent->valdata->after.exposed, &totalClip, &pParent->clipList);
        /* fall through */
    case VTMap:
        // 更新clipList为新的可见区域
        RegionCopy(&pParent->clipList, &totalClip);
        pParent->drawable.serialNumber = NEXT_SERIAL_NUMBER;
        break;
    }

    RegionUninit(&totalClip);
    RegionUninit(&exposed);
    if (pScreen->ClipNotify)
        (*pScreen->ClipNotify) (pParent, 0, 0);
    return 1;
}

HandleValidateExposures

发送重绘(Expose)事件,采用深度优先遍历窗口树

  1. 重绘父窗口图形

    1. Server调用PaintWindow绘制边框

    2. 调用WindowExposures

      1. Server调用PaintWindow绘制窗口背景

      2. 发送重绘事件给客户端

  2. 重绘子窗口图形,流程与上类似

void miHandleValidateExposures(WindowPtr pWin){
    WindowPtr pChild;
    ValidatePtr val;
    WindowExposuresProcPtr WindowExposures;
    pChild = pWin;
    WindowExposures = pChild->drawable.pScreen->WindowExposures;
    // 深度优先,先绘制边框,再绘制背景,然后发送Expose事件给客户端绘制
    while (1) {
        if ((val = pChild->valdata)) {
            if (RegionNotEmpty(&val->after.borderExposed))
                // 调用miPaintWindow
                pWin->drawable.pScreen->PaintWindow(pChild, &val->after.borderExposed, PW_BORDER);
            RegionUninit(&val->after.borderExposed);
            // xf86XVWindowExposures -> miWindowExposures
            (*WindowExposures) (pChild, &val->after.exposed);
            RegionUninit(&val->after.exposed);
            free(val);
            pChild->valdata = NULL;
            if (pChild->firstChild) {
                pChild = pChild->firstChild;
                continue;
            }
        }
        while (!pChild->nextSib && (pChild != pWin))
            pChild = pChild->parent;
        if (pChild == pWin)
            break;
        pChild = pChild->nextSib;
    }
}

void miWindowExposures(WindowPtr pWin, RegionPtr prgn){
    RegionPtr exposures = prgn;
    if (prgn && !RegionNil(prgn)) {
        RegionRec expRec;
        int clientInterested =
            (pWin->eventMask | wOtherEventMasks(pWin)) & ExposureMask;
        if (clientInterested && (RegionNumRects(prgn) > RECTLIMIT)) {
            // If we have LOTS of rectangles, we decide to take the extents and force an exposure on that.
            // This should require much less work overall, on both client and server.
            // This is cheating, but isn't prohibited by the protocol ("spontaneous combustion" :-).
            BoxRec box = *RegionExtents(prgn);
            exposures = &expRec;
            RegionInit(exposures, &box, 1);
            RegionReset(prgn, &box);
            /* miPaintWindow doesn't clip, so we have to */
            RegionIntersect(prgn, prgn, &pWin->clipList);
        }
        // 重绘窗口背景
        pWin->drawable.pScreen->PaintWindow(pWin, prgn, PW_BACKGROUND);
        if (clientInterested) // 发送Expose事件
            miSendExposures(pWin, exposures, pWin->drawable.x, pWin->drawable.y);
        if (exposures == &expRec)
            RegionUninit(exposures);
        RegionEmpty(prgn);
    }
}

WindowsRestructured

处理光标和键盘焦点,并重构光标窗口:

  1. 计算获得当前焦点坐标所在窗口(最低级窗口)

  2. 若当前焦点窗口与原窗口不同,则将焦点离开原窗口,进入新窗口

  3. 如果是光标设备,则重绘光标窗口,根据光标所在窗口特性,可绘制不同的光标图形,一般由miPointerDisplayCursor函数实现,动画光标由AnimCurDisplayCursor实现

void WindowsRestructured(void){
    DeviceIntPtr pDev = inputInfo.devices;
    while (pDev) {
        if (IsMaster(pDev) || IsFloating(pDev)) // 主输入设备或者与主输入设备关联的设备
            CheckMotion(NULL, pDev);
        pDev = pDev->next;
    }
}

Bool CheckMotion(DeviceEvent *ev, DeviceIntPtr pDev){
    WindowPtr prevSpriteWin, newSpriteWin;
    SpritePtr pSprite = pDev->spriteInfo->sprite;
    prevSpriteWin = pSprite->win;
    // ev == NULL
    if (ev && !syncEvents.playingEvents) {
        // skipped
    }
    // 根据精灵的坐标获取包含在坐标的窗口
    newSpriteWin = XYToWindow(pSprite, pSprite->hot.x, pSprite->hot.y);
    if (newSpriteWin != prevSpriteWin) { // 光标所在的窗口发生了变化
        int sourceid;
        if (!ev) {
            UpdateCurrentTimeIf();
            sourceid = pDev->id;        /* when from WindowsRestructured */
        }
        else
            sourceid = ev->sourceid;

        if (prevSpriteWin != NullWindow) {
            // 焦点离开prevSpriteWin窗口,进入newSpriteWin窗口,发送事件给client
            if (!ActivateEnterGrab(pDev, prevSpriteWin, newSpriteWin))
                DoEnterLeaveEvents(pDev, sourceid, prevSpriteWin, newSpriteWin, NotifyNormal);
        }
        pSprite->win = newSpriteWin;
        // 重绘新光标
        PostNewCursor(pDev);
        return FALSE;
    }
    return TRUE;
}

ConfigNotify

ConfigNotify回调通知X11扩展窗口配置发送变化,涉及一个回调函数链,其调用顺序为:

  1. present_config_notify:发送PresentConfigureNotify扩展事件给客户端

  2. compConfigNotify:针对拥有compWindow的窗口进行处理,若窗口尺寸有变化(ResizeWindow),则分配新的pixmap

  3. DRI2ConfigNotify:失效原有的显存内存

// 回调函数原型
// (x, y)相对与父窗口的窗口新坐标,包含边框
// (w,h)窗口的新尺寸(宽高),宽高不包括边框
// bw窗口的边框新大小
// next_sib:窗口的相邻兄弟窗口,及窗口在next_sib窗口之上,这是窗口在窗口栈的新位置;为空表示窗口置底
int ConfigNotify(WindowPtr pWin, int x, int y, int w, int h, int bw, WindowPtr pSib)

present_config_notify

发送PresentConfigureNotify扩展事件给客户端。

static int present_config_notify(WindowPtr window, int x, int y, int w, int h, int bw,  WindowPtr sibling) {
    int ret;
    ScreenPtr screen = window->drawable.pScreen;
    present_screen_priv_ptr screen_priv = present_screen_priv(screen);
    // 发送PresentConfigureNotify扩展事件给客户端
    present_send_config_notify(window, x, y, w, h, bw, sibling, 0);
    unwrap(screen_priv, screen, ConfigNotify);
    // 调用compConfigNotify
    if (screen->ConfigNotify)
        ret = screen->ConfigNotify (window, x, y, w, h, bw, sibling);
    else
        ret = 0;
    wrap(screen_priv, screen, ConfigNotify, present_config_notify);
    return ret;
}

compConfigNotify

针对拥有compWindow的窗口进行处理,若窗口尺寸有变化,则分配新的pixmap,并将父窗口pixmap中新区域的窗口内容拷贝至新的pixmap中,若窗口的色彩深度与父窗口不同,则内容需要进行合成,旧的pixmap缓冲在cw->pOldPixmap中

int compConfigNotify(WindowPtr pWin, int x, int y, int w, int h, int bw, WindowPtr pSib){
    ScreenPtr pScreen = pWin->drawable.pScreen;
    CompScreenPtr cs = GetCompScreen(pScreen);
    Bool ret = 0;
    WindowPtr pParent = pWin->parent;
    int draw_x, draw_y;
    Bool alloc_ret;
    // 调用DRI2ConfigNotify
    if (cs->ConfigNotify) {
        pScreen->ConfigNotify = cs->ConfigNotify;
        ret = (*pScreen->ConfigNotify) (pWin, x, y, w, h, bw, pSib);
        cs->ConfigNotify = pScreen->ConfigNotify;
        pScreen->ConfigNotify = compConfigNotify;
        if (ret)
            return ret;
    }
    if (pWin->redirectDraw == RedirectDrawNone)
        return Success;
    // 计算窗口可绘制区域屏幕坐标,当尺寸不同时,重新分配pixmap
    draw_x = pParent->drawable.x + x + bw;
    draw_y = pParent->drawable.y + y + bw;
    alloc_ret = compReallocPixmap(pWin, draw_x, draw_y, w, h, bw);
    if (alloc_ret == FALSE)
        return BadAlloc;
    return Success;
}

Bool compReallocPixmap(WindowPtr pWin, int draw_x, int draw_y, unsigned int w, unsigned int h, int bw){
    ScreenPtr pScreen = pWin->drawable.pScreen;
    PixmapPtr pOld = (*pScreen->GetWindowPixmap) (pWin);
    PixmapPtr pNew;
    CompWindowPtr cw = GetCompWindow(pWin);
    int pix_x, pix_y;
    int pix_w, pix_h;
    cw->oldx = pOld->screen_x;
    cw->oldy = pOld->screen_y;
    pix_x = draw_x - bw;
    pix_y = draw_y - bw;
    pix_w = w + (bw << 1);
    pix_h = h + (bw << 1);
    if (pix_w != pOld->drawable.width || pix_h != pOld->drawable.height) {
        // 创建一个新的pixmap,包括边框尺寸,从父窗口的画布的新区域中拷贝内容至新的pixmap中
        pNew = compNewPixmap(pWin, pix_x, pix_y, pix_w, pix_h);
        if (!pNew)
            return FALSE;
        // 缓冲旧的pixmap,设置新的pixmap给窗口
        cw->pOldPixmap = pOld;
        compSetPixmap(pWin, pNew, bw);
    }
    else {
        pNew = pOld; // 尺寸不变(包括了边框),这更新坐标信息,旧坐标信息缓冲在cw->oldX/Y中
        cw->pOldPixmap = 0;
    }
    pNew->screen_x = pix_x; // 包含的边框的屏幕坐标
    pNew->screen_y = pix_y;
    return TRUE;
}

MoveWindow

移动窗口涉及窗口原位置遮挡的兄弟窗口需要显示内容,窗口移动后须在新位置显示窗口内容。其实现涉及一个回调函数链,包括:xwayland、composite、machine independent的回调函数。其调用顺序为:

  1. xwl_move_window:更新顶层窗口的视口(viewport)

  2. compMoveWindow:处理compWindow

  3. miMoveWindow:移动窗口,重新计算子窗口的位置和尺寸,显示窗口内容等

// 回调函数原型
// (x, y)相对与父窗口的窗口新坐标
// next_sib:窗口的相邻兄弟窗口,及窗口在next_sib窗口之上,这是窗口在窗口栈的新位置;为空表示窗口置底
// kind:验证窗口树的类型,VTMove:只移动,VTOther:涉及边框的变化
void MoveWindow(WindowPtr window, int x, int y, WindowPtr next_sib, VTKind kind);

xwl_move_window

在非根模式下,额外处理顶级窗口与视口之间的关系,窗口内容在Wayland compositor视口中显示,可能只显示一部分内容。

void xwl_move_window(WindowPtr window, int x, int y, WindowPtr next_sib, VTKind kind){
    ScreenPtr screen = window->drawable.pScreen;
    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
    // 获取xwl_window窗口,window本身或者其祖先窗口对应的xwl_window窗口,存在xwl_window,则比存在一个wl_surface与window对应
    struct xwl_window *xwl_window = xwl_window_from_window(window);
    // 调用compMoveWindow
    screen->MoveWindow = xwl_screen->MoveWindow;
    (*screen->MoveWindow) (window, x, y, next_sib, kind);
    xwl_screen->MoveWindow = screen->MoveWindow;
    screen->MoveWindow = xwl_move_window;
    // window本身有对应的xwl_window窗口,如:窗口管理器创建的装饰窗口
    // 或window是顶层窗口(包括override-redirect和被窗口管理器装饰的顶层窗口)
    if (xwl_window && (xwl_window_get(window) || xwl_window_is_toplevel(window)))
        xwl_window_check_resolution_change_emulation(xwl_window);
}

// 处理窗口与视口(viewport)的关系
void xwl_window_check_resolution_change_emulation(struct xwl_window *xwl_window)
{
    struct xwl_emulated_mode emulated_mode;
    struct xwl_output *xwl_output;
    // Wayland compositor支持wp_viewporter_interface接口,获取一个xwl_output和对应的xwl_emulated_mode
    if (xwl_window_should_enable_viewport(xwl_window, &xwl_output, &emulated_mode))
        xwl_window_enable_viewport(xwl_window, xwl_output, &emulated_mode);
    else if (xwl_window_has_viewport_enabled(xwl_window))
        xwl_window_disable_viewport(xwl_window);
}

// 视口(viewport)与surface建立关系,并设置视口区域大小
static void xwl_window_enable_viewport(struct xwl_window *xwl_window,
                           struct xwl_output *xwl_output, struct xwl_emulated_mode *emulated_mode){
    if (!xwl_window_has_viewport_enabled(xwl_window)) {
        // 绑定视口与曲面(wl_surface)
        xwl_window->viewport = wp_viewporter_get_viewport(xwl_window->xwl_screen->viewporter, xwl_window->surface);
    }
    // 设置窗口的坐标和尺寸
    wp_viewport_set_source(xwl_window->viewport,  wl_fixed_from_int(0), wl_fixed_from_int(0),
                           wl_fixed_from_int(emulated_mode->width), wl_fixed_from_int(emulated_mode->height));
    // 设置视口(viewport)的尺寸,可显示区域
    wp_viewport_set_destination(xwl_window->viewport, xwl_output->width, xwl_output->height);
    // 计算缩放比例
    xwl_window->scale_x = (float)emulated_mode->width  / xwl_output->width;
    xwl_window->scale_y = (float)emulated_mode->height / xwl_output->height;
}

compMoveWindow

回收compWindow中旧的pixmap资源

void compMoveWindow(WindowPtr pWin, int x, int y, WindowPtr pSib, VTKind kind) {
    ScreenPtr pScreen = pWin->drawable.pScreen;
    CompScreenPtr cs = GetCompScreen(pScreen);
    // 调用miMoveWindow
    pScreen->MoveWindow = cs->MoveWindow;
    (*pScreen->MoveWindow) (pWin, x, y, pSib, kind);
    cs->MoveWindow = pScreen->MoveWindow;
    pScreen->MoveWindow = compMoveWindow;
    // 是否旧的pixmap,新pixmap可能在compCOnfigNofity中创建
    compFreeOldPixmap(pWin);
    compCheckTree(pScreen);
}

static void compFreeOldPixmap(WindowPtr pWin) {
    ScreenPtr pScreen = pWin->drawable.pScreen;
    if (pWin->redirectDraw != RedirectDrawNone) {
        // 该窗口存在redirectWindow,且缓冲了旧的pixmap,则释放旧的pixmap
        CompWindowPtr cw = GetCompWindow(pWin);
        if (cw->pOldPixmap) {
            (*pScreen->DestroyPixmap) (cw->pOldPixmap);
            cw->pOldPixmap = NullPixmap;
        }
    }
}

miMoveWindow

实现窗口移动功能,窗口移动涉及原被遮挡窗口内容显示,窗口新位置内容显示,窗口栈的更新等,同时其子窗口也随之移动。

  1. 标记在父窗口树中被pWin覆盖的窗口和子窗口,包括pWin本身以及子窗口

  2. 更新pWin窗口尺寸和坐标,坐标平移,截取超过父窗口边界的区域

  3. 更新pWin子窗口的尺寸和坐标,坐标平移,截取超过pWin窗口边界的区域

  4. 根据pWin的新窗口,计算屏幕可能变化的区域,并计算窗口及子窗口的可见区域,重绘区域等。

  5. 将pWin窗口的原有效内容迁移至新窗口内

  6. 根据窗口exposed向client发送重绘请求

void miMoveWindow(WindowPtr pWin, int x, int y, WindowPtr pNextSib, VTKind kind){
    WindowPtr pParent;
    Bool WasViewable = (Bool) (pWin->viewable);
    short bw;
    RegionPtr oldRegion = NULL;
    DDXPointRec oldpt;
    Bool anyMarked = FALSE;
    ScreenPtr pScreen;
    WindowPtr windowToValidate;
    WindowPtr pLayerWin;
    if (!(pParent = pWin->parent))
        return;
    pScreen = pWin->drawable.pScreen;
    bw = wBorderWidth(pWin);
    oldpt.x = pWin->drawable.x;
    oldpt.y = pWin->drawable.y;
    if (WasViewable) {
        oldRegion = RegionCreate(NullBox, 1);
        // 缓冲原剪接区间
        RegionCopy(oldRegion, &pWin->borderClip);
        // 调用miMarkOverlappedWindows,从pWin开始向后遍历其兄弟窗口,标记被pWin覆盖的窗口(窗口移动前),若存在返回true
        anyMarked = (*pScreen->MarkOverlappedWindows) (pWin, pWin, &pLayerWin);
    }
    // origin (x,y)为窗口可绘制区域的新坐标,相对于父窗口
    pWin->origin.x = x + (int) bw;
    pWin->origin.y = y + (int) bw;
    // (x,y)窗口可绘制区域的屏幕新坐标
    x = pWin->drawable.x = pParent->drawable.x + x + (int) bw;
    y = pWin->drawable.y = pParent->drawable.y + y + (int) bw;
    // 设置窗口区域,若有compWindow,则为窗口实际大小,否则为父窗口范围内窗口区域
    SetWinSize(pWin);
    // 设置包含边框的窗口区域,原理同上
    SetBorderSize(pWin);
    // 回调PositionWindow通知X11扩展窗口位置更新
    // miDbePositionWindow -> compPositionWindow -> fbPositionWindow
    (*pScreen->PositionWindow) (pWin, x, y);
    // 更新pWin在窗口栈的位置,并计算第一个在栈中重新暴露的窗口:
    // pWin在pNextSib之上(窗口下移),则为pWin->nextSib
    // pWin在pNextSib之下(窗口上移),则为pWin
    windowToValidate = MoveWindowInStack(pWin, pNextSib);
    // 重新计算子窗口在新位置的窗口尺寸,由于窗口的移动,可能导致某些内容移出父窗口,则必须截取窗口尺寸
    ResizeChildrenWinSize(pWin, x - oldpt.x, y - oldpt.y, 0, 0);
    if (WasViewable) {
        if (pLayerWin == pWin) // 该条件恒成立,不知为什么需要判断
            // 从windowToValidate开始向后遍历其兄弟窗口,标记与pWin重叠的窗口(窗口移动后(坐标已更新))
            anyMarked |= (*pScreen->MarkOverlappedWindows)(pWin, windowToValidate, NULL);
        else
            anyMarked |= (*pScreen->MarkOverlappedWindows)(pWin, pLayerWin, NULL);
        if (anyMarked) { // 有重叠,有些窗口被遮挡,有些窗口重新显示
            // 调用miValidateTree重新计算pWin父窗口及其子窗口的可见区域,并计算待重新绘制区域(需重新显示的区域)
            (*pScreen->ValidateTree) (pLayerWin->parent, NullWindow, kind);
            // compCopyWindow -> damageCopyWindow -> glamor_copy_window
            // 将window中的内容拷贝至新位置
            (*pWin->drawable.pScreen->CopyWindow) (pWin, oldpt, oldRegion);
            RegionDestroy(oldRegion);
            // 调用miHandleValidateExposures,深度优先,先绘制边框,再绘制背景,然后发送Expose事件给客户端绘制
            (*pScreen->HandleExposures) (pLayerWin->parent);
            if (pScreen->PostValidateTree)
                (*pScreen->PostValidateTree) (pLayerWin->parent, NULL, kind);
        }
    }
    if (pWin->realized) // 重构所有窗口
        WindowsRestructured();
}

compCopyWindow

分两种情况更新新区域内容

  1. 窗口有compWindow,若窗口尺寸未变,则创建Damage,告知新区域内容有变化;若窗口尺寸变化时,pixmap为新区域的内容,需将旧pixmap的内容拷贝至pixmap中,更新窗口内容至新区域。

  2. 窗口无compWindow,则传递给后续的CopyWindow处理

void compCopyWindow(WindowPtr pWin, DDXPointRec ptOldOrg, RegionPtr prgnSrc)
{
    ScreenPtr pScreen = pWin->drawable.pScreen;
    CompScreenPtr cs = GetCompScreen(pScreen);
    int dx = 0, dy = 0;
    if (pWin->redirectDraw != RedirectDrawNone) {
        PixmapPtr pPixmap = (*pScreen->GetWindowPixmap) (pWin);
        CompWindowPtr cw = GetCompWindow(pWin);
        if (cw->pOldPixmap) {
            // 窗口尺寸有改变,在compConfigNofity中创建了新的pixmap,旧的pixmap缓冲在pOldPixmap中
            // 将pOldPixmap的内容拷贝至新的pixmap中即可,覆盖新pixmap中响应区域内容
            RegionRec rgnDst;
            GCPtr pGC;
            // 计算窗口移动时产生的坐标偏移
            dx = ptOldOrg.x - pWin->drawable.x;
            dy = ptOldOrg.y - pWin->drawable.y;
            RegionTranslate(prgnSrc, -dx, -dy);
            RegionNull(&rgnDst);
            // 区域限制在borderClip范围内
            RegionIntersect(&rgnDst, &pWin->borderClip, prgnSrc);
            // 坐标转换,从屏幕坐标转换为本地坐标(画布内的坐标)
            RegionTranslate(&rgnDst, -pPixmap->screen_x, -pPixmap->screen_y);
            // 坐标偏移转换为old和new pixmap内的偏移
            dx = dx + pPixmap->screen_x - cw->oldx;
            dy = dy + pPixmap->screen_y - cw->oldy;
            pGC = GetScratchGC(pPixmap->drawable.depth, pScreen);
            if (pGC) {
                BoxPtr pBox = RegionRects(&rgnDst);
                int nBox = RegionNumRects(&rgnDst);
                ValidateGC(&pPixmap->drawable, pGC);
                while (nBox--) {
                    // 调用glamor_copy_area拷贝数据
                    (void) (*pGC->ops->CopyArea) (&cw->pOldPixmap->drawable, &pPixmap->drawable, pGC,
                                                  pBox->x1 + dx, pBox->y1 + dy, pBox->x2 - pBox->x1,
                                                  pBox->y2 - pBox->y1, pBox->x1, pBox->y1);
                    pBox++;
                }
                FreeScratchGC(pGC);
            }
            RegionUninit(&rgnDst);
            return;
        }
        // 矫正旧坐标,加上新pixmap的屏幕坐标与旧pixmap的偏移,方便后续操作区分
        dx = pPixmap->screen_x - cw->oldx;
        dy = pPixmap->screen_y - cw->oldy;
        ptOldOrg.x += dx;
        ptOldOrg.y += dy;
    }

    pScreen->CopyWindow = cs->CopyWindow;
    if (ptOldOrg.x != pWin->drawable.x || ptOldOrg.y != pWin->drawable.y) {
        // 窗口无compWindow,需要将内容从旧坐标移至新坐标
        if (dx || dy)
            RegionTranslate(prgnSrc, dx, dy);
        // damageCopyWindow
        (*pScreen->CopyWindow) (pWin, ptOldOrg, prgnSrc);
        if (dx || dy)
            RegionTranslate(prgnSrc, -dx, -dy);
    }
    else {
        // 还原旧坐标,见上的矫正
        ptOldOrg.x -= dx;
        ptOldOrg.y -= dy;
        // 更新prgnSrc至新坐标区域,构建Damage,通知新区域内容有变化
        RegionTranslate(prgnSrc, pWin->drawable.x - ptOldOrg.x, pWin->drawable.y - ptOldOrg.y);
        DamageDamageRegion(&pWin->drawable, prgnSrc);
    }
    cs->CopyWindow = pScreen->CopyWindow;
    pScreen->CopyWindow = compCopyWindow;
    compCheckTree(pWin->drawable.pScreen);
}

damageCopyWindow

创建Damage,记录变化区域

static void damageCopyWindow(WindowPtr pWindow, DDXPointRec ptOldOrg, RegionPtr prgnSrc){
    ScreenPtr pScreen = pWindow->drawable.pScreen;
    damageScrPriv(pScreen);
    if (getWindowDamage(pWindow)) {
        int dx = pWindow->drawable.x - ptOldOrg.x;
        int dy = pWindow->drawable.y - ptOldOrg.y;
        // The region comes in source relative, but the damage occurs at the destination location.
        // Translate back and forth.
        RegionTranslate(prgnSrc, dx, dy);
        damageRegionAppend(&pWindow->drawable, prgnSrc, FALSE, -1);
        RegionTranslate(prgnSrc, -dx, -dy);
    }
    unwrap(pScrPriv, pScreen, CopyWindow);
    // glamor_copy_window
    (*pScreen->CopyWindow) (pWindow, ptOldOrg, prgnSrc);
    damageRegionProcessPending(&pWindow->drawable);
    wrap(pScrPriv, pScreen, CopyWindow, damageCopyWindow);
}

glamor_copy_window

使用openGL拷贝数据

void glamor_copy_window(WindowPtr window, DDXPointRec old_origin, RegionPtr src_region){
    PixmapPtr pixmap = glamor_get_drawable_pixmap(&window->drawable);
    DrawablePtr drawable = &pixmap->drawable;
    RegionRec dst_region;
    int dx = old_origin.x - window->drawable.x;
    int dy = old_origin.y - window->drawable.y;
    RegionTranslate(src_region, -dx, -dy);
    RegionNull(&dst_region);
    RegionIntersect(&dst_region, &window->borderClip, src_region);
    // pixmap的屏幕起始坐标记录在(screen_x, screen_y)
    if (pixmap->screen_x || pixmap->screen_y)
        RegionTranslate(&dst_region, -pixmap->screen_x, -pixmap->screen_y);
    // 在同一个drawable中拷贝数据时,需考虑内容被覆盖的问题
    // miCopyRegion考虑的拷贝方向(从上往下,从左往右),以避免内容被覆盖的问题
    miCopyRegion(drawable, drawable, 0, &dst_region, dx, dy, glamor_copy, 0, 0);
    RegionUninit(&dst_region);
}

// 采用opengl进行拷贝,通过reverse和upsidedown指定了拷贝数据时的方向
// reverse:true为X轴上从右往左拷数据;false为X轴上从左往右拷数据
// upsidedown:true为Y轴上从网上下拷数据;false为Y轴上从下往上拷数据
void glamor_copy(DrawablePtr src, DrawablePtr dst, GCPtr gc,
            BoxPtr box, int nbox, int dx, int dy,
            Bool reverse, Bool upsidedown, Pixel bitplane, void *closure){
    if (nbox == 0)
	return;
    if (glamor_copy_gl(src, dst, gc, box, nbox, dx, dy, reverse, upsidedown, bitplane, closure))
        return;
    glamor_copy_bail(src, dst, gc, box, nbox, dx, dy, reverse, upsidedown, bitplane, closure);
}

ResizeWindow

窗口缩放包含窗口尺寸变化,窗口位置变化等。当窗口尺寸发生变化时,需要计算窗口预留的内容以及子窗口的位置,这些有两个gravity参数标记:bitGravity和winGravity,这些均在miResizeWindow中实现

  • bitGravity:父窗口尺寸发生变化时,确定父窗口预留的窗口内容,以及在新窗口的放置位置

  • winGravity:父窗口尺寸发生变化时,子窗口的winGravity指定了如何计算子窗口的坐标

假设父窗口的尺寸变化为(dw, dh),坐标变化为(dx,dy),子窗口的原坐标为(origx,origy),子窗口的winGravity值如表,可计算其新坐标位置。具体实现在ResizeChildrenWinSize中。

winGravity

Deltas

子窗口位置

UnmapGravity

N/A

UnmapWindow

NorthWestGravity

[0, 0]

[origx + dx, origy + dy]

North

[dw/2, 0]

[origx + dx + dw/2, origy + dy]

NorthEast

[dw, 0]

[origx + dx + dw, origy + dy]

West

[0, dh/2]

[origx + dx, origy + dy + dh/2]

Center

[dw/2, dh/2]

[origx + dx + dw/2, origy + dy + dh/2]

East

[dw, dh/2]

[origx + dx + dw, origy + dy + dh/2]

SouthWest

[0, dh]

[origx + dx, origy + dy + dh]

South

[dw/2, dh]

[origx + dx + dw/2, origy + dy + dh]

SouthEast

[dw, dh]

[origx + dx + dw, origy + dy + dh]

StaticGravity

N/A

[origx, origy]

// 回调函数原型
// (x, y)相对与父窗口的窗口新坐标,包含边框
// (width,hight)窗口的新尺寸(宽高),宽高不包括边框
// sib:窗口的相邻兄弟窗口,及窗口在sib窗口之上,这是窗口在窗口栈的新位置;为空表示窗口置底
int ResizeWindow(WindowPtr window, int x, int y, unsigned int width, unsigned int height,, WindowPtr sib)

xwl_resize_window

在非根模式下,额外处理顶级窗口与视口之间的关系,窗口内容在Wayland compositor视口中显示,可能只显示一部分内容。

void xwl_resize_window(WindowPtr window, int x, int y, unsigned int width, unsigned int height, WindowPtr sib){
    ScreenPtr screen = window->drawable.pScreen;
    struct xwl_screen *xwl_screen = xwl_screen_get(screen);
    // 获取xwl_window窗口,window本身或者其祖先窗口对应的xwl_window窗口,存在xwl_window,则比存在一个wl_surface与window对应
    struct xwl_window *xwl_window = xwl_window_from_window(window);
    // 调用compResizeWindow
    screen->ResizeWindow = xwl_screen->ResizeWindow;
    (*screen->ResizeWindow) (window, x, y, width, height, sib);
    xwl_screen->ResizeWindow = screen->ResizeWindow;
    screen->ResizeWindow = xwl_resize_window;
    // window本身有对应的xwl_window窗口,如:窗口管理器创建的装饰窗口
    // 或window是顶层窗口(包括override-redirect和被窗口管理器装饰的顶层窗口)
    if (xwl_window && (xwl_window_get(window) || xwl_window_is_toplevel(window)))
        xwl_window_check_resolution_change_emulation(xwl_window);
}

// 处理窗口与视口(viewport)的关系
void xwl_window_check_resolution_change_emulation(struct xwl_window *xwl_window){
    struct xwl_emulated_mode emulated_mode;
    struct xwl_output *xwl_output;
    // Wayland compositor支持wp_viewporter_interface接口,获取一个xwl_output和对应的xwl_emulated_mode
    if (xwl_window_should_enable_viewport(xwl_window, &xwl_output, &emulated_mode))
        xwl_window_enable_viewport(xwl_window, xwl_output, &emulated_mode);
    else if (xwl_window_has_viewport_enabled(xwl_window))
    // 找不到与窗口尺寸适配的xwl_emulated_mode,则需禁用原有的视口
        xwl_window_disable_viewport(xwl_window);
}

compResizeWindow

回收compWindow中旧的pixmap资源。在compConfigNofity中,由于窗口尺寸的变化,重新分配了窗口的pixmap,原窗口的pixmap缓冲在pOldPixmap中。在miResizeWindow已经利用pOldPixmap将窗口原有内容移至新的pixmap中,此时可销毁旧的pixmap。

void compResizeWindow(WindowPtr window,int x, int y, unsigned int width, unsigned int height, WindowPtr sib){
    ScreenPtr pScreen = pWin->drawable.pScreen;
    CompScreenPtr cs = GetCompScreen(pScreen);
    // 调用miResizeWindow
    pScreen->ResizeWindow = cs->ResizeWindow;
    (*pScreen->ResizeWindow) (pWin, x, y, w, h, pSib);
    cs->ResizeWindow = pScreen->ResizeWindow;
    pScreen->ResizeWindow = compResizeWindow;
    // 是否旧的pixmap,新pixmap可能在compCOnfigNofity中创建
    compFreeOldPixmap(pWin);
    compCheckTree(pScreen);
}

miResizeWindow

窗口缩放可能伴随窗口移动,窗口缩放涉及子窗口位置的变动,根据子窗口的winGravity来调整子窗口在窗口中的相对位置(子窗口的布局在新窗口内可能发生变化),子窗口不会缩放,但会窗口区域会截取在父窗口区域内;窗口缩放时涉及原窗口内容在新窗口中的放置位置,由窗口bitGravity来确定,窗口缩小,只能保留原窗口部分内容(保留哪些内容由bitGravity设置);窗口扩大,可保留原窗口所有内容,新窗口部分区域无内容(或者composite将新窗口内容初始化为该区域的父窗口内容)(初始放置在哪个位置由bitGravity设置)。实现思路为:

  1. 统计不同winGravity子窗口在原窗口中的可见区域

  2. 标记在父窗口树中被pWin覆盖的窗口和子窗口,包括pWin本身以及子窗口

  3. 更新pWin窗口尺寸和坐标,变为新窗口区域

  4. 更新pWin子窗口的尺寸和坐标,子窗口的坐标变化受其winGravity影响(而非平移),尺寸截取在pWin新窗口内。子窗口的子窗口不受winGravity影响,与其父窗口保存相对位置

  5. 根据pWin的新窗口,计算屏幕可能变化的区域,并计算窗口及子窗口的可见区域,重绘区域等。

  6. 根据pWin的bitGravity计算窗口预留内容在新窗口的起始位置(nx, ny)

  7. 计算原窗口已绘制区域在新窗口有效的区域。新窗口的有效可见区域与原窗口已绘制区域在新窗口的重叠区。并将该区域归至相同gravity的子窗口区域中

  8. 将子窗口原内容迁移至新窗口内

    1. 确定子窗口的有效区域:计算Gravity分组的原子窗口区域与其在新窗口的重叠区域,该区域为子窗口有效区域

    2. 将子窗口有效区域的内容移至新窗口中

    3. 计算子窗口其它可见区域,用于通知client重绘

  9. 根据窗口exposed向client发送重绘请求

void miResizeWindow(WindowPtr pWin, int x, int y, unsigned int w, unsigned int h, WindowPtr pSib){
    WindowPtr pParent;
    Bool WasViewable = (Bool) (pWin->viewable);
    unsigned short width = pWin->drawable.width, height = pWin->drawable.height;
    short oldx = pWin->drawable.x, oldy = pWin->drawable.y;
    int bw = wBorderWidth(pWin);
    short dw, dh;
    DDXPointRec oldpt;
    RegionPtr oldRegion = NULL;
    Bool anyMarked = FALSE;
    ScreenPtr pScreen;
    WindowPtr pFirstChange;
    WindowPtr pChild;
    RegionPtr gravitate[StaticGravity + 1];
    unsigned g;
    int nx, ny;                 /* destination x,y */
    int newx, newy;             /* new inner window position */
    RegionPtr pRegion = NULL;
    RegionPtr destClip;         /* portions of destination already written */
    RegionPtr oldWinClip = NULL;        /* old clip list for window */
    RegionPtr borderVisible = NullRegion;       /* visible area of the border */
    Bool shrunk = FALSE;        /* shrunk in an inner dimension */
    Bool moved = FALSE;         /* window position changed */
    WindowPtr pLayerWin;

    if (!(pParent = pWin->parent))
        return;
    pScreen = pWin->drawable.pScreen;
    newx = pParent->drawable.x + x + bw;
    newy = pParent->drawable.y + y + bw;
    if (WasViewable) {
        anyMarked = FALSE;
        oldRegion = RegionCreate(NullBox, 1); // save the visible region of the window
        RegionCopy(oldRegion, &pWin->winSize);
        // categorize child windows into regions to be moved
        for (g = 0; g <= StaticGravity; g++)
            gravitate[g] = (RegionPtr) NULL;
        // 按照子窗口的winGravity分类,统计每类子窗口的borderClip集合,也即可显示区域(缩放前)
        for (pChild = pWin->firstChild; pChild; pChild = pChild->nextSib) {
            g = pChild->winGravity;
            if (g != UnmapGravity) {
                if (!gravitate[g])
                    gravitate[g] = RegionCreate(NullBox, 1);
                RegionUnion(gravitate[g], gravitate[g], &pChild->borderClip);
            }
            else {
                UnmapWindow(pChild, TRUE);
                anyMarked = TRUE;
            }
        }
        // 标记窗口缩放前被pWin遮挡的窗口
        anyMarked |= (*pScreen->MarkOverlappedWindows) (pWin, pWin, &pLayerWin);
        oldWinClip = NULL;
        // bitGravity为ForgetGravity的窗口,每次缩放时需要重绘,因此无需缓冲其原可显示区域(clipList)
        if (pWin->bitGravity != ForgetGravity) {
            oldWinClip = RegionCreate(NullBox, 1);
            RegionCopy(oldWinClip, &pWin->clipList);
        }
        //* if the window is changing size, borderExposed can't be computed correctly without some help.
        if (pWin->drawable.height > h || pWin->drawable.width > w)
            shrunk = TRUE; // 窗口在某个方向缩小
        if (newx != oldx || newy != oldy)
            moved = TRUE; // 伴随窗口移动
        if ((pWin->drawable.height != h || pWin->drawable.width != w) && HasBorder(pWin)) {
            // 存在缩放,且窗口有边框,计算边框可见区域
            borderVisible = RegionCreate(NullBox, 1);
            /* for tiled borders, we punt and draw the whole thing */
            if (pWin->borderIsPixel || !moved) {
                if (shrunk || moved) // 缩小或者移动窗口,边框可能显示在原有的winSize内,因此需要去掉该区域
                    RegionSubtract(borderVisible, &pWin->borderClip, &pWin->winSize);
                else // 边框肯定不在winSize区域内
                    RegionCopy(borderVisible, &pWin->borderClip);
            }
        }
    }
    // 更新窗口的origin和drawable,设置为新的坐标和尺寸
    pWin->origin.x = x + bw;
    pWin->origin.y = y + bw;
    pWin->drawable.height = h;
    pWin->drawable.width = w;

    x = pWin->drawable.x = newx;
    y = pWin->drawable.y = newy;

    SetWinSize(pWin);
    SetBorderSize(pWin);
    // 更新子窗口的坐标和尺寸,若有窗口缩放,根据子窗口的winGravity计算新坐标
    dw = (int) w - (int) width;
    dh = (int) h - (int) height;
    ResizeChildrenWinSize(pWin, x - oldx, y - oldy, dw, dh);
    /* let the hardware adjust background and border pixmaps, if any */
    (*pScreen->PositionWindow) (pWin, x, y);
    // 根据窗口栈信息,重排pWin,并返回pWin缩放引起的第一个显示变化的窗口
    pFirstChange = MoveWindowInStack(pWin, pSib);
    if (WasViewable) {
        pRegion = RegionCreate(NullBox, 1);
        if (pLayerWin == pWin) // 标记窗口缩放后与pWin重叠的窗口
            anyMarked |= (*pScreen->MarkOverlappedWindows) (pWin, pFirstChange, NULL);
        else
            anyMarked |= (*pScreen->MarkOverlappedWindows) (pWin, pLayerWin, NULL);
        if (pWin->valdata) {
            pWin->valdata->before.resized = TRUE;
            pWin->valdata->before.borderVisible = borderVisible;
        }
        if (anyMarked)
            (*pScreen->ValidateTree) (pLayerWin->parent, pFirstChange, VTOther);
        // the entire window is trashed unless bitGravity recovers portions of it
        RegionCopy(&pWin->valdata->after.exposed, &pWin->clipList);
    }
    // 计算pWin窗口预留内容区域的新坐标(nx, ny)
    GravityTranslate(x, y, oldx, oldy, dw, dh, pWin->bitGravity, &nx, &ny);
    if (WasViewable) {
        if (HasBorder(pWin)) {
            int offx, offy, dx, dy;
            // kruft to avoid double translates for each gravity
            offx = 0;
            offy = 0;
            for (g = 0; g <= StaticGravity; g++) {
                if (!gravitate[g])
                    continue;
                // align winSize to gravitate[g].
                // winSize is in new coordinates, gravitate[g] is still in old coordinates
                // 相同gravity的子窗口大小集合不会超过winSize大小,因此winSize平移后
                // 计算根据子窗口的gravity得到的缩放后的坐标(nx,ny)
                GravityTranslate(x, y, oldx, oldy, dw, dh, g, &nx, &ny);
                dx = (oldx - nx) - offx;
                dy = (oldy - ny) - offy;
                if (dx || dy) {
                    // winSize为窗口缩放后的区域,包含了相同gravity的子窗口区域集
                    // 平移winSize得到窗口缩放后子窗口在原坐标的窗口区域
                    RegionTranslate(&pWin->winSize, dx, dy);
                    offx += dx;
                    offy += dy;
                }
                // 取交集,计算得到可预留的子窗口内容区域(pWin窗口的缩放可能会导致子窗口内容丢失)
                RegionIntersect(gravitate[g], gravitate[g], &pWin->winSize);
            }
            /* get winSize back where it belongs */
            if (offx || offy)
                RegionTranslate(&pWin->winSize, -offx, -offy);
        }
        // add screen bits to the appropriate bucket
        if (oldWinClip) {
            // clip to new clipList
            RegionCopy(pRegion, oldWinClip);
            // 平移,计算pWin预留窗口内容的新区域。此处好像有bug,当窗口有边框时修改了(nx,ny),与无边框时值不同
            RegionTranslate(pRegion, nx - oldx, ny - oldy);
            // 取窗口缩放后的clipList的交集,得到pWin已绘制的可见区域
            RegionIntersect(oldWinClip, pRegion, &pWin->clipList);
            // don't step on any gravity bits which will be copied after this region.
            // Note -- this assumes that the regions will be copied in gravity order.
            for (g = pWin->bitGravity + 1; g <= StaticGravity; g++) {
                if (gravitate[g]) // 去掉被原位置子窗口覆盖的区域,以减少计算量
                    RegionSubtract(oldWinClip, oldWinClip, gravitate[g]);
            }
            // 还原oldWinClip坐标
            RegionTranslate(oldWinClip, oldx - nx, oldy - ny);
            g = pWin->bitGravity;
            if (!gravitate[g])
                gravitate[g] = oldWinClip;
            else {
                RegionUnion(gravitate[g], gravitate[g], oldWinClip);
                RegionDestroy(oldWinClip);
            }
        }
        // move the bits on the screen
        destClip = NULL;
        for (g = 0; g <= StaticGravity; g++) {
            if (!gravitate[g])
                continue;
            GravityTranslate(x, y, oldx, oldy, dw, dh, g, &nx, &ny);
            oldpt.x = oldx + (x - nx);
            oldpt.y = oldy + (y - ny);
            // Note that gravitate[g] is *translated* by CopyWindow
            // only copy the remaining useful bits
            RegionIntersect(gravitate[g], gravitate[g], oldRegion);
            // clip to not overwrite already copied areas
            if (destClip) {
                // destClip为新坐标区域,需平移至旧坐标区域进行操作
                RegionTranslate(destClip, oldpt.x - x, oldpt.y - y);
                RegionSubtract(gravitate[g], gravitate[g], destClip);
                RegionTranslate(destClip, x - oldpt.x, y - oldpt.y);
            }
            // 窗口平移了,需较原窗口内容拷贝至新位置,不同winGravity的子窗口平移后相对位置有变化
            if (oldpt.x != x || oldpt.y != y || pWin->redirectDraw) {
                // 子窗口的预留内容拷贝至新区域,该函数还将gravitate[g]更新为新坐标区域
                (*pWin->drawable.pScreen->CopyWindow) (pWin, oldpt, gravitate[g]);
            }
            // remove any overwritten bits from the remaining useful bits
            RegionSubtract(oldRegion, oldRegion, gravitate[g]);
            // recompute exposed regions of child windows
            for (pChild = pWin->firstChild; pChild; pChild = pChild->nextSib) {
                if (pChild->winGravity != g)
                    continue;
                // 计算子窗口需要重绘的区域(exposed)
                RegionIntersect(pRegion, &pChild->borderClip, gravitate[g]);
                TraverseTree(pChild, miRecomputeExposures, (void *) pRegion);
            }
            // remove the successfully copied regions of the window from its exposed region
            if (g == pWin->bitGravity)
                // pWin的部分区域已包含在gravitate[g]中,因此可从重绘区域中去掉
                RegionSubtract(&pWin->valdata->after.exposed, &pWin->valdata->after.exposed, gravitate[g]);
            if (!destClip)
                destClip = gravitate[g];
            else {
                RegionUnion(destClip, destClip, gravitate[g]);
                RegionDestroy(gravitate[g]);
            }
        }
        RegionDestroy(oldRegion);
        RegionDestroy(pRegion);
        if (destClip)
            RegionDestroy(destClip);
        if (anyMarked) {
            (*pScreen->HandleExposures) (pLayerWin->parent);
            if (pScreen->PostValidateTree)
                (*pScreen->PostValidateTree) (pLayerWin->parent, pFirstChange, VTOther);
        }
    }
    if (pWin->realized)
        WindowsRestructured();
}

附录

ConfigureWindow

int ConfigureWindow(WindowPtr pWin, Mask mask, XID *vlist, ClientPtr client){
    WindowPtr pSib = NullWindow;
    WindowPtr pParent = pWin->parent;
    Window sibwid = 0;
    Mask index2, tmask;
    XID *pVlist;
    short x, y, beforeX, beforeY;
    unsigned short w = pWin->drawable.width,
        h = pWin->drawable.height, bw = pWin->borderWidth;
    int rc, action, smode = Above;
    // 输入窗口无边框
    if ((pWin->drawable.class == InputOnly) && (mask & CWBorderWidth))
        return BadMatch;
    // 指定了兄弟窗口,但未指定窗口栈掩码(调整窗口栈),改变窗口在窗口栈的顺序
    if ((mask & CWSibling) && !(mask & CWStackMode))
        return BadMatch;
    pVlist = vlist;
    // 计算窗口坐标位置(x,y),该坐标为父窗口的相对坐标
    if (pParent) {
        x = pWin->drawable.x - pParent->drawable.x - (int) bw;
        y = pWin->drawable.y - pParent->drawable.y - (int) bw;
    }
    else {
        x = pWin->drawable.x;
        y = pWin->drawable.y;
    }
    beforeX = x;
    beforeY = y;
    action = RESTACK_WIN;  // 默认为调整栈顺序
    if ((mask & (CWX | CWY)) && (!(mask & (CWHeight | CWWidth)))) {
        GET_INT16(CWX, x);
        GET_INT16(CWY, y);
        action = MOVE_WIN; // 窗口坐标有变化,窗口尺寸不变
    }
    else if (mask & (CWX | CWY | CWWidth | CWHeight)) {
        GET_INT16(CWX, x);
        GET_INT16(CWY, y);
        GET_CARD16(CWWidth, w);
        GET_CARD16(CWHeight, h);
        if (!w || !h) {
            client->errorValue = 0;
            return BadValue;
        }
        action = RESIZE_WIN; // 调整窗口大小,包括位置和尺寸
    }
    // 按照掩码获取请求参数,包括边框大小、兄弟窗口,窗口栈位置等
    tmask = mask & ~ChangeMask;
    while (tmask) {
        index2 = (Mask) lowbit(tmask);
        tmask &= ~index2;
        switch (index2) {
        case CWBorderWidth:
            GET_CARD16(CWBorderWidth, bw);
            break;
        case CWSibling:
            sibwid = (Window) *pVlist;
            pVlist++;
            rc = dixLookupWindow(&pSib, sibwid, client, DixGetAttrAccess);
            if (rc != Success) {
                client->errorValue = sibwid;
                return rc;
            }
            if (pSib->parent != pParent) // 必须为兄弟窗口
                return BadMatch;
            if (pSib == pWin)
                return BadMatch;
            break;
        case CWStackMode:
            GET_CARD8(CWStackMode, smode); // 窗口在窗口栈中的位置
            if ((smode != TopIf) && (smode != BottomIf) &&
                (smode != Opposite) && (smode != Above) && (smode != Below)) {
                client->errorValue = smode;
                return BadValue;
            }
            break;
        default:
            client->errorValue = mask;
            return BadValue;
        }
    }
    // root really can't be reconfigured, so just return
    if (!pParent)
        return Success;
    // Figure out if the window should be moved.  Doesn't make the changes to the window if event sent.
    if (mask & CWStackMode) // 计算pWin窗口在窗口栈的位置,窗口放置在返回的pSib窗口之上
        pSib = WhereDoIGoInTheStack(pWin, pSib, pParent->drawable.x + x, pParent->drawable.y + y,
                                    w + (bw << 1), h + (bw << 1), smode);
    else
        pSib = pWin->nextSib;
    // 重定向ConfigureWindow请求,如发送给窗口管理器处理等(发送ConfigureRequest事件)
    if ((!pWin->overrideRedirect) && (RedirectSend(pParent))) {
        xEvent event = {
            .u.configureRequest.window = pWin->drawable.id,
            .u.configureRequest.sibling = (mask & CWSibling) ? sibwid : None,
            .u.configureRequest.x = x,
            .u.configureRequest.y = y,
            .u.configureRequest.width = w,
            .u.configureRequest.height = h,
            .u.configureRequest.borderWidth = bw,
            .u.configureRequest.valueMask = mask,
            .u.configureRequest.parent = pParent->drawable.id
        };
        event.u.u.type = ConfigureRequest;
        event.u.u.detail = (mask & CWStackMode) ? smode : Above;
        if (MaybeDeliverEventsToClient(pParent, &event, 1, SubstructureRedirectMask, client) == 1)
            return Success;
    }
    if (action == RESIZE_WIN) {
        Bool size_change = (w != pWin->drawable.width) || (h != pWin->drawable.height);
        if (size_change && ((pWin->eventMask | wOtherEventMasks(pWin)) & ResizeRedirectMask)) {
            // 重定向Resize Window,发送ResizeRequest给感兴趣的client
            xEvent eventT = {
                .u.resizeRequest.window = pWin->drawable.id,
                .u.resizeRequest.width = w,
                .u.resizeRequest.height = h
            };
            eventT.u.u.type = ResizeRequest;
            if (MaybeDeliverEventsToClient(pWin, &eventT, 1, ResizeRedirectMask, client) == 1) {
                /* if event is delivered, leave the actual size alone. */
                w = pWin->drawable.width;
                h = pWin->drawable.height;
                size_change = FALSE;
            }
        }
        if (!size_change) {
            if (mask & (CWX | CWY))
                action = MOVE_WIN;
            else if (mask & (CWStackMode | CWBorderWidth))
                action = RESTACK_WIN;
            else                /* really nothing to do */
                return (Success);
        }
    }

    if (action == RESIZE_WIN)
        goto ActuallyDoSomething;
    if ((mask & CWX) && (x != beforeX))
        goto ActuallyDoSomething;
    if ((mask & CWY) && (y != beforeY))
        goto ActuallyDoSomething;
    if ((mask & CWBorderWidth) && (bw != wBorderWidth(pWin)))
        goto ActuallyDoSomething;
    if (mask & CWStackMode) {
        if (pWin->nextSib != pSib) // 相同,则窗口栈顺序不变
            goto ActuallyDoSomething;
    }
    return Success;

 ActuallyDoSomething:
    if (pWin->drawable.pScreen->ConfigNotify) {
        // 通过回调ConfigNotify函数告知扩展协议窗口配置变化
        int ret = (*pWin->drawable.pScreen->ConfigNotify) (pWin, x, y, w, h, bw, pSib);
        if (ret) {
            client->errorValue = 0;
            return ret;
        }
    }
    if (SubStrSend(pWin, pParent)) {
        // 发送ConfigureNotify事件,通知感兴趣的client窗口配置变化
        xEvent event = {
            .u.configureNotify.window = pWin->drawable.id,
            .u.configureNotify.aboveSibling = pSib ? pSib->drawable.id : None,
            .u.configureNotify.x = x,
            .u.configureNotify.y = y,
            .u.configureNotify.width = w,
            .u.configureNotify.height = h,
            .u.configureNotify.borderWidth = bw,
            .u.configureNotify.override = pWin->overrideRedirect
        };
        event.u.u.type = ConfigureNotify;
        DeliverEvents(pWin, &event, 1, NullWindow);
    }
    if (mask & CWBorderWidth) {
        if (action == RESTACK_WIN) {
            action = MOVE_WIN;
            pWin->borderWidth = bw;
        }
        else if ((action == MOVE_WIN) &&
                 (beforeX + wBorderWidth(pWin) == x + (int) bw) &&
                 (beforeY + wBorderWidth(pWin) == y + (int) bw)) {
            // 窗口边框大小引起的窗口坐标变化,即窗口可绘制区域未变化,且位置不变
            action = REBORDER_WIN;
            (*pWin->drawable.pScreen->ChangeBorderWidth) (pWin, bw);
        }
        else
            pWin->borderWidth = bw; 
    }
    if (action == MOVE_WIN)
        (*pWin->drawable.pScreen->MoveWindow) (pWin, x, y, pSib,
                                               (mask & CWBorderWidth) ? VTOther : VTMove);
    else if (action == RESIZE_WIN)
        (*pWin->drawable.pScreen->ResizeWindow) (pWin, x, y, w, h, pSib);
    else if (mask & CWStackMode)
        ReflectStackChange(pWin, pSib, VTOther);
    if (action != RESTACK_WIN)
        CheckCursorConfinement(pWin);
    return Success;
}

miComputeClips

static void miComputeClips(WindowPtr pParent, ScreenPtr pScreen, RegionPtr universe, VTKind kind, RegionPtr exposed) {
    int dx, dy;
    RegionRec childUniverse;
    WindowPtr pChild;
    int oldVis, newVis;
    BoxRec borderSize;
    RegionRec childUnion;
    Bool overlap;
    RegionPtr borderVisible;

    // Figure out the new visibility of this window.
    // The extent of the universe should be the same as the extent of the borderSize region. 
    // If the window is unobscured, this rectangle  will be completely inside the universe
    //  (the universe will cover it completely). 
    // If the window is completely obscured, none of the universe will cover the rectangle.
    borderSize.x1 = pParent->drawable.x - wBorderWidth(pParent);
    borderSize.y1 = pParent->drawable.y - wBorderWidth(pParent);
    // 坐标要比32K小(16bits)
    dx = (int) pParent->drawable.x + (int) pParent->drawable.width + wBorderWidth(pParent);
    if (dx > 32767) dx = 32767;
    borderSize.x2 = dx;
    dy = (int) pParent->drawable.y + (int) pParent->drawable.height + wBorderWidth(pParent);
    if (dy > 32767) dy = 32767;
    borderSize.y2 = dy;

    // In redirected drawing case, reset universe to borderSize
    if (pParent->redirectDraw != RedirectDrawNone) {
        if (TreatAsTransparent(pParent))
            RegionEmpty(universe);
        compSetRedirectBorderClip (pParent, universe);
        RegionCopy(universe, &pParent->borderSize);
    }
    oldVis = pParent->visibility;
    // 计算窗口的可见性,全部可见,部分可见,全不可见。borderSize被universe全包含,则全部可见
    switch (RegionContainsRect(universe, &borderSize)) {
    case rgnIN:
        newVis = VisibilityUnobscured;
        break;
    case rgnPART:
        newVis = VisibilityPartiallyObscured;
        {
            RegionPtr pBounding;
            // shape扩展根据指定的shape边界调整窗口可见性
            if ((pBounding = wBoundingShape(pParent))) {
                switch (miShapedWindowIn(universe, pBounding, &borderSize,  pParent->drawable.x, pParent->drawable.y)) {
                case rgnIN:
                    newVis = VisibilityUnobscured;
                    break;
                case rgnOUT:
                    newVis = VisibilityFullyObscured;
                    break;
                }
            }
        }
        break;
    default:
        newVis = VisibilityFullyObscured;
        break;
    }
    pParent->visibility = newVis;
    // 可见性有改变,且client关心该窗口可见性变化,则发送VisibilityNotify事件
    if (oldVis != newVis && ((pParent->eventMask | wOtherEventMasks(pParent)) & VisibilityChangeMask))
        SendVisibilityNotify(pParent);
    // 窗口在计算重叠窗口后,可能会有所调整。dx,dy表示起始坐标调整幅度
    dx = pParent->drawable.x - pParent->valdata->before.oldAbsCorner.x;
    dy = pParent->drawable.y - pParent->valdata->before.oldAbsCorner.y;

    // avoid computations when dealing with simple operations
    switch (kind) {
    case VTMap:
    case VTStack:
    case VTUnmap:
        break;
    case VTMove:
        if ((oldVis == newVis) && ((oldVis == VisibilityFullyObscured) || (oldVis == VisibilityUnobscured))) {
            pChild = pParent;
           // 深度优先,处理pParent所有子窗口,子窗口需随父窗口移动,无需重绘内容(除了边框)
            while (1) {
                if (pChild->viewable) {
                    if (pChild->visibility != VisibilityFullyObscured) {
                        // 窗口移动,更新borderClip和clipList区域坐标
                        RegionTranslate(&pChild->borderClip, dx, dy);
                        RegionTranslate(&pChild->clipList, dx, dy);
                        pChild->drawable.serialNumber = NEXT_SERIAL_NUMBER;
                        // ClipNotify函数回调:compClipNotify -> xf86XVClipNotify
                        if (pScreen->ClipNotify)
                            (*pScreen->ClipNotify) (pChild, dx, dy);
                    }
                    if (pChild->valdata) {
                        RegionNull(&pChild->valdata->after.borderExposed);
                        if (HasParentRelativeBorder(pChild)) {
                            // 有边框,则记录边框暴露区域(borderClip - winSize),4个边框区域
                            RegionSubtract(&pChild->valdata->after.borderExposed, &pChild->borderClip, &pChild->winSize);
                        }
                        // 因可见性没变,不需要重绘
                        RegionNull(&pChild->valdata->after.exposed);
                    }
                    if (pChild->firstChild) {
                        pChild = pChild->firstChild;
                        continue;
                    }
                }
                while (!pChild->nextSib && (pChild != pParent))
                    pChild = pChild->parent;
                if (pChild == pParent)
                    break;
                pChild = pChild->nextSib;
            }
            return;
        }
        /* fall through */
    default:
        // To calculate exposures correctly, we have to translate the old borderClip and clipList regions 
        // to the window's new location so there is a correspondence between pieces of the new and old clipping regions.
        if (dx || dy) {
            // We translate the old clipList because that will be exposed or copied if gravity is right.
            RegionTranslate(&pParent->borderClip, dx, dy);
            RegionTranslate(&pParent->clipList, dx, dy);
        }
        break;
    case VTBroken:
        RegionEmpty(&pParent->borderClip);
        RegionEmpty(&pParent->clipList);
        break;
    }

    borderVisible = pParent->valdata->before.borderVisible;
    RegionNull(&pParent->valdata->after.borderExposed);
    RegionNull(&pParent->valdata->after.exposed);

    // Since the borderClip must not be clipped by the children, we do the border exposure first...
    // 'universe' is the window's borderClip. 
    // To figure the exposures, remove the area that used to be exposed from the new.
    // This leaves a region of pieces that weren't exposed before.
    if (HasBorder(pParent)) {
        if (borderVisible) {
            // when the border changes shape, the old visible portions of the border will be saved by DIX in borderVisible --
            // use that region and destroy it
	 // 包含边框的区域已显示,去掉该区域
            RegionSubtract(exposed, universe, borderVisible);
            RegionDestroy(borderVisible);
        }
        else {
            // 暴露区域为去掉已绘制的包含边框的区域
            RegionSubtract(exposed, universe, &pParent->borderClip);
        }
        if (HasParentRelativeBorder(pParent) && (dx || dy))
            // 边框需要重绘,(窗口移动且backgroundState == ParentRelative)
            RegionSubtract(&pParent->valdata->after.borderExposed, universe, &pParent->winSize);
        else
            // 在可暴露区域范围内重绘边框,去掉可绘制窗口区域
            RegionSubtract(&pParent->valdata->after.borderExposed, exposed, &pParent->winSize);
        // 缓冲包含边框的可绘制区域
        RegionCopy(&pParent->borderClip, universe);

        // To get the right clipList for the parent, and to make doubly sure that no child overlaps the parent's border, 
        // we remove the parent's border from the universe before proceeding.
        // 可绘制窗口区域
        RegionIntersect(universe, universe, &pParent->winSize);
    }
    else
        RegionCopy(&pParent->borderClip, universe);

    if ((pChild = pParent->firstChild) && pParent->mapped) {
        // 获取子窗口覆盖的区域childUnion,包括边框
        RegionNull(&childUniverse);
        RegionNull(&childUnion);
        if ((pChild->drawable.y < pParent->lastChild->drawable.y) ||
            ((pChild->drawable.y == pParent->lastChild->drawable.y) &&
             (pChild->drawable.x < pParent->lastChild->drawable.x))) {
            for (; pChild; pChild = pChild->nextSib) {
                if (pChild->viewable && !TreatAsTransparent(pChild))
                    RegionAppend(&childUnion, &pChild->borderSize);
            }
        }
        else {
            for (pChild = pParent->lastChild; pChild; pChild = pChild->prevSib) {
                if (pChild->viewable && !TreatAsTransparent(pChild))
                    RegionAppend(&childUnion, &pChild->borderSize);
            }
        }
        RegionValidate(&childUnion, &overlap);
        // 按照深度优先递归处理子窗口的可剪接区域
        for (pChild = pParent->firstChild; pChild; pChild = pChild->nextSib) {
            if (pChild->viewable) {
                // If the child is viewable, we want to remove its extents from the current universe,
                // but we only re-clip it if it's been marked.
                if (pChild->valdata) {
                    // Figure out the new universe from the child's perspective and recurse.
                    // 子窗口可绘制区域。所有子窗口可绘制区域universe与窗口大小的交集
                    RegionIntersect(&childUniverse, universe, &pChild->borderSize);
                    miComputeClips(pChild, pScreen, &childUniverse, kind, exposed);
                }
                // Once the child has been processed, 
                // we remove its extents from the current universe, thus denying its space to any other sibling.
                if (overlap && !TreatAsTransparent(pChild))
                    // 有子窗口间有重叠,减去pChild窗口大小
               // 窗口树堆叠顺序,确保后处理的子窗口被前处理的子窗口覆盖,因此可视区域可直接减去窗口区域
                    RegionSubtract(universe, universe, &pChild->borderSize);
            }
        }
        // 子窗口无重叠,减去所有子窗口的区域,即为父窗口可见区域。universe为最终父窗口可见区域
        if (!overlap)
            RegionSubtract(universe, universe, &childUnion);
        RegionUninit(&childUnion);
        RegionUninit(&childUniverse);
    }                           /* if any children */

    // 'universe' now contains the new clipList for the parent window.
    // To figure the exposure of the window we subtract the old clip from the new, just as for the border.
     if (oldVis == VisibilityFullyObscured || oldVis == VisibilityNotViewable) {
        // 之前被遮挡,现在被暴露,需重绘
        RegionCopy(&pParent->valdata->after.exposed, universe);
    }
    else if (newVis != VisibilityFullyObscured && newVis != VisibilityNotViewable) {
        // 之前有显示区域(clipList),现在只需重绘未显示的可见区域
        RegionSubtract(&pParent->valdata->after.exposed, universe, &pParent->clipList);
    }

    /* HACK ALERT - copying contents of regions, instead of regions */
    {
        RegionRec tmp;
        tmp = pParent->clipList;
        // 更新父窗口的可见区域,这些区域已或者将被重绘
        pParent->clipList = *universe;
        // 更改为原有的可剪接区域(clipList)
        *universe = tmp;
    }

    pParent->drawable.serialNumber = NEXT_SERIAL_NUMBER;
    // 调用ClipNotify回调,坐标偏移为(dx, dy)
    if (pScreen->ClipNotify)
        (*pScreen->ClipNotify) (pParent, dx, dy);
}

WhereDoIGoInTheStack

根据pWin,pSib,smode以及pWin的原始坐标,计算pWin重排位置,返回pWin窗口之下的第一个兄弟窗口

smode

pSib

NULL

Aboe

pWin重排在pSib之上

pWin重排在栈的顶层

Below

pWin重排在pSib之下

pWin重排在栈的底层

TopIf

若pSib遮挡了pWin,则pWin重排在栈顶层

若有兄弟窗口遮挡pWin,则pWin重排在栈顶层

BottomIf

若pWin遮挡了pSib,则pWin重排在栈底层

若pWin遮挡任意兄弟窗口,则pWin重排在栈底层

Opposite

若pSib遮挡了pWin,则pWin重排在栈顶层

若pWin遮挡了pSib,则pWin重排在栈底层

若有兄弟窗口遮挡pWin,则pWin重排在栈顶层

若pWin遮挡任意兄弟窗口,则pWin重排在栈底层

// 返回新栈中pWin窗口下的第一个兄弟窗口
// 返回NULL,表示pWin重排在栈低
// 返回pWin->nextSib,表示栈不变
static WindowPtr WhereDoIGoInTheStack(WindowPtr pWin, WindowPtr pSib,
                     short x, short y, unsigned short w, unsigned short h, int smode){
    BoxRec box;
    WindowPtr pHead, pFirst;

    if ((pWin == pWin->parent->firstChild) && (pWin == pWin->parent->lastChild))
        return NULL;
    pHead = RealChildHead(pWin->parent);
    pFirst = pHead ? pHead->nextSib : pWin->parent->firstChild;
    box.x1 = x;
    box.y1 = y;
    box.x2 = x + (int) w;
    box.y2 = y + (int) h;
    switch (smode) {
    case Above:
        if (pSib)
            return pSib;
        else if (pWin == pFirst)
            return pWin->nextSib;
        else
            return pFirst;
    case Below:
        if (pSib)
            if (pSib->nextSib != pWin)
                return pSib->nextSib;
            else
                return pWin->nextSib;
        else
            return NullWindow;
    case TopIf:
        if ((!pWin->mapped || (pSib && !pSib->mapped)))
            return pWin->nextSib;
        else if (pSib) {
            if ((IsSiblingAboveMe(pWin, pSib) == Above) &&
                (RegionContainsRect(&pSib->borderSize, &box) != rgnOUT))
                return pFirst;
            else
                return pWin->nextSib;
        }
        else if (AnyWindowOverlapsMe(pWin, pHead, &box))
            return pFirst;
        else
            return pWin->nextSib;
    case BottomIf:
        if ((!pWin->mapped || (pSib && !pSib->mapped)))
            return pWin->nextSib;
        else if (pSib) {
            if ((IsSiblingAboveMe(pWin, pSib) == Below) &&
                (RegionContainsRect(&pSib->borderSize, &box) != rgnOUT))
                return NullWindow;
            else
                return pWin->nextSib;
        }
        else if (IOverlapAnyWindow(pWin, &box))
            return NullWindow;
        else
            return pWin->nextSib;
    case Opposite:
        if ((!pWin->mapped || (pSib && !pSib->mapped)))
            return pWin->nextSib;
        else if (pSib) {
            if (RegionContainsRect(&pSib->borderSize, &box) != rgnOUT) {
                if (IsSiblingAboveMe(pWin, pSib) == Above)
                    return pFirst;
                else
                    return NullWindow;
            }
            else
                return pWin->nextSib;
        }
        else if (AnyWindowOverlapsMe(pWin, pHead, &box)) {
            /* If I'm occluded, I can't possibly be the first child
             * if (pWin == pWin->parent->firstChild)
             *    return pWin->nextSib;
             */
            return pFirst;
        }
        else if (IOverlapAnyWindow(pWin, &box))
            return NullWindow;
        else
            return pWin->nextSib;
    default:
        return pWin->nextSib; // should never happen; make something up.
    }
}

0 评论

你还没有登录。你所做的任何更改会将作者标记为匿名用户。 如果你已经拥有帐户,请登录